| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | import libraryLoader from "../services/library_loader.js"; | 
					
						
							|  |  |  | import server from "../services/server.js"; | 
					
						
							|  |  |  | import attributeService from "../services/attributes.js"; | 
					
						
							|  |  |  | import hoistedNoteService from "../services/hoisted_note.js"; | 
					
						
							|  |  |  | import appContext from "../services/app_context.js"; | 
					
						
							|  |  |  | import NoteContextAwareWidget from "./note_context_aware_widget.js"; | 
					
						
							| 
									
										
										
										
											2021-09-24 21:40:02 +02:00
										 |  |  | import linkContextMenuService from "../services/link_context_menu.js"; | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | const TPL = `<div class="note-map-widget" style="position: relative;">
 | 
					
						
							| 
									
										
										
										
											2021-05-28 23:52:42 +02:00
										 |  |  |     <style> | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         .type-special .note-detail, .note-detail-note-map { | 
					
						
							|  |  |  |             height: 100%; | 
					
						
							| 
									
										
										
										
											2021-05-28 23:52:42 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-29 22:52:32 +02:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         .map-type-switcher { | 
					
						
							|  |  |  |             position: absolute;  | 
					
						
							|  |  |  |             top: 10px;  | 
					
						
							|  |  |  |             right: 10px;  | 
					
						
							|  |  |  |             background-color: var(--accented-background-color); | 
					
						
							| 
									
										
										
										
											2021-05-28 23:52:42 +02:00
										 |  |  |             z-index: 1000; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         .map-type-switcher .bx { | 
					
						
							| 
									
										
										
										
											2021-09-29 12:39:28 +02:00
										 |  |  |             font-size: 120%; | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-28 23:52:42 +02:00
										 |  |  |     </style> | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2021-09-29 12:39:28 +02:00
										 |  |  |     <div class="btn-group btn-group-sm map-type-switcher floating-button" role="group"> | 
					
						
							|  |  |  |       <button type="button" class="btn icon-button bx bx-network-chart" title="Link Map" data-type="link"></button> | 
					
						
							|  |  |  |       <button type="button" class="btn icon-button bx bx-sitemap" title="Tree map" data-type="tree"></button> | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     </div> | 
					
						
							| 
									
										
										
										
											2021-06-27 12:53:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |     <div class="style-resolver"></div> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     <div class="note-map-container"></div> | 
					
						
							|  |  |  | </div>`; | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | export default class NoteMapWidget extends NoteContextAwareWidget { | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |     constructor(widgetMode) { | 
					
						
							|  |  |  |         super(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.widgetMode = widgetMode; // 'type' or 'ribbon'
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |     doRender() { | 
					
						
							|  |  |  |         this.$widget = $(TPL); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:20:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 16:38:37 +02:00
										 |  |  |         const documentStyle = window.getComputedStyle(document.documentElement); | 
					
						
							|  |  |  |         this.themeStyle = documentStyle.getPropertyValue('--theme-style')?.trim(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.$container = this.$widget.find(".note-map-container"); | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         this.$styleResolver = this.$widget.find('.style-resolver'); | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         window.addEventListener('resize', () => this.setHeight(), false); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:20:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.$widget.find(".map-type-switcher button").on("click",  async e => { | 
					
						
							|  |  |  |             const type = $(e.target).closest("button").attr("data-type"); | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             await attributeService.setLabel(this.noteId, 'mapType', type); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:20:30 +02:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         super.doRender(); | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |     setHeight() { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         if (!this.graph) { // no graph has been even rendered
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         const $parent = this.$widget.parent(); | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         this.graph | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |             .height($parent.height()) | 
					
						
							|  |  |  |             .width($parent.width()); | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     async refreshWithNote() { | 
					
						
							|  |  |  |         this.$widget.show(); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         this.css = { | 
					
						
							|  |  |  |             fontFamily: this.$container.css("font-family"), | 
					
						
							|  |  |  |             textColor: this.rgb2hex(this.$container.css("color")), | 
					
						
							|  |  |  |             mutedTextColor: this.rgb2hex(this.$styleResolver.css("color")) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.mapType = this.note.getLabelValue("mapType") === "tree" ? "tree" : "link"; | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         this.setHeight(); | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |         await libraryLoader.requireLibrary(libraryLoader.FORCE_GRAPH); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.graph = ForceGraph()(this.$container[0]) | 
					
						
							|  |  |  |             .width(this.$container.width()) | 
					
						
							|  |  |  |             .height(this.$container.height()) | 
					
						
							|  |  |  |             .onZoom(zoom => this.setZoomLevel(zoom.k)) | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             .d3AlphaDecay(0.01) | 
					
						
							|  |  |  |             .d3VelocityDecay(0.08) | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |             .nodeCanvasObject((node, ctx) => this.paintNode(node, this.stringToColor(node.type), ctx)) | 
					
						
							|  |  |  |             .nodePointerAreaPaint((node, ctx) => this.paintNode(node, this.stringToColor(node.type), ctx)) | 
					
						
							|  |  |  |             .nodePointerAreaPaint((node, color, ctx) => { | 
					
						
							|  |  |  |                 ctx.fillStyle = color; | 
					
						
							|  |  |  |                 ctx.beginPath(); | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |                 ctx.arc(node.x, node.y, this.noteIdToSizeMap[node.id], 0, 2 * Math.PI, false); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |                 ctx.fill(); | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             .nodeLabel(node => node.name) | 
					
						
							|  |  |  |             .maxZoom(7) | 
					
						
							| 
									
										
										
										
											2021-09-29 12:39:28 +02:00
										 |  |  |             .warmupTicks(30) | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             .linkDirectionalArrowLength(5) | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |             .linkDirectionalArrowRelPos(1) | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             .linkWidth(1) | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |             .linkColor(() => this.css.mutedTextColor) | 
					
						
							| 
									
										
										
										
											2021-09-24 21:40:02 +02:00
										 |  |  |             .onNodeClick(node => appContext.tabManager.getActiveContext().setNote(node.id)) | 
					
						
							|  |  |  |             .onNodeRightClick((node, e) => linkContextMenuService.openContextMenu(node.id, e)); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         if (this.mapType === 'link') { | 
					
						
							|  |  |  |             this.graph | 
					
						
							|  |  |  |                 .linkLabel(l => `${l.source.name} - <strong>${l.name}</strong> - ${l.target.name}`) | 
					
						
							|  |  |  |                 .linkCanvasObject((link, ctx) => this.paintLink(link, ctx)) | 
					
						
							|  |  |  |                 .linkCanvasObjectMode(() => "after"); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 13:27:21 +02:00
										 |  |  |         let mapRootNoteId = this.getMapRootNoteId(); | 
					
						
							|  |  |  |         const data = await this.loadNotesAndRelations(mapRootNoteId); | 
					
						
							| 
									
										
										
										
											2021-10-03 11:04:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 13:27:21 +02:00
										 |  |  |         const nodeLinkRatio = data.nodes.length / data.links.length; | 
					
						
							| 
									
										
										
										
											2021-10-03 11:04:56 +02:00
										 |  |  |         const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5); | 
					
						
							|  |  |  |         const charge = -20 / magnifiedRatio; | 
					
						
							|  |  |  |         const boundedCharge = Math.min(-3, charge); | 
					
						
							| 
									
										
										
										
											2021-09-28 13:27:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.graph.d3Force('link').distance(40); | 
					
						
							| 
									
										
										
										
											2021-09-29 12:39:28 +02:00
										 |  |  |         this.graph.d3Force('center').strength(0.2); | 
					
						
							| 
									
										
										
										
											2021-10-03 11:04:56 +02:00
										 |  |  |         this.graph.d3Force('charge').strength(boundedCharge); | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.graph.d3Force('charge').distanceMax(1000); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         this.renderData(data); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getMapRootNoteId() { | 
					
						
							|  |  |  |         if (this.widgetMode === 'ribbon') { | 
					
						
							|  |  |  |             return this.noteId; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         let mapRootNoteId = this.note.getLabelValue("mapRootNoteId"); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         if (mapRootNoteId === 'hoisted') { | 
					
						
							|  |  |  |             mapRootNoteId = hoistedNoteService.getHoistedNoteId(); | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         } else if (!mapRootNoteId) { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             mapRootNoteId = appContext.tabManager.getActiveContext().parentNoteId; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         return mapRootNoteId; | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     stringToColor(str) { | 
					
						
							|  |  |  |         let hash = 0; | 
					
						
							|  |  |  |         for (let i = 0; i < str.length; i++) { | 
					
						
							|  |  |  |             hash = str.charCodeAt(i) + ((hash << 5) - hash); | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         let colour = '#'; | 
					
						
							|  |  |  |         for (let i = 0; i < 3; i++) { | 
					
						
							| 
									
										
										
										
											2021-10-08 16:38:37 +02:00
										 |  |  |             let value = (hash >> (i * 8)) & 0xFF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             console.log("this.themeStyle", this.themeStyle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (this.themeStyle === "dark" && value < 128) { | 
					
						
							|  |  |  |                 value += 128; // lighten up the colors
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             colour += ('00' + value.toString(16)).substr(-2); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         return colour; | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     rgb2hex(rgb) { | 
					
						
							|  |  |  |         return `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) | 
					
						
							|  |  |  |             .slice(1) | 
					
						
							|  |  |  |             .map(n => parseInt(n, 10).toString(16).padStart(2, '0')) | 
					
						
							|  |  |  |             .join('')}`
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     setZoomLevel(level) { | 
					
						
							|  |  |  |         this.zoomLevel = level; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     paintNode(node, color, ctx) { | 
					
						
							|  |  |  |         const {x, y} = node; | 
					
						
							|  |  |  |         const size = this.noteIdToSizeMap[node.id]; | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |         ctx.fillStyle = (this.widgetMode === 'ribbon' && node.id === this.noteId) ? 'red' : color; | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         ctx.beginPath(); | 
					
						
							|  |  |  |         ctx.arc(x, y, size, 0, 2 * Math.PI, false); | 
					
						
							|  |  |  |         ctx.fill(); | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         const toRender = this.zoomLevel > 2 | 
					
						
							|  |  |  |             || (this.zoomLevel > 1 && size > 6) | 
					
						
							|  |  |  |             || (this.zoomLevel > 0.3 && size > 10); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         if (!toRender) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-31 23:38:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         ctx.fillStyle = this.css.textColor; | 
					
						
							|  |  |  |         ctx.font = size + 'px ' + this.css.fontFamily; | 
					
						
							|  |  |  |         ctx.textAlign = 'center'; | 
					
						
							|  |  |  |         ctx.textBaseline = 'middle'; | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         let title = node.name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (title.length > 15) { | 
					
						
							|  |  |  |             title = title.substr(0, 15) + "..."; | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         ctx.fillText(title, x, y + Math.round(size * 1.5)); | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |     paintLink(link, ctx) { | 
					
						
							| 
									
										
										
										
											2021-06-02 21:39:18 +02:00
										 |  |  |         if (this.zoomLevel < 5) { | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |         ctx.font = '3px ' + this.css.fontFamily; | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |         ctx.textAlign = 'center'; | 
					
						
							|  |  |  |         ctx.textBaseline = 'middle'; | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |         ctx.fillStyle = this.css.mutedTextColor; | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const {source, target} = link; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const x = (source.x + target.x) / 2; | 
					
						
							|  |  |  |         const y = (source.y + target.y) / 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ctx.save(); | 
					
						
							|  |  |  |         ctx.translate(x, y); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const deltaY = source.y - target.y; | 
					
						
							|  |  |  |         const deltaX = source.x - target.x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let angle = Math.atan2(deltaY, deltaX); | 
					
						
							|  |  |  |         let moveY = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (angle < -Math.PI / 2 || angle > Math.PI / 2) { | 
					
						
							|  |  |  |             angle += Math.PI; | 
					
						
							|  |  |  |             moveY = -2; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ctx.rotate(angle); | 
					
						
							|  |  |  |         ctx.fillText(link.name, 0, moveY); | 
					
						
							|  |  |  |         ctx.restore(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     async loadNotesAndRelations(mapRootNoteId) { | 
					
						
							|  |  |  |         const resp = await server.post(`note-map/${mapRootNoteId}/${this.mapType}`); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |         this.calculateNodeSizes(resp); | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |         const links = this.getGroupedLinks(resp.links); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |         this.nodes = resp.notes.map(([noteId, title, type]) => ({ | 
					
						
							|  |  |  |             id: noteId, | 
					
						
							|  |  |  |             name: title, | 
					
						
							|  |  |  |             type: type, | 
					
						
							|  |  |  |         })); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |         return { | 
					
						
							|  |  |  |             nodes: this.nodes, | 
					
						
							|  |  |  |             links: links.map(link => ({ | 
					
						
							|  |  |  |                 id: link.id, | 
					
						
							|  |  |  |                 source: link.sourceNoteId, | 
					
						
							|  |  |  |                 target: link.targetNoteId, | 
					
						
							|  |  |  |                 name: link.names.join(", ") | 
					
						
							|  |  |  |             })) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |     getGroupedLinks(links) { | 
					
						
							|  |  |  |         const linksGroupedBySourceTarget = {}; | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |         for (const link of links) { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             const key = `${link.sourceNoteId}-${link.targetNoteId}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (key in linksGroupedBySourceTarget) { | 
					
						
							|  |  |  |                 if (!linksGroupedBySourceTarget[key].names.includes(link.name)) { | 
					
						
							|  |  |  |                     linksGroupedBySourceTarget[key].names.push(link.name); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |                 linksGroupedBySourceTarget[key] = { | 
					
						
							|  |  |  |                     id: key, | 
					
						
							|  |  |  |                     sourceNoteId: link.sourceNoteId, | 
					
						
							|  |  |  |                     targetNoteId: link.targetNoteId, | 
					
						
							|  |  |  |                     names: [link.name] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:41:31 +02:00
										 |  |  |         return Object.values(linksGroupedBySourceTarget); | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |     calculateNodeSizes(resp) { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.noteIdToSizeMap = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |         if (this.mapType === 'tree') { | 
					
						
							|  |  |  |             const {noteIdToDescendantCountMap} = resp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const noteId in noteIdToDescendantCountMap) { | 
					
						
							|  |  |  |                 this.noteIdToSizeMap[noteId] = 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 const count = noteIdToDescendantCountMap[noteId]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (count > 0) { | 
					
						
							|  |  |  |                     this.noteIdToSizeMap[noteId] += 1 + Math.round(Math.log(count) / Math.log(1.5)); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (this.mapType === 'link') { | 
					
						
							|  |  |  |             const noteIdToLinkCount = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const link of resp.links) { | 
					
						
							|  |  |  |                 noteIdToLinkCount[link.targetNoteId] = 1 + (noteIdToLinkCount[link.targetNoteId] || 0); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |             for (const [noteId] of resp.notes) { | 
					
						
							|  |  |  |                 this.noteIdToSizeMap[noteId] = 4; | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 23:02:38 +02:00
										 |  |  |                 if (noteId in noteIdToLinkCount) { | 
					
						
							|  |  |  |                     this.noteIdToSizeMap[noteId] += Math.min(Math.pow(noteIdToLinkCount[noteId], 0.5), 15); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-31 21:31:07 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |     renderData(data) { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         this.graph.graphData(data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 22:25:39 +02:00
										 |  |  |         if (this.widgetMode === 'ribbon') { | 
					
						
							|  |  |  |             setTimeout(() => { | 
					
						
							|  |  |  |                 const node = this.nodes.find(node => node.id === this.noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.graph.centerAt(node.x, node.y, 500); | 
					
						
							|  |  |  |             }, 1000); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (this.widgetMode === 'type') { | 
					
						
							|  |  |  |             if (data.nodes.length > 1) { | 
					
						
							|  |  |  |                 setTimeout(() => this.graph.zoomToFit(400, 10), 1000); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |     cleanup() { | 
					
						
							|  |  |  |         this.$container.html(''); | 
					
						
							| 
									
										
										
										
											2021-06-02 21:23:40 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |     entitiesReloadedEvent({loadResults}) { | 
					
						
							| 
									
										
										
										
											2021-09-22 21:11:36 +02:00
										 |  |  |         if (loadResults.getAttributes(this.componentId).find(attr => attr.name === 'mapType' && attributeService.isAffecting(attr, this.note))) { | 
					
						
							| 
									
										
										
										
											2021-06-01 22:03:38 +02:00
										 |  |  |             this.refresh(); | 
					
						
							| 
									
										
										
										
											2021-05-28 23:19:11 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |