mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	basic refactoring of relation map code
This commit is contained in:
		| @@ -10,28 +10,7 @@ let mapData; | |||||||
| let instance; | let instance; | ||||||
| let initDone = false; | let initDone = false; | ||||||
|  |  | ||||||
| async function show() { | const uniDirectionalOverlays = [ | ||||||
|     $noteDetailRelationMap.show(); |  | ||||||
|  |  | ||||||
|     await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); |  | ||||||
|  |  | ||||||
|     const currentNote = noteDetailService.getCurrentNote(); |  | ||||||
|     mapData = { |  | ||||||
|         notes: [], |  | ||||||
|         relations: [] |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if (currentNote.content) { |  | ||||||
|         try { |  | ||||||
|             mapData = JSON.parse(currentNote.content); |  | ||||||
|         } |  | ||||||
|         catch (e) { |  | ||||||
|             console.log("Could not parse content: ", e); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     jsPlumb.ready(async function () { |  | ||||||
|         const uniDirectionalOverlays = [ |  | ||||||
|     [ "Arrow", { |     [ "Arrow", { | ||||||
|         location: 1, |         location: 1, | ||||||
|         id: "arrow", |         id: "arrow", | ||||||
| @@ -39,9 +18,9 @@ async function show() { | |||||||
|         foldback: 0.8 |         foldback: 0.8 | ||||||
|     } ], |     } ], | ||||||
|     [ "Label", { label: "", id: "label", cssClass: "aLabel" }] |     [ "Label", { label: "", id: "label", cssClass: "aLabel" }] | ||||||
|         ]; | ]; | ||||||
|  |  | ||||||
|         const biDirectionalOverlays = [ | const biDirectionalOverlays = [ | ||||||
|     [ "Arrow", { |     [ "Arrow", { | ||||||
|         location: 1, |         location: 1, | ||||||
|         id: "arrow", |         id: "arrow", | ||||||
| @@ -56,8 +35,124 @@ async function show() { | |||||||
|         direction: -1, |         direction: -1, | ||||||
|         foldback: 0.8 |         foldback: 0.8 | ||||||
|     } ] |     } ] | ||||||
|         ]; | ]; | ||||||
|  |  | ||||||
|  | function loadMapData() { | ||||||
|  |     const currentNote = noteDetailService.getCurrentNote(); | ||||||
|  |     mapData = { | ||||||
|  |         notes: [], | ||||||
|  |         relations: [] | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (currentNote.content) { | ||||||
|  |         try { | ||||||
|  |             mapData = JSON.parse(currentNote.content); | ||||||
|  |         } catch (e) { | ||||||
|  |             console.log("Could not parse content: ", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function show() { | ||||||
|  |     $noteDetailRelationMap.show(); | ||||||
|  |  | ||||||
|  |     await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP); | ||||||
|  |  | ||||||
|  |     loadMapData(); | ||||||
|  |  | ||||||
|  |     jsPlumb.ready(initJsPlumb); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function loadNotesAndRelations() { | ||||||
|  |     const noteIds = mapData.notes.map(note => note.id); | ||||||
|  |     const data = await server.post("notes/relation-map", {noteIds}); | ||||||
|  |  | ||||||
|  |     const relations = []; | ||||||
|  |  | ||||||
|  |     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))); | ||||||
|  |  | ||||||
|  |         if (match) { | ||||||
|  |             match.type = 'biDirectional'; | ||||||
|  |         } else { | ||||||
|  |             relation.type = 'uniDirectional'; | ||||||
|  |             relations.push(relation); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     mapData.notes = mapData.notes.filter(note => note.id in data.noteTitles); | ||||||
|  |  | ||||||
|  |     instance.batch(function () { | ||||||
|  |         const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | ||||||
|  |         let curX = 100; | ||||||
|  |         let curY = maxY + 200; | ||||||
|  |  | ||||||
|  |         for (const note of mapData.notes) { | ||||||
|  |             const title = data.noteTitles[note.id]; | ||||||
|  |  | ||||||
|  |             if (note.x && note.y) { | ||||||
|  |                 newNode(note.id, title, note.x, note.y); | ||||||
|  |             } else { | ||||||
|  |                 newNode(note.id, title, curX, curY); | ||||||
|  |  | ||||||
|  |                 if (curX > 1000) { | ||||||
|  |                     curX = 100; | ||||||
|  |                     curY += 200; | ||||||
|  |                 } else { | ||||||
|  |                     curX += 200; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (const relation of relations) { | ||||||
|  |             if (relation.name === 'isChildOf') { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             const connection = instance.connect({ | ||||||
|  |                 id: `${relation.sourceNoteId}${relation.targetNoteId}`, | ||||||
|  |                 source: relation.sourceNoteId, | ||||||
|  |                 target: relation.targetNoteId, | ||||||
|  |                 type: relation.type | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             relation.connectionId = connection.id; | ||||||
|  |  | ||||||
|  |             connection.getOverlay("label").setLabel(relation.name); | ||||||
|  |             connection.canvas.setAttribute("data-connection-id", connection.id); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         initDone = true; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function initPanZoom() { | ||||||
|  |     const pz = panzoom($relationMapCanvas[0], { | ||||||
|  |         maxZoom: 2, | ||||||
|  |         minZoom: 0.1, | ||||||
|  |         smoothScroll: false | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (mapData.transform) { | ||||||
|  |         pz.moveTo(mapData.transform.x, mapData.transform.y); | ||||||
|  |         pz.zoomTo(0, 0, mapData.transform.scale); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $relationMapCanvas[0].addEventListener('zoom', function (e) { | ||||||
|  |         mapData.transform = pz.getTransform(); | ||||||
|  |         saveData(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $relationMapCanvas[0].addEventListener('panend', function (e) { | ||||||
|  |         mapData.transform = pz.getTransform(); | ||||||
|  |         saveData(); | ||||||
|  |     }, true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function initJsPlumb () { | ||||||
|     instance = jsPlumb.getInstance({ |     instance = jsPlumb.getInstance({ | ||||||
|         Endpoint: ["Dot", {radius: 2}], |         Endpoint: ["Dot", {radius: 2}], | ||||||
|         Connector: "StateMachine", |         Connector: "StateMachine", | ||||||
| @@ -100,7 +195,63 @@ async function show() { | |||||||
|             {title: "Remove note", cmd: "remove", uiIcon: "ui-icon-trash"}, |             {title: "Remove note", cmd: "remove", uiIcon: "ui-icon-trash"}, | ||||||
|             {title: "Edit title", cmd: "edit-title", uiIcon: "ui-icon-pencil"}, |             {title: "Edit title", cmd: "edit-title", uiIcon: "ui-icon-pencil"}, | ||||||
|         ], |         ], | ||||||
|             select: function(event, ui) { |         select: noteContextMenuHandler | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $.widget("moogle.contextmenuRelation", $.moogle.contextmenu, {}); | ||||||
|  |  | ||||||
|  |     $relationMapCanvas.contextmenuRelation({ | ||||||
|  |         delegate: ".aLabel,.jtk-connector", | ||||||
|  |         autoTrigger: false, // it doesn't open automatically, needs to be triggered explicitly by .open() call | ||||||
|  |         menu: [ | ||||||
|  |             {title: "Remove relation", cmd: "remove", uiIcon: "ui-icon-trash"}, | ||||||
|  |             {title: "Edit relation name", cmd: "edit-name", uiIcon: "ui-icon-pencil"}, | ||||||
|  |         ], | ||||||
|  |         select: relationContextMenuHandler | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     instance.bind("contextmenu", function (c, e) { | ||||||
|  |         e.preventDefault(); | ||||||
|  |  | ||||||
|  |         $relationMapCanvas.contextmenuRelation("open", e, { connection: c }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await loadNotesAndRelations(); | ||||||
|  |  | ||||||
|  |     // so that canvas is not panned when clicking/dragging note box | ||||||
|  |     $relationMapCanvas.on('mousedown touchstart', '.note-box, .aLabel', e => e.stopPropagation()); | ||||||
|  |  | ||||||
|  |     jsPlumb.fire("jsPlumbDemoLoaded", instance); | ||||||
|  |  | ||||||
|  |     initPanZoom(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function relationContextMenuHandler(event, ui) { | ||||||
|  |     const {connection} = ui.extraData; | ||||||
|  |  | ||||||
|  |     if (ui.cmd === 'remove') { | ||||||
|  |         if (!confirm("Are you sure you want to remove the relation?")) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         instance.deleteConnection(connection); | ||||||
|  |  | ||||||
|  |         mapData.relations = mapData.relations.filter(relation => relation.connectionId !== connection.id); | ||||||
|  |         saveData(); | ||||||
|  |     } | ||||||
|  |     else if (ui.cmd === 'edit-name') { | ||||||
|  |         const relationName = prompt("Specify new relation name:"); | ||||||
|  |  | ||||||
|  |         connection.getOverlay("label").setLabel(relationName); | ||||||
|  |  | ||||||
|  |         const relation = mapData.relations.find(relation => relation.connectionId === connection.id); | ||||||
|  |         relation.name = relationName; | ||||||
|  |  | ||||||
|  |         saveData(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function noteContextMenuHandler(event, ui) { | ||||||
|     const $noteBox = ui.target.closest(".note-box"); |     const $noteBox = ui.target.closest(".note-box"); | ||||||
|     const noteId = $noteBox.prop("id"); |     const noteId = $noteBox.prop("id"); | ||||||
|  |  | ||||||
| @@ -130,138 +281,6 @@ async function show() { | |||||||
|  |  | ||||||
|         saveData(); |         saveData(); | ||||||
|     } |     } | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         $.widget("moogle.contextmenuRelation", $.moogle.contextmenu, {}); |  | ||||||
|  |  | ||||||
|         $relationMapCanvas.contextmenuRelation({ |  | ||||||
|             delegate: ".aLabel,.jtk-connector", |  | ||||||
|             autoTrigger: false, // it doesn't open automatically, needs to be triggered explicitly by .open() call |  | ||||||
|             menu: [ |  | ||||||
|                 {title: "Remove relation", cmd: "remove", uiIcon: "ui-icon-trash"}, |  | ||||||
|                 {title: "Edit relation name", cmd: "edit-name", uiIcon: "ui-icon-pencil"}, |  | ||||||
|             ], |  | ||||||
|             select: function(event, ui) { |  | ||||||
|                 const {connection} = ui.extraData; |  | ||||||
|  |  | ||||||
|                 if (ui.cmd === 'remove') { |  | ||||||
|                     if (!confirm("Are you sure you want to remove the relation?")) { |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     instance.deleteConnection(connection); |  | ||||||
|  |  | ||||||
|                     mapData.relations = mapData.relations.filter(relation => relation.connectionId !== connection.id); |  | ||||||
|                     saveData(); |  | ||||||
|                 } |  | ||||||
|                 else if (ui.cmd === 'edit-name') { |  | ||||||
|                     const relationName = prompt("Specify new relation name:"); |  | ||||||
|  |  | ||||||
|                     connection.getOverlay("label").setLabel(relationName); |  | ||||||
|  |  | ||||||
|                     const relation = mapData.relations.find(relation => relation.connectionId === connection.id); |  | ||||||
|                     relation.name = relationName; |  | ||||||
|  |  | ||||||
|                     saveData(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         instance.bind("contextmenu", function (c, e) { |  | ||||||
|             e.preventDefault(); |  | ||||||
|  |  | ||||||
|             $relationMapCanvas.contextmenuRelation("open", e, { connection: c }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         const noteIds = mapData.notes.map(note => note.id); |  | ||||||
|         const data = await server.post("notes/relation-map", { noteIds }); |  | ||||||
|  |  | ||||||
|         const relations = []; |  | ||||||
|  |  | ||||||
|         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))); |  | ||||||
|  |  | ||||||
|             if (match) { |  | ||||||
|                 match.type = 'biDirectional'; |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 relation.type = 'uniDirectional'; |  | ||||||
|                 relations.push(relation); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         mapData.notes = mapData.notes.filter(note => note.id in data.noteTitles); |  | ||||||
|  |  | ||||||
|         instance.batch(function () { |  | ||||||
|             const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); |  | ||||||
|             let curX = 100; |  | ||||||
|             let curY = maxY + 200; |  | ||||||
|  |  | ||||||
|             for (const note of mapData.notes) { |  | ||||||
|                 const title = data.noteTitles[note.id]; |  | ||||||
|  |  | ||||||
|                 if (note.x && note.y) { |  | ||||||
|                     newNode(note.id, title, note.x, note.y); |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     newNode(note.id, title, curX, curY); |  | ||||||
|  |  | ||||||
|                     if (curX > 1000) { |  | ||||||
|                         curX = 100; |  | ||||||
|                         curY += 200; |  | ||||||
|                     } |  | ||||||
|                     else { |  | ||||||
|                         curX += 200; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             for (const relation of relations) { |  | ||||||
|                 if (relation.name === 'isChildOf') { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 const connection = instance.connect({ id: `${relation.sourceNoteId}${relation.targetNoteId}`, source: relation.sourceNoteId, target: relation.targetNoteId, type: relation.type }); |  | ||||||
|  |  | ||||||
|                 relation.connectionId = connection.id; |  | ||||||
|  |  | ||||||
|                 connection.getOverlay("label").setLabel(relation.name); |  | ||||||
|                 connection.canvas.setAttribute("data-connection-id", connection.id); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             initDone = true; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // so that canvas is not panned when clicking/dragging note box |  | ||||||
|         $relationMapCanvas.on('mousedown touchstart', '.note-box, .aLabel', e => e.stopPropagation()); |  | ||||||
|  |  | ||||||
|         jsPlumb.fire("jsPlumbDemoLoaded", instance); |  | ||||||
|  |  | ||||||
|         const pz = panzoom($relationMapCanvas[0], { |  | ||||||
|             maxZoom: 2, |  | ||||||
|             minZoom: 0.1, |  | ||||||
|             smoothScroll: false |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         if (mapData.transform) { |  | ||||||
|             pz.moveTo(mapData.transform.x, mapData.transform.y); |  | ||||||
|             pz.zoomTo(0, 0, mapData.transform.scale); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $relationMapCanvas[0].addEventListener('zoom', function(e) { |  | ||||||
|             mapData.transform = pz.getTransform(); |  | ||||||
|             saveData(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         $relationMapCanvas[0].addEventListener('panend', function(e) { |  | ||||||
|             mapData.transform = pz.getTransform(); |  | ||||||
|             saveData(); |  | ||||||
|         }, true); |  | ||||||
|     }); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| function saveData() { | function saveData() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user