From 68921ee59b3605f33b643c0be9cdc6324c0e4d40 Mon Sep 17 00:00:00 2001 From: azivner Date: Tue, 27 Mar 2018 00:22:02 -0400 Subject: [PATCH] separated text and code handling out of note detail service --- src/public/javascripts/services/bootstrap.js | 2 +- src/public/javascripts/services/link.js | 6 +- .../javascripts/services/note_detail.js | 198 +++++------------- .../javascripts/services/note_detail_code.js | 90 ++++++++ .../javascripts/services/note_detail_text.js | 48 +++++ src/public/stylesheets/style.css | 4 + src/views/index.ejs | 10 +- 7 files changed, 205 insertions(+), 153 deletions(-) create mode 100644 src/public/javascripts/services/note_detail_code.js create mode 100644 src/public/javascripts/services/note_detail_text.js diff --git a/src/public/javascripts/services/bootstrap.js b/src/public/javascripts/services/bootstrap.js index a1dcc5f1b..ffb58b447 100644 --- a/src/public/javascripts/services/bootstrap.js +++ b/src/public/javascripts/services/bootstrap.js @@ -13,7 +13,7 @@ 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 messagingService from './messaging.js'; import noteDetailService from './note_detail.js'; import noteType from './note_type.js'; import protected_session from './protected_session.js'; diff --git a/src/public/javascripts/services/link.js b/src/public/javascripts/services/link.js index 874c5106a..7f2d30827 100644 --- a/src/public/javascripts/services/link.js +++ b/src/public/javascripts/services/link.js @@ -1,5 +1,5 @@ import treeService from './tree.js'; -import noteDetailService from './note_detail.js'; +import noteDetailText from './note_detail_text.js'; import treeUtils from './tree_utils.js'; function getNotePathFromLink(url) { @@ -75,14 +75,14 @@ function goToLink(e) { } function addLinkToEditor(linkTitle, linkHref) { - const editor = noteDetailService.getEditor(); + const editor = noteDetailText.getEditor(); const doc = editor.document; doc.enqueueChanges(() => editor.data.insertLink(linkTitle, linkHref), doc.selection); } function addTextToEditor(text) { - const editor = noteDetailService.getEditor(); + const editor = noteDetailText.getEditor(); const doc = editor.document; doc.enqueueChanges(() => editor.data.insertText(text), doc.selection); diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 54e802bd5..55c413e35 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -9,11 +9,12 @@ import bundleService from "./bundle.js"; import infoService from "./info.js"; import treeCache from "./tree_cache.js"; import NoteFull from "../entities/note_full.js"; +import noteDetailCode from './note_detail_code.js'; +import noteDetailText from './note_detail_text.js'; const $noteTitle = $("#note-title"); -const $noteDetailText = $('#note-detail-text'); -const $noteDetailCode = $('#note-detail-code'); +const $noteDetailComponents = $(".note-detail-component"); const $noteDetailSearch = $('#note-detail-search'); const $noteDetailRender = $('#note-detail-render'); const $noteDetailAttachment = $('#note-detail-attachment'); @@ -31,11 +32,6 @@ const $attachmentDownload = $("#attachment-download"); const $attachmentOpen = $("#attachment-open"); const $searchString = $("#search-string"); -const $executeScriptButton = $("#execute-script-button"); - -let textEditor = null; -let codeEditor = null; - let currentNote = null; let noteChangeDisabled = false; @@ -50,6 +46,12 @@ function getCurrentNoteId() { return currentNote ? currentNote.noteId : null; } +function getCurrentNoteType() { + const currentNote = getCurrentNote(); + + return currentNote ? currentNote.type : null; +} + function noteChanged() { if (noteChangeDisabled) { return; @@ -90,18 +92,10 @@ async function saveNoteIfChanged() { function updateNoteFromInputs(note) { if (note.type === 'text') { - let content = textEditor.getData(); - - // if content is only tags/whitespace (typically

 

), then just make it empty - // this is important when setting new note to code - if (jQuery(content).text().trim() === '' && !content.includes("

"); - - $noteDetailText.show(); -} - -async function showCodeNote() { - if (!codeEditor) { - await utils.requireLibrary(utils.CODE_MIRROR); - - CodeMirror.keyMap.default["Shift-Tab"] = "indentLess"; - CodeMirror.keyMap.default["Tab"] = "indentMore"; - - CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js'; - - codeEditor = CodeMirror($("#note-detail-code")[0], { - value: "", - viewportMargin: Infinity, - indentUnit: 4, - matchBrackets: true, - matchTags: {bothTags: true}, - highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, - lint: true, - gutters: ["CodeMirror-lint-markers"], - lineNumbers: true - }); - - codeEditor.on('change', noteChanged); - } - - $noteDetailCode.show(); - - // this needs to happen after the element is shown, otherwise the editor won't be refresheds - codeEditor.setValue(currentNote.content); - - const info = CodeMirror.findModeByMIME(currentNote.mime); - - if (info) { - codeEditor.setOption("mode", info.mime); - CodeMirror.autoLoadMode(codeEditor, info.mode); - } - - codeEditor.refresh(); -} - function showSearchNote() { $noteDetailSearch.show(); @@ -235,6 +175,18 @@ function showSearchNote() { $searchString.on('input', noteChanged); } +async function handleProtectedSession() { + await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false); + + if (currentNote.isProtected) { + protectedSessionHolder.touchProtectedSession(); + } + + // this might be important if we focused on protected note when not in protected note and we got a dialog + // to login, but we chose instead to come to another node - at that point the dialog is still visible and this will close it. + protectedSessionService.ensureDialogIsClosed(); +} + async function loadNoteToEditor(noteId) { currentNote = await loadNote(noteId); @@ -246,48 +198,39 @@ async function loadNoteToEditor(noteId) { $noteIdDisplay.html(noteId); - await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false); - - if (currentNote.isProtected) { - protectedSessionHolder.touchProtectedSession(); - } - - // this might be important if we focused on protected note when not in protected note and we got a dialog - // to login, but we chose instead to come to another node - at that point the dialog is still visible and this will close it. - protectedSessionService.ensureDialogIsClosed(); + await handleProtectedSession(); $noteDetailWrapper.show(); noteChangeDisabled = true; - $noteTitle.val(currentNote.title); + try { + $noteTitle.val(currentNote.title); - noteTypeService.setNoteType(currentNote.type); - noteTypeService.setNoteMime(currentNote.mime); + noteTypeService.setNoteType(currentNote.type); + noteTypeService.setNoteMime(currentNote.mime); - $noteDetailText.hide(); - $noteDetailSearch.hide(); - $noteDetailCode.hide(); - $noteDetailRender.html('').hide(); - $noteDetailAttachment.hide(); + $noteDetailComponents.hide(); - if (currentNote.type === 'render') { - await showRenderNote(); + if (currentNote.type === 'render') { + await showRenderNote(); + } + else if (currentNote.type === 'file') { + await showFileNote(); + } + else if (currentNote.type === 'text') { + await noteDetailText.showTextNote(); + } + else if (currentNote.type === 'code') { + await noteDetailCode.showCodeNote(); + } + else if (currentNote.type === 'search') { + showSearchNote(); + } } - else if (currentNote.type === 'file') { - await showFileNote(); + finally { + noteChangeDisabled = false; } - else if (currentNote.type === 'text') { - await showTextNote(); - } - else if (currentNote.type === 'code') { - await showCodeNote(); - } - else if (currentNote.type === 'search') { - showSearchNote(); - } - - noteChangeDisabled = false; setNoteBackgroundIfProtected(currentNote); treeService.setBranchBackgroundBasedOnProtectedStatus(noteId); @@ -295,7 +238,7 @@ async function loadNoteToEditor(noteId) { // after loading new note make sure editor is scrolled to the top $noteDetailWrapper.scrollTop(0); - loadLabelList(); + await loadLabelList(); } async function loadLabelList() { @@ -323,18 +266,14 @@ async function loadNote(noteId) { return new NoteFull(treeCache, row); } -function getEditor() { - return textEditor; -} - function focus() { const note = getCurrentNote(); if (note.type === 'text') { - $noteDetailText.focus(); + noteDetailText.focus(); } else if (note.type === 'code') { - codeEditor.focus(); + noteDetailCode.focus(); } else if (note.type === 'render' || note.type === 'file' || note.type === 'search') { // do nothing @@ -344,31 +283,6 @@ function focus() { } } -function getCurrentNoteType() { - const currentNote = getCurrentNote(); - - return currentNote ? currentNote.type : null; -} - -async function executeCurrentNote() { - if (getCurrentNoteType() === 'code') { - // make sure note is saved so we load latest changes - await saveNoteIfChanged(); - - if (currentNote.mime.endsWith("env=frontend")) { - const bundle = await server.get('script/bundle/' + getCurrentNoteId()); - - bundleService.executeBundle(bundle); - } - - if (currentNote.mime.endsWith("env=backend")) { - await server.post('script/run/' + getCurrentNoteId()); - } - - infoService.showMessage("Note executed"); - } -} - $attachmentDownload.click(() => utils.download(getAttachmentUrl())); $attachmentOpen.click(() => { @@ -405,18 +319,13 @@ $(document).ready(() => { treeService.setNoteTitle(getCurrentNoteId(), title); }); - // so that tab jumps from note title (which has tabindex 1) - $noteDetailText.attr("tabindex", 2); + noteDetailText.focus(); }); // 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()); - setInterval(saveNoteIfChanged, 5000); export default { @@ -430,7 +339,8 @@ export default { getCurrentNoteType, getCurrentNoteId, newNoteCreated, - getEditor, focus, - loadLabelList + loadLabelList, + saveNoteIfChanged, + noteChanged }; \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_code.js b/src/public/javascripts/services/note_detail_code.js new file mode 100644 index 000000000..00326f146 --- /dev/null +++ b/src/public/javascripts/services/note_detail_code.js @@ -0,0 +1,90 @@ +import utils from "./utils.js"; +import bundleService from "./bundle.js"; +import infoService from "./info.js"; +import server from "./server.js"; +import noteDetailService from "./note_detail.js"; + +let codeEditor = null; + +const $noteDetailCode = $('#note-detail-code'); +const $executeScriptButton = $("#execute-script-button"); + +async function showCodeNote() { + if (!codeEditor) { + await utils.requireLibrary(utils.CODE_MIRROR); + + CodeMirror.keyMap.default["Shift-Tab"] = "indentLess"; + CodeMirror.keyMap.default["Tab"] = "indentMore"; + + CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js'; + + codeEditor = CodeMirror($("#note-detail-code")[0], { + value: "", + viewportMargin: Infinity, + indentUnit: 4, + matchBrackets: true, + matchTags: {bothTags: true}, + highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, + lint: true, + gutters: ["CodeMirror-lint-markers"], + lineNumbers: true + }); + + codeEditor.on('change', noteDetailService.noteChanged); + } + + $noteDetailCode.show(); + + const currentNote = noteDetailService.getCurrentNote(); + + // this needs to happen after the element is shown, otherwise the editor won't be refresheds + codeEditor.setValue(currentNote.content); + + const info = CodeMirror.findModeByMIME(currentNote.mime); + + if (info) { + codeEditor.setOption("mode", info.mime); + CodeMirror.autoLoadMode(codeEditor, info.mode); + } + + codeEditor.refresh(); +} + +function getContent() { + return codeEditor.getValue(); +} + +function focus() { + codeEditor.focus(); +} + +async function executeCurrentNote() { + if (noteDetailService.getCurrentNoteType() === 'code') { + // make sure note is saved so we load latest changes + await noteDetailService.saveNoteIfChanged(); + + const currentNote = noteDetailService.getCurrentNote(); + + if (currentNote.mime.endsWith("env=frontend")) { + const bundle = await server.get('script/bundle/' + getCurrentNoteId()); + + bundleService.executeBundle(bundle); + } + + if (currentNote.mime.endsWith("env=backend")) { + await server.post('script/run/' + getCurrentNoteId()); + } + + infoService.showMessage("Note executed"); + } +} + +$(document).bind('keydown', "ctrl+return", executeCurrentNote); + +$executeScriptButton.click(executeCurrentNote); + +export default { + showCodeNote, + getContent, + focus +} \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_text.js b/src/public/javascripts/services/note_detail_text.js new file mode 100644 index 000000000..8ad913b0f --- /dev/null +++ b/src/public/javascripts/services/note_detail_text.js @@ -0,0 +1,48 @@ +import utils from "./utils.js"; +import noteDetailService from './note_detail.js'; + +const $noteDetailText = $('#note-detail-text'); + +let textEditor = null; + +async function showTextNote() { + if (!textEditor) { + await utils.requireLibrary(utils.CKEDITOR); + + textEditor = await BalloonEditor.create($noteDetailText[0], {}); + + textEditor.document.on('change', noteDetailService.noteChanged); + } + + // temporary workaround for https://github.com/ckeditor/ckeditor5-enter/issues/49 + textEditor.setData(noteDetailService.getCurrentNote().content || "

"); + + $noteDetailText.show(); +} + +function getContent() { + let content = textEditor.getData(); + + // if content is only tags/whitespace (typically

 

), then just make it empty + // this is important when setting new note to code + if (jQuery(content).text().trim() === '' && !content.includes("
- +
-