From eacefeb08bce26b9d134ac89e754afc9a680c1be Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 22 Jan 2020 21:38:00 +0100 Subject: [PATCH] converted remaining type widgets --- src/public/javascripts/widgets/note_detail.js | 12 +- .../{note_detail_book.js => book.js} | 135 +++++++++++--- .../javascripts/widgets/type_widgets/code.js | 6 +- .../javascripts/widgets/type_widgets/empty.js | 6 +- .../javascripts/widgets/type_widgets/file.js | 6 +- .../javascripts/widgets/type_widgets/image.js | 168 ++++++++++++++++++ .../widgets/type_widgets/note_detail_image.js | 122 ------------- .../note_detail_protected_session.js | 42 ----- .../type_widgets/note_detail_render.js | 45 ----- .../type_widgets/note_detail_search.js | 58 ------ .../widgets/type_widgets/protected_session.js | 60 +++++++ ...detail_relation_map.js => relation_map.js} | 78 +++++--- .../widgets/type_widgets/render.js | 55 ++++++ .../widgets/type_widgets/search.js | 75 ++++++++ .../javascripts/widgets/type_widgets/text.js | 9 +- src/public/stylesheets/style.css | 81 +-------- src/views/details/book.ejs | 21 --- src/views/details/image.ejs | 32 ---- .../details/protected_session_password.ejs | 10 -- src/views/details/relation_map.ejs | 27 --- src/views/details/render.ejs | 9 - src/views/details/search.ejs | 15 -- 22 files changed, 537 insertions(+), 535 deletions(-) rename src/public/javascripts/widgets/type_widgets/{note_detail_book.js => book.js} (61%) create mode 100644 src/public/javascripts/widgets/type_widgets/image.js delete mode 100644 src/public/javascripts/widgets/type_widgets/note_detail_image.js delete mode 100644 src/public/javascripts/widgets/type_widgets/note_detail_protected_session.js delete mode 100644 src/public/javascripts/widgets/type_widgets/note_detail_render.js delete mode 100644 src/public/javascripts/widgets/type_widgets/note_detail_search.js create mode 100644 src/public/javascripts/widgets/type_widgets/protected_session.js rename src/public/javascripts/widgets/type_widgets/{note_detail_relation_map.js => relation_map.js} (89%) create mode 100644 src/public/javascripts/widgets/type_widgets/render.js create mode 100644 src/public/javascripts/widgets/type_widgets/search.js delete mode 100644 src/views/details/book.ejs delete mode 100644 src/views/details/image.ejs delete mode 100644 src/views/details/protected_session_password.ejs delete mode 100644 src/views/details/relation_map.ejs delete mode 100644 src/views/details/render.ejs delete mode 100644 src/views/details/search.ejs diff --git a/src/public/javascripts/widgets/note_detail.js b/src/public/javascripts/widgets/note_detail.js index 98a84d8a7..e40683091 100644 --- a/src/public/javascripts/widgets/note_detail.js +++ b/src/public/javascripts/widgets/note_detail.js @@ -21,12 +21,12 @@ const typeWidgetClasses = { 'text': "./type_widgets/text.js", 'code': "./type_widgets/code.js", 'file': "./type_widgets/file.js", - 'image': "./type_widgets/note_detail_image.js", - 'search': "./type_widgets/note_detail_search.js", - 'render': "./type_widgets/note_detail_render.js", - 'relation-map': "./type_widgets/note_detail_relation_map.js", - 'protected-session': "./type_widgets/note_detail_protected_session.js", - 'book': "./type_widgets/note_detail_book.js" + 'image': "./type_widgets/image.js", + 'search': "./type_widgets/search.js", + 'render': "./type_widgets/render.js", + 'relation-map': "./type_widgets/relation_map.js", + 'protected-session': "./type_widgets/protected_session.js", + 'book': "./type_widgets/book.js" }; export default class NoteDetailWidget extends TabAwareWidget { diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_book.js b/src/public/javascripts/widgets/type_widgets/book.js similarity index 61% rename from src/public/javascripts/widgets/type_widgets/note_detail_book.js rename to src/public/javascripts/widgets/type_widgets/book.js index 12f0a04bb..d08f2936d 100644 --- a/src/public/javascripts/widgets/type_widgets/note_detail_book.js +++ b/src/public/javascripts/widgets/type_widgets/book.js @@ -1,6 +1,7 @@ import linkService from "../../services/link.js"; import treeCache from "../../services/tree_cache.js"; import noteContentRenderer from "../../services/note_content_renderer.js"; +import TypeWidget from "./type_widget.js"; const MIN_ZOOM_LEVEL = 1; const MAX_ZOOM_LEVEL = 6; @@ -32,18 +33,104 @@ const ZOOMS = { } }; -class NoteDetailBook { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find('.note-detail-book'); - this.$content = this.$component.find('.note-detail-book-content'); - this.$zoomInButton = this.$component.find('.book-zoom-in-button'); - this.$zoomOutButton = this.$component.find('.book-zoom-out-button'); - this.$expandChildrenButton = this.$component.find('.expand-children-button'); - this.$help = this.$component.find('.note-detail-book-help'); +const TPL = ` +
+ + +
+ + + + + +
+ +
+ This note of type Book doesn't have any child notes so there's nothing to display. See wiki for details. +
+ +
+
`; + +export default class BookTypeWidget extends TypeWidget { + static getType() { return "book"; } + + doRender() { + this.$widget = $(TPL); + this.$content = this.$widget.find('.note-detail-book-content'); + this.$zoomInButton = this.$widget.find('.book-zoom-in-button'); + this.$zoomOutButton = this.$widget.find('.book-zoom-out-button'); + this.$expandChildrenButton = this.$widget.find('.expand-children-button'); + this.$help = this.$widget.find('.note-detail-book-help'); this.$zoomInButton.on('click', () => this.setZoom(this.zoomLevel - 1)); this.$zoomOutButton.on('click', () => this.setZoom(this.zoomLevel + 1)); @@ -78,6 +165,8 @@ class NoteDetailBook { $card.find('.note-book-children-content').empty(); }); + + return this.$widget; } async expandCard($card) { @@ -104,13 +193,13 @@ class NoteDetailBook { this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height); } - async render() { + async doRefresh() { this.$content.empty(); this.$help.hide(); if (this.isAutoBook()) { const $addTextLink = $('here').on('click', () => { - this.ctx.renderComponent(true); + // FIXME }); this.$content.append($('
') @@ -119,17 +208,17 @@ class NoteDetailBook { .append(' if you want to add some text.')) } - const zoomLevel = parseInt(await this.ctx.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel(); + const zoomLevel = parseInt(await this.tabContext.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel(); this.setZoom(zoomLevel); - await this.renderIntoElement(this.ctx.note, this.$content); + await this.renderIntoElement(this.tabContext.note, this.$content); } async renderIntoElement(note, $container) { const childNotes = await note.getChildNotes(); for (const childNote of childNotes) { - const childNotePath = this.ctx.notePath + '/' + childNote.noteId; + const childNotePath = this.tabContext.notePath + '/' + childNote.noteId; const {type, renderedContent} = await noteContentRenderer.getRenderedContent(childNote); @@ -164,12 +253,12 @@ class NoteDetailBook { /** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */ isAutoBook() { - return this.ctx.note.type !== 'book'; + return this.tabContext.note.type !== 'book'; } getDefaultZoomLevel() { if (this.isAutoBook()) { - const w = this.$component.width(); + const w = this.$widget.width(); if (w <= 600) { return 1; @@ -192,7 +281,7 @@ class NoteDetailBook { } show() { - this.$component.show(); + this.$widget.show(); } focus() {} @@ -204,8 +293,6 @@ class NoteDetailBook { } scrollToTop() { - this.$component.scrollTop(0); + this.$widget.scrollTop(0); } -} - -export default NoteDetailBook; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/code.js b/src/public/javascripts/widgets/type_widgets/code.js index 66ab4ee4f..99ab0fe53 100644 --- a/src/public/javascripts/widgets/type_widgets/code.js +++ b/src/public/javascripts/widgets/type_widgets/code.js @@ -22,7 +22,7 @@ const TPL = `
`; -class CodeTypeWidget extends TypeWidget { +export default class CodeTypeWidget extends TypeWidget { static getType() { return "code"; } doRender() { @@ -138,6 +138,4 @@ class CodeTypeWidget extends TypeWidget { scrollToTop() { this.$widget.scrollTop(0); } -} - -export default CodeTypeWidget; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/empty.js b/src/public/javascripts/widgets/type_widgets/empty.js index 65bbc1f5d..f433f02ed 100644 --- a/src/public/javascripts/widgets/type_widgets/empty.js +++ b/src/public/javascripts/widgets/type_widgets/empty.js @@ -12,7 +12,7 @@ const TPL = ` `; -class NoteDetailEmpty extends TypeWidget { +export default class EmptyTypeWidget extends TypeWidget { static getType() { return "empty"; } doRender() { @@ -56,6 +56,4 @@ class NoteDetailEmpty extends TypeWidget { cleanup() {} scrollToTop() {} -} - -export default NoteDetailEmpty; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/file.js b/src/public/javascripts/widgets/type_widgets/file.js index 1069eef53..02c445a81 100644 --- a/src/public/javascripts/widgets/type_widgets/file.js +++ b/src/public/javascripts/widgets/type_widgets/file.js @@ -55,7 +55,7 @@ const TPL = ` `; -class FileTypeWidget extends TypeWidget { +export default class FileTypeWidget extends TypeWidget { static getType() { return "file"; } doRender() { @@ -155,6 +155,4 @@ class FileTypeWidget extends TypeWidget { cleanup() {} scrollToTop() {} -} - -export default FileTypeWidget; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/image.js b/src/public/javascripts/widgets/type_widgets/image.js new file mode 100644 index 000000000..dbae14967 --- /dev/null +++ b/src/public/javascripts/widgets/type_widgets/image.js @@ -0,0 +1,168 @@ +import utils from "../../services/utils.js"; +import toastService from "../../services/toast.js"; +import server from "../../services/server.js"; +import noteDetailService from "../../services/note_detail.js"; +import TypeWidget from "./type_widget.js"; + +const TPL = ` +
+ + +
+ + + + + +
+ +
+ +
+ +
+ + Original file name: + + + + + File type: + + + + + File size: + + +
+ + +
`; + +class NoteDetailImage extends TypeWidget { + static getType() { return "image"; } + + doRender() { + this.$widget = $(TPL); + this.$imageWrapper = this.$widget.find('.note-detail-image-wrapper'); + this.$imageView = this.$widget.find('.note-detail-image-view'); + this.$copyToClipboardButton = this.$widget.find(".image-copy-to-clipboard"); + this.$uploadNewRevisionButton = this.$widget.find(".image-upload-new-revision"); + this.$uploadNewRevisionInput = this.$widget.find(".image-upload-new-revision-input"); + this.$fileName = this.$widget.find(".image-filename"); + this.$fileType = this.$widget.find(".image-filetype"); + this.$fileSize = this.$widget.find(".image-filesize"); + + this.$imageDownloadButton = this.$widget.find(".image-download"); + this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl())); + + this.$copyToClipboardButton.on('click',() => { + this.$imageWrapper.attr('contenteditable','true'); + + try { + this.selectImage(this.$imageWrapper.get(0)); + + const success = document.execCommand('copy'); + + if (success) { + toastService.showMessage("Image copied to the clipboard"); + } + else { + toastService.showAndLogError("Could not copy the image to clipboard."); + } + } + finally { + window.getSelection().removeAllRanges(); + this.$imageWrapper.removeAttr('contenteditable'); + } + }); + + this.$uploadNewRevisionButton.on("click", () => { + this.$uploadNewRevisionInput.trigger("click"); + }); + + this.$uploadNewRevisionInput.on('change', async () => { + const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below + this.$uploadNewRevisionInput.val(''); + + const formData = new FormData(); + formData.append('upload', fileToUpload); + + const result = await $.ajax({ + url: baseApiUrl + 'images/' + this.tabContext.note.noteId, + headers: server.getHeaders(), + data: formData, + type: 'PUT', + timeout: 60 * 60 * 1000, + contentType: false, // NEEDED, DON'T REMOVE THIS + processData: false, // NEEDED, DON'T REMOVE THIS + }); + + if (result.uploaded) { + toastService.showMessage("New image revision has been uploaded."); + + await utils.clearBrowserCache(); + + await noteDetailService.reload(); + } + else { + toastService.showError("Upload of a new image revision failed: " + result.message); + } + }); + + return this.$widget; + } + + async doRefresh() { + const note = this.tabContext.note; + const attributes = await server.get('notes/' + note.noteId + '/attributes'); + const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); + + this.$widget.show(); + + this.$fileName.text(attributeMap.originalFileName || "?"); + this.$fileSize.text(note.contentLength + " bytes"); + this.$fileType.text(note.mime); + + const imageHash = note.utcDateModified.replace(" ", "_"); + + this.$imageView.prop("src", `api/images/${note.noteId}/${note.title}?${imageHash}`); + } + + selectImage(element) { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + } + + getFileUrl() { + return utils.getUrlForDownload(`api/notes/${this.tabContext.note.noteId}/download`); + } + + show() {} + + getContent() {} + + focus() {} + + onNoteChange() {} + + cleanup() {} + + scrollToTop() { + this.$widget.scrollTop(0); + } +} + +export default NoteDetailImage \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_image.js b/src/public/javascripts/widgets/type_widgets/note_detail_image.js deleted file mode 100644 index 456a45f07..000000000 --- a/src/public/javascripts/widgets/type_widgets/note_detail_image.js +++ /dev/null @@ -1,122 +0,0 @@ -import utils from "../../services/utils.js"; -import toastService from "../../services/toast.js"; -import server from "../../services/server.js"; -import noteDetailService from "../../services/note_detail.js"; - -class NoteDetailImage { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find('.note-detail-image'); - this.$imageWrapper = ctx.$tabContent.find('.note-detail-image-wrapper'); - this.$imageView = ctx.$tabContent.find('.note-detail-image-view'); - this.$copyToClipboardButton = ctx.$tabContent.find(".image-copy-to-clipboard"); - this.$uploadNewRevisionButton = ctx.$tabContent.find(".image-upload-new-revision"); - this.$uploadNewRevisionInput = ctx.$tabContent.find(".image-upload-new-revision-input"); - this.$fileName = ctx.$tabContent.find(".image-filename"); - this.$fileType = ctx.$tabContent.find(".image-filetype"); - this.$fileSize = ctx.$tabContent.find(".image-filesize"); - - this.$imageDownloadButton = ctx.$tabContent.find(".image-download"); - this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl())); - - this.$copyToClipboardButton.on('click',() => { - this.$imageWrapper.attr('contenteditable','true'); - - try { - this.selectImage(this.$imageWrapper.get(0)); - - const success = document.execCommand('copy'); - - if (success) { - toastService.showMessage("Image copied to the clipboard"); - } - else { - toastService.showAndLogError("Could not copy the image to clipboard."); - } - } - finally { - window.getSelection().removeAllRanges(); - this.$imageWrapper.removeAttr('contenteditable'); - } - }); - - this.$uploadNewRevisionButton.on("click", () => { - this.$uploadNewRevisionInput.trigger("click"); - }); - - this.$uploadNewRevisionInput.on('change', async () => { - const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below - this.$uploadNewRevisionInput.val(''); - - const formData = new FormData(); - formData.append('upload', fileToUpload); - - const result = await $.ajax({ - url: baseApiUrl + 'images/' + this.ctx.note.noteId, - headers: server.getHeaders(), - data: formData, - type: 'PUT', - timeout: 60 * 60 * 1000, - contentType: false, // NEEDED, DON'T REMOVE THIS - processData: false, // NEEDED, DON'T REMOVE THIS - }); - - if (result.uploaded) { - toastService.showMessage("New image revision has been uploaded."); - - await utils.clearBrowserCache(); - - await noteDetailService.reload(); - } - else { - toastService.showError("Upload of a new image revision failed: " + result.message); - } - }); - } - - async render() { - const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes'); - const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); - - this.$component.show(); - - this.$fileName.text(attributeMap.originalFileName || "?"); - this.$fileSize.text(this.ctx.note.contentLength + " bytes"); - this.$fileType.text(this.ctx.note.mime); - - const imageHash = this.ctx.note.utcDateModified.replace(" ", "_"); - - this.$imageView.prop("src", `api/images/${this.ctx.note.noteId}/${this.ctx.note.title}?${imageHash}`); - } - - selectImage(element) { - const selection = window.getSelection(); - const range = document.createRange(); - range.selectNodeContents(element); - selection.removeAllRanges(); - selection.addRange(range); - } - - getFileUrl() { - return utils.getUrlForDownload(`api/notes/${this.ctx.note.noteId}/download`); - } - - show() {} - - getContent() {} - - focus() {} - - onNoteChange() {} - - cleanup() {} - - scrollToTop() { - this.$component.scrollTop(0); - } -} - -export default NoteDetailImage \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_protected_session.js b/src/public/javascripts/widgets/type_widgets/note_detail_protected_session.js deleted file mode 100644 index ac3af78a5..000000000 --- a/src/public/javascripts/widgets/type_widgets/note_detail_protected_session.js +++ /dev/null @@ -1,42 +0,0 @@ -import protectedSessionService from '../../services/protected_session.js'; - -class NoteDetailProtectedSession { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find(".protected-session-password-component"); - this.$passwordForm = ctx.$tabContent.find(".protected-session-password-form"); - this.$passwordInput = ctx.$tabContent.find(".protected-session-password"); - - this.$passwordForm.on('submit', () => { - const password = this.$passwordInput.val(); - this.$passwordInput.val(""); - - protectedSessionService.setupProtectedSession(password); - - return false; - }); - } - - render() { - this.$component.show(); - } - - show() {} - - getContent() {} - - focus() {} - - onNoteChange() {} - - cleanup() {} - - scrollToTop() { - this.$component.scrollTop(0); - } -} - -export default NoteDetailProtectedSession; \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_render.js b/src/public/javascripts/widgets/type_widgets/note_detail_render.js deleted file mode 100644 index eabe98805..000000000 --- a/src/public/javascripts/widgets/type_widgets/note_detail_render.js +++ /dev/null @@ -1,45 +0,0 @@ -import renderService from "../../services/render.js"; - -class NoteDetailRender { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find('.note-detail-render'); - this.$noteDetailRenderHelp = ctx.$tabContent.find('.note-detail-render-help'); - this.$noteDetailRenderContent = ctx.$tabContent.find('.note-detail-render-content'); - this.$renderButton = ctx.$tabContent.find('.render-button'); - - this.$renderButton.on('click', () => this.render()); // long form! - } - - async render() { - this.$component.show(); - this.$noteDetailRenderHelp.hide(); - - const renderNotesFound = await renderService.render(this.ctx.note, this.$noteDetailRenderContent, this.ctx); - - if (!renderNotesFound) { - this.$noteDetailRenderHelp.show(); - } - } - - getContent() {} - - show() {} - - focus() {} - - onNoteChange() {} - - cleanup() { - this.$noteDetailRenderContent.empty(); - } - - scrollToTop() { - this.$component.scrollTop(0); - } -} - -export default NoteDetailRender; \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_search.js b/src/public/javascripts/widgets/type_widgets/note_detail_search.js deleted file mode 100644 index 46e19292f..000000000 --- a/src/public/javascripts/widgets/type_widgets/note_detail_search.js +++ /dev/null @@ -1,58 +0,0 @@ -import noteDetailService from "../../services/note_detail.js"; -import searchNotesService from "../../services/search_notes.js"; - -class NoteDetailSearch { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$searchString = ctx.$tabContent.find(".search-string"); - this.$component = ctx.$tabContent.find('.note-detail-search'); - this.$help = ctx.$tabContent.find(".note-detail-search-help"); - this.$refreshButton = ctx.$tabContent.find('.note-detail-search-refresh-results-button'); - - this.$refreshButton.on('click', async () => { - // FIXME - await noteDetailService.saveNotesIfChanged(); - - await searchNotesService.refreshSearch(); - }); - } - - render() { - this.$help.html(searchNotesService.getHelpText()); - - this.$component.show(); - - try { - const json = JSON.parse(this.ctx.note.content); - - this.$searchString.val(json.searchString); - } - catch (e) { - console.log(e); - this.$searchString.val(''); - } - - this.$searchString.on('input', () => this.ctx.noteChanged()); - } - - getContent() { - return JSON.stringify({ - searchString: this.$searchString.val() - }); - } - - focus() {} - - show() {} - - onNoteChange() {} - - cleanup() {} - - scrollToTop() {} -} - -export default NoteDetailSearch; \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/protected_session.js b/src/public/javascripts/widgets/type_widgets/protected_session.js new file mode 100644 index 000000000..8844582fc --- /dev/null +++ b/src/public/javascripts/widgets/type_widgets/protected_session.js @@ -0,0 +1,60 @@ +import protectedSessionService from '../../services/protected_session.js'; +import TypeWidget from "./type_widget.js"; + +const TPL = ` +
+ + +
+
+ + +
+ + +
+
`; + +export default class ProtectedSessionTypeWidget extends TypeWidget { + static getType() { return "protected-session"; } + + doRender() { + this.$widget = $(TPL); + this.$passwordForm = this.$widget.find(".protected-session-password-form"); + this.$passwordInput = this.$widget.find(".protected-session-password"); + + this.$passwordForm.on('submit', () => { + const password = this.$passwordInput.val(); + this.$passwordInput.val(""); + + protectedSessionService.setupProtectedSession(password); + + return false; + }); + + return this.$widget; + } + + render() { + this.$widget.show(); + } + + show() {} + + getContent() {} + + focus() {} + + onNoteChange() {} + + cleanup() {} + + scrollToTop() { + this.$widget.scrollTop(0); + } +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_relation_map.js b/src/public/javascripts/widgets/type_widgets/relation_map.js similarity index 89% rename from src/public/javascripts/widgets/type_widgets/note_detail_relation_map.js rename to src/public/javascripts/widgets/type_widgets/relation_map.js index 705c00f7a..10c14b2f1 100644 --- a/src/public/javascripts/widgets/type_widgets/note_detail_relation_map.js +++ b/src/public/javascripts/widgets/type_widgets/relation_map.js @@ -6,6 +6,7 @@ import treeService from "../../services/tree.js"; import contextMenuWidget from "../../services/context_menu.js"; import toastService from "../../services/toast.js"; import attributeAutocompleteService from "../../services/attribute_autocomplete.js"; +import TypeWidget from "./type_widget.js"; const uniDirectionalOverlays = [ [ "Arrow", { @@ -61,20 +62,47 @@ const linkOverlays = [ } ] ]; +const TPL = ` +
+ + + + +
+ + + +
+ +
+
+
+
`; + let containerCounter = 1; -class NoteDetailRelationMap { - /** - * @param {TabContext} ctx - */ - constructor(ctx) { - this.ctx = ctx; - this.$component = ctx.$tabContent.find(".note-detail-relation-map"); - this.$relationMapContainer = ctx.$tabContent.find(".relation-map-container"); - this.$createChildNote = ctx.$tabContent.find(".relation-map-create-child-note"); - this.$zoomInButton = ctx.$tabContent.find(".relation-map-zoom-in"); - this.$zoomOutButton = ctx.$tabContent.find(".relation-map-zoom-out"); - this.$resetPanZoomButton = ctx.$tabContent.find(".relation-map-reset-pan-zoom"); +export default class RelationMapTypeWidget extends TypeWidget { + static getType() { return "relation-map"; } + + doRender() { + this.$widget = $(TPL); + this.$relationMapContainer = this.$widget.find(".relation-map-container"); + this.$createChildNote = this.$widget.find(".relation-map-create-child-note"); + this.$zoomInButton = this.$widget.find(".relation-map-zoom-in"); + this.$zoomOutButton = this.$widget.find(".relation-map-zoom-out"); + this.$resetPanZoomButton = this.$widget.find(".relation-map-reset-pan-zoom"); this.mapData = null; this.jsPlumbInstance = null; @@ -82,7 +110,7 @@ class NoteDetailRelationMap { this.relations = null; this.pzInstance = null; - this.$relationMapWrapper = ctx.$tabContent.find('.relation-map-wrapper'); + this.$relationMapWrapper = this.$widget.find('.relation-map-wrapper'); this.$relationMapWrapper.on('click', event => { if (this.clipboard) { let {x, y} = this.getMousePosition(event); @@ -129,7 +157,7 @@ class NoteDetailRelationMap { return; } - const {note} = await server.post(`notes/${this.ctx.note.noteId}/children?target=into`, { + const {note} = await server.post(`notes/${this.tabContext.note.noteId}/children?target=into`, { title, content: '', type: 'text' @@ -150,8 +178,10 @@ class NoteDetailRelationMap { this.pzInstance.moveTo(0, 0); }); - this.$component.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev)); - this.$component.on("dragover", ev => ev.preventDefault()); + this.$widget.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev)); + this.$widget.on("dragover", ev => ev.preventDefault()); + + return this.$widget; } async tabContextMenuHandler(event, cmd) { @@ -217,9 +247,9 @@ class NoteDetailRelationMap { } }; - if (this.ctx.note.content) { + if (this.tabContext.note.content) { try { - this.mapData = JSON.parse(this.ctx.note.content); + this.mapData = JSON.parse(this.tabContext.note.content); } catch (e) { console.log("Could not parse content: ", e); } @@ -234,14 +264,14 @@ class NoteDetailRelationMap { return id.substr(13); } - async render() { - this.$component.show(); + async doRefresh() { + this.$widget.show(); await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); jsPlumb.ready(() => { // lazy loading above can take time and tab might have been already switched to another note - if (this.ctx.note && this.ctx.note.type === 'relation-map') { + if (this.tabContext.note && this.tabContext.note.type === 'relation-map') { this.loadMapData(); this.initJsPlumbInstance(); @@ -490,7 +520,7 @@ class NoteDetailRelationMap { } saveData() { - this.ctx.noteChanged(); + this.tabContext.noteChanged(); } async createNoteBox(noteId, title, x, y) { @@ -623,6 +653,4 @@ class NoteDetailRelationMap { onNoteChange() {} scrollToTop() {} -} - -export default NoteDetailRelationMap; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/render.js b/src/public/javascripts/widgets/type_widgets/render.js new file mode 100644 index 000000000..19258f673 --- /dev/null +++ b/src/public/javascripts/widgets/type_widgets/render.js @@ -0,0 +1,55 @@ +import renderService from "../../services/render.js"; +import TypeWidget from "./type_widget.js"; + +const TPL = ` +
+
+

This help note is shown because this note of type Render HTML doesn't have required relation to function properly.

+ +

Render HTML note type is used for scripting. In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a relation (in Attributes dialog) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.

+
+ +
+
`; + +export default class RenderTypeWidget extends TypeWidget { + static getType() { return "render"; } + + doRender() { + this.$widget = $(TPL); + this.$noteDetailRenderHelp = this.$widget.find('.note-detail-render-help'); + this.$noteDetailRenderContent = this.$widget.find('.note-detail-render-content'); + this.$renderButton = this.$widget.find('.render-button'); + + this.$renderButton.on('click', () => this.render()); // long form! + + return this.$widget; + } + + async doRefresh() { + this.$widget.show(); + this.$noteDetailRenderHelp.hide(); + + const renderNotesFound = await renderService.render(this.ctx.note, this.$noteDetailRenderContent, this.ctx); + + if (!renderNotesFound) { + this.$noteDetailRenderHelp.show(); + } + } + + getContent() {} + + show() {} + + focus() {} + + onNoteChange() {} + + cleanup() { + this.$noteDetailRenderContent.empty(); + } + + scrollToTop() { + this.$widget.scrollTop(0); + } +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/search.js b/src/public/javascripts/widgets/type_widgets/search.js new file mode 100644 index 000000000..b7bddda14 --- /dev/null +++ b/src/public/javascripts/widgets/type_widgets/search.js @@ -0,0 +1,75 @@ +import noteDetailService from "../../services/note_detail.js"; +import searchNotesService from "../../services/search_notes.js"; +import TypeWidget from "./type_widget.js"; + +const TPL = ` +`; + +export default class SearchTypeWidget extends TypeWidget { + static getType() { return "search"; } + + doRender() { + this.$widget = $(TPL); + this.$searchString = this.$widget.find(".search-string"); + this.$component = this.$widget.find('.note-detail-search'); + this.$help = this.$widget.find(".note-detail-search-help"); + this.$refreshButton = this.$widget.find('.note-detail-search-refresh-results-button'); + + this.$refreshButton.on('click', async () => { + // FIXME + await noteDetailService.saveNotesIfChanged(); + + await searchNotesService.refreshSearch(); + }); + + return this.$widget; + } + + doRefresh() { + this.$help.html(searchNotesService.getHelpText()); + + this.$component.show(); + + try { + const json = JSON.parse(this.ctx.note.content); + + this.$searchString.val(json.searchString); + } + catch (e) { + console.log(e); + this.$searchString.val(''); + } + + this.$searchString.on('input', () => this.ctx.noteChanged()); + } + + getContent() { + return JSON.stringify({ + searchString: this.$searchString.val() + }); + } + + focus() {} + + show() {} + + onNoteChange() {} + + cleanup() {} + + scrollToTop() {} +} \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/text.js b/src/public/javascripts/widgets/type_widgets/text.js index 5be5c01ec..0b382b48b 100644 --- a/src/public/javascripts/widgets/type_widgets/text.js +++ b/src/public/javascripts/widgets/type_widgets/text.js @@ -4,9 +4,6 @@ import noteAutocompleteService from '../../services/note_autocomplete.js'; import mimeTypesService from '../../services/mime_types.js'; import TypeWidget from "./type_widget.js"; import utils from "../../services/utils.js"; -import linkService from "../../services/link.js"; -import appContext from "../../services/app_context.js"; -import noteDetailService from "../../services/note_detail.js"; const ENABLE_INSPECTOR = false; @@ -77,7 +74,7 @@ const TPL = ` `; -class TextTypeWidget extends TypeWidget { +export default class TextTypeWidget extends TypeWidget { static getType() { return "text"; } doRender() { @@ -217,6 +214,4 @@ class TextTypeWidget extends TypeWidget { writer.insertText(text, insertPosition); }); } -} - -export default TextTypeWidget; \ No newline at end of file +} \ No newline at end of file diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 3a9247910..b78c44bf5 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -398,14 +398,6 @@ button.icon-button { padding: 0; } -.note-detail-image { - text-align: center; -} - -.note-detail-image-view { - max-width: 100%; -} - pre:not(.CodeMirror-line) { color: var(--main-text-color) !important; white-space: pre-wrap; @@ -625,12 +617,6 @@ div[data-notify="container"] { cursor: pointer !important; } -#protected-session-password-component { - max-width: 450px; - margin: auto; - padding-top: 50px; -} - .ck-editor__is-empty.ck-content.ck-editor__editable::before { content: 'You can start writing note here ...'; position: absolute; @@ -711,10 +697,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href display: inline-block; } -.protected-session-password-component { - width: 300px; - margin: 30px auto auto; -} + .note-detail-empty { margin: 50px; @@ -729,68 +712,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href z-index: 100; } -.note-detail-book { - height: 100%; -} - -.note-detail-book-content { - display: flex; - flex-wrap: wrap; - overflow: auto; - height: 100%; - align-content: start; -} - -.note-book-card { - border-radius: 10px; - background-color: var(--accented-background-color); - padding: 15px; - padding-bottom: 5px; - margin: 5px; - margin-left: 0; - overflow: hidden; - display: flex; - flex-direction: column; - flex-shrink: 0; -} - -.note-book-card .note-book-card { - border: 1px solid var(--main-border-color); -} - -.note-book-content { - overflow: hidden; -} - -.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content { - display: flex; - align-items: center; - justify-content: center; - text-align: center; -} - -.note-book-card.type-image .note-book-content img { - max-width: 100%; - max-height: 100%; -} - -.note-book-title { - flex-grow: 0; -} - -.note-book-content { - flex-grow: 1; -} - -.note-book-auto-message { - background-color: var(--accented-background-color); - text-align: center; - width: 100%; - border-radius: 10px; - padding: 5px; - margin-top: 5px; -} - #toast-container { position: absolute; width: 100%; diff --git a/src/views/details/book.ejs b/src/views/details/book.ejs deleted file mode 100644 index beeae0536..000000000 --- a/src/views/details/book.ejs +++ /dev/null @@ -1,21 +0,0 @@ -
-
- - - - - -
- -
- This note of type Book doesn't have any child notes so there's nothing to display. See wiki for details. -
- -
-
\ No newline at end of file diff --git a/src/views/details/image.ejs b/src/views/details/image.ejs deleted file mode 100644 index a64c94c67..000000000 --- a/src/views/details/image.ejs +++ /dev/null @@ -1,32 +0,0 @@ -
-
- - - - - -
- -
- -
- -
- - Original file name: - - - - - File type: - - - - - File size: - - -
- - -
\ No newline at end of file diff --git a/src/views/details/protected_session_password.ejs b/src/views/details/protected_session_password.ejs deleted file mode 100644 index f683190c3..000000000 --- a/src/views/details/protected_session_password.ejs +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- - -
- - -
-
\ No newline at end of file diff --git a/src/views/details/relation_map.ejs b/src/views/details/relation_map.ejs deleted file mode 100644 index 71edb466c..000000000 --- a/src/views/details/relation_map.ejs +++ /dev/null @@ -1,27 +0,0 @@ -
- - - - -
- - - -
- -
-
-
-
\ No newline at end of file diff --git a/src/views/details/render.ejs b/src/views/details/render.ejs deleted file mode 100644 index e7125cd24..000000000 --- a/src/views/details/render.ejs +++ /dev/null @@ -1,9 +0,0 @@ -
-
-

This help note is shown because this note of type Render HTML doesn't have required relation to function properly.

- -

Render HTML note type is used for scripting. In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a relation (in Attributes dialog) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.

-
- -
-
\ No newline at end of file diff --git a/src/views/details/search.ejs b/src/views/details/search.ejs deleted file mode 100644 index 0014a2df6..000000000 --- a/src/views/details/search.ejs +++ /dev/null @@ -1,15 +0,0 @@ - \ No newline at end of file