| 
									
										
										
										
											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-11-11 23:21:43 +01:00
										 |  |  | import infoService from "./info.js"; | 
					
						
							| 
									
										
										
										
											2018-11-13 10:42:55 +01:00
										 |  |  | import attributeAutocompleteService from "./attribute_autocomplete.js"; | 
					
						
							| 
									
										
										
										
											2018-11-12 20:17:48 +01:00
										 |  |  | import promptDialog from "../dialogs/prompt.js"; | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  | import infoDialog from "../dialogs/info.js"; | 
					
						
							| 
									
										
										
										
											2018-11-14 19:03:55 +01:00
										 |  |  | import confirmDialog from "../dialogs/confirm.js"; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:25:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 10:30:35 +01:00
										 |  |  | const $component = $("#note-detail-relation-map"); | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  | const $relationMapContainer = $("#relation-map-container"); | 
					
						
							| 
									
										
										
										
											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-11-19 11:14:55 +01:00
										 |  |  | const $resetPanZoomButton = $("#relation-map-reset-pan-zoom"); | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |     } ] | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-13 12:50:08 +01:00
										 |  |  | const mirrorOverlays = [ | 
					
						
							|  |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 1, | 
					
						
							|  |  |  |         id: "arrow", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ], | 
					
						
							|  |  |  |     [ "Label", { label: "", location: 0.2, id: "label-source", cssClass: "connection-label" }], | 
					
						
							|  |  |  |     [ "Label", { label: "", location: 0.8, id: "label-target", cssClass: "connection-label" }], | 
					
						
							|  |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 0, | 
					
						
							|  |  |  |         id: "arrow2", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         direction: -1, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ] | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 10:11:15 +01:00
										 |  |  | const linkOverlays = [ | 
					
						
							|  |  |  |     [ "Arrow", { | 
					
						
							|  |  |  |         location: 1, | 
					
						
							|  |  |  |         id: "arrow", | 
					
						
							|  |  |  |         length: 14, | 
					
						
							|  |  |  |         foldback: 0.8 | 
					
						
							|  |  |  |     } ] | 
					
						
							|  |  |  | ]; | 
					
						
							| 
									
										
										
										
											2018-11-13 12:50:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 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-11-14 14:52:05 +01:00
										 |  |  | function noteIdToId(noteId) { | 
					
						
							| 
									
										
										
										
											2018-11-14 15:11:39 +01:00
										 |  |  |     return "rel-map-note-" + noteId; | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function idToNoteId(id) { | 
					
						
							| 
									
										
										
										
											2018-11-14 15:11:39 +01:00
										 |  |  |     return id.substr(13); | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01: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-11-18 23:01:48 +01:00
										 |  |  | function clearMap() { | 
					
						
							|  |  |  |     // delete all endpoints and connections
 | 
					
						
							|  |  |  |     // this is done at this point (after async operations) to reduce flicker to the minimum
 | 
					
						
							|  |  |  |     jsPlumbInstance.deleteEveryEndpoint(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // without this we still end up with note boxes remaining in the canvas
 | 
					
						
							|  |  |  |     $relationMapContainer.empty(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | async function loadNotesAndRelations() { | 
					
						
							| 
									
										
										
										
											2018-11-14 17:54:28 +01:00
										 |  |  |     const noteIds = mapData.notes.map(note => note.noteId); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     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 => | 
					
						
							| 
									
										
										
										
											2018-11-13 12:50:08 +01:00
										 |  |  |             rel.name === data.mirrorRelations[relation.name] | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |             && ((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) { | 
					
						
							| 
									
										
										
										
											2018-11-13 12:50:08 +01:00
										 |  |  |             match.type = relation.type = relation.name === data.mirrorRelations[relation.name] ? 'biDirectional' : 'mirror'; | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  |             relation.render = false; // don't render second relation
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             relation.type = 'uniDirectional'; | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  |             relation.render = true; | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         relations.push(relation); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |     mapData.notes = mapData.notes.filter(note => note.noteId in data.noteTitles); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |     jsPlumbInstance.batch(async function () { | 
					
						
							| 
									
										
										
										
											2018-11-18 23:01:48 +01:00
										 |  |  |         clearMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         for (const note of mapData.notes) { | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |             const title = data.noteTitles[note.noteId]; | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |             await createNoteBox(note.noteId, 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) { | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  |             if (!relation.render) { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |             const connection = jsPlumbInstance.connect({ | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |                 source: noteIdToId(relation.sourceNoteId), | 
					
						
							|  |  |  |                 target: noteIdToId(relation.targetNoteId), | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |                 type: relation.type | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2018-10-18 11:53:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |             connection.id = relation.attributeId; | 
					
						
							| 
									
										
										
										
											2018-11-13 12:50:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (relation.type === 'mirror') { | 
					
						
							|  |  |  |                 connection.getOverlay("label-source").setLabel(relation.name); | 
					
						
							|  |  |  |                 connection.getOverlay("label-target").setLabel(data.mirrorRelations[relation.name]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 connection.getOverlay("label").setLabel(relation.name); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |             connection.canvas.setAttribute("data-connection-id", connection.id); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-14 10:11:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for (const link of data.links) { | 
					
						
							|  |  |  |             jsPlumbInstance.connect({ | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |                 source: noteIdToId(link.sourceNoteId), | 
					
						
							|  |  |  |                 target: noteIdToId(link.targetNoteId), | 
					
						
							| 
									
										
										
										
											2018-11-14 10:11:15 +01:00
										 |  |  |                 type: 'link' | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | function initPanZoom() { | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |     if (pzInstance) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |     pzInstance = panzoom($relationMapContainer[0], { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |         maxZoom: 2, | 
					
						
							| 
									
										
										
										
											2018-11-13 22:05:32 +01:00
										 |  |  |         minZoom: 0.3, | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |         smoothScroll: false, | 
					
						
							|  |  |  |         onMouseDown: function(event) { | 
					
						
							|  |  |  |             if (clipboard) { | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |                 let {x, y} = getMousePosition(event); | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |                 // modifying position so that cursor is on the top-center of the box
 | 
					
						
							|  |  |  |                 x -= 80; | 
					
						
							|  |  |  |                 y -= 15; | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |                 createNoteBox(clipboard.noteId, clipboard.title, x, y); | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |                 mapData.notes.push({ noteId: clipboard.noteId, x, y }); | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 23:57:39 +01:00
										 |  |  |                 saveData(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |                 clipboard = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2018-11-18 23:57:39 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         filterKey: function(e, dx, dy, dz) { | 
					
						
							|  |  |  |             // if ALT is pressed then panzoom should bubble the event up
 | 
					
						
							|  |  |  |             // this is to preserve ALT-LEFT, ALT-RIGHT navigation working
 | 
					
						
							|  |  |  |             return e.altKey; | 
					
						
							| 
									
										
										
										
											2018-11-01 09:41:03 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |     pzInstance.on('transform', () => { // gets triggered on any transform change
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |         jsPlumbInstance.setZoom(getZoom()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |         saveCurrentTransform(); | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01: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); | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         pzInstance.moveTo(mapData.transform.x, mapData.transform.y); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-19 11:14:55 +01:00
										 |  |  |     else { | 
					
						
							|  |  |  |         // set to initial coordinates
 | 
					
						
							|  |  |  |         pzInstance.moveTo(0, 0); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-25 14:01:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |     $zoomInButton.click(() => pzInstance.zoomTo(0, 0, 1.2)); | 
					
						
							|  |  |  |     $zoomOutButton.click(() => pzInstance.zoomTo(0, 0, 0.8)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  | function saveCurrentTransform() { | 
					
						
							|  |  |  |     const newTransform = pzInstance.getTransform(); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |     if (JSON.stringify(newTransform) !== JSON.stringify(mapData.transform)) { | 
					
						
							| 
									
										
										
										
											2018-11-13 19:16:21 +01:00
										 |  |  |         // clone transform object
 | 
					
						
							|  |  |  |         mapData.transform = JSON.parse(JSON.stringify(newTransform)); | 
					
						
							| 
									
										
										
										
											2018-10-30 20:22:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  |         saveData(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											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-11-18 23:01:48 +01:00
										 |  |  |         clearMap(); | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											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-11-11 23:21:43 +01:00
										 |  |  |         Container: "relation-map-container" | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											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-11-13 12:50:08 +01:00
										 |  |  |     jsPlumbInstance.registerConnectionType("mirror", { anchor:"Continuous", connector:"StateMachine", overlays: mirrorOverlays }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 10:11:15 +01:00
										 |  |  |     jsPlumbInstance.registerConnectionType("link", { anchor:"Continuous", connector:"StateMachine", overlays: linkOverlays }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |     $relationMapContainer.on('mousedown touchstart', '.note-box, .connection-label', e => e.stopPropagation()); | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											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') { | 
					
						
							| 
									
										
										
										
											2018-11-14 19:03:55 +01:00
										 |  |  |             if (!await confirmDialog.confirm("Are you sure you want to remove the relation?")) { | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |                 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 10:11:15 +01:00
										 |  |  |     connection.bind("contextmenu", (obj, event) => { | 
					
						
							|  |  |  |         if (connection.getType().includes("link")) { | 
					
						
							|  |  |  |             // don't create context menu if it's a link since there's nothing to do with link from relation map
 | 
					
						
							|  |  |  |             // (don't open browser menu either)
 | 
					
						
							|  |  |  |             event.preventDefault(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             connectionContextMenuHandler(connection, event); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  |     // if there's no event, then this has been triggered programatically
 | 
					
						
							|  |  |  |     if (!originalEvent) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-13 10:42:55 +01:00
										 |  |  |     const name = await promptDialog.ask({ | 
					
						
							|  |  |  |         message: "Specify new relation name:", | 
					
						
							|  |  |  |         shown: ({ $answer }) => | 
					
						
							|  |  |  |             attributeAutocompleteService.initAttributeNameAutocomplete({ | 
					
						
							|  |  |  |                 $el: $answer, | 
					
						
							|  |  |  |                 attributeType: "relation", | 
					
						
							|  |  |  |                 open: true | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!name || !name.trim()) { | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.deleteConnection(connection); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |     const targetNoteId = idToNoteId(connection.target.id); | 
					
						
							|  |  |  |     const sourceNoteId = idToNoteId(connection.source.id); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-11-12 21:18:22 +01:00
										 |  |  |         await infoDialog.info("Connection '" + name + "' between these notes already exists."); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  |         jsPlumbInstance.deleteConnection(connection); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 23:57:39 +01:00
										 |  |  |     await server.put(`notes/${sourceNoteId}/relations/${name}/to/${targetNoteId}`); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  |     await refresh(); | 
					
						
							| 
									
										
										
										
											2018-10-30 08:53:30 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  | $relationMapContainer.on("contextmenu", ".note-box", e => { | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     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-11-14 23:30:28 +01:00
										 |  |  |     const $title = $noteBox.find(".title a"); | 
					
						
							| 
									
										
										
										
											2018-11-14 19:03:55 +01:00
										 |  |  |     const noteId = idToNoteId($noteBox.prop("id")); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  |     if (cmd === "remove") { | 
					
						
							| 
									
										
										
										
											2018-11-14 23:30:28 +01:00
										 |  |  |         if (!await confirmDialog.confirmDeleteNoteBoxWithNote($title.text())) { | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 19:03:55 +01:00
										 |  |  |         jsPlumbInstance.remove(noteIdToId(noteId)); | 
					
						
							| 
									
										
										
										
											2018-10-29 19:25:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 23:30:28 +01:00
										 |  |  |         if (confirmDialog.isDeleteNoteChecked()) { | 
					
						
							|  |  |  |             await server.remove("notes/" + noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // to force it to disappear from the tree
 | 
					
						
							|  |  |  |             treeService.reload(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |         mapData.notes = mapData.notes.filter(note => note.noteId !== 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-11-14 23:30:28 +01:00
										 |  |  |         const title = await promptDialog.ask({ | 
					
						
							|  |  |  |             message: "Enter new note title:", | 
					
						
							|  |  |  |             defaultValue: $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-11-14 14:52:05 +01:00
										 |  |  | async function createNoteBox(noteId, title, x, y) { | 
					
						
							| 
									
										
										
										
											2018-10-30 11:28:48 +01:00
										 |  |  |     const $noteBox = $("<div>") | 
					
						
							|  |  |  |         .addClass("note-box") | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |         .prop("id", noteIdToId(noteId)) | 
					
						
							| 
									
										
										
										
											2018-11-14 17:26:07 +01:00
										 |  |  |         .append($("<span>").addClass("title").html(await linkService.createNoteLink(noteId, 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-11-12 09:57:48 +01:00
										 |  |  |         start: params => {}, | 
					
						
							|  |  |  |         drag: params => {}, | 
					
						
							|  |  |  |         stop: params => { | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |             const noteId = idToNoteId(params.el.id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const note = mapData.notes.find(note => note.noteId === noteId); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (!note) { | 
					
						
							| 
									
										
										
										
											2018-11-14 14:52:05 +01:00
										 |  |  |                 console.error(`Note ${noteId} not found!`); | 
					
						
							| 
									
										
										
										
											2018-10-21 10:26:14 +02:00
										 |  |  |                 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-11-12 15:10:49 +01:00
										 |  |  | async function refresh() { | 
					
						
							|  |  |  |     await loadNotesAndRelations(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 23:55:14 +01:00
										 |  |  | let clipboard = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | $createChildNote.click(async () => { | 
					
						
							| 
									
										
										
										
											2018-11-14 23:30:28 +01:00
										 |  |  |     const title = await promptDialog.ask({ message: "Enter title of new note",  defaultValue: "new note" }); | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!title.trim()) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const {note} = await server.post(`notes/${noteDetailService.getCurrentNoteId()}/children`, { | 
					
						
							|  |  |  |         title, | 
					
						
							|  |  |  |         target: 'into' | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |     infoService.showMessage("Click on canvas to place new note"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-11-14 14:52:05 +01:00
										 |  |  |     clipboard = { noteId: note.noteId, title }; | 
					
						
							| 
									
										
										
										
											2018-10-31 12:29:01 +01:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  | function getZoom() { | 
					
						
							|  |  |  |     const matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+\)/; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const matches = $relationMapContainer.css('transform').match(matrixRegex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return matches[1]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  | async function dropNoteOntoRelationMapHandler(ev) { | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |     ev.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  |     const notes = JSON.parse(ev.originalEvent.dataTransfer.getData("text")); | 
					
						
							| 
									
										
										
										
											2018-11-12 13:05:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     let {x, y} = getMousePosition(ev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // modifying position so that cursor is on the top-center of the box
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  |     const startX = x -= 80; | 
					
						
							| 
									
										
										
										
											2018-11-12 13:05:09 +01:00
										 |  |  |     y -= 15; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  |     const currentNoteId = treeService.getCurrentNode().data.noteId; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const note of notes) { | 
					
						
							|  |  |  |         if (note.noteId === currentNoteId) { | 
					
						
							|  |  |  |             // we don't allow placing current (relation map) into itself
 | 
					
						
							|  |  |  |             // the reason is that when dragging notes from the tree, the relation map is always selected
 | 
					
						
							|  |  |  |             // since it's focused.
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const exists = mapData.notes.some(n => n.noteId === note.noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (exists) { | 
					
						
							| 
									
										
										
										
											2018-11-12 21:18:22 +01:00
										 |  |  |             await infoDialog.info(`Note "${note.title}" is already placed into the diagram`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 23:30:28 +01:00
										 |  |  |         mapData.notes.push({noteId: note.noteId, x, y}); | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (x - startX > 1000) { | 
					
						
							|  |  |  |             x = startX; | 
					
						
							|  |  |  |             y += 200; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             x += 200; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-16 23:27:57 +01:00
										 |  |  |     saveData(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  |     await refresh(); | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 09:57:48 +01:00
										 |  |  | function getMousePosition(evt) { | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +01:00
										 |  |  |     const rect = $relationMapContainer[0].getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const zoom = getZoom(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         x: (evt.clientX - rect.left) / zoom, | 
					
						
							|  |  |  |         y: (evt.clientY - rect.top) / zoom | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 11:14:55 +01:00
										 |  |  | $resetPanZoomButton.click(() => { | 
					
						
							|  |  |  |     // reset to initial pan & zoom state
 | 
					
						
							|  |  |  |     pzInstance.zoomTo(0, 0, 1 / getZoom()); | 
					
						
							|  |  |  |     pzInstance.moveTo(0, 0); | 
					
						
							| 
									
										
										
										
											2018-11-13 22:05:32 +01:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 15:10:49 +01:00
										 |  |  | $component.on("drop", dropNoteOntoRelationMapHandler); | 
					
						
							|  |  |  | $component.on("dragover", ev => ev.preventDefault()); | 
					
						
							| 
									
										
										
										
											2018-11-11 23:21:43 +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
										 |  |  | } |