diff --git a/src/public/javascripts/services/attributes.js b/src/public/javascripts/services/attributes.js index e1ca025fc..54f8702ef 100644 --- a/src/public/javascripts/services/attributes.js +++ b/src/public/javascripts/services/attributes.js @@ -21,7 +21,12 @@ class Attributes extends Component { } reloadAttributes() { - this.attributePromise = server.get(`notes/${this.tabContext.note.noteId}/attributes`); + if (this.tabContext.note) { + this.attributePromise = server.get(`notes/${this.tabContext.note.noteId}/attributes`); + } + else { + this.invalidateAttributes(); + } } async refreshAttributes() { diff --git a/src/public/javascripts/widgets/basic_widget.js b/src/public/javascripts/widgets/basic_widget.js index 2f5c5a40b..4c2df5cb5 100644 --- a/src/public/javascripts/widgets/basic_widget.js +++ b/src/public/javascripts/widgets/basic_widget.js @@ -14,7 +14,7 @@ class BasicWidget extends Component { /** * for overriding */ - async doRender() {} + doRender() {} toggle(show) { this.$widget.toggle(show); diff --git a/src/public/javascripts/widgets/detail/note_detail_code.js b/src/public/javascripts/widgets/detail/note_detail_code.js index 4b5acc10b..50a6b5d82 100644 --- a/src/public/javascripts/widgets/detail/note_detail_code.js +++ b/src/public/javascripts/widgets/detail/note_detail_code.js @@ -4,69 +4,68 @@ import toastService from "../../services/toast.js"; import server from "../../services/server.js"; import noteDetailService from "../../services/note_detail.js"; import keyboardActionService from "../../services/keyboard_actions.js"; +import TabAwareWidget from "../tab_aware_widget.js"; const TPL = `
`; -class NoteDetailCode { +class NoteDetailCode extends TabAwareWidget { + doRender() { + this.$widget = $(TPL); + this.$editor = this.$widget.find('.note-detail-code-editor'); + this.$executeScriptButton = this.$widget.find(".execute-script-button"); - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.codeEditor = null; - this.$component = ctx.$tabContent.find('.note-detail-code'); - this.$editorEl = this.$component.find('.note-detail-code-editor'); - this.$executeScriptButton = ctx.$tabContent.find(".execute-script-button"); - - keyboardActionService.setElementActionHandler(ctx.$tabContent, 'RunActiveNote', () => this.executeCurrentNote()); + keyboardActionService.setElementActionHandler(this.$widget, 'RunActiveNote', () => this.executeCurrentNote()); this.$executeScriptButton.on('click', () => this.executeCurrentNote()); + + this.initialized = this.initEditor(); + + return this.$widget; } - async render() { + async initEditor() { await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR); + + CodeMirror.keyMap.default["Shift-Tab"] = "indentLess"; + CodeMirror.keyMap.default["Tab"] = "indentMore"; - if (!this.codeEditor) { - CodeMirror.keyMap.default["Shift-Tab"] = "indentLess"; - CodeMirror.keyMap.default["Tab"] = "indentMore"; + // these conflict with backward/forward navigation shortcuts + delete CodeMirror.keyMap.default["Alt-Left"]; + delete CodeMirror.keyMap.default["Alt-Right"]; - // these conflict with backward/forward navigation shortcuts - delete CodeMirror.keyMap.default["Alt-Left"]; - delete CodeMirror.keyMap.default["Alt-Right"]; + CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js'; - CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js'; - - this.codeEditor = CodeMirror(this.$editorEl[0], { - value: "", - viewportMargin: Infinity, - indentUnit: 4, - matchBrackets: true, - matchTags: {bothTags: true}, - highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, - lint: true, - gutters: ["CodeMirror-lint-markers"], - lineNumbers: true, - tabindex: 100, - // we linewrap partly also because without it horizontal scrollbar displays only when you scroll - // all the way to the bottom of the note. With line wrap there's no horizontal scrollbar so no problem - lineWrapping: true, - dragDrop: false // with true the editor inlines dropped files which is not what we expect - }); - - this.onNoteChange(() => this.ctx.noteChanged()); - } + this.codeEditor = CodeMirror(this.$editor[0], { + value: "", + viewportMargin: Infinity, + indentUnit: 4, + matchBrackets: true, + matchTags: {bothTags: true}, + highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, + lint: true, + gutters: ["CodeMirror-lint-markers"], + lineNumbers: true, + tabindex: 100, + // we linewrap partly also because without it horizontal scrollbar displays only when you scroll + // all the way to the bottom of the note. With line wrap there's no horizontal scrollbar so no problem + lineWrapping: true, + dragDrop: false // with true the editor inlines dropped files which is not what we expect + }); + //this.onNoteChange(() => this.tabContext.noteChanged()); + } + + refresh() { // lazy loading above can take time and tab might have been already switched to another note - if (this.ctx.note && this.ctx.note.type === 'code') { + if (this.tabContext.note && this.tabContext.note.type === 'code') { // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check) // we provide fallback - this.codeEditor.setValue(this.ctx.note.content || ""); + this.codeEditor.setValue(this.tabContext.note.content || ""); - const info = CodeMirror.findModeByMIME(this.ctx.note.mime); + const info = CodeMirror.findModeByMIME(this.tabContext.note.mime); if (info) { this.codeEditor.setOption("mode", info.mime); @@ -78,7 +77,7 @@ class NoteDetailCode { } show() { - this.$component.show(); + this.$widget.show(); if (this.codeEditor) { // show can be called before render this.codeEditor.refresh(); @@ -95,19 +94,19 @@ class NoteDetailCode { async executeCurrentNote() { // ctrl+enter is also used elsewhere so make sure we're running only when appropriate - if (this.ctx.note.type !== 'code') { + if (this.tabContext.note.type !== 'code') { return; } // make sure note is saved so we load latest changes await noteDetailService.saveNotesIfChanged(); - if (this.ctx.note.mime.endsWith("env=frontend")) { - await bundleService.getAndExecuteBundle(this.ctx.note.noteId); + if (this.tabContext.note.mime.endsWith("env=frontend")) { + await bundleService.getAndExecuteBundle(this.tabContext.note.noteId); } - if (this.ctx.note.mime.endsWith("env=backend")) { - await server.post('script/run/' + this.ctx.note.noteId); + if (this.tabContext.note.mime.endsWith("env=backend")) { + await server.post('script/run/' + this.tabContext.note.noteId); } toastService.showMessage("Note executed"); @@ -124,7 +123,7 @@ class NoteDetailCode { } scrollToTop() { - this.$component.scrollTop(0); + this.$widget.scrollTop(0); } } diff --git a/src/public/javascripts/widgets/detail/note_detail_file.js b/src/public/javascripts/widgets/detail/note_detail_file.js index 69321da7b..6060be826 100644 --- a/src/public/javascripts/widgets/detail/note_detail_file.js +++ b/src/public/javascripts/widgets/detail/note_detail_file.js @@ -2,24 +2,60 @@ import utils from "../../services/utils.js"; import server from "../../services/server.js"; import toastService from "../../services/toast.js"; import noteDetailService from "../../services/note_detail.js"; +import TabAwareWidget from "../tab_aware_widget.js"; -class NoteDetailFile { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find('.note-detail-file'); - this.$fileNoteId = ctx.$tabContent.find(".file-note-id"); - this.$fileName = ctx.$tabContent.find(".file-filename"); - this.$fileType = ctx.$tabContent.find(".file-filetype"); - this.$fileSize = ctx.$tabContent.find(".file-filesize"); - this.$previewRow = ctx.$tabContent.find(".file-preview-row"); - this.$previewContent = ctx.$tabContent.find(".file-preview-content"); - this.$downloadButton = ctx.$tabContent.find(".file-download"); - this.$openButton = ctx.$tabContent.find(".file-open"); - this.$uploadNewRevisionButton = ctx.$tabContent.find(".file-upload-new-revision"); - this.$uploadNewRevisionInput = ctx.$tabContent.find(".file-upload-new-revision-input"); +const TPL = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Note ID:
Original file name:
File type:
File size:
Preview: +

+            
+ +   + +   + +
+ + +
`; + +class NoteDetailFile extends TabAwareWidget{ + doRender() { + this.$widget = $(TPL); + this.$fileNoteId = this.$widget.find(".file-note-id"); + this.$fileName = this.$widget.find(".file-filename"); + this.$fileType = this.$widget.find(".file-filetype"); + this.$fileSize = this.$widget.find(".file-filesize"); + this.$previewRow = this.$widget.find(".file-preview-row"); + this.$previewContent = this.$widget.find(".file-preview-content"); + this.$downloadButton = this.$widget.find(".file-download"); + this.$openButton = this.$widget.find(".file-open"); + this.$uploadNewRevisionButton = this.$widget.find(".file-upload-new-revision"); + this.$uploadNewRevisionInput = this.$widget.find(".file-upload-new-revision-input"); this.$downloadButton.on('click', () => utils.download(this.getFileUrl())); @@ -46,7 +82,7 @@ class NoteDetailFile { formData.append('upload', fileToUpload); const result = await $.ajax({ - url: baseApiUrl + 'notes/' + this.ctx.note.noteId + '/file', + url: baseApiUrl + 'notes/' + this.tabContext.note.noteId + '/file', headers: server.getHeaders(), data: formData, type: 'PUT', @@ -64,33 +100,35 @@ class NoteDetailFile { toastService.showError("Upload of a new file revision failed."); } }); + + return this.$widget; } - async render() { - const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes'); + async refresh() { + const attributes = await server.get('notes/' + this.tabContext.note.noteId + '/attributes'); const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); - this.$component.show(); + this.$widget.show(); - this.$fileNoteId.text(this.ctx.note.noteId); + this.$fileNoteId.text(this.tabContext.note.noteId); this.$fileName.text(attributeMap.originalFileName || "?"); - this.$fileSize.text(this.ctx.note.contentLength + " bytes"); - this.$fileType.text(this.ctx.note.mime); + this.$fileSize.text(this.tabContext.note.contentLength + " bytes"); + this.$fileType.text(this.tabContext.note.mime); - if (this.ctx.note.content) { + if (this.tabContext.note.content) { this.$previewRow.show(); - this.$previewContent.text(this.ctx.note.content); + this.$previewContent.text(this.tabContext.note.content); } else { this.$previewRow.hide(); } // open doesn't work for protected notes since it works through browser which isn't in protected session - this.$openButton.toggle(!this.ctx.note.isProtected); + this.$openButton.toggle(!this.tabContext.note.isProtected); } getFileUrl() { - return utils.getUrlForDownload("api/notes/" + this.ctx.note.noteId + "/download"); + return utils.getUrlForDownload("api/notes/" + this.tabContext.note.noteId + "/download"); } show() {} diff --git a/src/views/details/file.ejs b/src/views/details/file.ejs deleted file mode 100644 index dd96c9e4d..000000000 --- a/src/views/details/file.ejs +++ /dev/null @@ -1,37 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - -
Note ID:
Original file name:
File type:
File size:
Preview: -

-            
- -   - -   - -
- - -
\ No newline at end of file diff --git a/src/views/mobile.ejs b/src/views/mobile.ejs index 2591c7f45..fd2971c16 100644 --- a/src/views/mobile.ejs +++ b/src/views/mobile.ejs @@ -66,8 +66,6 @@ <% include details/render.ejs %> - <% include details/file.ejs %> - <% include details/image.ejs %> <% include details/relation_map.ejs %>