From 788ac43ad1ab577611f82c1abecbbc5494f70abb Mon Sep 17 00:00:00 2001 From: azivner Date: Mon, 26 Mar 2018 22:29:14 -0400 Subject: [PATCH] further refactorings, got rid of init.js --- src/public/javascripts/dialogs/labels.js | 4 +- src/public/javascripts/services/attachment.js | 29 +++ src/public/javascripts/services/bootstrap.js | 70 ++++--- src/public/javascripts/services/bundle.js | 12 +- .../javascripts/services/entrypoints.js | 130 ++++++++++++ src/public/javascripts/services/init.js | 194 ------------------ .../javascripts/services/note_detail.js | 4 + src/public/javascripts/services/tooltip.js | 32 +++ src/public/javascripts/services/tree.js | 11 +- .../javascripts/services/tree_changes.js | 4 +- 10 files changed, 264 insertions(+), 226 deletions(-) create mode 100644 src/public/javascripts/services/attachment.js create mode 100644 src/public/javascripts/services/entrypoints.js delete mode 100644 src/public/javascripts/services/init.js create mode 100644 src/public/javascripts/services/tooltip.js diff --git a/src/public/javascripts/dialogs/labels.js b/src/public/javascripts/dialogs/labels.js index 178639519..1476a991b 100644 --- a/src/public/javascripts/dialogs/labels.js +++ b/src/public/javascripts/dialogs/labels.js @@ -170,7 +170,7 @@ $(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 - // because we have overriden filter() function in init.js + // because we have overriden filter() function in autocomplete.js source: labelNames.map(attr => { return { label: attr, @@ -200,7 +200,7 @@ $(document).on('focus', '.label-value', async function (e) { $(this).autocomplete({ // shouldn't be required and autocomplete should just accept array of strings, but that fails - // because we have overriden filter() function in init.js + // because we have overriden filter() function in autocomplete.js source: labelValues.map(attr => { return { label: attr, diff --git a/src/public/javascripts/services/attachment.js b/src/public/javascripts/services/attachment.js new file mode 100644 index 000000000..20689f354 --- /dev/null +++ b/src/public/javascripts/services/attachment.js @@ -0,0 +1,29 @@ +import noteDetailService from "./note_detail.js"; +import treeService from "./tree.js"; +import server from "./server.js"; + +function uploadAttachment() { + $("#attachment-upload").trigger('click'); +} + +$("#attachment-upload").change(async function() { + const formData = new FormData(); + formData.append('upload', this.files[0]); + + const resp = await $.ajax({ + url: baseApiUrl + 'attachments/upload/' + noteDetailService.getCurrentNoteId(), + headers: server.getHeaders(), + data: formData, + type: 'POST', + contentType: false, // NEEDED, DON'T OMIT THIS + processData: false, // NEEDED, DON'T OMIT THIS + }); + + await treeService.reload(); + + await treeService.activateNode(resp.noteId); +}); + +export default { + uploadAttachment +} \ No newline at end of file diff --git a/src/public/javascripts/services/bootstrap.js b/src/public/javascripts/services/bootstrap.js index a31c0b846..a1dcc5f1b 100644 --- a/src/public/javascripts/services/bootstrap.js +++ b/src/public/javascripts/services/bootstrap.js @@ -26,8 +26,10 @@ import treeChanges from './tree_changes.js'; import treeUtils from './tree_utils.js'; import utils from './utils.js'; import server from './server.js'; - -import './init.js'; +import entrypoints from './entrypoints.js'; +import tooltip from './tooltip.js'; +import bundle from "./bundle.js"; +import treeCache from "./tree_cache.js"; // required for CKEditor image upload plugin window.glob.getCurrentNode = treeService.getCurrentNode; @@ -38,32 +40,52 @@ window.glob.getCurrentNote = noteDetailService.getCurrentNote; window.glob.requireLibrary = utils.requireLibrary; window.glob.ESLINT = utils.ESLINT; -utils.bindShortcut('ctrl+l', addLinkDialog.showDialog); +window.onerror = function (msg, url, lineNo, columnNo, error) { + const string = msg.toLowerCase(); -$("#jump-to-note-button").click(jumpToNoteDialog.showDialog); -utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog); + let message = "Uncaught error: "; -$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions); + if (string.indexOf("script error") > -1){ + message += 'No details available'; + } + else { + message += [ + 'Message: ' + msg, + 'URL: ' + url, + 'Line: ' + lineNo, + 'Column: ' + columnNo, + 'Error object: ' + JSON.stringify(error) + ].join(' - '); + } -$("#show-source-button").click(noteSourceDialog.showDialog); -utils.bindShortcut('ctrl+u', noteSourceDialog.showDialog); + messagingService.logError(message); -$("#recent-changes-button").click(recentChangesDialog.showDialog); + return false; +}; -$("#recent-notes-button").click(recentNotesDialog.showDialog); -utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog); - -$("#toggle-search-button").click(searchTreeService.toggleSearch); -utils.bindShortcut('ctrl+s', searchTreeService.toggleSearch); - -$(".show-labels-button").click(labelsDialog.showDialog); -utils.bindShortcut('alt+l', labelsDialog.showDialog); - -$("#settings-button").click(settingsDialog.showDialog); - -utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog); +$("#logout-button").toggle(!utils.isElectron()); if (utils.isElectron()) { - utils.bindShortcut('alt+left', window.history.back); - utils.bindShortcut('alt+right', window.history.forward); -} \ No newline at end of file + require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) { + // this might occur when day note had to be created + if (!await treeCache.getNote(parentNoteId)) { + await treeService.reload(); + } + + await treeService.activateNode(parentNoteId); + + setTimeout(() => { + const node = treeService.getCurrentNode(); + + treeService.createNote(node, node.data.noteId, 'into', node.data.isProtected); + }, 500); + }); +} + +treeService.showTree(); + +entrypoints.registerEntrypoints(); + +tooltip.setupTooltip(); + +$(document).ready(bundle.executeStartupBundles); \ No newline at end of file diff --git a/src/public/javascripts/services/bundle.js b/src/public/javascripts/services/bundle.js index 578a1ba9a..89157a564 100644 --- a/src/public/javascripts/services/bundle.js +++ b/src/public/javascripts/services/bundle.js @@ -1,4 +1,5 @@ import ScriptContext from "./script_context.js"; +import server from "./server.js"; async function executeBundle(bundle) { const apiContext = ScriptContext(bundle.note, bundle.allNotes); @@ -8,6 +9,15 @@ async function executeBundle(bundle) { }.call(apiContext)); } +async function executeStartupBundles() { + const scriptBundles = await server.get("script/startup"); + + for (const bundle of scriptBundles) { + await executeBundle(bundle); + } +} + export default { - executeBundle + executeBundle, + executeStartupBundles } \ No newline at end of file diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js new file mode 100644 index 000000000..956d86cb2 --- /dev/null +++ b/src/public/javascripts/services/entrypoints.js @@ -0,0 +1,130 @@ +import utils from "./utils.js"; +import treeService from "./tree.js"; +import linkService from "./link.js"; +import attachmentService from "./attachment.js"; +import noteRevisionsDialog from "../dialogs/note_revisions.js"; +import settingsDialog from "../dialogs/settings.js"; +import addLinkDialog from "../dialogs/add_link.js"; +import recentNotesDialog from "../dialogs/recent_notes.js"; +import jumpToNoteDialog from "../dialogs/jump_to_note.js"; +import noteSourceDialog from "../dialogs/note_source.js"; +import recentChangesDialog from "../dialogs/recent_changes.js"; +import sqlConsoleDialog from "../dialogs/sql_console.js"; +import searchTreeService from "./search_tree.js"; +import labelsDialog from "../dialogs/labels.js"; + +function registerEntrypoints() { + // 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; + + utils.bindShortcut('ctrl+l', addLinkDialog.showDialog); + + $("#jump-to-note-button").click(jumpToNoteDialog.showDialog); + utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog); + + $("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions); + + $("#show-source-button").click(noteSourceDialog.showDialog); + utils.bindShortcut('ctrl+u', noteSourceDialog.showDialog); + + $("#recent-changes-button").click(recentChangesDialog.showDialog); + + $("#recent-notes-button").click(recentNotesDialog.showDialog); + utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog); + + $("#toggle-search-button").click(searchTreeService.toggleSearch); + utils.bindShortcut('ctrl+s', searchTreeService.toggleSearch); + + $(".show-labels-button").click(labelsDialog.showDialog); + utils.bindShortcut('alt+l', labelsDialog.showDialog); + + $("#settings-button").click(settingsDialog.showDialog); + + utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog); + + if (utils.isElectron()) { + utils.bindShortcut('alt+left', window.history.back); + utils.bindShortcut('alt+right', window.history.forward); + } + + utils.bindShortcut('alt+m', e => $(".hide-toggle").toggleClass("suppressed")); + + // hide (toggle) everything except for the note content for distraction free writing + utils.bindShortcut('alt+t', e => { + const date = new Date(); + const dateString = utils.formatDateTime(date); + + linkService.addTextToEditor(dateString); + }); + + utils.bindShortcut('f5', utils.reloadApp); + + utils.bindShortcut('ctrl+r', utils.reloadApp); + + $(document).bind('keydown', 'ctrl+shift+i', () => { + if (utils.isElectron()) { + require('electron').remote.getCurrentWindow().toggleDevTools(); + + return false; + } + }); + + $(document).bind('keydown', 'ctrl+f', () => { + if (utils.isElectron()) { + const searchInPage = require('electron-in-page-search').default; + const remote = require('electron').remote; + + const inPageSearch = searchInPage(remote.getCurrentWebContents()); + + inPageSearch.openSearchWindow(); + + return false; + } + }); + + utils.bindShortcut("ctrl+shift+up", () => { + const node = treeService.getCurrentNode(); + node.navigate($.ui.keyCode.UP, true); + + $("#note-detail").focus(); + }); + + utils.bindShortcut("ctrl+shift+down", () => { + const node = treeService.getCurrentNode(); + node.navigate($.ui.keyCode.DOWN, true); + + $("#note-detail").focus(); + }); + + $(document).bind('keydown', 'ctrl+-', () => { + if (utils.isElectron()) { + const webFrame = require('electron').webFrame; + + if (webFrame.getZoomFactor() > 0.2) { + webFrame.setZoomFactor(webFrame.getZoomFactor() - 0.1); + } + + return false; + } + }); + + $(document).bind('keydown', 'ctrl+=', () => { + if (utils.isElectron()) { + const webFrame = require('electron').webFrame; + + webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1); + + return false; + } + }); + + $("#note-title").bind('keydown', 'return', () => $("#note-detail").focus()); + + $("#upload-attachment-button").click(attachmentService.uploadAttachment); +} + +export default { + registerEntrypoints +} \ No newline at end of file diff --git a/src/public/javascripts/services/init.js b/src/public/javascripts/services/init.js deleted file mode 100644 index f50d392ad..000000000 --- a/src/public/javascripts/services/init.js +++ /dev/null @@ -1,194 +0,0 @@ -import treeService from './tree.js'; -import linkService from './link.js'; -import messagingService from './messaging.js'; -import noteDetailService from './note_detail.js'; -import treeUtils from './tree_utils.js'; -import utils from './utils.js'; -import server from './server.js'; -import bundleService from './bundle.js'; -import treeCache from "./tree_cache.js"; - -// 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; - -utils.bindShortcut('alt+m', e => $(".hide-toggle").toggleClass("suppressed")); - -// hide (toggle) everything except for the note content for distraction free writing -utils.bindShortcut('alt+t', e => { - const date = new Date(); - const dateString = utils.formatDateTime(date); - - linkService.addTextToEditor(dateString); -}); - -utils.bindShortcut('f5', utils.reloadApp); - -utils.bindShortcut('ctrl+r', utils.reloadApp); - -$(document).bind('keydown', 'ctrl+shift+i', () => { - if (utils.isElectron()) { - require('electron').remote.getCurrentWindow().toggleDevTools(); - - return false; - } -}); - -$(document).bind('keydown', 'ctrl+f', () => { - if (utils.isElectron()) { - const searchInPage = require('electron-in-page-search').default; - const remote = require('electron').remote; - - const inPageSearch = searchInPage(remote.getCurrentWebContents()); - - inPageSearch.openSearchWindow(); - - return false; - } -}); - -utils.bindShortcut("ctrl+shift+up", () => { - const node = treeService.getCurrentNode(); - node.navigate($.ui.keyCode.UP, true); - - $("#note-detail").focus(); -}); - -utils.bindShortcut("ctrl+shift+down", () => { - const node = treeService.getCurrentNode(); - node.navigate($.ui.keyCode.DOWN, true); - - $("#note-detail").focus(); -}); - -$(document).bind('keydown', 'ctrl+-', () => { - if (utils.isElectron()) { - const webFrame = require('electron').webFrame; - - if (webFrame.getZoomFactor() > 0.2) { - webFrame.setZoomFactor(webFrame.getZoomFactor() - 0.1); - } - - return false; - } -}); - -$(document).bind('keydown', 'ctrl+=', () => { - if (utils.isElectron()) { - const webFrame = require('electron').webFrame; - - webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1); - - return false; - } -}); - -$("#note-title").bind('keydown', 'return', () => $("#note-detail").focus()); - -$(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 - noteDetailService.saveNoteIfChanged(); -}); - -$(document).tooltip({ - items: "#note-detail a", - content: function(callback) { - const notePath = linkService.getNotePathFromLink($(this).attr("href")); - - if (notePath !== null) { - const noteId = treeUtils.getNoteIdFromNotePath(notePath); - - noteDetailService.loadNote(noteId).then(note => callback(note.content)); - } - }, - close: function(event, ui) - { - ui.tooltip.hover(function() - { - $(this).stop(true).fadeTo(400, 1); - }, - function() - { - $(this).fadeOut('400', function() - { - $(this).remove(); - }); - }); - } -}); - -window.onerror = function (msg, url, lineNo, columnNo, error) { - const string = msg.toLowerCase(); - - let message = "Uncaught error: "; - - if (string.indexOf("script error") > -1){ - message += 'No details available'; - } - else { - message += [ - 'Message: ' + msg, - 'URL: ' + url, - 'Line: ' + lineNo, - 'Column: ' + columnNo, - 'Error object: ' + JSON.stringify(error) - ].join(' - '); - } - - messagingService.logError(message); - - return false; -}; - -$("#logout-button").toggle(!utils.isElectron()); - -$(document).ready(() => { - server.get("script/startup").then(scriptBundles => { - for (const bundle of scriptBundles) { - bundleService.executeBundle(bundle); - } - }); -}); - -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 treeCache.getNote(parentNoteId)) { - await treeService.reload(); - } - - await treeService.activateNode(parentNoteId); - - setTimeout(() => { - const node = treeService.getCurrentNode(); - - treeService.createNote(node, node.data.noteId, 'into', node.data.isProtected); - }, 500); - }); -} - -function uploadAttachment() { - $("#attachment-upload").trigger('click'); -} - -$("#upload-attachment-button").click(uploadAttachment); - -$("#attachment-upload").change(async function() { - const formData = new FormData(); - formData.append('upload', this.files[0]); - - const resp = await $.ajax({ - url: baseApiUrl + 'attachments/upload/' + noteDetailService.getCurrentNoteId(), - headers: server.getHeaders(), - data: formData, - type: 'POST', - contentType: false, // NEEDED, DON'T OMIT THIS - processData: false, // NEEDED, DON'T OMIT THIS - }); - - await treeService.reload(); - - await treeService.activateNode(resp.noteId); -}); diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index b7a821768..c5fb61fd1 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -395,6 +395,10 @@ $(document).ready(() => { $noteDetail.attr("tabindex", 2); }); +// 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 +$(window).on('beforeunload', saveNoteIfChanged); + $(document).bind('keydown', "ctrl+return", executeCurrentNote); $executeScriptButton.click(executeCurrentNote()); diff --git a/src/public/javascripts/services/tooltip.js b/src/public/javascripts/services/tooltip.js new file mode 100644 index 000000000..eef064ad3 --- /dev/null +++ b/src/public/javascripts/services/tooltip.js @@ -0,0 +1,32 @@ +import noteDetailService from "./note_detail.js"; +import treeUtils from "./tree_utils.js"; +import linkService from "./link.js"; + +function setupTooltip() { + $(document).tooltip({ + items: "#note-detail a", + content: function (callback) { + const notePath = linkService.getNotePathFromLink($(this).attr("href")); + + if (notePath !== null) { + const noteId = treeUtils.getNoteIdFromNotePath(notePath); + + noteDetailService.loadNote(noteId).then(note => callback(note.content)); + } + }, + close: function (event, ui) { + ui.tooltip.hover(function () { + $(this).stop(true).fadeTo(400, 1); + }, + function () { + $(this).fadeOut('400', function () { + $(this).remove(); + }); + }); + } + }); +} + +export default { + setupTooltip +} \ No newline at end of file diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 4b374bec9..7b4403905 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -610,8 +610,6 @@ async function loadTree() { return await prepareBranch(resp.notes, resp.branches); } -$(() => loadTree().then(branch => initFancyTree(branch))); - function collapseTree(node = null) { if (!node) { node = $tree.fancytree("getRootNode"); @@ -733,6 +731,12 @@ async function sortAlphabetically(noteId) { await reload(); } +async function showTree() { + const tree = await loadTree(); + + initFancyTree(tree); +} + messagingService.subscribeToMessages(syncData => { if (syncData.some(sync => sync.entityName === 'branches') || syncData.some(sync => sync.entityName === 'notes')) { @@ -797,5 +801,6 @@ export default { createNewTopLevelNote, createNote, getSelectedNodes, - sortAlphabetically + sortAlphabetically, + showTree }; \ No newline at end of file diff --git a/src/public/javascripts/services/tree_changes.js b/src/public/javascripts/services/tree_changes.js index 67716ba65..e45688dc6 100644 --- a/src/public/javascripts/services/tree_changes.js +++ b/src/public/javascripts/services/tree_changes.js @@ -86,9 +86,9 @@ async function deleteNodes(nodes) { treeService.setCurrentNotePathToHash(next); } - treeService.reload(); - infoService.showMessage("Note(s) has been deleted."); + + await treeService.reload(); } async function moveNodeUpInHierarchy(node) {