mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	converted remaining type widgets
This commit is contained in:
		
							parent
							
								
									11a61325f9
								
							
						
					
					
						commit
						eacefeb08b
					
				| @ -21,12 +21,12 @@ const typeWidgetClasses = { | |||||||
|     'text': "./type_widgets/text.js", |     'text': "./type_widgets/text.js", | ||||||
|     'code': "./type_widgets/code.js", |     'code': "./type_widgets/code.js", | ||||||
|     'file': "./type_widgets/file.js", |     'file': "./type_widgets/file.js", | ||||||
|     'image': "./type_widgets/note_detail_image.js", |     'image': "./type_widgets/image.js", | ||||||
|     'search': "./type_widgets/note_detail_search.js", |     'search': "./type_widgets/search.js", | ||||||
|     'render': "./type_widgets/note_detail_render.js", |     'render': "./type_widgets/render.js", | ||||||
|     'relation-map': "./type_widgets/note_detail_relation_map.js", |     'relation-map': "./type_widgets/relation_map.js", | ||||||
|     'protected-session': "./type_widgets/note_detail_protected_session.js", |     'protected-session': "./type_widgets/protected_session.js", | ||||||
|     'book': "./type_widgets/note_detail_book.js" |     'book': "./type_widgets/book.js" | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default class NoteDetailWidget extends TabAwareWidget { | export default class NoteDetailWidget extends TabAwareWidget { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import linkService from "../../services/link.js"; | import linkService from "../../services/link.js"; | ||||||
| import treeCache from "../../services/tree_cache.js"; | import treeCache from "../../services/tree_cache.js"; | ||||||
| import noteContentRenderer from "../../services/note_content_renderer.js"; | import noteContentRenderer from "../../services/note_content_renderer.js"; | ||||||
|  | import TypeWidget from "./type_widget.js"; | ||||||
| 
 | 
 | ||||||
| const MIN_ZOOM_LEVEL = 1; | const MIN_ZOOM_LEVEL = 1; | ||||||
| const MAX_ZOOM_LEVEL = 6; | const MAX_ZOOM_LEVEL = 6; | ||||||
| @ -32,18 +33,104 @@ const ZOOMS = { | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class NoteDetailBook { | const TPL = ` | ||||||
|     /** | <div class="note-detail-book note-detail-printable"> | ||||||
|      * @param {TabContext} ctx |     <style> | ||||||
|      */ |     .note-detail-book { | ||||||
|     constructor(ctx) { |         height: 100%; | ||||||
|         this.ctx = ctx; |         padding: 10px; | ||||||
|         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'); |     .note-detail-book-content { | ||||||
|         this.$zoomOutButton = this.$component.find('.book-zoom-out-button'); |         display: flex; | ||||||
|         this.$expandChildrenButton = this.$component.find('.expand-children-button'); |         flex-wrap: wrap; | ||||||
|         this.$help = this.$component.find('.note-detail-book-help'); |         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; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  | 
 | ||||||
|  |     <div class="btn-group floating-button" style="right: 20px; top: 20px;"> | ||||||
|  |         <button type="button" | ||||||
|  |                 class="expand-children-button btn icon-button bx bx-move-vertical" | ||||||
|  |                 title="Expand all children"></button> | ||||||
|  | 
 | ||||||
|  |         <button type="button" | ||||||
|  |                 class="book-zoom-in-button btn icon-button bx bx-zoom-in" | ||||||
|  |                 title="Zoom In"></button> | ||||||
|  | 
 | ||||||
|  |         <button type="button" | ||||||
|  |                 class="book-zoom-out-button btn icon-button bx bx-zoom-out" | ||||||
|  |                 title="Zoom Out"></button> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="note-detail-book-help alert alert-warning"> | ||||||
|  |         This note of type Book doesn't have any child notes so there's nothing to display. See <a href="https://github.com/zadam/trilium/wiki/Book-note">wiki</a> for details. | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="note-detail-book-content"></div> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | 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.$zoomInButton.on('click', () => this.setZoom(this.zoomLevel - 1)); | ||||||
|         this.$zoomOutButton.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(); |             $card.find('.note-book-children-content').empty(); | ||||||
|         }); |         }); | ||||||
|  |          | ||||||
|  |         return this.$widget; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async expandCard($card) { |     async expandCard($card) { | ||||||
| @ -104,13 +193,13 @@ class NoteDetailBook { | |||||||
|         this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height); |         this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async render() { |     async doRefresh() { | ||||||
|         this.$content.empty(); |         this.$content.empty(); | ||||||
|         this.$help.hide(); |         this.$help.hide(); | ||||||
| 
 | 
 | ||||||
|         if (this.isAutoBook()) { |         if (this.isAutoBook()) { | ||||||
|             const $addTextLink = $('<a href="javascript:">here</a>').on('click', () => { |             const $addTextLink = $('<a href="javascript:">here</a>').on('click', () => { | ||||||
|                 this.ctx.renderComponent(true); |                 // FIXME
 | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             this.$content.append($('<div class="note-book-auto-message"></div>') |             this.$content.append($('<div class="note-book-auto-message"></div>') | ||||||
| @ -119,17 +208,17 @@ class NoteDetailBook { | |||||||
|                 .append(' if you want to add some text.')) |                 .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); |         this.setZoom(zoomLevel); | ||||||
| 
 | 
 | ||||||
|         await this.renderIntoElement(this.ctx.note, this.$content); |         await this.renderIntoElement(this.tabContext.note, this.$content); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async renderIntoElement(note, $container) { |     async renderIntoElement(note, $container) { | ||||||
|         const childNotes = await note.getChildNotes(); |         const childNotes = await note.getChildNotes(); | ||||||
| 
 | 
 | ||||||
|         for (const childNote of childNotes) { |         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); |             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 */ |     /** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */ | ||||||
|     isAutoBook() { |     isAutoBook() { | ||||||
|         return this.ctx.note.type !== 'book'; |         return this.tabContext.note.type !== 'book'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getDefaultZoomLevel() { |     getDefaultZoomLevel() { | ||||||
|         if (this.isAutoBook()) { |         if (this.isAutoBook()) { | ||||||
|             const w = this.$component.width(); |             const w = this.$widget.width(); | ||||||
| 
 | 
 | ||||||
|             if (w <= 600) { |             if (w <= 600) { | ||||||
|                 return 1; |                 return 1; | ||||||
| @ -192,7 +281,7 @@ class NoteDetailBook { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     show() { |     show() { | ||||||
|         this.$component.show(); |         this.$widget.show(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     focus() {} |     focus() {} | ||||||
| @ -204,8 +293,6 @@ class NoteDetailBook { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     scrollToTop() { |     scrollToTop() { | ||||||
|         this.$component.scrollTop(0); |         this.$widget.scrollTop(0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default NoteDetailBook; |  | ||||||
| @ -22,7 +22,7 @@ const TPL = ` | |||||||
|     <div class="note-detail-code-editor"></div> |     <div class="note-detail-code-editor"></div> | ||||||
| </div>`; | </div>`; | ||||||
| 
 | 
 | ||||||
| class CodeTypeWidget extends TypeWidget { | export default class CodeTypeWidget extends TypeWidget { | ||||||
|     static getType() { return "code"; } |     static getType() { return "code"; } | ||||||
| 
 | 
 | ||||||
|     doRender() { |     doRender() { | ||||||
| @ -139,5 +139,3 @@ class CodeTypeWidget extends TypeWidget { | |||||||
|         this.$widget.scrollTop(0); |         this.$widget.scrollTop(0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default CodeTypeWidget; |  | ||||||
| @ -12,7 +12,7 @@ const TPL = ` | |||||||
|     </div> |     </div> | ||||||
| </div>`; | </div>`; | ||||||
| 
 | 
 | ||||||
| class NoteDetailEmpty extends TypeWidget { | export default class EmptyTypeWidget extends TypeWidget { | ||||||
|     static getType() { return "empty"; } |     static getType() { return "empty"; } | ||||||
| 
 | 
 | ||||||
|     doRender() { |     doRender() { | ||||||
| @ -57,5 +57,3 @@ class NoteDetailEmpty extends TypeWidget { | |||||||
| 
 | 
 | ||||||
|     scrollToTop() {} |     scrollToTop() {} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default NoteDetailEmpty; |  | ||||||
| @ -55,7 +55,7 @@ const TPL = ` | |||||||
|     <input type="file" class="file-upload-new-revision-input" style="display: none"> |     <input type="file" class="file-upload-new-revision-input" style="display: none"> | ||||||
| </div>`; | </div>`; | ||||||
| 
 | 
 | ||||||
| class FileTypeWidget extends TypeWidget { | export default class FileTypeWidget extends TypeWidget { | ||||||
|     static getType() { return "file"; } |     static getType() { return "file"; } | ||||||
| 
 | 
 | ||||||
|     doRender() { |     doRender() { | ||||||
| @ -156,5 +156,3 @@ class FileTypeWidget extends TypeWidget { | |||||||
| 
 | 
 | ||||||
|     scrollToTop() {} |     scrollToTop() {} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default FileTypeWidget; |  | ||||||
							
								
								
									
										168
									
								
								src/public/javascripts/widgets/type_widgets/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/public/javascripts/widgets/type_widgets/image.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 = ` | ||||||
|  | <div class="note-detail-image note-detail-printable"> | ||||||
|  |     <style> | ||||||
|  |     .note-detail-image { | ||||||
|  |         text-align: center; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .note-detail-image-view { | ||||||
|  |         max-width: 100%; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  | 
 | ||||||
|  |     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> | ||||||
|  |         <button class="image-download btn btn-sm btn-primary" type="button">Download</button> | ||||||
|  | 
 | ||||||
|  |         <button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button> | ||||||
|  | 
 | ||||||
|  |         <button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="note-detail-image-wrapper"> | ||||||
|  |         <img class="note-detail-image-view" /> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> | ||||||
|  |         <span> | ||||||
|  |             <strong>Original file name:</strong> | ||||||
|  |             <span class="image-filename"></span> | ||||||
|  |         </span> | ||||||
|  | 
 | ||||||
|  |         <span> | ||||||
|  |             <strong>File type:</strong> | ||||||
|  |             <span class="image-filetype"></span> | ||||||
|  |         </span> | ||||||
|  | 
 | ||||||
|  |         <span> | ||||||
|  |             <strong>File size:</strong> | ||||||
|  |             <span class="image-filesize"></span> | ||||||
|  |         </span> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <input type="file" class="image-upload-new-revision-input" style="display: none"> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
| @ -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 |  | ||||||
| @ -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; |  | ||||||
| @ -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; |  | ||||||
| @ -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; |  | ||||||
| @ -0,0 +1,60 @@ | |||||||
|  | import protectedSessionService from '../../services/protected_session.js'; | ||||||
|  | import TypeWidget from "./type_widget.js"; | ||||||
|  | 
 | ||||||
|  | const TPL = ` | ||||||
|  | <div class="protected-session-password-component note-detail-printable"> | ||||||
|  |     <style> | ||||||
|  |     .protected-session-password-component { | ||||||
|  |         width: 300px; | ||||||
|  |         margin: 30px auto auto; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  | 
 | ||||||
|  |     <form class="protected-session-password-form"> | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label> | ||||||
|  |             <input class="protected-session-password-in-detail form-control protected-session-password" type="password"> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <button class="btn btn-primary">Start protected session <kbd>enter</kbd></button> | ||||||
|  |     </form> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -6,6 +6,7 @@ import treeService from "../../services/tree.js"; | |||||||
| import contextMenuWidget from "../../services/context_menu.js"; | import contextMenuWidget from "../../services/context_menu.js"; | ||||||
| import toastService from "../../services/toast.js"; | import toastService from "../../services/toast.js"; | ||||||
| import attributeAutocompleteService from "../../services/attribute_autocomplete.js"; | import attributeAutocompleteService from "../../services/attribute_autocomplete.js"; | ||||||
|  | import TypeWidget from "./type_widget.js"; | ||||||
| 
 | 
 | ||||||
| const uniDirectionalOverlays = [ | const uniDirectionalOverlays = [ | ||||||
|     [ "Arrow", { |     [ "Arrow", { | ||||||
| @ -61,20 +62,47 @@ const linkOverlays = [ | |||||||
|     } ] |     } ] | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
|  | const TPL = ` | ||||||
|  | <div class="note-detail-relation-map note-detail-printable"> | ||||||
|  |     <button class="relation-map-create-child-note btn btn-sm floating-button" type="button" | ||||||
|  |             title="Create new child note and add it into this relation map"> | ||||||
|  |         <span class="bx bx-folder-plus"></span> | ||||||
|  | 
 | ||||||
|  |         Create child note | ||||||
|  |     </button> | ||||||
|  | 
 | ||||||
|  |     <button type="button" | ||||||
|  |             class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop" | ||||||
|  |             title="Reset pan & zoom to initial coordinates and magnification" | ||||||
|  |             style="right: 70px;"></button> | ||||||
|  | 
 | ||||||
|  |     <div class="btn-group floating-button" style="right: 10px;"> | ||||||
|  |         <button type="button" | ||||||
|  |                 class="relation-map-zoom-in btn icon-button bx bx-zoom-in" | ||||||
|  |                 title="Zoom In"></button> | ||||||
|  | 
 | ||||||
|  |         <button type="button" | ||||||
|  |                 class="relation-map-zoom-out btn icon-button bx bx-zoom-out" | ||||||
|  |                 title="Zoom Out"></button> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="relation-map-wrapper"> | ||||||
|  |        <div class="relation-map-container"></div> | ||||||
|  |     </div> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
| let containerCounter = 1; | let containerCounter = 1; | ||||||
| 
 | 
 | ||||||
| class NoteDetailRelationMap { | export default class RelationMapTypeWidget extends TypeWidget { | ||||||
|     /** |     static getType() { return "relation-map"; } | ||||||
|      * @param {TabContext} ctx | 
 | ||||||
|      */ |     doRender() { | ||||||
|     constructor(ctx) { |         this.$widget = $(TPL); | ||||||
|         this.ctx = ctx; |         this.$relationMapContainer = this.$widget.find(".relation-map-container"); | ||||||
|         this.$component = ctx.$tabContent.find(".note-detail-relation-map"); |         this.$createChildNote = this.$widget.find(".relation-map-create-child-note"); | ||||||
|         this.$relationMapContainer = ctx.$tabContent.find(".relation-map-container"); |         this.$zoomInButton = this.$widget.find(".relation-map-zoom-in"); | ||||||
|         this.$createChildNote = ctx.$tabContent.find(".relation-map-create-child-note"); |         this.$zoomOutButton = this.$widget.find(".relation-map-zoom-out"); | ||||||
|         this.$zoomInButton = ctx.$tabContent.find(".relation-map-zoom-in"); |         this.$resetPanZoomButton = this.$widget.find(".relation-map-reset-pan-zoom"); | ||||||
|         this.$zoomOutButton = ctx.$tabContent.find(".relation-map-zoom-out"); |  | ||||||
|         this.$resetPanZoomButton = ctx.$tabContent.find(".relation-map-reset-pan-zoom"); |  | ||||||
| 
 | 
 | ||||||
|         this.mapData = null; |         this.mapData = null; | ||||||
|         this.jsPlumbInstance = null; |         this.jsPlumbInstance = null; | ||||||
| @ -82,7 +110,7 @@ class NoteDetailRelationMap { | |||||||
|         this.relations = null; |         this.relations = null; | ||||||
|         this.pzInstance = 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 => { |         this.$relationMapWrapper.on('click', event => { | ||||||
|             if (this.clipboard) { |             if (this.clipboard) { | ||||||
|                 let {x, y} = this.getMousePosition(event); |                 let {x, y} = this.getMousePosition(event); | ||||||
| @ -129,7 +157,7 @@ class NoteDetailRelationMap { | |||||||
|                 return; |                 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, |                 title, | ||||||
|                 content: '', |                 content: '', | ||||||
|                 type: 'text' |                 type: 'text' | ||||||
| @ -150,8 +178,10 @@ class NoteDetailRelationMap { | |||||||
|             this.pzInstance.moveTo(0, 0); |             this.pzInstance.moveTo(0, 0); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         this.$component.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev)); |         this.$widget.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev)); | ||||||
|         this.$component.on("dragover", ev => ev.preventDefault()); |         this.$widget.on("dragover", ev => ev.preventDefault()); | ||||||
|  | 
 | ||||||
|  |         return this.$widget; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async tabContextMenuHandler(event, cmd) { |     async tabContextMenuHandler(event, cmd) { | ||||||
| @ -217,9 +247,9 @@ class NoteDetailRelationMap { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if (this.ctx.note.content) { |         if (this.tabContext.note.content) { | ||||||
|             try { |             try { | ||||||
|                 this.mapData = JSON.parse(this.ctx.note.content); |                 this.mapData = JSON.parse(this.tabContext.note.content); | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.log("Could not parse content: ", e); |                 console.log("Could not parse content: ", e); | ||||||
|             } |             } | ||||||
| @ -234,14 +264,14 @@ class NoteDetailRelationMap { | |||||||
|         return id.substr(13); |         return id.substr(13); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async render() { |     async doRefresh() { | ||||||
|         this.$component.show(); |         this.$widget.show(); | ||||||
| 
 | 
 | ||||||
|         await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); |         await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); | ||||||
| 
 | 
 | ||||||
|         jsPlumb.ready(() => { |         jsPlumb.ready(() => { | ||||||
|             // lazy loading above can take time and tab might have been already switched to another note
 |             // 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.loadMapData(); | ||||||
| 
 | 
 | ||||||
|                 this.initJsPlumbInstance(); |                 this.initJsPlumbInstance(); | ||||||
| @ -490,7 +520,7 @@ class NoteDetailRelationMap { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     saveData() { |     saveData() { | ||||||
|         this.ctx.noteChanged(); |         this.tabContext.noteChanged(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createNoteBox(noteId, title, x, y) { |     async createNoteBox(noteId, title, x, y) { | ||||||
| @ -624,5 +654,3 @@ class NoteDetailRelationMap { | |||||||
| 
 | 
 | ||||||
|     scrollToTop() {} |     scrollToTop() {} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default NoteDetailRelationMap; |  | ||||||
							
								
								
									
										55
									
								
								src/public/javascripts/widgets/type_widgets/render.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/public/javascripts/widgets/type_widgets/render.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | import renderService from "../../services/render.js"; | ||||||
|  | import TypeWidget from "./type_widget.js"; | ||||||
|  | 
 | ||||||
|  | const TPL = ` | ||||||
|  | <div class="note-detail-render note-detail-printable"> | ||||||
|  |     <div class="note-detail-render-help alert alert-warning"> | ||||||
|  |         <p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p> | ||||||
|  | 
 | ||||||
|  |         <p>Render HTML note type is used for <a href="https://github.com/zadam/trilium/wiki/Scripts">scripting</a>. 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 <a class="show-attributes-button">Attributes dialog</a>) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.</p> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="note-detail-render-content"></div> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								src/public/javascripts/widgets/type_widgets/search.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/public/javascripts/widgets/type_widgets/search.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 = ` | ||||||
|  | <div class="note-detail-search note-detail-printable"> | ||||||
|  |     <div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;"> | ||||||
|  |         <strong>Search string:    </strong> | ||||||
|  |         <textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea> | ||||||
|  | 
 | ||||||
|  |         <span> | ||||||
|  |                 | ||||||
|  |             <button type="button" class="btn btn-primary note-detail-search-refresh-results-button">Refresh search results</button> | ||||||
|  |         </span> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <br /> | ||||||
|  | 
 | ||||||
|  |     <div class="note-detail-search-help"></div> | ||||||
|  | </div>`; | ||||||
|  | 
 | ||||||
|  | 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() {} | ||||||
|  | } | ||||||
| @ -4,9 +4,6 @@ import noteAutocompleteService from '../../services/note_autocomplete.js'; | |||||||
| import mimeTypesService from '../../services/mime_types.js'; | import mimeTypesService from '../../services/mime_types.js'; | ||||||
| import TypeWidget from "./type_widget.js"; | import TypeWidget from "./type_widget.js"; | ||||||
| import utils from "../../services/utils.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; | const ENABLE_INSPECTOR = false; | ||||||
| 
 | 
 | ||||||
| @ -77,7 +74,7 @@ const TPL = ` | |||||||
| </div> | </div> | ||||||
| `;
 | `;
 | ||||||
| 
 | 
 | ||||||
| class TextTypeWidget extends TypeWidget { | export default class TextTypeWidget extends TypeWidget { | ||||||
|     static getType() { return "text"; } |     static getType() { return "text"; } | ||||||
| 
 | 
 | ||||||
|     doRender() { |     doRender() { | ||||||
| @ -218,5 +215,3 @@ class TextTypeWidget extends TypeWidget { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default TextTypeWidget; |  | ||||||
| @ -398,14 +398,6 @@ button.icon-button { | |||||||
|     padding: 0; |     padding: 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .note-detail-image { |  | ||||||
|     text-align: center; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .note-detail-image-view { |  | ||||||
|     max-width: 100%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pre:not(.CodeMirror-line) { | pre:not(.CodeMirror-line) { | ||||||
|     color: var(--main-text-color) !important; |     color: var(--main-text-color) !important; | ||||||
|     white-space: pre-wrap; |     white-space: pre-wrap; | ||||||
| @ -625,12 +617,6 @@ div[data-notify="container"] { | |||||||
|     cursor: pointer !important; |     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 { | .ck-editor__is-empty.ck-content.ck-editor__editable::before { | ||||||
|     content: 'You can start writing note here ...'; |     content: 'You can start writing note here ...'; | ||||||
|     position: absolute; |     position: absolute; | ||||||
| @ -711,10 +697,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href | |||||||
|     display: inline-block; |     display: inline-block; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .protected-session-password-component { | 
 | ||||||
|     width: 300px; |  | ||||||
|     margin: 30px auto auto; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| .note-detail-empty { | .note-detail-empty { | ||||||
|     margin: 50px; |     margin: 50px; | ||||||
| @ -729,68 +712,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href | |||||||
|     z-index: 100; |     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 { | #toast-container { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  | |||||||
| @ -1,21 +0,0 @@ | |||||||
| <div class="note-detail-book note-detail-printable"> |  | ||||||
|     <div class="btn-group floating-button" style="right: 20px; top: 20px;"> |  | ||||||
|         <button type="button" |  | ||||||
|                 class="expand-children-button btn icon-button bx bx-move-vertical" |  | ||||||
|                 title="Expand all children"></button> |  | ||||||
| 
 |  | ||||||
|         <button type="button" |  | ||||||
|                 class="book-zoom-in-button btn icon-button bx bx-zoom-in" |  | ||||||
|                 title="Zoom In"></button> |  | ||||||
| 
 |  | ||||||
|         <button type="button" |  | ||||||
|                 class="book-zoom-out-button btn icon-button bx bx-zoom-out" |  | ||||||
|                 title="Zoom Out"></button> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="note-detail-book-help alert alert-warning"> |  | ||||||
|         This note of type Book doesn't have any child notes so there's nothing to display. See <a href="https://github.com/zadam/trilium/wiki/Book-note">wiki</a> for details. |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="note-detail-book-content"></div> |  | ||||||
| </div> |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| <div class="note-detail-image note-detail-printable"> |  | ||||||
|     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> |  | ||||||
|         <button class="image-download btn btn-sm btn-primary" type="button">Download</button> |  | ||||||
| 
 |  | ||||||
|         <button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button> |  | ||||||
| 
 |  | ||||||
|         <button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="note-detail-image-wrapper"> |  | ||||||
|         <img class="note-detail-image-view" /> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> |  | ||||||
|         <span> |  | ||||||
|             <strong>Original file name:</strong> |  | ||||||
|             <span class="image-filename"></span> |  | ||||||
|         </span> |  | ||||||
| 
 |  | ||||||
|         <span> |  | ||||||
|             <strong>File type:</strong> |  | ||||||
|             <span class="image-filetype"></span> |  | ||||||
|         </span> |  | ||||||
| 
 |  | ||||||
|         <span> |  | ||||||
|             <strong>File size:</strong> |  | ||||||
|             <span class="image-filesize"></span> |  | ||||||
|         </span> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <input type="file" class="image-upload-new-revision-input" style="display: none"> |  | ||||||
| </div> |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| <div class="protected-session-password-component note-detail-printable"> |  | ||||||
|     <form class="protected-session-password-form"> |  | ||||||
|         <div class="form-group"> |  | ||||||
|             <label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label> |  | ||||||
|             <input class="protected-session-password-in-detail form-control protected-session-password" type="password"> |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|         <button class="btn btn-primary">Start protected session <kbd>enter</kbd></button> |  | ||||||
|     </form> |  | ||||||
| </div> |  | ||||||
| @ -1,27 +0,0 @@ | |||||||
| <div class="note-detail-relation-map note-detail-printable"> |  | ||||||
|     <button class="relation-map-create-child-note btn btn-sm floating-button" type="button" |  | ||||||
|             title="Create new child note and add it into this relation map"> |  | ||||||
|         <span class="bx bx-folder-plus"></span> |  | ||||||
| 
 |  | ||||||
|         Create child note |  | ||||||
|     </button> |  | ||||||
| 
 |  | ||||||
|     <button type="button" |  | ||||||
|             class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop" |  | ||||||
|             title="Reset pan & zoom to initial coordinates and magnification" |  | ||||||
|             style="right: 70px;"></button> |  | ||||||
| 
 |  | ||||||
|     <div class="btn-group floating-button" style="right: 10px;"> |  | ||||||
|         <button type="button" |  | ||||||
|                 class="relation-map-zoom-in btn icon-button bx bx-zoom-in" |  | ||||||
|                 title="Zoom In"></button> |  | ||||||
| 
 |  | ||||||
|         <button type="button" |  | ||||||
|                 class="relation-map-zoom-out btn icon-button bx bx-zoom-out" |  | ||||||
|                 title="Zoom Out"></button> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="relation-map-wrapper"> |  | ||||||
|        <div class="relation-map-container"></div> |  | ||||||
|     </div> |  | ||||||
| </div> |  | ||||||
| @ -1,9 +0,0 @@ | |||||||
| <div class="note-detail-render note-detail-printable"> |  | ||||||
|     <div class="note-detail-render-help alert alert-warning"> |  | ||||||
|         <p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p> |  | ||||||
| 
 |  | ||||||
|         <p>Render HTML note type is used for <a href="https://github.com/zadam/trilium/wiki/Scripts">scripting</a>. 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 <a class="show-attributes-button">Attributes dialog</a>) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.</p> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="note-detail-render-content"></div> |  | ||||||
| </div> |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| <div class="note-detail-search note-detail-printable"> |  | ||||||
|     <div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;"> |  | ||||||
|         <strong>Search string:    </strong> |  | ||||||
|         <textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea> |  | ||||||
| 
 |  | ||||||
|         <span> |  | ||||||
|                 |  | ||||||
|             <button type="button" class="btn btn-primary note-detail-search-refresh-results-button">Refresh search results</button> |  | ||||||
|         </span> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <br /> |  | ||||||
| 
 |  | ||||||
|     <div class="note-detail-search-help"></div> |  | ||||||
| </div> |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam