| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | import server from "./server.js"; | 
					
						
							|  |  |  | import noteDetailService from "./note_detail.js"; | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | import linkService from "./link.js"; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:46:07 +02:00
										 |  |  | import libraryLoader from "./library_loader.js"; | 
					
						
							| 
									
										
										
										
											2018-10-30 22:18:20 +01:00
										 |  |  | import treeService from "./tree.js"; | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | import contextMenuWidget from "./context_menu.js"; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 10:30:35 +01:00
										 |  |  | const $component = $("#note-detail-relation-map"); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | const $relationMapCanvas = $("#relation-map-canvas"); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | const $addChildNotesButton = $("#relation-map-add-child-notes"); | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | const $createChildNote = $("#relation-map-create-child-note"); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | const $zoomInButton = $("#relation-map-zoom-in"); | 
					
						
							|  |  |  | const $zoomOutButton = $("#relation-map-zoom-out"); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | let mapData; | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  | let jsPlumbInstance; | 
					
						
							| 
									
										
										
										
											2018-10-29 22:38:51 +01:00
										 |  |  | // outside of mapData because they are not persisted in the note content
 | 
					
						
							|  |  |  | let relations; | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  | let pzInstance; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | const uniDirectionalOverlays = [ | 
					
						
							|  |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 1, | 
					
						
							|  |  |  |         id: "arrow", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ], | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |     [ "Label", { label: "", id: "label", cssClass: "connection-label" }] | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const biDirectionalOverlays = [ | 
					
						
							|  |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 1, | 
					
						
							|  |  |  |         id: "arrow", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ], | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |     [ "Label", { label: "", id: "label", cssClass: "connection-label" }], | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 0, | 
					
						
							|  |  |  |         id: "arrow2", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         direction: -1, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ] | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function loadMapData() { | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |     const currentNote = noteDetailService.getCurrentNote(); | 
					
						
							|  |  |  |     mapData = { | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |         notes: [] | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (currentNote.content) { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             mapData = JSON.parse(currentNote.content); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         } catch (e) { | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |             console.log("Could not parse content: ", e); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | async function show() { | 
					
						
							| 
									
										
										
										
											2018-11-08 10:30:35 +01:00
										 |  |  |     $component.show(); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     loadMapData(); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |     jsPlumb.ready(() => { | 
					
						
							|  |  |  |         initJsPlumbInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |         initPanZoom(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |         loadNotesAndRelations(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | async function loadNotesAndRelations() { | 
					
						
							|  |  |  |     const noteIds = mapData.notes.map(note => note.id); | 
					
						
							|  |  |  |     const data = await server.post("notes/relation-map", {noteIds}); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 22:38:51 +01:00
										 |  |  |     relations = []; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     for (const relation of data.relations) { | 
					
						
							|  |  |  |         const match = relations.find(rel => | 
					
						
							|  |  |  |             rel.name === relation.name | 
					
						
							|  |  |  |             && ((rel.sourceNoteId === relation.sourceNoteId && rel.targetNoteId === relation.targetNoteId) | 
					
						
							|  |  |  |             || (rel.sourceNoteId === relation.targetNoteId && rel.targetNoteId === relation.sourceNoteId))); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         if (match) { | 
					
						
							|  |  |  |             match.type = 'biDirectional'; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             relation.type = 'uniDirectional'; | 
					
						
							|  |  |  |             relations.push(relation); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     mapData.notes = mapData.notes.filter(note => note.id in data.noteTitles); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.batch(async function () { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         for (const note of mapData.notes) { | 
					
						
							|  |  |  |             const title = data.noteTitles[note.id]; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |             await createNoteBox(note.id, title, note.x, note.y); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         for (const relation of relations) { | 
					
						
							|  |  |  |             if (relation.name === 'isChildOf') { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |             const connection = jsPlumbInstance.connect({ | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |                 source: relation.sourceNoteId, | 
					
						
							|  |  |  |                 target: relation.targetNoteId, | 
					
						
							|  |  |  |                 type: relation.type | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |             connection.id = relation.attributeId; | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |             connection.getOverlay("label").setLabel(relation.name); | 
					
						
							|  |  |  |             connection.canvas.setAttribute("data-connection-id", connection.id); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | function getMousePos(canvas, evt) { | 
					
						
							|  |  |  |     var rect = canvas.getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log(rect); | 
					
						
							|  |  |  |     console.log(canvas); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log(`(${evt.clientX} - ${rect.left}) / (${rect.right} - ${rect.left}) * ${canvas.width}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         x: (evt.clientX - rect.left) / (rect.right - rect.left) * canvas.width, | 
					
						
							|  |  |  |         y: (evt.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | function initPanZoom() { | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |     if (pzInstance) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     pzInstance = panzoom($relationMapCanvas[0], { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         maxZoom: 2, | 
					
						
							|  |  |  |         minZoom: 0.1, | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |         smoothScroll: false, | 
					
						
							|  |  |  |         onMouseDown: function(event) { | 
					
						
							|  |  |  |             if (clipboard) { | 
					
						
							|  |  |  |                 const {x, y} = getMousePos($relationMapCanvas[0].getContext("2d"), event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 console.log(x, y); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 createNoteBox(clipboard.id, clipboard.title, x, y); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 mapData.notes.push({ id: clipboard.id, x, y }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 clipboard = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     if (mapData.transform) { | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         pzInstance.zoomTo(0, 0, mapData.transform.scale); | 
					
						
							|  |  |  |         pzInstance.moveTo(mapData.transform.x, mapData.transform.y); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     pzInstance.on('zoom', function (e) { | 
					
						
							|  |  |  |         mapData.transform = pzInstance.getTransform(); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         saveData(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     pzInstance.on('panend', function (e) { | 
					
						
							|  |  |  |         mapData.transform = pzInstance.getTransform(); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         saveData(); | 
					
						
							|  |  |  |     }, true); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     $zoomInButton.click(() => pzInstance.zoomTo(0, 0, 1.2)); | 
					
						
							|  |  |  |     $zoomOutButton.click(() => pzInstance.zoomTo(0, 0, 0.8)); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | function cleanup() { | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     if (jsPlumbInstance) { | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |         // delete all endpoints and connections
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.deleteEveryEndpoint(); | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // without this we still end up with note boxes remaining in the canvas
 | 
					
						
							|  |  |  |         $relationMapCanvas.empty(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (pzInstance) { | 
					
						
							|  |  |  |         pzInstance.dispose(); | 
					
						
							|  |  |  |         pzInstance = null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function initJsPlumbInstance () { | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     if (jsPlumbInstance) { | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |         cleanup(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance = jsPlumb.getInstance({ | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         Endpoint: ["Dot", {radius: 2}], | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |         Connector: "StateMachine", | 
					
						
							|  |  |  |         ConnectionOverlays: uniDirectionalOverlays, | 
					
						
							|  |  |  |         HoverPaintStyle: { stroke: "#777", strokeWidth: 1 }, | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         Container: "relation-map-canvas" | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.registerConnectionType("uniDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: uniDirectionalOverlays }); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.registerConnectionType("biDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: biDirectionalOverlays }); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.bind("connection", connectionCreatedHandler); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     // so that canvas is not panned when clicking/dragging note box
 | 
					
						
							|  |  |  |     $relationMapCanvas.on('mousedown touchstart', '.note-box, .connection-label', e => e.stopPropagation()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | function connectionContextMenuHandler(connection, event) { | 
					
						
							|  |  |  |     event.preventDefault(); | 
					
						
							|  |  |  |     event.stopPropagation(); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     const contextMenuItems = [ {title: "Remove relation", cmd: "remove", uiIcon: "trash"} ]; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     contextMenuWidget.initContextMenu(event, contextMenuItems, async (event, cmd) => { | 
					
						
							|  |  |  |         if (cmd === 'remove') { | 
					
						
							|  |  |  |             if (!confirm("Are you sure you want to remove the relation?")) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |             const relation = relations.find(rel => rel.attributeId === connection.id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             await server.remove(`notes/${relation.sourceNoteId}/relations/${relation.name}/to/${relation.targetNoteId}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             jsPlumbInstance.deleteConnection(connection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             relations = relations.filter(relation => relation.attributeId !== connection.id); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | async function connectionCreatedHandler(info, originalEvent) { | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     const connection = info.connection; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     connection.bind("contextmenu", (obj, event) => connectionContextMenuHandler(connection, event)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |     // if there's no event, then this has been triggered programatically
 | 
					
						
							|  |  |  |     if (!originalEvent) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const name = prompt("Specify new relation name:"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!name || !name.trim()) { | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.deleteConnection(connection); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const targetNoteId = connection.target.id; | 
					
						
							|  |  |  |     const sourceNoteId = connection.source.id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |     const relationExists = relations.some(rel => | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |         rel.targetNoteId === targetNoteId | 
					
						
							|  |  |  |         && rel.sourceNoteId === sourceNoteId | 
					
						
							|  |  |  |         && rel.name === name); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |     if (relationExists) { | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |         alert("Connection '" + name + "' between these notes already exists."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.deleteConnection(connection); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |     const attribute = await server.put(`notes/${sourceNoteId}/relations/${name}/to/${targetNoteId}`); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |     relations.push({ attributeId: attribute.attributeId , targetNoteId, sourceNoteId, name }); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |     connection.id = attribute.attributeId; | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |     connection.getOverlay("label").setLabel(name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | $relationMapCanvas.on("contextmenu", ".note-box", e => { | 
					
						
							|  |  |  |     const contextMenuItems = [ | 
					
						
							|  |  |  |         {title: "Remove note", cmd: "remove", uiIcon: "trash"}, | 
					
						
							|  |  |  |         {title: "Edit title", cmd: "edit-title", uiIcon: "pencil"}, | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2018-10-25 15:45:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     contextMenuWidget.initContextMenu(e, contextMenuItems, noteContextMenuHandler); | 
					
						
							| 
									
										
										
										
											2018-10-25 15:45:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     return false; | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | async function noteContextMenuHandler(event, cmd) { | 
					
						
							|  |  |  |     const $noteBox = $(event.originalTarget).closest(".note-box"); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     const noteId = $noteBox.prop("id"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     if (cmd === "remove") { | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |         if (!confirm("Are you sure you want to remove the note from this diagram?")) { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.remove(noteId); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         mapData.notes = mapData.notes.filter(note => note.id !== noteId); | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         relations = relations.filter(relation => relation.sourceNoteId !== noteId && relation.targetNoteId !== noteId); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         saveData(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     else if (cmd === "edit-title") { | 
					
						
							| 
									
										
										
										
											2018-10-30 22:18:20 +01:00
										 |  |  |         const $title = $noteBox.find(".title a"); | 
					
						
							|  |  |  |         const title = prompt("Enter new note title:", $title.text()); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!title) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 22:18:20 +01:00
										 |  |  |         await server.put(`notes/${noteId}/change-title`, { title }); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 22:18:20 +01:00
										 |  |  |         treeService.setNoteTitle(noteId, title); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 22:18:20 +01:00
										 |  |  |         $title.text(title); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | function saveData() { | 
					
						
							| 
									
										
										
										
											2018-10-25 15:45:14 +02:00
										 |  |  |     noteDetailService.noteChanged(); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  | async function createNoteBox(id, title, x, y) { | 
					
						
							|  |  |  |     const $noteBox = $("<div>") | 
					
						
							|  |  |  |         .addClass("note-box") | 
					
						
							|  |  |  |         .prop("id", id) | 
					
						
							|  |  |  |         .append($("<span>").addClass("title").html(await linkService.createNoteLink(id, title))) | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |         .append($("<div>").addClass("endpoint").attr("title", "Start dragging relations from here and drop them on another note.")) | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |         .css("left", x + "px") | 
					
						
							|  |  |  |         .css("top", y + "px"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.getContainer().appendChild($noteBox[0]); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.draggable($noteBox[0], { | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |         start:function(params) {}, | 
					
						
							|  |  |  |         drag:function(params) {}, | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |         stop:function(params) { | 
					
						
							|  |  |  |             const note = mapData.notes.find(note => note.id === params.el.id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!note) { | 
					
						
							|  |  |  |                 console.error(`Note ${params.el.id} not found!`); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             [note.x, note.y] = params.finalPos; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             saveData(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.makeSource($noteBox[0], { | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |         filter: ".endpoint", | 
					
						
							|  |  |  |         anchor: "Continuous", | 
					
						
							| 
									
										
										
										
											2018-10-30 10:36:19 +01:00
										 |  |  |         connectorStyle: { stroke: "#000", strokeWidth: 1 }, | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |         connectionType: "basic", | 
					
						
							|  |  |  |         extract:{ | 
					
						
							|  |  |  |             "action": "the-action" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.makeTarget($noteBox[0], { | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |         dropOptions: { hoverClass: "dragHover" }, | 
					
						
							|  |  |  |         anchor: "Continuous", | 
					
						
							|  |  |  |         allowLoopback: true | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | function getFreePosition() { | 
					
						
							|  |  |  |     const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return [100, maxY + 200]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | $addChildNotesButton.click(async () => { | 
					
						
							|  |  |  |     const children = await server.get("notes/" + noteDetailService.getCurrentNoteId() + "/children"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |     let [curX, curY] = getFreePosition(); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (const child of children) { | 
					
						
							| 
									
										
										
										
											2018-10-25 12:06:36 +02:00
										 |  |  |         if (mapData.notes.some(note => note.id === child.noteId)) { | 
					
						
							|  |  |  |             // note already exists
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |         mapData.notes.push({ | 
					
						
							|  |  |  |             id: child.noteId, | 
					
						
							|  |  |  |             x: curX, | 
					
						
							|  |  |  |             y: curY | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (curX > 1000) { | 
					
						
							|  |  |  |             curX = 100; | 
					
						
							|  |  |  |             curY += 200; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             curX += 200; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |     saveData(); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |     // delete all endpoints and connections
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.deleteEveryEndpoint(); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  |     await loadNotesAndRelations(); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  | let clipboard = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | $createChildNote.click(async () => { | 
					
						
							|  |  |  |     const title = prompt("Enter title of new note", "new note"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!title.trim()) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const {note} = await server.post(`notes/${noteDetailService.getCurrentNoteId()}/children`, { | 
					
						
							|  |  |  |         title, | 
					
						
							|  |  |  |         target: 'into' | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 18:21:58 +01:00
										 |  |  |     // reloading tree so that the new note appears there
 | 
					
						
							|  |  |  |     // no need to wait for it to finish
 | 
					
						
							|  |  |  |     treeService.reload(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     clipboard = { id: note.noteId, title }; | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | export default { | 
					
						
							| 
									
										
										
										
											2018-10-18 11:46:07 +02:00
										 |  |  |     show, | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |     getContent: () => JSON.stringify(mapData), | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  |     focus: () => null, | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |     onNoteChange: () => null, | 
					
						
							|  |  |  |     cleanup | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | } |