mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	using ES6 modules for whole frontend SPA app
This commit is contained in:
		
							
								
								
									
										35
									
								
								src/public/javascripts/bootstrap.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								src/public/javascripts/bootstrap.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,38 @@ | ||||
| import searchTree from './search_tree.js'; | ||||
| import addLink from './dialogs/add_link.js'; | ||||
| import editTreePrefix from './dialogs/edit_tree_prefix.js'; | ||||
| import eventLog from './dialogs/event_log.js'; | ||||
| import jumpToNote from './dialogs/jump_to_note.js'; | ||||
| import labelsDialog from './dialogs/labels.js'; | ||||
| import noteHistory from './dialogs/note_history.js'; | ||||
| import noteSource from './dialogs/note_source.js'; | ||||
| import recentChanges from './dialogs/recent_changes.js'; | ||||
| import recentNotes from './dialogs/recent_notes.js'; | ||||
| import settings from './dialogs/settings.js'; | ||||
| import sqlConsole from './dialogs/sql_console.js'; | ||||
|  | ||||
| import cloning from './cloning.js'; | ||||
| import contextMenu from './context_menu.js'; | ||||
| import dragAndDropSetup from './drag_and_drop.js'; | ||||
| import exportService from './export.js'; | ||||
| import link from './link.js'; | ||||
| import messaging from './messaging.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import noteType from './note_type.js'; | ||||
| import protected_session from './protected_session.js'; | ||||
| import ScriptApi from './script_api.js'; | ||||
| import ScriptContext from './script_context.js'; | ||||
| import sync from './sync.js'; | ||||
| import treeChanges from './tree_changes.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| import searchTreeService from './search_tree.js'; | ||||
| import './init.js'; | ||||
| import treeService from './note_tree.js'; | ||||
| const $toggleSearchButton = $("#toggle-search-button"); | ||||
|  | ||||
| $toggleSearchButton.click(searchTree.toggleSearch); | ||||
| bindShortcut('ctrl+s', searchTree.toggleSearch); | ||||
| $toggleSearchButton.click(searchTreeService.toggleSearch); | ||||
| bindShortcut('ctrl+s', searchTreeService.toggleSearch); | ||||
|  | ||||
| function bindShortcut(keyboardShortcut, handler) { | ||||
|     $(document).bind('keydown', keyboardShortcut, e => { | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const cloning = (function() { | ||||
|     async function cloneNoteTo(childNoteId, parentNoteId, prefix) { | ||||
| import treeService from './note_tree.js'; | ||||
|  | ||||
| async function cloneNoteTo(childNoteId, parentNoteId, prefix) { | ||||
|     const resp = await server.put('notes/' + childNoteId + '/clone-to/' + parentNoteId, { | ||||
|         prefix: prefix | ||||
|     }); | ||||
| @@ -12,10 +13,10 @@ const cloning = (function() { | ||||
|     } | ||||
|  | ||||
|     await treeService.reload(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     // beware that first arg is noteId and second is branchId! | ||||
|     async function cloneNoteAfter(noteId, afterBranchId) { | ||||
| // beware that first arg is noteId and second is branchId! | ||||
| async function cloneNoteAfter(noteId, afterBranchId) { | ||||
|     const resp = await server.put('notes/' + noteId + '/clone-after/' + afterBranchId); | ||||
|  | ||||
|     if (!resp.success) { | ||||
| @@ -24,10 +25,9 @@ const cloning = (function() { | ||||
|     } | ||||
|  | ||||
|     await treeService.reload(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     cloneNoteAfter, | ||||
|     cloneNoteTo | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,12 +1,20 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const contextMenu = (function() { | ||||
|     const $tree = $("#tree"); | ||||
| import treeService from './note_tree.js'; | ||||
| import cloning from './cloning.js'; | ||||
| import exportService from './export.js'; | ||||
| import messaging from './messaging.js'; | ||||
| import protected_session from './protected_session.js'; | ||||
| import treeChanges from './tree_changes.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
|     let clipboardIds = []; | ||||
|     let clipboardMode = null; | ||||
| const $tree = $("#tree"); | ||||
|  | ||||
|     async function pasteAfter(node) { | ||||
| let clipboardIds = []; | ||||
| let clipboardMode = null; | ||||
|  | ||||
| async function pasteAfter(node) { | ||||
|     if (clipboardMode === 'cut') { | ||||
|         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||
|  | ||||
| @@ -28,9 +36,9 @@ const contextMenu = (function() { | ||||
|     else { | ||||
|         utils.throwError("Unrecognized clipboard mode=" + clipboardMode); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function pasteInto(node) { | ||||
| async function pasteInto(node) { | ||||
|     if (clipboardMode === 'cut') { | ||||
|         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||
|  | ||||
| @@ -51,23 +59,23 @@ const contextMenu = (function() { | ||||
|     else { | ||||
|         utils.throwError("Unrecognized clipboard mode=" + mode); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function copy(nodes) { | ||||
| function copy(nodes) { | ||||
|     clipboardIds = nodes.map(node => node.data.noteId); | ||||
|     clipboardMode = 'copy'; | ||||
|  | ||||
|     utils.showMessage("Note(s) have been copied into clipboard."); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function cut(nodes) { | ||||
| function cut(nodes) { | ||||
|     clipboardIds = nodes.map(node => node.key); | ||||
|     clipboardMode = 'cut'; | ||||
|  | ||||
|     utils.showMessage("Note(s) have been cut into clipboard."); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     const contextMenuSettings = { | ||||
| const contextMenuSettings = { | ||||
|     delegate: "span.fancytree-title", | ||||
|     autoFocus: true, | ||||
|     menu: [ | ||||
| @@ -169,13 +177,12 @@ const contextMenu = (function() { | ||||
|             messaging.logError("Unknown command: " + ui.cmd); | ||||
|         } | ||||
|     } | ||||
|     }; | ||||
| }; | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     pasteAfter, | ||||
|     pasteInto, | ||||
|     cut, | ||||
|     copy, | ||||
|     contextMenuSettings | ||||
|     } | ||||
| })(); | ||||
| }; | ||||
| @@ -1,25 +1,30 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const addLink = (function() { | ||||
|     const $dialog = $("#add-link-dialog"); | ||||
|     const $form = $("#add-link-form"); | ||||
|     const $autoComplete = $("#note-autocomplete"); | ||||
|     const $linkTitle = $("#link-title"); | ||||
|     const $clonePrefix = $("#clone-prefix"); | ||||
|     const $linkTitleFormGroup = $("#add-link-title-form-group"); | ||||
|     const $prefixFormGroup = $("#add-link-prefix-form-group"); | ||||
|     const $linkTypes = $("input[name='add-link-type']"); | ||||
|     const $linkTypeHtml = $linkTypes.filter('input[value="html"]'); | ||||
| import treeService from '../note_tree.js'; | ||||
| import cloning from '../cloning.js'; | ||||
| import link from '../link.js'; | ||||
| import noteEditor from '../note_editor.js'; | ||||
| import treeUtils from '../tree_utils.js'; | ||||
|  | ||||
|     function setLinkType(linkType) { | ||||
| const $dialog = $("#add-link-dialog"); | ||||
| const $form = $("#add-link-form"); | ||||
| const $autoComplete = $("#note-autocomplete"); | ||||
| const $linkTitle = $("#link-title"); | ||||
| const $clonePrefix = $("#clone-prefix"); | ||||
| const $linkTitleFormGroup = $("#add-link-title-form-group"); | ||||
| const $prefixFormGroup = $("#add-link-prefix-form-group"); | ||||
| const $linkTypes = $("input[name='add-link-type']"); | ||||
| const $linkTypeHtml = $linkTypes.filter('input[value="html"]'); | ||||
|  | ||||
| function setLinkType(linkType) { | ||||
|     $linkTypes.each(function () { | ||||
|         $(this).prop('checked', $(this).val() === linkType); | ||||
|     }); | ||||
|  | ||||
|     linkTypeChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     if (noteEditor.getCurrentNoteType() === 'text') { | ||||
| @@ -73,9 +78,9 @@ const addLink = (function() { | ||||
|             setDefaultLinkTitle(noteId); | ||||
|         } | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $form.submit(() => { | ||||
| $form.submit(() => { | ||||
|     const value = $autoComplete.val(); | ||||
|  | ||||
|     const notePath = link.getNodePathFromLabel(value); | ||||
| @@ -108,9 +113,9 @@ const addLink = (function() { | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     function linkTypeChanged() { | ||||
| function linkTypeChanged() { | ||||
|     const value = $linkTypes.filter(":checked").val(); | ||||
|  | ||||
|     if (value === 'html') { | ||||
| @@ -121,17 +126,16 @@ const addLink = (function() { | ||||
|         $linkTitleFormGroup.hide(); | ||||
|         $prefixFormGroup.show(); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $linkTypes.change(linkTypeChanged); | ||||
| $linkTypes.change(linkTypeChanged); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+l', e => { | ||||
| $(document).bind('keydown', 'ctrl+l', e => { | ||||
|     showDialog(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,14 +1,15 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const editTreePrefix = (function() { | ||||
|     const $dialog = $("#edit-tree-prefix-dialog"); | ||||
|     const $form = $("#edit-tree-prefix-form"); | ||||
|     const $treePrefixInput = $("#tree-prefix-input"); | ||||
|     const $noteTitle = $('#tree-prefix-note-title'); | ||||
| import treeService from '../note_tree.js'; | ||||
|  | ||||
|     let branchId; | ||||
| const $dialog = $("#edit-tree-prefix-dialog"); | ||||
| const $form = $("#edit-tree-prefix-form"); | ||||
| const $treePrefixInput = $("#tree-prefix-input"); | ||||
| const $noteTitle = $('#tree-prefix-note-title'); | ||||
|  | ||||
|     async function showDialog() { | ||||
| let branchId; | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     await $dialog.dialog({ | ||||
| @@ -26,9 +27,9 @@ const editTreePrefix = (function() { | ||||
|     const noteTitle = treeService.getNoteTitle(currentNode.data.noteId); | ||||
|  | ||||
|     $noteTitle.html(noteTitle); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $form.submit(() => { | ||||
| $form.submit(() => { | ||||
|     const prefix = $treePrefixInput.val(); | ||||
|  | ||||
|     server.put('tree/' + branchId + '/set-prefix', { | ||||
| @@ -38,9 +39,8 @@ const editTreePrefix = (function() { | ||||
|     $dialog.dialog("close"); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,10 +1,12 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const eventLog = (function() { | ||||
|     const $dialog = $("#event-log-dialog"); | ||||
|     const $list = $("#event-log-list"); | ||||
| import link from '../link.js'; | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     async function showDialog() { | ||||
| const $dialog = $("#event-log-dialog"); | ||||
| const $list = $("#event-log-list"); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -30,9 +32,8 @@ const eventLog = (function() { | ||||
|  | ||||
|         $list.append(eventEl); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
|   | ||||
| @@ -1,12 +1,15 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const jumpToNote = (function() { | ||||
|     const $showDialogButton = $("#jump-to-note-button"); | ||||
|     const $dialog = $("#jump-to-note-dialog"); | ||||
|     const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
|     const $form = $("#jump-to-note-form"); | ||||
| import treeService from '../note_tree.js'; | ||||
| import link from '../link.js'; | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     async function showDialog() { | ||||
| const $showDialogButton = $("#jump-to-note-button"); | ||||
| const $dialog = $("#jump-to-note-dialog"); | ||||
| const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
| const $form = $("#jump-to-note-form"); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $autoComplete.val(''); | ||||
| @@ -20,14 +23,14 @@ const jumpToNote = (function() { | ||||
|         source: await utils.stopWatch("building autocomplete", treeService.getAutocompleteItems), | ||||
|         minLength: 0 | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getSelectedNotePath() { | ||||
| function getSelectedNotePath() { | ||||
|     const val = $autoComplete.val(); | ||||
|     return link.getNodePathFromLabel(val); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function goToNote() { | ||||
| function goToNote() { | ||||
|     const notePath = getSelectedNotePath(); | ||||
|  | ||||
|     if (notePath) { | ||||
| @@ -35,25 +38,24 @@ const jumpToNote = (function() { | ||||
|  | ||||
|         $dialog.dialog('close'); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+j', e => { | ||||
| $(document).bind('keydown', 'ctrl+j', e => { | ||||
|     showDialog(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $form.submit(() => { | ||||
| $form.submit(() => { | ||||
|     const action = $dialog.find("button:focus").val(); | ||||
|  | ||||
|     goToNote(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,15 +1,17 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const labelsDialog = (function() { | ||||
|     const $showDialogButton = $(".show-labels-button"); | ||||
|     const $dialog = $("#labels-dialog"); | ||||
|     const $saveLabelsButton = $("#save-labels-button"); | ||||
|     const $labelsBody = $('#labels-table tbody'); | ||||
| import noteEditor from '../note_editor.js'; | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     const labelsModel = new LabelsModel(); | ||||
|     let labelNames = []; | ||||
| const $showDialogButton = $(".show-labels-button"); | ||||
| const $dialog = $("#labels-dialog"); | ||||
| const $saveLabelsButton = $("#save-labels-button"); | ||||
| const $labelsBody = $('#labels-table tbody'); | ||||
|  | ||||
|     function LabelsModel() { | ||||
| const labelsModel = new LabelsModel(); | ||||
| let labelNames = []; | ||||
|  | ||||
| function LabelsModel() { | ||||
|     const self = this; | ||||
|  | ||||
|     this.labels = ko.observableArray(); | ||||
| @@ -149,9 +151,9 @@ const labelsDialog = (function() { | ||||
|  | ||||
|         return self.labels()[index]; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     await labelsModel.loadLabels(); | ||||
| @@ -161,17 +163,17 @@ const labelsDialog = (function() { | ||||
|         width: 800, | ||||
|         height: 500 | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+a', e => { | ||||
| $(document).bind('keydown', 'alt+a', e => { | ||||
|     showDialog(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     ko.applyBindings(labelsModel, document.getElementById('labels-dialog')); | ||||
| ko.applyBindings(labelsModel, document.getElementById('labels-dialog')); | ||||
|  | ||||
|     $(document).on('focus', '.label-name', function (e) { | ||||
| $(document).on('focus', '.label-name', function (e) { | ||||
|     if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|         $(this).autocomplete({ | ||||
|             // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||
| @@ -187,9 +189,9 @@ const labelsDialog = (function() { | ||||
|     } | ||||
|  | ||||
|     $(this).autocomplete("search", $(this).val()); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).on('focus', '.label-value', async function (e) { | ||||
| $(document).on('focus', '.label-value', async function (e) { | ||||
|     if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|         const labelName = $(this).parent().parent().find('.label-name').val(); | ||||
|  | ||||
| @@ -217,11 +219,10 @@ const labelsDialog = (function() { | ||||
|     } | ||||
|  | ||||
|     $(this).autocomplete("search", $(this).val()); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,19 +1,21 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const noteHistory = (function() { | ||||
|     const $showDialogButton = $("#show-history-button"); | ||||
|     const $dialog = $("#note-history-dialog"); | ||||
|     const $list = $("#note-history-list"); | ||||
|     const $content = $("#note-history-content"); | ||||
|     const $title = $("#note-history-title"); | ||||
| import noteEditor from '../note_editor.js'; | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     let historyItems = []; | ||||
| const $showDialogButton = $("#show-history-button"); | ||||
| const $dialog = $("#note-history-dialog"); | ||||
| const $list = $("#note-history-list"); | ||||
| const $content = $("#note-history-content"); | ||||
| const $title = $("#note-history-title"); | ||||
|  | ||||
|     async function showCurrentNoteHistory() { | ||||
| let historyItems = []; | ||||
|  | ||||
| async function showCurrentNoteHistory() { | ||||
|     await showNoteHistoryDialog(noteEditor.getCurrentNoteId()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function showNoteHistoryDialog(noteId, noteRevisionId) { | ||||
| async function showNoteHistoryDialog(noteId, noteRevisionId) { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -46,24 +48,24 @@ const noteHistory = (function() { | ||||
|     else { | ||||
|         $title.text("No history for this note yet..."); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+h', e => { | ||||
| $(document).bind('keydown', 'alt+h', e => { | ||||
|     showCurrentNoteHistory(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $list.on('change', () => { | ||||
| $list.on('change', () => { | ||||
|     const optVal = $list.find(":selected").val(); | ||||
|  | ||||
|     const historyItem = historyItems.find(r => r.noteRevisionId === optVal); | ||||
|  | ||||
|     $title.html(historyItem.title); | ||||
|     $content.html(historyItem.content); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).on('click', "a[action='note-history']", event => { | ||||
| $(document).on('click', "a[action='note-history']", event => { | ||||
|     const linkEl = $(event.target); | ||||
|     const noteId = linkEl.attr('note-path'); | ||||
|     const noteRevisionId = linkEl.attr('note-history-id'); | ||||
| @@ -71,11 +73,10 @@ const noteHistory = (function() { | ||||
|     showNoteHistoryDialog(noteId, noteRevisionId); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $showDialogButton.click(showCurrentNoteHistory); | ||||
| $showDialogButton.click(showCurrentNoteHistory); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showCurrentNoteHistory | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,11 +1,12 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const noteSource = (function() { | ||||
|     const $showDialogButton = $("#show-source-button"); | ||||
|     const $dialog = $("#note-source-dialog"); | ||||
|     const $noteSource = $("#note-source"); | ||||
| import noteEditor from '../note_editor.js'; | ||||
|  | ||||
|     function showDialog() { | ||||
| const $showDialogButton = $("#show-source-button"); | ||||
| const $dialog = $("#note-source-dialog"); | ||||
| const $noteSource = $("#note-source"); | ||||
|  | ||||
| function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -17,16 +18,16 @@ const noteSource = (function() { | ||||
|     const noteText = noteEditor.getCurrentNote().detail.content; | ||||
|  | ||||
|     $noteSource.text(formatHtml(noteText)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatHtml(str) { | ||||
| function formatHtml(str) { | ||||
|     const div = document.createElement('div'); | ||||
|     div.innerHTML = str.trim(); | ||||
|  | ||||
|     return formatNode(div, 0).innerHTML.trim(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatNode(node, level) { | ||||
| function formatNode(node, level) { | ||||
|     const indentBefore = new Array(level++ + 1).join('  '); | ||||
|     const indentAfter  = new Array(level - 1).join('  '); | ||||
|     let textNode; | ||||
| @@ -44,17 +45,16 @@ const noteSource = (function() { | ||||
|     } | ||||
|  | ||||
|     return node; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+u', e => { | ||||
| $(document).bind('keydown', 'ctrl+u', e => { | ||||
|     showDialog(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,10 +1,12 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const recentChanges = (function() { | ||||
|     const $showDialogButton = $("#recent-changes-button"); | ||||
|     const $dialog = $("#recent-changes-dialog"); | ||||
| import link from '../link.js'; | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     async function showDialog() { | ||||
| const $showDialogButton = $("#recent-changes-button"); | ||||
| const $dialog = $("#recent-changes-dialog"); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -51,9 +53,9 @@ const recentChanges = (function() { | ||||
|  | ||||
|         $dialog.append(dayEl); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function groupByDate(result) { | ||||
| function groupByDate(result) { | ||||
|     const groupedByDate = new Map(); | ||||
|     const dayCache = {}; | ||||
|  | ||||
| @@ -80,13 +82,12 @@ const recentChanges = (function() { | ||||
|         groupedByDate.get(dateDay).push(row); | ||||
|     } | ||||
|     return groupedByDate; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+r', showDialog); | ||||
| $(document).bind('keydown', 'alt+r', showDialog); | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,20 +1,23 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const recentNotes = (function() { | ||||
|     const $showDialogButton = $("#recent-notes-button"); | ||||
|     const $dialog = $("#recent-notes-dialog"); | ||||
|     const $searchInput = $('#recent-notes-search-input'); | ||||
| import treeService from '../note_tree.js'; | ||||
| import server from '../server.js'; | ||||
| import messaging from '../messaging.js'; | ||||
|  | ||||
|     // list of recent note paths | ||||
|     let list = []; | ||||
| const $showDialogButton = $("#recent-notes-button"); | ||||
| const $dialog = $("#recent-notes-dialog"); | ||||
| const $searchInput = $('#recent-notes-search-input'); | ||||
|  | ||||
|     async function reload() { | ||||
| // list of recent note paths | ||||
| let list = []; | ||||
|  | ||||
| async function reload() { | ||||
|     const result = await server.get('recent-notes'); | ||||
|  | ||||
|     list = result.map(r => r.notePath); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function addRecentNote(branchId, notePath) { | ||||
| function addRecentNote(branchId, notePath) { | ||||
|     setTimeout(async () => { | ||||
|         // we include the note into recent list only if the user stayed on the note at least 5 seconds | ||||
|         if (notePath && notePath === treeService.getCurrentNotePath()) { | ||||
| @@ -23,9 +26,9 @@ const recentNotes = (function() { | ||||
|             list = result.map(r => r.notePath); | ||||
|         } | ||||
|     }, 1500); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function showDialog() { | ||||
| function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -85,21 +88,20 @@ const recentNotes = (function() { | ||||
|             "ui-autocomplete": "recent-notes-autocomplete" | ||||
|         } | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     reload(); | ||||
| reload(); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+e', e => { | ||||
| $(document).bind('keydown', 'ctrl+e', e => { | ||||
|     showDialog(); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog, | ||||
|     addRecentNote, | ||||
|     reload | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,17 +1,20 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const settings = (function() { | ||||
|     const $showDialogButton = $("#settings-button"); | ||||
|     const $dialog = $("#settings-dialog"); | ||||
|     const $tabs = $("#settings-tabs"); | ||||
| import protected_session from '../protected_session.js'; | ||||
| import utils from '../utils.js'; | ||||
| import server from '../server.js'; | ||||
|  | ||||
|     const settingModules = []; | ||||
| const $showDialogButton = $("#settings-button"); | ||||
| const $dialog = $("#settings-dialog"); | ||||
| const $tabs = $("#settings-tabs"); | ||||
|  | ||||
|     function addModule(module) { | ||||
| const settingModules = []; | ||||
|  | ||||
| function addModule(module) { | ||||
|     settingModules.push(module); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     const settings = await server.get('settings'); | ||||
| @@ -28,27 +31,26 @@ const settings = (function() { | ||||
|             module.settingsLoaded(settings); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function saveSettings(settingName, settingValue) { | ||||
| async function saveSettings(settingName, settingValue) { | ||||
|     await server.post('settings', { | ||||
|         name: settingName, | ||||
|         value: settingValue | ||||
|     }); | ||||
|  | ||||
|     utils.showMessage("Settings change have been saved."); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $showDialogButton.click(showDialog); | ||||
| $showDialogButton.click(showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog, | ||||
|     saveSettings, | ||||
|     addModule | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
|  | ||||
| settings.addModule((function() { | ||||
| addModule((function() { | ||||
|     const $form = $("#change-password-form"); | ||||
|     const $oldPassword = $("#old-password"); | ||||
|     const $newPassword1 = $("#new-password1"); | ||||
| @@ -94,7 +96,7 @@ settings.addModule((function() { | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| settings.addModule((function() { | ||||
| addModule((function() { | ||||
|     const $form = $("#protected-session-timeout-form"); | ||||
|     const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds"); | ||||
|     const settingName = 'protected_session_timeout'; | ||||
| @@ -118,7 +120,7 @@ settings.addModule((function() { | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| settings.addModule((function () { | ||||
| addModule((function () { | ||||
|     const $form = $("#history-snapshot-time-interval-form"); | ||||
|     const $timeInterval = $("#history-snapshot-time-interval-in-seconds"); | ||||
|     const settingName = 'history_snapshot_time_interval'; | ||||
| @@ -138,7 +140,7 @@ settings.addModule((function () { | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| settings.addModule((async function () { | ||||
| addModule((async function () { | ||||
|     const $appVersion = $("#app-version"); | ||||
|     const $dbVersion = $("#db-version"); | ||||
|     const $buildDate = $("#build-date"); | ||||
| @@ -155,7 +157,7 @@ settings.addModule((async function () { | ||||
|     return {}; | ||||
| })()); | ||||
|  | ||||
| settings.addModule((async function () { | ||||
| addModule((async function () { | ||||
|     const $forceFullSyncButton = $("#force-full-sync-button"); | ||||
|     const $fillSyncRowsButton = $("#fill-sync-rows-button"); | ||||
|     const $anonymizeButton = $("#anonymize-button"); | ||||
|   | ||||
| @@ -1,15 +1,16 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const sqlConsole = (function() { | ||||
|     const $dialog = $("#sql-console-dialog"); | ||||
|     const $query = $('#sql-console-query'); | ||||
|     const $executeButton = $('#sql-console-execute'); | ||||
|     const $resultHead = $('#sql-console-results thead'); | ||||
|     const $resultBody = $('#sql-console-results tbody'); | ||||
| import utils from '../utils.js'; | ||||
|  | ||||
|     let codeEditor; | ||||
| const $dialog = $("#sql-console-dialog"); | ||||
| const $query = $('#sql-console-query'); | ||||
| const $executeButton = $('#sql-console-execute'); | ||||
| const $resultHead = $('#sql-console-results thead'); | ||||
| const $resultBody = $('#sql-console-results tbody'); | ||||
|  | ||||
|     function showDialog() { | ||||
| let codeEditor; | ||||
|  | ||||
| function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -20,9 +21,9 @@ const sqlConsole = (function() { | ||||
|             initEditor(); | ||||
|         } | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function initEditor() { | ||||
| async function initEditor() { | ||||
|     if (!codeEditor) { | ||||
|         await utils.requireLibrary(utils.CODE_MIRROR); | ||||
|  | ||||
| @@ -46,9 +47,9 @@ const sqlConsole = (function() { | ||||
|     } | ||||
|  | ||||
|     codeEditor.focus(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function execute(e) { | ||||
| async function execute(e) { | ||||
|     // stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes) | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
| @@ -92,15 +93,14 @@ const sqlConsole = (function() { | ||||
|  | ||||
|         $resultBody.append(rowEl); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+o', showDialog); | ||||
| $(document).bind('keydown', 'alt+o', showDialog); | ||||
|  | ||||
|     $query.bind('keydown', 'ctrl+return', execute); | ||||
| $query.bind('keydown', 'ctrl+return', execute); | ||||
|  | ||||
|     $executeButton.click(execute); | ||||
| $executeButton.click(execute); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,5 +1,8 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import treeService from './note_tree.js'; | ||||
| import treeChanges from './tree_changes.js'; | ||||
|  | ||||
| const dragAndDropSetup = { | ||||
|     autoExpandMS: 600, | ||||
|     draggable: { // modify default jQuery draggable options | ||||
| @@ -65,3 +68,5 @@ const dragAndDropSetup = { | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| export default dragAndDropSetup; | ||||
| @@ -1,22 +1,25 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const exportService = (function () { | ||||
|     function exportSubTree(noteId) { | ||||
| import treeService from './note_tree.js'; | ||||
| import protected_session from './protected_session.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| function exportSubTree(noteId) { | ||||
|     const url = utils.getHost() + "/api/export/" + noteId + "?protectedSessionId=" | ||||
|         + encodeURIComponent(protected_session.getProtectedSessionId()); | ||||
|  | ||||
|     utils.download(url); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     let importNoteId; | ||||
| let importNoteId; | ||||
|  | ||||
|     function importSubTree(noteId) { | ||||
| function importSubTree(noteId) { | ||||
|     importNoteId = noteId; | ||||
|  | ||||
|     $("#import-upload").trigger('click'); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $("#import-upload").change(async function() { | ||||
| $("#import-upload").change(async function() { | ||||
|     const formData = new FormData(); | ||||
|     formData.append('upload', this.files[0]); | ||||
|  | ||||
| @@ -30,10 +33,9 @@ const exportService = (function () { | ||||
|     }); | ||||
|  | ||||
|     await treeService.reload(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     exportSubTree, | ||||
|     importSubTree | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,48 +1,55 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const initService = (function() { | ||||
|     // hot keys are active also inside inputs and content editables | ||||
|     jQuery.hotkeys.options.filterInputAcceptingElements = false; | ||||
|     jQuery.hotkeys.options.filterContentEditable = false; | ||||
|     jQuery.hotkeys.options.filterTextInputs = false; | ||||
| import treeService from './note_tree.js'; | ||||
| import link from './link.js'; | ||||
| import messaging from './messaging.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+m', e => { | ||||
| // hot keys are active also inside inputs and content editables | ||||
| 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(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     // hide (toggle) everything except for the note content for distraction free writing | ||||
|     $(document).bind('keydown', 'alt+t', e => { | ||||
| // hide (toggle) everything except for the note content for distraction free writing | ||||
| $(document).bind('keydown', 'alt+t', e => { | ||||
|     const date = new Date(); | ||||
|     const dateString = utils.formatDateTime(date); | ||||
|  | ||||
|     link.addTextToEditor(dateString); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'f5', () => { | ||||
| $(document).bind('keydown', 'f5', () => { | ||||
|     utils.reloadApp(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+r', () => { | ||||
| $(document).bind('keydown', 'ctrl+r', () => { | ||||
|     utils.reloadApp(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+shift+i', () => { | ||||
| $(document).bind('keydown', 'ctrl+shift+i', () => { | ||||
|     if (utils.isElectron()) { | ||||
|         require('electron').remote.getCurrentWindow().toggleDevTools(); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+f', () => { | ||||
| $(document).bind('keydown', 'ctrl+f', () => { | ||||
|     if (utils.isElectron()) { | ||||
|         const searchInPage = require('electron-in-page-search').default; | ||||
|         const remote = require('electron').remote; | ||||
| @@ -53,27 +60,27 @@ const initService = (function() { | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', "ctrl+shift+up", () => { | ||||
| $(document).bind('keydown', "ctrl+shift+up", () => { | ||||
|     const node = treeService.getCurrentNode(); | ||||
|     node.navigate($.ui.keyCode.UP, true); | ||||
|  | ||||
|     $("#note-detail").focus(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', "ctrl+shift+down", () => { | ||||
| $(document).bind('keydown', "ctrl+shift+down", () => { | ||||
|     const node = treeService.getCurrentNode(); | ||||
|     node.navigate($.ui.keyCode.DOWN, true); | ||||
|  | ||||
|     $("#note-detail").focus(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+-', () => { | ||||
| $(document).bind('keydown', 'ctrl+-', () => { | ||||
|     if (utils.isElectron()) { | ||||
|         const webFrame = require('electron').webFrame; | ||||
|  | ||||
| @@ -83,9 +90,9 @@ const initService = (function() { | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+=', () => { | ||||
| $(document).bind('keydown', 'ctrl+=', () => { | ||||
|     if (utils.isElectron()) { | ||||
|         const webFrame = require('electron').webFrame; | ||||
|  | ||||
| @@ -93,18 +100,18 @@ const initService = (function() { | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $("#note-title").bind('keydown', 'return', () => $("#note-detail").focus()); | ||||
| $("#note-title").bind('keydown', 'return', () => $("#note-detail").focus()); | ||||
|  | ||||
|     $(window).on('beforeunload', () => { | ||||
| $(window).on('beforeunload', () => { | ||||
|     // this makes sure that when user e.g. reloads the page or navigates away from the page, the note's content is saved | ||||
|     // this sends the request asynchronously and doesn't wait for result | ||||
|     noteEditor.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) => { | ||||
| // 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; | ||||
|     } | ||||
| @@ -147,9 +154,9 @@ const initService = (function() { | ||||
|     console.log("Search took " + (new Date().getTime() - startDate.getTime()) + "ms"); | ||||
|  | ||||
|     return results; | ||||
|     }; | ||||
| }; | ||||
|  | ||||
|     $(document).tooltip({ | ||||
| $(document).tooltip({ | ||||
|     items: "#note-detail a", | ||||
|     content: function(callback) { | ||||
|         const notePath = link.getNotePathFromLink($(this).attr("href")); | ||||
| @@ -174,9 +181,9 @@ const initService = (function() { | ||||
|                 }); | ||||
|             }); | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     window.onerror = function (msg, url, lineNo, columnNo, error) { | ||||
| window.onerror = function (msg, url, lineNo, columnNo, error) { | ||||
|     const string = msg.toLowerCase(); | ||||
|  | ||||
|     let message = "Uncaught error: "; | ||||
| @@ -197,19 +204,19 @@ const initService = (function() { | ||||
|     messaging.logError(message); | ||||
|  | ||||
|     return false; | ||||
|     }; | ||||
| }; | ||||
|  | ||||
|     $("#logout-button").toggle(!utils.isElectron()); | ||||
| $("#logout-button").toggle(!utils.isElectron()); | ||||
|  | ||||
|     $(document).ready(() => { | ||||
| $(document).ready(() => { | ||||
|     server.get("script/startup").then(scriptBundles => { | ||||
|         for (const bundle of scriptBundles) { | ||||
|             utils.executeBundle(bundle); | ||||
|         } | ||||
|     }); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
| if (utils.isElectron()) { | ||||
|     require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) { | ||||
|         // this might occur when day note had to be created | ||||
|         if (!await treeService.noteExists(parentNoteId)) { | ||||
| @@ -224,15 +231,15 @@ const initService = (function() { | ||||
|             treeService.createNote(node, node.data.noteId, 'into', node.data.isProtected); | ||||
|         }, 500); | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function uploadAttachment() { | ||||
| function uploadAttachment() { | ||||
|     $("#attachment-upload").trigger('click'); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $("#upload-attachment-button").click(uploadAttachment); | ||||
| $("#upload-attachment-button").click(uploadAttachment); | ||||
|  | ||||
|     $("#attachment-upload").change(async function() { | ||||
| $("#attachment-upload").change(async function() { | ||||
|     const formData = new FormData(); | ||||
|     formData.append('upload', this.files[0]); | ||||
|  | ||||
| @@ -248,5 +255,4 @@ const initService = (function() { | ||||
|     await treeService.reload(); | ||||
|  | ||||
|     await treeService.activateNode(resp.noteId); | ||||
|     }); | ||||
| })(); | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const link = (function() { | ||||
|     function getNotePathFromLink(url) { | ||||
| import treeService from './note_tree.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
|  | ||||
| function getNotePathFromLink(url) { | ||||
|     const notePathMatch = /#([A-Za-z0-9/]+)$/.exec(url); | ||||
|  | ||||
|     if (notePathMatch === null) { | ||||
| @@ -10,9 +13,9 @@ const link = (function() { | ||||
|     else { | ||||
|         return notePathMatch[1]; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNodePathFromLabel(label) { | ||||
| function getNodePathFromLabel(label) { | ||||
|     const notePathMatch = / \(([A-Za-z0-9/]+)\)/.exec(label); | ||||
|  | ||||
|     if (notePathMatch !== null) { | ||||
| @@ -20,9 +23,9 @@ const link = (function() { | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function createNoteLink(notePath, noteTitle) { | ||||
| function createNoteLink(notePath, noteTitle) { | ||||
|     if (!noteTitle) { | ||||
|         const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|  | ||||
| @@ -36,9 +39,9 @@ const link = (function() { | ||||
|         .attr('note-path', notePath); | ||||
|  | ||||
|     return noteLink; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function goToLink(e) { | ||||
| function goToLink(e) { | ||||
|     e.preventDefault(); | ||||
|  | ||||
|     const $link = $(e.target); | ||||
| @@ -71,33 +74,32 @@ const link = (function() { | ||||
|         } | ||||
|         catch (e) {} | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function addLinkToEditor(linkTitle, linkHref) { | ||||
| function addLinkToEditor(linkTitle, linkHref) { | ||||
|     const editor = noteEditor.getEditor(); | ||||
|     const doc = editor.document; | ||||
|  | ||||
|     doc.enqueueChanges(() => editor.data.insertLink(linkTitle, linkHref), doc.selection); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function addTextToEditor(text) { | ||||
| function addTextToEditor(text) { | ||||
|     const editor = noteEditor.getEditor(); | ||||
|     const doc = editor.document; | ||||
|  | ||||
|     doc.enqueueChanges(() => editor.data.insertText(text), doc.selection); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     // when click on link popup, in case of internal link, just go the the referenced note instead of default behavior | ||||
|     // of opening the link in new window/tab | ||||
|     $(document).on('click', "a[action='note']", goToLink); | ||||
|     $(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink); | ||||
|     $(document).on('dblclick', '#note-detail a', goToLink); | ||||
| // when click on link popup, in case of internal link, just go the the referenced note instead of default behavior | ||||
| // of opening the link in new window/tab | ||||
| $(document).on('click', "a[action='note']", goToLink); | ||||
| $(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink); | ||||
| $(document).on('dblclick', '#note-detail a', goToLink); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     getNodePathFromLabel, | ||||
|     getNotePathFromLink, | ||||
|     createNoteLink, | ||||
|     addLinkToEditor, | ||||
|     addTextToEditor | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,9 +1,13 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const messaging = (function() { | ||||
|     const $changesToPushCount = $("#changes-to-push-count"); | ||||
| import treeService from './note_tree.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import sync from './sync.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
|     function logError(message) { | ||||
| const $changesToPushCount = $("#changes-to-push-count"); | ||||
|  | ||||
| function logError(message) { | ||||
|     console.log(utils.now(), message); // needs to be separate from .trace() | ||||
|     console.trace(); | ||||
|  | ||||
| @@ -13,9 +17,9 @@ const messaging = (function() { | ||||
|             error: message | ||||
|         })); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function messageHandler(event) { | ||||
| function messageHandler(event) { | ||||
|     const message = JSON.parse(event.data); | ||||
|  | ||||
|     if (message.type === 'sync') { | ||||
| @@ -60,9 +64,9 @@ const messaging = (function() { | ||||
|     else if (message.type === 'consistency-checks-failed') { | ||||
|         utils.showError("Consistency checks failed! See logs for details.", 50 * 60000); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function connectWebSocket() { | ||||
| function connectWebSocket() { | ||||
|     const protocol = document.location.protocol === 'https:' ? 'wss' : 'ws'; | ||||
|  | ||||
|     // use wss for secure messaging | ||||
| @@ -75,15 +79,15 @@ const messaging = (function() { | ||||
|     }; | ||||
|  | ||||
|     return ws; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     const ws = connectWebSocket(); | ||||
| const ws = connectWebSocket(); | ||||
|  | ||||
|     let lastSyncId = glob.maxSyncIdAtLoad; | ||||
|     let lastPingTs = new Date().getTime(); | ||||
|     let connectionBrokenNotification = null; | ||||
| let lastSyncId = glob.maxSyncIdAtLoad; | ||||
| let lastPingTs = new Date().getTime(); | ||||
| let connectionBrokenNotification = null; | ||||
|  | ||||
|     setInterval(async () => { | ||||
| setInterval(async () => { | ||||
|     if (new Date().getTime() - lastPingTs > 30000) { | ||||
|         if (!connectionBrokenNotification) { | ||||
|             connectionBrokenNotification = $.notify({ | ||||
| @@ -107,9 +111,8 @@ const messaging = (function() { | ||||
|         type: 'ping', | ||||
|         lastSyncId: lastSyncId | ||||
|     })); | ||||
|     }, 1000); | ||||
| }, 1000); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     logError | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,74 +1,79 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const noteEditor = (function() { | ||||
|     const $noteTitle = $("#note-title"); | ||||
| import treeService from './note_tree.js'; | ||||
| import noteType from './note_type.js'; | ||||
| import protected_session from './protected_session.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
|  | ||||
|     const $noteDetail = $('#note-detail'); | ||||
|     const $noteDetailCode = $('#note-detail-code'); | ||||
|     const $noteDetailSearch = $('#note-detail-search'); | ||||
|     const $noteDetailRender = $('#note-detail-render'); | ||||
|     const $noteDetailAttachment = $('#note-detail-attachment'); | ||||
| const $noteTitle = $("#note-title"); | ||||
|  | ||||
|     const $protectButton = $("#protect-button"); | ||||
|     const $unprotectButton = $("#unprotect-button"); | ||||
|     const $noteDetailWrapper = $("#note-detail-wrapper"); | ||||
|     const $noteIdDisplay = $("#note-id-display"); | ||||
|     const $labelList = $("#label-list"); | ||||
|     const $labelListInner = $("#label-list-inner"); | ||||
|     const $attachmentFileName = $("#attachment-filename"); | ||||
|     const $attachmentFileType = $("#attachment-filetype"); | ||||
|     const $attachmentFileSize = $("#attachment-filesize"); | ||||
|     const $attachmentDownload = $("#attachment-download"); | ||||
|     const $attachmentOpen = $("#attachment-open"); | ||||
|     const $searchString = $("#search-string"); | ||||
| const $noteDetail = $('#note-detail'); | ||||
| const $noteDetailCode = $('#note-detail-code'); | ||||
| const $noteDetailSearch = $('#note-detail-search'); | ||||
| const $noteDetailRender = $('#note-detail-render'); | ||||
| const $noteDetailAttachment = $('#note-detail-attachment'); | ||||
|  | ||||
|     const $executeScriptButton = $("#execute-script-button"); | ||||
| const $protectButton = $("#protect-button"); | ||||
| const $unprotectButton = $("#unprotect-button"); | ||||
| const $noteDetailWrapper = $("#note-detail-wrapper"); | ||||
| const $noteIdDisplay = $("#note-id-display"); | ||||
| const $labelList = $("#label-list"); | ||||
| const $labelListInner = $("#label-list-inner"); | ||||
| const $attachmentFileName = $("#attachment-filename"); | ||||
| const $attachmentFileType = $("#attachment-filetype"); | ||||
| const $attachmentFileSize = $("#attachment-filesize"); | ||||
| const $attachmentDownload = $("#attachment-download"); | ||||
| const $attachmentOpen = $("#attachment-open"); | ||||
| const $searchString = $("#search-string"); | ||||
|  | ||||
|     let editor = null; | ||||
|     let codeEditor = null; | ||||
| const $executeScriptButton = $("#execute-script-button"); | ||||
|  | ||||
|     let currentNote = null; | ||||
| let editor = null; | ||||
| let codeEditor = null; | ||||
|  | ||||
|     let noteChangeDisabled = false; | ||||
| let currentNote = null; | ||||
|  | ||||
|     let isNoteChanged = false; | ||||
| let noteChangeDisabled = false; | ||||
|  | ||||
|     function getCurrentNote() { | ||||
| let isNoteChanged = false; | ||||
|  | ||||
| function getCurrentNote() { | ||||
|     return currentNote; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getCurrentNoteId() { | ||||
| function getCurrentNoteId() { | ||||
|     return currentNote ? currentNote.detail.noteId : null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function noteChanged() { | ||||
| function noteChanged() { | ||||
|     if (noteChangeDisabled) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     isNoteChanged = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function reload() { | ||||
| async function reload() { | ||||
|     // no saving here | ||||
|  | ||||
|     await loadNoteToEditor(getCurrentNoteId()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function switchToNote(noteId) { | ||||
| async function switchToNote(noteId) { | ||||
|     if (getCurrentNoteId() !== noteId) { | ||||
|         await saveNoteIfChanged(); | ||||
|  | ||||
|         await loadNoteToEditor(noteId); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function saveNoteIfChanged() { | ||||
| async function saveNoteIfChanged() { | ||||
|     if (!isNoteChanged) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|         const note = noteEditor.getCurrentNote(); | ||||
|     const note = getCurrentNote(); | ||||
|  | ||||
|     updateNoteFromInputs(note); | ||||
|  | ||||
| @@ -77,9 +82,9 @@ const noteEditor = (function() { | ||||
|     if (note.detail.isProtected) { | ||||
|         protected_session.touchProtectedSession(); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function updateNoteFromInputs(note) { | ||||
| function updateNoteFromInputs(note) { | ||||
|     if (note.detail.type === 'text') { | ||||
|         let content = editor.getData(); | ||||
|  | ||||
| @@ -111,31 +116,31 @@ const noteEditor = (function() { | ||||
|     note.detail.title = title; | ||||
|  | ||||
|     treeService.setNoteTitle(note.detail.noteId, title); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function saveNoteToServer(note) { | ||||
| async function saveNoteToServer(note) { | ||||
|     await server.put('notes/' + note.detail.noteId, note); | ||||
|  | ||||
|     isNoteChanged = false; | ||||
|  | ||||
|     utils.showMessage("Saved!"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setNoteBackgroundIfProtected(note) { | ||||
| function setNoteBackgroundIfProtected(note) { | ||||
|     const isProtected = !!note.detail.isProtected; | ||||
|  | ||||
|     $noteDetailWrapper.toggleClass("protected", isProtected); | ||||
|     $protectButton.toggle(!isProtected); | ||||
|     $unprotectButton.toggle(isProtected); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     let isNewNoteCreated = false; | ||||
| let isNewNoteCreated = false; | ||||
|  | ||||
|     function newNoteCreated() { | ||||
| function newNoteCreated() { | ||||
|     isNewNoteCreated = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function setContent(content) { | ||||
| async function setContent(content) { | ||||
|     if (currentNote.detail.type === 'text') { | ||||
|         if (!editor) { | ||||
|             await utils.requireLibrary(utils.CKEDITOR); | ||||
| @@ -203,9 +208,9 @@ const noteEditor = (function() { | ||||
|  | ||||
|         $searchString.on('input', noteChanged); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function loadNoteToEditor(noteId) { | ||||
| async function loadNoteToEditor(noteId) { | ||||
|     currentNote = await loadNote(noteId); | ||||
|  | ||||
|     if (isNewNoteCreated) { | ||||
| @@ -270,9 +275,9 @@ const noteEditor = (function() { | ||||
|     $noteDetailWrapper.scrollTop(0); | ||||
|  | ||||
|     loadLabelList(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function loadLabelList() { | ||||
| async function loadLabelList() { | ||||
|     const noteId = getCurrentNoteId(); | ||||
|  | ||||
|     const labels = await server.get('notes/' + noteId + '/labels'); | ||||
| @@ -289,17 +294,17 @@ const noteEditor = (function() { | ||||
|     else { | ||||
|         $labelList.hide(); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function loadNote(noteId) { | ||||
| async function loadNote(noteId) { | ||||
|     return await server.get('notes/' + noteId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getEditor() { | ||||
| function getEditor() { | ||||
|     return editor; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function focus() { | ||||
| function focus() { | ||||
|     const note = getCurrentNote(); | ||||
|  | ||||
|     if (note.detail.type === 'text') { | ||||
| @@ -314,15 +319,15 @@ const noteEditor = (function() { | ||||
|     else { | ||||
|         utils.throwError('Unrecognized type: ' + note.detail.type); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getCurrentNoteType() { | ||||
| function getCurrentNoteType() { | ||||
|     const currentNote = getCurrentNote(); | ||||
|  | ||||
|     return currentNote ? currentNote.detail.type : null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function executeCurrentNote() { | ||||
| async function executeCurrentNote() { | ||||
|     if (getCurrentNoteType() === 'code') { | ||||
|         // make sure note is saved so we load latest changes | ||||
|         await saveNoteIfChanged(); | ||||
| @@ -339,11 +344,11 @@ const noteEditor = (function() { | ||||
|  | ||||
|         utils.showMessage("Note executed"); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $attachmentDownload.click(() => utils.download(getAttachmentUrl())); | ||||
| $attachmentDownload.click(() => utils.download(getAttachmentUrl())); | ||||
|  | ||||
|     $attachmentOpen.click(() => { | ||||
| $attachmentOpen.click(() => { | ||||
|     if (utils.isElectron()) { | ||||
|         const open = require("open"); | ||||
|  | ||||
| @@ -352,15 +357,15 @@ const noteEditor = (function() { | ||||
|     else { | ||||
|         window.location.href = getAttachmentUrl(); | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     function getAttachmentUrl() { | ||||
| function getAttachmentUrl() { | ||||
|     // electron needs absolute URL so we extract current host, port, protocol | ||||
|     return utils.getHost() + "/api/attachments/download/" + getCurrentNoteId() | ||||
|         + "?protectedSessionId=" + encodeURIComponent(protected_session.getProtectedSessionId()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).ready(() => { | ||||
| $(document).ready(() => { | ||||
|     $noteTitle.on('input', () => { | ||||
|         noteChanged(); | ||||
|  | ||||
| @@ -371,15 +376,15 @@ const noteEditor = (function() { | ||||
|  | ||||
|     // so that tab jumps from note title (which has tabindex 1) | ||||
|     $noteDetail.attr("tabindex", 2); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', "ctrl+return", executeCurrentNote); | ||||
| $(document).bind('keydown', "ctrl+return", executeCurrentNote); | ||||
|  | ||||
|     $executeScriptButton.click(executeCurrentNote()); | ||||
| $executeScriptButton.click(executeCurrentNote()); | ||||
|  | ||||
|     setInterval(saveNoteIfChanged, 5000); | ||||
| setInterval(saveNoteIfChanged, 5000); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     reload, | ||||
|     switchToNote, | ||||
|     saveNoteIfChanged, | ||||
| @@ -396,5 +401,4 @@ const noteEditor = (function() { | ||||
|     executeCurrentNote, | ||||
|     loadLabelList, | ||||
|     setContent | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,5 +1,17 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import contextMenu from './context_menu.js'; | ||||
| import dragAndDropSetup from './drag_and_drop.js'; | ||||
| import link from './link.js'; | ||||
| import messaging from './messaging.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import protected_session from './protected_session.js'; | ||||
| import treeChanges from './tree_changes.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
| import recentNotes from './dialogs/recent_notes.js'; | ||||
|  | ||||
| class TreeCache { | ||||
|     constructor(noteRows, branchRows) { | ||||
|         this.parents = []; | ||||
| @@ -126,21 +138,20 @@ class Branch { | ||||
|     } | ||||
| } | ||||
|  | ||||
| const treeService = (function() { | ||||
|     let treeCache; | ||||
| let treeCache; | ||||
|  | ||||
|     const $tree = $("#tree"); | ||||
|     const $parentList = $("#parent-list"); | ||||
|     const $parentListList = $("#parent-list-inner"); | ||||
|     const $createTopLevelNoteButton = $("#create-top-level-note-button"); | ||||
|     const $collapseTreeButton = $("#collapse-tree-button"); | ||||
|     const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button"); | ||||
| const $tree = $("#tree"); | ||||
| const $parentList = $("#parent-list"); | ||||
| const $parentListList = $("#parent-list-inner"); | ||||
| const $createTopLevelNoteButton = $("#create-top-level-note-button"); | ||||
| const $collapseTreeButton = $("#collapse-tree-button"); | ||||
| const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button"); | ||||
|  | ||||
|     let instanceName = null; // should have better place | ||||
| let instanceName = null; // should have better place | ||||
|  | ||||
|     let startNotePath = null; | ||||
| let startNotePath = null; | ||||
|  | ||||
|     function getNote(noteId) { | ||||
| function getNote(noteId) { | ||||
|     const note = treeCache.getNote(noteId); | ||||
|  | ||||
|     if (!note) { | ||||
| @@ -148,9 +159,9 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return note; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNoteTitle(noteId, parentNoteId = null) { | ||||
| function getNoteTitle(noteId, parentNoteId = null) { | ||||
|     utils.assertArguments(noteId); | ||||
|  | ||||
|     let title = treeCache.getNote(noteId).title; | ||||
| @@ -164,43 +175,43 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return title; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     // note that if you want to access data like noteId or isProtected, you need to go into "data" property | ||||
|     function getCurrentNode() { | ||||
| // 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"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getCurrentNotePath() { | ||||
| function getCurrentNotePath() { | ||||
|     const node = getCurrentNode(); | ||||
|  | ||||
|     return treeUtils.getNotePath(node); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNodesByBranchId(branchId) { | ||||
| function getNodesByBranchId(branchId) { | ||||
|     utils.assertArguments(branchId); | ||||
|  | ||||
|     const branch = treeCache.getBranch(branchId); | ||||
|  | ||||
|     return getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branchId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNodesByNoteId(noteId) { | ||||
| function getNodesByNoteId(noteId) { | ||||
|     utils.assertArguments(noteId); | ||||
|  | ||||
|     const list = getTree().getNodesByRef(noteId); | ||||
|     return list ? list : []; // if no nodes with this refKey are found, fancy tree returns null | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setPrefix(branchId, prefix) { | ||||
| function setPrefix(branchId, prefix) { | ||||
|     utils.assertArguments(branchId); | ||||
|  | ||||
|     treeCache.getBranch(branchId).prefix = prefix; | ||||
|  | ||||
|     getNodesByBranchId(branchId).map(node => setNodeTitleWithPrefix(node)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setNodeTitleWithPrefix(node) { | ||||
| function setNodeTitleWithPrefix(node) { | ||||
|     const noteTitle = getNoteTitle(node.data.noteId); | ||||
|     const branch = treeCache.getBranch(node.data.branchId); | ||||
|  | ||||
| @@ -209,18 +220,18 @@ const treeService = (function() { | ||||
|     const title = (prefix ? (prefix + " - ") : "") + noteTitle; | ||||
|  | ||||
|     node.setTitle(utils.escapeHtml(title)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function removeParentChildRelation(parentNoteId, childNoteId) { | ||||
| function removeParentChildRelation(parentNoteId, childNoteId) { | ||||
|     utils.assertArguments(parentNoteId, childNoteId); | ||||
|  | ||||
|     treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p.noteId !== parentNoteId); | ||||
|     treeCache.children[parentNoteId] = treeCache.children[parentNoteId].filter(ch => ch.noteId !== childNoteId); | ||||
|  | ||||
|     delete treeCache.childParentToBranch[childNoteId + '-' + parentNoteId]; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setParentChildRelation(branchId, parentNoteId, childNoteId) { | ||||
| function setParentChildRelation(branchId, parentNoteId, childNoteId) { | ||||
|     treeCache.parents[childNoteId] = treeCache.parents[childNoteId] || []; | ||||
|     treeCache.parents[childNoteId].push(treeCache.getNote(parentNoteId)); | ||||
|  | ||||
| @@ -228,17 +239,17 @@ const treeService = (function() { | ||||
|     treeCache.children[parentNoteId].push(treeCache.getNote(childNoteId)); | ||||
|  | ||||
|     treeCache.childParentToBranch[childNoteId + '-' + parentNoteId] = treeCache.getBranch(branchId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function prepareBranch(noteRows, branchRows) { | ||||
| async function prepareBranch(noteRows, branchRows) { | ||||
|     utils.assertArguments(noteRows); | ||||
|  | ||||
|     treeCache = new TreeCache(noteRows, branchRows); | ||||
|  | ||||
|     return await prepareBranchInner(treeCache.getNote('root')); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function getExtraClasses(note) { | ||||
| async function getExtraClasses(note) { | ||||
|     utils.assertArguments(note); | ||||
|  | ||||
|     const extraClasses = []; | ||||
| @@ -254,9 +265,9 @@ const treeService = (function() { | ||||
|     extraClasses.push(note.type); | ||||
|  | ||||
|     return extraClasses.join(" "); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function prepareBranchInner(parentNote) { | ||||
| async function prepareBranchInner(parentNote) { | ||||
|     utils.assertArguments(parentNote); | ||||
|  | ||||
|     const childBranches = await parentNote.getChildBranches(); | ||||
| @@ -300,9 +311,9 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return noteList; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function expandToNote(notePath, expandOpts) { | ||||
| async function expandToNote(notePath, expandOpts) { | ||||
|     utils.assertArguments(notePath); | ||||
|  | ||||
|     const runPath = await getRunPath(notePath); | ||||
| @@ -323,9 +334,9 @@ const treeService = (function() { | ||||
|  | ||||
|         parentNoteId = childNoteId; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function activateNode(notePath) { | ||||
| async function activateNode(notePath) { | ||||
|     utils.assertArguments(notePath); | ||||
|  | ||||
|     const node = await expandToNote(notePath); | ||||
| @@ -333,13 +344,13 @@ const treeService = (function() { | ||||
|     await node.setActive(); | ||||
|  | ||||
|     clearSelectedNodes(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     /** | ||||
| /** | ||||
|  * Accepts notePath and tries to resolve it. Part of the path might not be valid because of note moving (which causes | ||||
|  * path change) or other corruption, in that case this will try to get some other valid path to the correct note. | ||||
|  */ | ||||
|     async function getRunPath(notePath) { | ||||
| async function getRunPath(notePath) { | ||||
|     utils.assertArguments(notePath); | ||||
|  | ||||
|     const path = notePath.split("/").reverse(); | ||||
| @@ -400,9 +411,9 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return effectivePath.reverse(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function showParentList(noteId, node) { | ||||
| async function showParentList(noteId, node) { | ||||
|     utils.assertArguments(noteId, node); | ||||
|  | ||||
|     const note = treeCache.getNote(noteId); | ||||
| @@ -437,9 +448,9 @@ const treeService = (function() { | ||||
|             $parentListList.append($("<li/>").append(item)); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNotePathTitle(notePath) { | ||||
| function getNotePathTitle(notePath) { | ||||
|     utils.assertArguments(notePath); | ||||
|  | ||||
|     const titlePath = []; | ||||
| @@ -453,9 +464,9 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return titlePath.join(' / '); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function getSomeNotePath(note) { | ||||
| async function getSomeNotePath(note) { | ||||
|     utils.assertArguments(note); | ||||
|  | ||||
|     const path = []; | ||||
| @@ -475,17 +486,17 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return path.reverse().join('/'); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function setExpandedToServer(branchId, isExpanded) { | ||||
| async function setExpandedToServer(branchId, isExpanded) { | ||||
|     utils.assertArguments(branchId); | ||||
|  | ||||
|     const expandedNum = isExpanded ? 1 : 0; | ||||
|  | ||||
|     await server.put('tree/' + branchId + '/expanded/' + expandedNum); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setCurrentNotePathToHash(node) { | ||||
| function setCurrentNotePathToHash(node) { | ||||
|     utils.assertArguments(node); | ||||
|  | ||||
|     const currentNotePath = treeUtils.getNotePath(node); | ||||
| @@ -494,13 +505,13 @@ const treeService = (function() { | ||||
|     document.location.hash = currentNotePath; | ||||
|  | ||||
|     recentNotes.addRecentNote(currentBranchId, currentNotePath); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getSelectedNodes(stopOnParents = false) { | ||||
| function getSelectedNodes(stopOnParents = false) { | ||||
|     return getTree().getSelectedNodes(stopOnParents); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function clearSelectedNodes() { | ||||
| function clearSelectedNodes() { | ||||
|     for (const selectedNode of getSelectedNodes()) { | ||||
|         selectedNode.setSelected(false); | ||||
|     } | ||||
| @@ -510,9 +521,9 @@ const treeService = (function() { | ||||
|     if (currentNode) { | ||||
|         currentNode.setSelected(true); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function initFancyTree(branch) { | ||||
| function initFancyTree(branch) { | ||||
|     utils.assertArguments(branch); | ||||
|  | ||||
|     const keybindings = { | ||||
| @@ -732,9 +743,9 @@ const treeService = (function() { | ||||
|     }); | ||||
|  | ||||
|     $tree.contextmenu(contextMenu.contextMenuSettings); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function loadSearchNote(searchNoteId) { | ||||
| async function loadSearchNote(searchNoteId) { | ||||
|     const note = await server.get('notes/' + searchNoteId); | ||||
|  | ||||
|     const json = JSON.parse(note.detail.content); | ||||
| @@ -754,24 +765,24 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return await prepareBranchInner(treeCache.getNote(searchNoteId)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getTree() { | ||||
| function getTree() { | ||||
|     return $tree.fancytree('getTree'); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function reload() { | ||||
| async function reload() { | ||||
|     const notes = await loadTree(); | ||||
|  | ||||
|     // this will also reload the note content | ||||
|     await getTree().reload(notes); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNotePathFromAddress() { | ||||
| function getNotePathFromAddress() { | ||||
|     return document.location.hash.substr(1); // strip initial # | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function loadTree() { | ||||
| async function loadTree() { | ||||
|     const resp = await server.get('tree'); | ||||
|     startNotePath = resp.start_note_path; | ||||
|     instanceName = resp.instanceName; | ||||
| @@ -781,11 +792,11 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return await prepareBranch(resp.notes, resp.branches); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(() => loadTree().then(branch => initFancyTree(branch))); | ||||
| $(() => loadTree().then(branch => initFancyTree(branch))); | ||||
|  | ||||
|     function collapseTree(node = null) { | ||||
| function collapseTree(node = null) { | ||||
|     if (!node) { | ||||
|         node = $tree.fancytree("getRootNode"); | ||||
|     } | ||||
| @@ -793,11 +804,11 @@ const treeService = (function() { | ||||
|     node.setExpanded(false); | ||||
|  | ||||
|     node.visit(node => node.setExpanded(false)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument | ||||
| $(document).bind('keydown', 'alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument | ||||
|  | ||||
|     function scrollToCurrentNote() { | ||||
| function scrollToCurrentNote() { | ||||
|     const node = getCurrentNode(); | ||||
|  | ||||
|     if (node) { | ||||
| @@ -805,19 +816,19 @@ const treeService = (function() { | ||||
|  | ||||
|         node.setFocus(); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setBranchBackgroundBasedOnProtectedStatus(noteId) { | ||||
| function setBranchBackgroundBasedOnProtectedStatus(noteId) { | ||||
|     getNodesByNoteId(noteId).map(node => node.toggleClass("protected", !!node.data.isProtected)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setProtected(noteId, isProtected) { | ||||
| function setProtected(noteId, isProtected) { | ||||
|     getNodesByNoteId(noteId).map(node => node.data.isProtected = isProtected); | ||||
|  | ||||
|     setBranchBackgroundBasedOnProtectedStatus(noteId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function getAutocompleteItems(parentNoteId, notePath, titlePath) { | ||||
| async function getAutocompleteItems(parentNoteId, notePath, titlePath) { | ||||
|     if (!parentNoteId) { | ||||
|         parentNoteId = 'root'; | ||||
|     } | ||||
| @@ -863,23 +874,23 @@ const treeService = (function() { | ||||
|     } | ||||
|  | ||||
|     return autocompleteItems; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function setNoteTitle(noteId, title) { | ||||
| function setNoteTitle(noteId, title) { | ||||
|     utils.assertArguments(noteId); | ||||
|  | ||||
|     getNote(noteId).title = title; | ||||
|  | ||||
|     getNodesByNoteId(noteId).map(clone => setNodeTitleWithPrefix(clone)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function createNewTopLevelNote() { | ||||
| async function createNewTopLevelNote() { | ||||
|     const rootNode = $tree.fancytree("getRootNode"); | ||||
|  | ||||
|     await createNote(rootNode, "root", "into"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function createNote(node, parentNoteId, target, isProtected) { | ||||
| async function createNote(node, parentNoteId, target, isProtected) { | ||||
|     utils.assertArguments(node, parentNoteId, target); | ||||
|  | ||||
|     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted | ||||
| @@ -944,27 +955,27 @@ const treeService = (function() { | ||||
|     clearSelectedNodes(); // to unmark previously active node | ||||
|  | ||||
|     utils.showMessage("Created!"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function sortAlphabetically(noteId) { | ||||
| async function sortAlphabetically(noteId) { | ||||
|     await server.put('notes/' + noteId + '/sort'); | ||||
|  | ||||
|     await reload(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function noteExists(noteId) { | ||||
| async function noteExists(noteId) { | ||||
|     return !!treeCache.getNote(noteId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getInstanceName() { | ||||
| function getInstanceName() { | ||||
|     return instanceName; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getBranch(branchId) { | ||||
| function getBranch(branchId) { | ||||
|     return branchMap[branchId]; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+o', e => { | ||||
| $(document).bind('keydown', 'ctrl+o', e => { | ||||
|     const node = getCurrentNode(); | ||||
|     const parentNoteId = node.data.parentNoteId; | ||||
|     const isProtected = treeUtils.getParentProtectedStatus(node); | ||||
| @@ -972,27 +983,27 @@ const treeService = (function() { | ||||
|     createNote(node, parentNoteId, 'after', isProtected); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+p', e => { | ||||
| $(document).bind('keydown', 'ctrl+p', e => { | ||||
|     const node = getCurrentNode(); | ||||
|  | ||||
|     createNote(node, node.data.noteId, 'into', node.data.isProtected); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+del', e => { | ||||
| $(document).bind('keydown', 'ctrl+del', e => { | ||||
|     const node = getCurrentNode(); | ||||
|  | ||||
|     treeChanges.deleteNodes([node]); | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+.', scrollToCurrentNote); | ||||
| $(document).bind('keydown', 'ctrl+.', scrollToCurrentNote); | ||||
|  | ||||
|     $(window).bind('hashchange', function() { | ||||
| $(window).bind('hashchange', function() { | ||||
|     const notePath = getNotePathFromAddress(); | ||||
|  | ||||
|     if (getCurrentNotePath() !== notePath) { | ||||
| @@ -1000,9 +1011,9 @@ const treeService = (function() { | ||||
|  | ||||
|         activateNode(notePath); | ||||
|     } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
| if (utils.isElectron()) { | ||||
|     $(document).bind('keydown', 'alt+left', e => { | ||||
|         window.history.back(); | ||||
|  | ||||
| @@ -1014,13 +1025,13 @@ const treeService = (function() { | ||||
|  | ||||
|         e.preventDefault(); | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $createTopLevelNoteButton.click(createNewTopLevelNote); | ||||
|     $collapseTreeButton.click(collapseTree); | ||||
|     $scrollToCurrentNoteButton.click(scrollToCurrentNote); | ||||
| $createTopLevelNoteButton.click(createNewTopLevelNote); | ||||
| $collapseTreeButton.click(collapseTree); | ||||
| $scrollToCurrentNoteButton.click(scrollToCurrentNote); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     reload, | ||||
|     collapseTree, | ||||
|     scrollToCurrentNote, | ||||
| @@ -1046,5 +1057,4 @@ const treeService = (function() { | ||||
|     getInstanceName, | ||||
|     getBranch, | ||||
|     getNote | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,10 +1,13 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const noteType = (function() { | ||||
|     const $executeScriptButton = $("#execute-script-button"); | ||||
|     const noteTypeModel = new NoteTypeModel(); | ||||
| import treeService from './note_tree.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
|     function NoteTypeModel() { | ||||
| const $executeScriptButton = $("#execute-script-button"); | ||||
| const noteTypeModel = new NoteTypeModel(); | ||||
|  | ||||
| function NoteTypeModel() { | ||||
|     const self = this; | ||||
|  | ||||
|     this.type = ko.observable('text'); | ||||
| @@ -127,11 +130,11 @@ const noteType = (function() { | ||||
|     this.updateExecuteScriptButtonVisibility = function() { | ||||
|         $executeScriptButton.toggle(self.mime().startsWith('application/javascript')); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     ko.applyBindings(noteTypeModel, document.getElementById('note-type')); | ||||
| ko.applyBindings(noteTypeModel, document.getElementById('note-type')); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     getNoteType: () => noteTypeModel.type(), | ||||
|     setNoteType: type => noteTypeModel.type(type), | ||||
|  | ||||
| @@ -141,5 +144,4 @@ const noteType = (function() { | ||||
|  | ||||
|         noteTypeModel.updateExecuteScriptButtonVisibility(); | ||||
|     } | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,27 +1,31 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const protected_session = (function() { | ||||
|     const $dialog = $("#protected-session-password-dialog"); | ||||
|     const $passwordForm = $("#protected-session-password-form"); | ||||
|     const $password = $("#protected-session-password"); | ||||
|     const $noteDetailWrapper = $("#note-detail-wrapper"); | ||||
|     const $protectButton = $("#protect-button"); | ||||
|     const $unprotectButton = $("#unprotect-button"); | ||||
| import treeService from './note_tree.js'; | ||||
| import noteEditor from './note_editor.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
|  | ||||
|     let protectedSessionDeferred = null; | ||||
|     let lastProtectedSessionOperationDate = null; | ||||
|     let protectedSessionTimeout = null; | ||||
|     let protectedSessionId = null; | ||||
| const $dialog = $("#protected-session-password-dialog"); | ||||
| const $passwordForm = $("#protected-session-password-form"); | ||||
| const $password = $("#protected-session-password"); | ||||
| const $noteDetailWrapper = $("#note-detail-wrapper"); | ||||
| const $protectButton = $("#protect-button"); | ||||
| const $unprotectButton = $("#unprotect-button"); | ||||
|  | ||||
|     $(document).ready(() => { | ||||
| let protectedSessionDeferred = null; | ||||
| let lastProtectedSessionOperationDate = null; | ||||
| let protectedSessionTimeout = null; | ||||
| let protectedSessionId = null; | ||||
|  | ||||
| $(document).ready(() => { | ||||
|     server.get('settings/all').then(settings => protectedSessionTimeout = settings.protected_session_timeout); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     function setProtectedSessionTimeout(encSessTimeout) { | ||||
| function setProtectedSessionTimeout(encSessTimeout) { | ||||
|     protectedSessionTimeout = encSessTimeout; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function ensureProtectedSession(requireProtectedSession, modal) { | ||||
| function ensureProtectedSession(requireProtectedSession, modal) { | ||||
|     const dfd = $.Deferred(); | ||||
|  | ||||
|     if (requireProtectedSession && !isProtectedSessionAvailable()) { | ||||
| @@ -47,9 +51,9 @@ const protected_session = (function() { | ||||
|     } | ||||
|  | ||||
|     return dfd.promise(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function setupProtectedSession() { | ||||
| async function setupProtectedSession() { | ||||
|     const password = $password.val(); | ||||
|     $password.val(""); | ||||
|  | ||||
| @@ -76,9 +80,9 @@ const protected_session = (function() { | ||||
|  | ||||
|         protectedSessionDeferred = null; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function ensureDialogIsClosed() { | ||||
| function ensureDialogIsClosed() { | ||||
|     // this may fal if the dialog has not been previously opened | ||||
|     try { | ||||
|         $dialog.dialog('close'); | ||||
| @@ -86,31 +90,31 @@ const protected_session = (function() { | ||||
|     catch (e) {} | ||||
|  | ||||
|     $password.val(''); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function enterProtectedSession(password) { | ||||
| async function enterProtectedSession(password) { | ||||
|     return await server.post('login/protected', { | ||||
|         password: password | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getProtectedSessionId() { | ||||
| function getProtectedSessionId() { | ||||
|     return protectedSessionId; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function resetProtectedSession() { | ||||
| function resetProtectedSession() { | ||||
|     protectedSessionId = null; | ||||
|  | ||||
|     // most secure solution - guarantees nothing remained in memory | ||||
|     // since this expires because user doesn't use the app, it shouldn't be disruptive | ||||
|     utils.reloadApp(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function isProtectedSessionAvailable() { | ||||
| function isProtectedSessionAvailable() { | ||||
|     return protectedSessionId !== null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function protectNoteAndSendToServer() { | ||||
| async function protectNoteAndSendToServer() { | ||||
|     await ensureProtectedSession(true, true); | ||||
|  | ||||
|     const note = noteEditor.getCurrentNote(); | ||||
| @@ -124,9 +128,9 @@ const protected_session = (function() { | ||||
|     treeService.setProtected(note.detail.noteId, note.detail.isProtected); | ||||
|  | ||||
|     noteEditor.setNoteBackgroundIfProtected(note); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function unprotectNoteAndSendToServer() { | ||||
| async function unprotectNoteAndSendToServer() { | ||||
|     await ensureProtectedSession(true, true); | ||||
|  | ||||
|     const note = noteEditor.getCurrentNote(); | ||||
| @@ -140,15 +144,15 @@ const protected_session = (function() { | ||||
|     treeService.setProtected(note.detail.noteId, note.detail.isProtected); | ||||
|  | ||||
|     noteEditor.setNoteBackgroundIfProtected(note); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function touchProtectedSession() { | ||||
| function touchProtectedSession() { | ||||
|     if (isProtectedSessionAvailable()) { | ||||
|         lastProtectedSessionOperationDate = new Date(); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function protectSubTree(noteId, protect) { | ||||
| async function protectSubTree(noteId, protect) { | ||||
|     await ensureProtectedSession(true, true); | ||||
|  | ||||
|     await server.put('notes/' + noteId + "/protect-sub-tree/" + (protect ? 1 : 0)); | ||||
| @@ -157,24 +161,24 @@ const protected_session = (function() { | ||||
|  | ||||
|     treeService.reload(); | ||||
|     noteEditor.reload(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $passwordForm.submit(() => { | ||||
| $passwordForm.submit(() => { | ||||
|     setupProtectedSession(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     setInterval(() => { | ||||
| setInterval(() => { | ||||
|     if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { | ||||
|         resetProtectedSession(); | ||||
|     } | ||||
|     }, 5000); | ||||
| }, 5000); | ||||
|  | ||||
|     $protectButton.click(protectNoteAndSendToServer); | ||||
|     $unprotectButton.click(unprotectNoteAndSendToServer); | ||||
| $protectButton.click(protectNoteAndSendToServer); | ||||
| $unprotectButton.click(unprotectNoteAndSendToServer); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     setProtectedSessionTimeout, | ||||
|     ensureProtectedSession, | ||||
|     resetProtectedSession, | ||||
| @@ -185,5 +189,4 @@ const protected_session = (function() { | ||||
|     touchProtectedSession, | ||||
|     protectSubTree, | ||||
|     ensureDialogIsClosed | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,3 +1,5 @@ | ||||
| import treeService from './note_tree.js'; | ||||
|  | ||||
| function ScriptApi(startNote, currentNote) { | ||||
|     const $pluginButtons = $("#plugin-buttons"); | ||||
|  | ||||
| @@ -52,3 +54,5 @@ function ScriptApi(startNote, currentNote) { | ||||
|         runOnServer | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default ScriptApi; | ||||
| @@ -1,3 +1,8 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import ScriptApi from './script_api.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| function ScriptContext(startNote, allNotes) { | ||||
|     const modules = {}; | ||||
|  | ||||
| @@ -19,3 +24,5 @@ function ScriptContext(startNote, allNotes) { | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| export default ScriptContext; | ||||
| @@ -1,5 +1,7 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import treeService from './note_tree.js'; | ||||
|  | ||||
| const $tree = $("#tree"); | ||||
| const $searchInput = $("input[name='search-text']"); | ||||
| const $resetSearchButton = $("#reset-search-button"); | ||||
|   | ||||
| @@ -1,5 +1,9 @@ | ||||
| const server = (function() { | ||||
|     function getHeaders() { | ||||
| "use strict"; | ||||
|  | ||||
| import protected_session from './protected_session.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| function getHeaders() { | ||||
|     let protectedSessionId = null; | ||||
|  | ||||
|     try { // this is because protected session might not be declared in some cases - like when it's included in migration page | ||||
| @@ -13,28 +17,28 @@ const server = (function() { | ||||
|         protected_session_id: protectedSessionId, | ||||
|         source_id: glob.sourceId | ||||
|     }; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function get(url) { | ||||
| async function get(url) { | ||||
|     return await call('GET', url); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function post(url, data) { | ||||
| async function post(url, data) { | ||||
|     return await call('POST', url, data); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function put(url, data) { | ||||
| async function put(url, data) { | ||||
|     return await call('PUT', url, data); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function remove(url) { | ||||
| async function remove(url) { | ||||
|     return await call('DELETE', url); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     let i = 1; | ||||
|     const reqResolves = {}; | ||||
| let i = 1; | ||||
| const reqResolves = {}; | ||||
|  | ||||
|     async function call(method, url, data) { | ||||
| async function call(method, url, data) { | ||||
|     if (utils.isElectron()) { | ||||
|         const ipc = require('electron').ipcRenderer; | ||||
|         const requestId = i++; | ||||
| @@ -56,9 +60,9 @@ const server = (function() { | ||||
|     else { | ||||
|         return await ajax(url, method, data); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
| if (utils.isElectron()) { | ||||
|     const ipc = require('electron').ipcRenderer; | ||||
|  | ||||
|     ipc.on('server-response', (event, arg) => { | ||||
| @@ -68,9 +72,9 @@ const server = (function() { | ||||
|  | ||||
|         delete reqResolves[arg.requestId]; | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function ajax(url, method, data) { | ||||
| async function ajax(url, method, data) { | ||||
|     const options = { | ||||
|         url: baseApiUrl + url, | ||||
|         type: method, | ||||
| @@ -87,9 +91,9 @@ const server = (function() { | ||||
|         utils.showError(message); | ||||
|         utils.throwError(message); | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     get, | ||||
|     post, | ||||
|     put, | ||||
| @@ -97,5 +101,4 @@ const server = (function() { | ||||
|     ajax, | ||||
|     // don't remove, used from CKEditor image upload! | ||||
|     getHeaders | ||||
|     } | ||||
| })(); | ||||
| }; | ||||
| @@ -1,7 +1,8 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const syncService = (function() { | ||||
|     async function syncNow() { | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| async function syncNow() { | ||||
|     const result = await server.post('sync/now'); | ||||
|  | ||||
|     if (result.success) { | ||||
| @@ -14,18 +15,17 @@ const syncService = (function() { | ||||
|  | ||||
|         utils.showError("Sync failed: " + result.message); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $("#sync-now-button").click(syncNow); | ||||
| $("#sync-now-button").click(syncNow); | ||||
|  | ||||
|     async function forceNoteSync(noteId) { | ||||
| async function forceNoteSync(noteId) { | ||||
|     const result = await server.post('sync/force-note-sync/' + noteId); | ||||
|  | ||||
|     utils.showMessage("Note added to sync queue."); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     syncNow, | ||||
|     forceNoteSync | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,7 +1,9 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const treeChanges = (function() { | ||||
|     async function moveBeforeNode(nodesToMove, beforeNode) { | ||||
| import treeService from './note_tree.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| async function moveBeforeNode(nodesToMove, beforeNode) { | ||||
|     for (const nodeToMove of nodesToMove) { | ||||
|         const resp = await server.put('tree/' + nodeToMove.data.branchId + '/move-before/' + beforeNode.data.branchId); | ||||
|  | ||||
| @@ -12,9 +14,9 @@ const treeChanges = (function() { | ||||
|  | ||||
|         changeNode(nodeToMove, node => node.moveTo(beforeNode, 'before')); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function moveAfterNode(nodesToMove, afterNode) { | ||||
| async function moveAfterNode(nodesToMove, afterNode) { | ||||
|     nodesToMove.reverse(); // need to reverse to keep the note order | ||||
|  | ||||
|     for (const nodeToMove of nodesToMove) { | ||||
| @@ -27,9 +29,9 @@ const treeChanges = (function() { | ||||
|  | ||||
|         changeNode(nodeToMove, node => node.moveTo(afterNode, 'after')); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function moveToNode(nodesToMove, toNode) { | ||||
| async function moveToNode(nodesToMove, toNode) { | ||||
|     for (const nodeToMove of nodesToMove) { | ||||
|         const resp = await server.put('tree/' + nodeToMove.data.branchId + '/move-to/' + toNode.data.noteId); | ||||
|  | ||||
| @@ -53,9 +55,9 @@ const treeChanges = (function() { | ||||
|             toNode.setExpanded(true); | ||||
|         }); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function deleteNodes(nodes) { | ||||
| async function deleteNodes(nodes) { | ||||
|     if (nodes.length === 0 || !confirm('Are you sure you want to delete select note(s) and all the sub-notes?')) { | ||||
|         return; | ||||
|     } | ||||
| @@ -86,9 +88,9 @@ const treeChanges = (function() { | ||||
|     treeService.reload(); | ||||
|  | ||||
|     utils.showMessage("Note(s) has been deleted."); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function moveNodeUpInHierarchy(node) { | ||||
| async function moveNodeUpInHierarchy(node) { | ||||
|     if (utils.isTopLevelNode(node)) { | ||||
|         return; | ||||
|     } | ||||
| @@ -106,9 +108,9 @@ const treeChanges = (function() { | ||||
|     } | ||||
|  | ||||
|     changeNode(node, node => node.moveTo(node.getParent(), 'after')); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function changeNode(node, func) { | ||||
| function changeNode(node, func) { | ||||
|     utils.assertArguments(node.data.parentNoteId, node.data.noteId); | ||||
|  | ||||
|     treeService.removeParentChildRelation(node.data.parentNoteId, node.data.noteId); | ||||
| @@ -120,13 +122,12 @@ const treeChanges = (function() { | ||||
|     treeService.setParentChildRelation(node.data.branchId, node.data.parentNoteId, node.data.noteId); | ||||
|  | ||||
|     treeService.setCurrentNotePathToHash(node); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     moveBeforeNode, | ||||
|     moveAfterNode, | ||||
|     moveToNode, | ||||
|     deleteNodes, | ||||
|     moveNodeUpInHierarchy | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,23 +1,24 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const treeUtils = (function() { | ||||
|     const $tree = $("#tree"); | ||||
| import utils from './utils.js'; | ||||
|  | ||||
|     function getParentProtectedStatus(node) { | ||||
| const $tree = $("#tree"); | ||||
|  | ||||
| function getParentProtectedStatus(node) { | ||||
|     return utils.isTopLevelNode(node) ? 0 : node.getParent().data.isProtected; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNodeByKey(key) { | ||||
| function getNodeByKey(key) { | ||||
|     return $tree.fancytree('getNodeByKey', key); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNoteIdFromNotePath(notePath) { | ||||
| function getNoteIdFromNotePath(notePath) { | ||||
|     const path = notePath.split("/"); | ||||
|  | ||||
|     return path[path.length - 1]; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getNotePath(node) { | ||||
| function getNotePath(node) { | ||||
|     const path = []; | ||||
|  | ||||
|     while (node && !utils.isRootNode(node)) { | ||||
| @@ -29,12 +30,11 @@ const treeUtils = (function() { | ||||
|     } | ||||
|  | ||||
|     return path.reverse().join("/"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     getParentProtectedStatus, | ||||
|     getNodeByKey, | ||||
|     getNotePath, | ||||
|     getNoteIdFromNotePath, | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,11 +1,14 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const utils = (function() { | ||||
|     function reloadApp() { | ||||
|         window.location.reload(true); | ||||
|     } | ||||
| import link from './link.js'; | ||||
| import messaging from './messaging.js'; | ||||
| import ScriptContext from './script_context.js'; | ||||
|  | ||||
|     function showMessage(message) { | ||||
| function reloadApp() { | ||||
|     window.location.reload(true); | ||||
| } | ||||
|  | ||||
| function showMessage(message) { | ||||
|     console.log(now(), "message: ", message); | ||||
|  | ||||
|     $.notify({ | ||||
| @@ -16,9 +19,9 @@ const utils = (function() { | ||||
|         type: 'success', | ||||
|         delay: 3000 | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function showError(message, delay = 10000) { | ||||
| function showError(message, delay = 10000) { | ||||
|     console.log(now(), "error: ", message); | ||||
|  | ||||
|     $.notify({ | ||||
| @@ -29,82 +32,82 @@ const utils = (function() { | ||||
|         type: 'danger', | ||||
|         delay: delay | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function throwError(message) { | ||||
| function throwError(message) { | ||||
|     messaging.logError(message); | ||||
|  | ||||
|     throw new Error(message); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function parseDate(str) { | ||||
| function parseDate(str) { | ||||
|     try { | ||||
|         return new Date(Date.parse(str)); | ||||
|     } | ||||
|     catch (e) { | ||||
|         throw new Error("Can't parse date from " + str + ": " + e.stack); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function padNum(num) { | ||||
| function padNum(num) { | ||||
|     return (num <= 9 ? "0" : "") + num; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatTime(date) { | ||||
| function formatTime(date) { | ||||
|     return padNum(date.getHours()) + ":" + padNum(date.getMinutes()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatTimeWithSeconds(date) { | ||||
| function formatTimeWithSeconds(date) { | ||||
|     return padNum(date.getHours()) + ":" + padNum(date.getMinutes()) + ":" + padNum(date.getSeconds()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatDate(date) { | ||||
| function formatDate(date) { | ||||
|     return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatDateISO(date) { | ||||
| function formatDateISO(date) { | ||||
|     return date.getFullYear() + "-" + padNum(date.getMonth() + 1) + "-" + padNum(date.getDate()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatDateTime(date) { | ||||
| function formatDateTime(date) { | ||||
|     return formatDate(date) + " " + formatTime(date); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function now() { | ||||
| function now() { | ||||
|     return formatTimeWithSeconds(new Date()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function isElectron() { | ||||
| function isElectron() { | ||||
|     return window && window.process && window.process.type; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function assertArguments() { | ||||
| function assertArguments() { | ||||
|     for (const i in arguments) { | ||||
|         if (!arguments[i]) { | ||||
|             throwError(`Argument idx#${i} should not be falsy: ${arguments[i]}`); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function assert(expr, message) { | ||||
| function assert(expr, message) { | ||||
|     if (!expr) { | ||||
|         throwError(message); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function isTopLevelNode(node) { | ||||
| function isTopLevelNode(node) { | ||||
|     return isRootNode(node.getParent()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function isRootNode(node) { | ||||
| function isRootNode(node) { | ||||
|     return node.key === "root_1"; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function escapeHtml(str) { | ||||
| function escapeHtml(str) { | ||||
|     return $('<div/>').text(str).html(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function stopWatch(what, func) { | ||||
| async function stopWatch(what, func) { | ||||
|     const start = new Date(); | ||||
|  | ||||
|     const ret = await func(); | ||||
| @@ -114,21 +117,21 @@ const utils = (function() { | ||||
|     console.log(`${what} took ${tookMs}ms`); | ||||
|  | ||||
|     return ret; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function executeBundle(bundle) { | ||||
| async function executeBundle(bundle) { | ||||
|     const apiContext = ScriptContext(bundle.note, bundle.allNotes); | ||||
|  | ||||
|     return await (function () { | ||||
|         return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); | ||||
|     }.call(apiContext)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatValueWithWhitespace(val) { | ||||
| function formatValueWithWhitespace(val) { | ||||
|     return /[^\w_-]/.test(val) ? '"' + val + '"' : val; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatLabel(attr) { | ||||
| function formatLabel(attr) { | ||||
|     let str = "@" + formatValueWithWhitespace(attr.name); | ||||
|  | ||||
|     if (attr.value !== "") { | ||||
| @@ -136,11 +139,11 @@ const utils = (function() { | ||||
|     } | ||||
|  | ||||
|     return str; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     const CKEDITOR = {"js": ["libraries/ckeditor/ckeditor.js"]}; | ||||
| const CKEDITOR = {"js": ["libraries/ckeditor/ckeditor.js"]}; | ||||
|  | ||||
|     const CODE_MIRROR = { | ||||
| const CODE_MIRROR = { | ||||
|     js: [ | ||||
|         "libraries/codemirror/codemirror.js", | ||||
|         "libraries/codemirror/addon/mode/loadmode.js", | ||||
| @@ -156,11 +159,11 @@ const utils = (function() { | ||||
|         "libraries/codemirror/codemirror.css", | ||||
|         "libraries/codemirror/addon/lint/lint.css" | ||||
|     ] | ||||
|     }; | ||||
| }; | ||||
|  | ||||
|     const ESLINT = {js: ["libraries/eslint.js"]}; | ||||
| const ESLINT = {js: ["libraries/eslint.js"]}; | ||||
|  | ||||
|     async function requireLibrary(library) { | ||||
| async function requireLibrary(library) { | ||||
|     if (library.css) { | ||||
|         library.css.map(cssUrl => requireCss(cssUrl)); | ||||
|     } | ||||
| @@ -170,11 +173,11 @@ const utils = (function() { | ||||
|             await requireScript(scriptUrl); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     const dynamicallyLoadedScripts = []; | ||||
| const dynamicallyLoadedScripts = []; | ||||
|  | ||||
|     async function requireScript(url) { | ||||
| async function requireScript(url) { | ||||
|     if (!dynamicallyLoadedScripts.includes(url)) { | ||||
|         dynamicallyLoadedScripts.push(url); | ||||
|  | ||||
| @@ -184,9 +187,9 @@ const utils = (function() { | ||||
|             cache: true | ||||
|         }) | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     async function requireCss(url) { | ||||
| async function requireCss(url) { | ||||
|     const css = Array | ||||
|         .from(document.querySelectorAll('link')) | ||||
|         .map(scr => scr.href); | ||||
| @@ -194,14 +197,14 @@ const utils = (function() { | ||||
|     if (!css.includes(url)) { | ||||
|         $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', url)); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getHost() { | ||||
| function getHost() { | ||||
|     const url = new URL(window.location.href); | ||||
|     return url.protocol + "//" + url.hostname + ":" + url.port; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function download(url) { | ||||
| function download(url) { | ||||
|     if (isElectron()) { | ||||
|         const remote = require('electron').remote; | ||||
|  | ||||
| @@ -210,9 +213,9 @@ const utils = (function() { | ||||
|     else { | ||||
|         window.location.href = url; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function toObject(array, fn) { | ||||
| function toObject(array, fn) { | ||||
|     const obj = {}; | ||||
|  | ||||
|     for (const item of array) { | ||||
| @@ -222,9 +225,9 @@ const utils = (function() { | ||||
|     } | ||||
|  | ||||
|     return obj; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function randomString(len) { | ||||
| function randomString(len) { | ||||
|     let text = ""; | ||||
|     const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
|  | ||||
| @@ -233,9 +236,9 @@ const utils = (function() { | ||||
|     } | ||||
|  | ||||
|     return text; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     reloadApp, | ||||
|     showMessage, | ||||
|     showError, | ||||
| @@ -266,5 +269,4 @@ const utils = (function() { | ||||
|     download, | ||||
|     toObject, | ||||
|     randomString | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -521,43 +521,6 @@ | ||||
|  | ||||
|     <script src="/javascripts/bootstrap.js" type="module"></script> | ||||
|  | ||||
|     <script src="/javascripts/utils.js"></script> | ||||
|     <script src="/javascripts/init.js"></script> | ||||
|     <script src="/javascripts/server.js"></script> | ||||
|  | ||||
|     <!-- Tree scripts --> | ||||
|     <script src="/javascripts/note_tree.js"></script> | ||||
|     <script src="/javascripts/tree_changes.js"></script> | ||||
|     <script src="/javascripts/cloning.js"></script> | ||||
|     <script src="/javascripts/tree_utils.js"></script> | ||||
|     <script src="/javascripts/drag_and_drop.js"></script> | ||||
|     <script src="/javascripts/context_menu.js"></script> | ||||
|     <script src="/javascripts/export.js"></script> | ||||
|  | ||||
|     <!-- Note detail --> | ||||
|     <script src="/javascripts/note_editor.js"></script> | ||||
|     <script src="/javascripts/protected_session.js"></script> | ||||
|     <script src="/javascripts/note_type.js"></script> | ||||
|  | ||||
|     <!-- dialogs --> | ||||
|     <script src="/javascripts/dialogs/recent_notes.js"></script> | ||||
|     <script src="/javascripts/dialogs/add_link.js"></script> | ||||
|     <script src="/javascripts/dialogs/jump_to_note.js"></script> | ||||
|     <script src="/javascripts/dialogs/settings.js"></script> | ||||
|     <script src="/javascripts/dialogs/note_history.js"></script> | ||||
|     <script src="/javascripts/dialogs/recent_changes.js"></script> | ||||
|     <script src="/javascripts/dialogs/event_log.js"></script> | ||||
|     <script src="/javascripts/dialogs/edit_tree_prefix.js"></script> | ||||
|     <script src="/javascripts/dialogs/sql_console.js"></script> | ||||
|     <script src="/javascripts/dialogs/note_source.js"></script> | ||||
|     <script src="/javascripts/dialogs/labels.js"></script> | ||||
|  | ||||
|     <script src="/javascripts/link.js"></script> | ||||
|     <script src="/javascripts/sync.js"></script> | ||||
|     <script src="/javascripts/messaging.js"></script> | ||||
|     <script src="/javascripts/script_context.js"></script> | ||||
|     <script src="/javascripts/script_api.js"></script> | ||||
|  | ||||
|     <script type="text/javascript"> | ||||
|       // we hide container initally because otherwise it is rendered first without CSS and then flickers into | ||||
|       // final form which is pretty ugly. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user