From 0b85f87cb2ab10b8cc6a76e2cb0d3da43015371f Mon Sep 17 00:00:00 2001 From: azivner Date: Tue, 13 Nov 2018 12:50:08 +0100 Subject: [PATCH] display mirror relations correctly on relation map --- docs/backend_api/ApiToken.html | 2 +- docs/backend_api/Attribute.html | 306 ++++++++++++- docs/backend_api/BackendScriptApi.html | 2 +- docs/backend_api/Branch.html | 2 +- docs/backend_api/Entity.html | 2 +- docs/backend_api/Link.html | 2 +- docs/backend_api/Note.html | 418 ++++++++++++++++-- docs/backend_api/NoteRevision.html | 2 +- docs/backend_api/Option.html | 2 +- docs/backend_api/RecentNote.html | 2 +- docs/backend_api/entities_api_token.js.html | 2 +- docs/backend_api/entities_attribute.js.html | 23 +- docs/backend_api/entities_branch.js.html | 2 +- docs/backend_api/entities_entity.js.html | 2 +- docs/backend_api/entities_link.js.html | 2 +- docs/backend_api/entities_note.js.html | 20 +- .../entities_note_revision.js.html | 2 +- docs/backend_api/entities_option.js.html | 2 +- docs/backend_api/entities_recent_note.js.html | 2 +- docs/backend_api/global.html | 2 +- docs/backend_api/index.html | 2 +- .../services_backend_script_api.js.html | 2 +- docs/frontend_api/Branch.html | 2 +- docs/frontend_api/FrontendScriptApi.html | 30 +- docs/frontend_api/NoteFull.html | 2 +- docs/frontend_api/NoteShort.html | 2 +- docs/frontend_api/entities_branch.js.html | 2 +- docs/frontend_api/entities_note_full.js.html | 2 +- docs/frontend_api/entities_note_short.js.html | 2 +- docs/frontend_api/global.html | 2 +- docs/frontend_api/index.html | 2 +- .../services_frontend_script_api.js.html | 16 +- src/entities/note.js | 18 + .../services/attribute_autocomplete.js | 2 +- .../services/note_detail_relation_map.js | 37 +- src/routes/api/notes.js | 36 +- 36 files changed, 843 insertions(+), 115 deletions(-) diff --git a/docs/backend_api/ApiToken.html b/docs/backend_api/ApiToken.html index 0860ebfdb..a0c9867ef 100644 --- a/docs/backend_api/ApiToken.html +++ b/docs/backend_api/ApiToken.html @@ -288,7 +288,7 @@
diff --git a/docs/backend_api/Attribute.html b/docs/backend_api/Attribute.html index 3d2aadf8a..841556a23 100644 --- a/docs/backend_api/Attribute.html +++ b/docs/backend_api/Attribute.html @@ -406,6 +406,310 @@ +

Methods

+ + + + + + + +

(async) getNote() → {Promise.<(Note|null)>}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<(Note|null)> + + +
+
+ + + + + + + + + + + + + +

(async) getTargetNote() → {Promise.<(Note|null)>}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<(Note|null)> + + +
+
+ + + + + + + + + + + + + +

isDefinition() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + @@ -426,7 +730,7 @@
diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html index 5659ad3ad..fd7cc35f3 100644 --- a/docs/backend_api/BackendScriptApi.html +++ b/docs/backend_api/BackendScriptApi.html @@ -3814,7 +3814,7 @@ transactional by default.
diff --git a/docs/backend_api/Branch.html b/docs/backend_api/Branch.html index e4b259864..0fb9512b4 100644 --- a/docs/backend_api/Branch.html +++ b/docs/backend_api/Branch.html @@ -511,7 +511,7 @@ Each note can have multiple (at least one) branches, meaning it can be placed in
diff --git a/docs/backend_api/Entity.html b/docs/backend_api/Entity.html index 9888804b6..5738a6d65 100644 --- a/docs/backend_api/Entity.html +++ b/docs/backend_api/Entity.html @@ -216,7 +216,7 @@
diff --git a/docs/backend_api/Link.html b/docs/backend_api/Link.html index 7ba997e97..2247f9bfe 100644 --- a/docs/backend_api/Link.html +++ b/docs/backend_api/Link.html @@ -358,7 +358,7 @@ this is different concept than attribute/relation.
diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html index e3dbcb795..04446ec7e 100644 --- a/docs/backend_api/Note.html +++ b/docs/backend_api/Note.html @@ -373,7 +373,7 @@
Source:
@@ -613,7 +613,7 @@
Source:
@@ -809,7 +809,7 @@
Source:
@@ -1005,7 +1005,7 @@
Source:
@@ -1177,7 +1177,7 @@
Source:
@@ -1342,7 +1342,7 @@
Source:
@@ -1518,7 +1518,7 @@
Source:
@@ -1622,7 +1622,7 @@
Source:
@@ -1722,7 +1722,7 @@
Source:
@@ -1826,7 +1826,7 @@
Source:
@@ -1979,7 +1979,7 @@
Source:
@@ -2035,6 +2035,171 @@ +

(async) getLabelDefinitions(nameopt) → {Promise.<Array.<Attribute>>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
name + + +string + + + + + + <optional>
+ + + + + +
label name to filter
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ all note's label definitions, including inherited ones +
+ + + +
+
+ Type +
+
+ +Promise.<Array.<Attribute>> + + +
+
+ + + + + + + + + + + + +

(async) getLabels(nameopt) → {Promise.<Array.<Attribute>>}

@@ -2144,7 +2309,7 @@
Source:
@@ -2297,7 +2462,7 @@
Source:
@@ -2401,7 +2566,7 @@
Source:
@@ -2505,7 +2670,7 @@
Source:
@@ -2605,7 +2770,7 @@
Source:
@@ -2709,7 +2874,7 @@
Source:
@@ -2862,7 +3027,7 @@
Source:
@@ -2918,6 +3083,171 @@ +

(async) getRelationDefinitions(nameopt) → {Promise.<Array.<Attribute>>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
name + + +string + + + + + + <optional>
+ + + + + +
relation name to filter
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ all note's relation definitions including inherited ones +
+ + + +
+
+ Type +
+
+ +Promise.<Array.<Attribute>> + + +
+
+ + + + + + + + + + + + +

(async) getRelations(nameopt) → {Promise.<Array.<Attribute>>}

@@ -3027,7 +3357,7 @@
Source:
@@ -3180,7 +3510,7 @@
Source:
@@ -3336,7 +3666,7 @@
Source:
@@ -3444,7 +3774,7 @@
Source:
@@ -3544,7 +3874,7 @@
Source:
@@ -3648,7 +3978,7 @@
Source:
@@ -3824,7 +4154,7 @@
Source:
@@ -3928,7 +4258,7 @@
Source:
@@ -4081,7 +4411,7 @@
Source:
@@ -4234,7 +4564,7 @@
Source:
@@ -4343,7 +4673,7 @@ Cache is note instance scoped.
Source:
@@ -4425,7 +4755,7 @@ Cache is note instance scoped.
Source:
@@ -4529,7 +4859,7 @@ Cache is note instance scoped.
Source:
@@ -4633,7 +4963,7 @@ Cache is note instance scoped.
Source:
@@ -4737,7 +5067,7 @@ Cache is note instance scoped.
Source:
@@ -4841,7 +5171,7 @@ Cache is note instance scoped.
Source:
@@ -5068,7 +5398,7 @@ Cache is note instance scoped.
Source:
@@ -5264,7 +5594,7 @@ Cache is note instance scoped.
Source:
@@ -5460,7 +5790,7 @@ Cache is note instance scoped.
Source:
@@ -5687,7 +6017,7 @@ Cache is note instance scoped.
Source:
@@ -5883,7 +6213,7 @@ Cache is note instance scoped.
Source:
@@ -6079,7 +6409,7 @@ Cache is note instance scoped.
Source:
@@ -6337,7 +6667,7 @@ Cache is note instance scoped.
Source:
@@ -6564,7 +6894,7 @@ Cache is note instance scoped.
Source:
@@ -6791,7 +7121,7 @@ Cache is note instance scoped.
Source:
@@ -6859,7 +7189,7 @@ Cache is note instance scoped.
diff --git a/docs/backend_api/NoteRevision.html b/docs/backend_api/NoteRevision.html index 4a49565fa..87fdf35ae 100644 --- a/docs/backend_api/NoteRevision.html +++ b/docs/backend_api/NoteRevision.html @@ -403,7 +403,7 @@
diff --git a/docs/backend_api/Option.html b/docs/backend_api/Option.html index fd10300fd..155eb46dc 100644 --- a/docs/backend_api/Option.html +++ b/docs/backend_api/Option.html @@ -311,7 +311,7 @@
diff --git a/docs/backend_api/RecentNote.html b/docs/backend_api/RecentNote.html index f5b3e375b..fa9e3e9d7 100644 --- a/docs/backend_api/RecentNote.html +++ b/docs/backend_api/RecentNote.html @@ -288,7 +288,7 @@
diff --git a/docs/backend_api/entities_api_token.js.html b/docs/backend_api/entities_api_token.js.html index ba4bef39b..a123fcacc 100644 --- a/docs/backend_api/entities_api_token.js.html +++ b/docs/backend_api/entities_api_token.js.html @@ -75,7 +75,7 @@ module.exports = ApiToken;
diff --git a/docs/backend_api/entities_attribute.js.html b/docs/backend_api/entities_attribute.js.html index 466968b15..10ed9ffbf 100644 --- a/docs/backend_api/entities_attribute.js.html +++ b/docs/backend_api/entities_attribute.js.html @@ -68,10 +68,20 @@ class Attribute extends Entity { } } + /** + * @returns {Promise<Note|null>} + */ async getNote() { - return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); + if (!this.__note) { + this.__note = await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); + } + + return this.__note; } + /** + * @returns {Promise<Note|null>} + */ async getTargetNote() { if (this.type !== 'relation') { throw new Error(`Attribute ${this.attributeId} is not relation`); @@ -81,9 +91,16 @@ class Attribute extends Entity { return null; } - return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.value]); + if (!this.__targetNote) { + this.__targetNote = await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.value]); + } + + return this.__targetNote; } + /** + * @return {boolean} + */ isDefinition() { return this.type === 'label-definition' || this.type === 'relation-definition'; } @@ -134,7 +151,7 @@ module.exports = Attribute;
diff --git a/docs/backend_api/entities_branch.js.html b/docs/backend_api/entities_branch.js.html index 432f2ef64..da5323629 100644 --- a/docs/backend_api/entities_branch.js.html +++ b/docs/backend_api/entities_branch.js.html @@ -105,7 +105,7 @@ module.exports = Branch;
diff --git a/docs/backend_api/entities_entity.js.html b/docs/backend_api/entities_entity.js.html index ac9df1cc0..e02c82614 100644 --- a/docs/backend_api/entities_entity.js.html +++ b/docs/backend_api/entities_entity.js.html @@ -93,7 +93,7 @@ module.exports = Entity;
diff --git a/docs/backend_api/entities_link.js.html b/docs/backend_api/entities_link.js.html index 82a6c7804..2912ef666 100644 --- a/docs/backend_api/entities_link.js.html +++ b/docs/backend_api/entities_link.js.html @@ -92,7 +92,7 @@ module.exports = Link;
diff --git a/docs/backend_api/entities_note.js.html b/docs/backend_api/entities_note.js.html index f35313b7a..dedb6c6f9 100644 --- a/docs/backend_api/entities_note.js.html +++ b/docs/backend_api/entities_note.js.html @@ -35,7 +35,9 @@ const repository = require('../services/repository'); const dateUtils = require('../services/date_utils'); const LABEL = 'label'; +const LABEL_DEFINITION = 'label-definition'; const RELATION = 'relation'; +const RELATION_DEFINITION = 'relation-definition'; /** * This represents a Note which is a central object in the Trilium Notes project. @@ -164,6 +166,14 @@ class Note extends Entity { return (await this.getAttributes(name)).filter(attr => attr.type === LABEL); } + /** + * @param {string} [name] - label name to filter + * @returns {Promise<Attribute[]>} all note's label definitions, including inherited ones + */ + async getLabelDefinitions(name) { + return (await this.getAttributes(name)).filter(attr => attr.type === LABEL_DEFINITION); + } + /** * @param {string} [name] - relation name to filter * @returns {Promise<Attribute[]>} all note's relations (attributes with type relation), including inherited ones @@ -172,6 +182,14 @@ class Note extends Entity { return (await this.getAttributes(name)).filter(attr => attr.type === RELATION); } + /** + * @param {string} [name] - relation name to filter + * @returns {Promise<Attribute[]>} all note's relation definitions including inherited ones + */ + async getRelationDefinitions(name) { + return (await this.getAttributes(name)).filter(attr => attr.type === RELATION_DEFINITION); + } + /** * Clear note's attributes cache to force fresh reload for next attribute request. * Cache is note instance scoped. @@ -622,7 +640,7 @@ module.exports = Note;
diff --git a/docs/backend_api/entities_note_revision.js.html b/docs/backend_api/entities_note_revision.js.html index 8423fa335..6b9c5de29 100644 --- a/docs/backend_api/entities_note_revision.js.html +++ b/docs/backend_api/entities_note_revision.js.html @@ -91,7 +91,7 @@ module.exports = NoteRevision;
diff --git a/docs/backend_api/entities_option.js.html b/docs/backend_api/entities_option.js.html index e0d78488a..06d3c5f02 100644 --- a/docs/backend_api/entities_option.js.html +++ b/docs/backend_api/entities_option.js.html @@ -78,7 +78,7 @@ module.exports = Option;
diff --git a/docs/backend_api/entities_recent_note.js.html b/docs/backend_api/entities_recent_note.js.html index ca0c1cab0..289d64f84 100644 --- a/docs/backend_api/entities_recent_note.js.html +++ b/docs/backend_api/entities_recent_note.js.html @@ -75,7 +75,7 @@ module.exports = RecentNote;
diff --git a/docs/backend_api/global.html b/docs/backend_api/global.html index fa45b532d..00a68a563 100644 --- a/docs/backend_api/global.html +++ b/docs/backend_api/global.html @@ -594,7 +594,7 @@
diff --git a/docs/backend_api/index.html b/docs/backend_api/index.html index d9146110d..8197f413b 100644 --- a/docs/backend_api/index.html +++ b/docs/backend_api/index.html @@ -56,7 +56,7 @@
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html index 8857d5439..c9ac8a9d3 100644 --- a/docs/backend_api/services_backend_script_api.js.html +++ b/docs/backend_api/services_backend_script_api.js.html @@ -278,7 +278,7 @@ module.exports = BackendScriptApi;
diff --git a/docs/frontend_api/Branch.html b/docs/frontend_api/Branch.html index 9cb26a0de..8f2470bf9 100644 --- a/docs/frontend_api/Branch.html +++ b/docs/frontend_api/Branch.html @@ -719,7 +719,7 @@
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html index c259cbc3d..d198bb54d 100644 --- a/docs/frontend_api/FrontendScriptApi.html +++ b/docs/frontend_api/FrontendScriptApi.html @@ -1057,7 +1057,7 @@
Source:
@@ -1188,7 +1188,7 @@
Source:
@@ -1292,7 +1292,7 @@
Source:
@@ -1396,7 +1396,7 @@
Source:
@@ -1500,7 +1500,7 @@
Source:
@@ -1609,7 +1609,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -1808,7 +1808,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -1957,7 +1957,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2088,7 +2088,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2196,7 +2196,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2373,7 +2373,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -2526,7 +2526,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -2661,7 +2661,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -2796,7 +2796,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -2846,7 +2846,7 @@ Internally this serializes the anonymous function into string and sends it to ba
diff --git a/docs/frontend_api/NoteFull.html b/docs/frontend_api/NoteFull.html index 5a626119d..7a49c7a15 100644 --- a/docs/frontend_api/NoteFull.html +++ b/docs/frontend_api/NoteFull.html @@ -279,7 +279,7 @@
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html index a1fe24219..df0266233 100644 --- a/docs/frontend_api/NoteShort.html +++ b/docs/frontend_api/NoteShort.html @@ -1316,7 +1316,7 @@ Its notable omission is the note content.
diff --git a/docs/frontend_api/entities_branch.js.html b/docs/frontend_api/entities_branch.js.html index 636a5b374..c9d5338a9 100644 --- a/docs/frontend_api/entities_branch.js.html +++ b/docs/frontend_api/entities_branch.js.html @@ -76,7 +76,7 @@ export default Branch;
diff --git a/docs/frontend_api/entities_note_full.js.html b/docs/frontend_api/entities_note_full.js.html index 045ef4aee..93312d6c0 100644 --- a/docs/frontend_api/entities_note_full.js.html +++ b/docs/frontend_api/entities_note_full.js.html @@ -64,7 +64,7 @@ export default NoteFull;
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html index 14ea8a0ef..9b56f110e 100644 --- a/docs/frontend_api/entities_note_short.js.html +++ b/docs/frontend_api/entities_note_short.js.html @@ -128,7 +128,7 @@ export default NoteShort;
diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html index cb112cfce..901fbe515 100644 --- a/docs/frontend_api/global.html +++ b/docs/frontend_api/global.html @@ -339,7 +339,7 @@
diff --git a/docs/frontend_api/index.html b/docs/frontend_api/index.html index 21ecb0409..9ba1bcdba 100644 --- a/docs/frontend_api/index.html +++ b/docs/frontend_api/index.html @@ -56,7 +56,7 @@
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html index b13f21bba..5c5517160 100644 --- a/docs/frontend_api/services_frontend_script_api.js.html +++ b/docs/frontend_api/services_frontend_script_api.js.html @@ -88,14 +88,16 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) { this.addButtonToToolbar = opts => { const buttonId = "toolbar-button-" + opts.title.replace(/[^a-zA-Z0-9]/g, "-"); - const icon = $("<span>") - .addClass("jam jam-" + opts.icon); - const button = $('<button>') .addClass("btn btn-sm") - .click(opts.action) - .append(icon) - .append($("<span>").text(opts.title)); + .click(opts.action); + + if (opts.icon) { + button.append($("<span>").addClass("jam jam-" + opts.icon)) + .append("&nbsp;"); + } + + button.append($("<span>").text(opts.title)); button.attr('id', buttonId); @@ -269,7 +271,7 @@ export default FrontendScriptApi;
diff --git a/src/entities/note.js b/src/entities/note.js index 4875242a7..efeade002 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -7,7 +7,9 @@ const repository = require('../services/repository'); const dateUtils = require('../services/date_utils'); const LABEL = 'label'; +const LABEL_DEFINITION = 'label-definition'; const RELATION = 'relation'; +const RELATION_DEFINITION = 'relation-definition'; /** * This represents a Note which is a central object in the Trilium Notes project. @@ -136,6 +138,14 @@ class Note extends Entity { return (await this.getAttributes(name)).filter(attr => attr.type === LABEL); } + /** + * @param {string} [name] - label name to filter + * @returns {Promise} all note's label definitions, including inherited ones + */ + async getLabelDefinitions(name) { + return (await this.getAttributes(name)).filter(attr => attr.type === LABEL_DEFINITION); + } + /** * @param {string} [name] - relation name to filter * @returns {Promise} all note's relations (attributes with type relation), including inherited ones @@ -144,6 +154,14 @@ class Note extends Entity { return (await this.getAttributes(name)).filter(attr => attr.type === RELATION); } + /** + * @param {string} [name] - relation name to filter + * @returns {Promise} all note's relation definitions including inherited ones + */ + async getRelationDefinitions(name) { + return (await this.getAttributes(name)).filter(attr => attr.type === RELATION_DEFINITION); + } + /** * Clear note's attributes cache to force fresh reload for next attribute request. * Cache is note instance scoped. diff --git a/src/public/javascripts/services/attribute_autocomplete.js b/src/public/javascripts/services/attribute_autocomplete.js index c7d7aa5eb..804ba79e3 100644 --- a/src/public/javascripts/services/attribute_autocomplete.js +++ b/src/public/javascripts/services/attribute_autocomplete.js @@ -24,7 +24,7 @@ function initAttributeNameAutocomplete({ $el, attributeType, open }) { }); if (result.length === 0) { - result.push({name: "No results"}) + result.push({name: "No results"}); } cb(result); diff --git a/src/public/javascripts/services/note_detail_relation_map.js b/src/public/javascripts/services/note_detail_relation_map.js index 76a506d98..d7bade9ad 100644 --- a/src/public/javascripts/services/note_detail_relation_map.js +++ b/src/public/javascripts/services/note_detail_relation_map.js @@ -48,6 +48,25 @@ const biDirectionalOverlays = [ } ] ]; +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 + } ] +]; + + function loadMapData() { const currentNote = noteDetailService.getCurrentNote(); mapData = { @@ -88,12 +107,12 @@ async function loadNotesAndRelations() { for (const relation of data.relations) { const match = relations.find(rel => - rel.name === relation.name + rel.name === data.mirrorRelations[relation.name] && ((rel.sourceNoteId === relation.sourceNoteId && rel.targetNoteId === relation.targetNoteId) || (rel.sourceNoteId === relation.targetNoteId && rel.targetNoteId === relation.sourceNoteId))); if (match) { - match.type = relation.type = 'biDirectional'; + match.type = relation.type = relation.name === data.mirrorRelations[relation.name] ? 'biDirectional' : 'mirror'; relation.render = false; // don't render second relation } else { relation.type = 'uniDirectional'; @@ -128,7 +147,15 @@ async function loadNotesAndRelations() { }); connection.id = relation.attributeId; - connection.getOverlay("label").setLabel(relation.name); + + 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); + } + connection.canvas.setAttribute("data-connection-id", connection.id); } }); @@ -222,6 +249,8 @@ function initJsPlumbInstance () { jsPlumbInstance.registerConnectionType("biDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: biDirectionalOverlays }); + jsPlumbInstance.registerConnectionType("mirror", { anchor:"Continuous", connector:"StateMachine", overlays: mirrorOverlays }); + jsPlumbInstance.bind("connection", connectionCreatedHandler); // so that canvas is not panned when clicking/dragging note box @@ -330,7 +359,7 @@ async function noteContextMenuHandler(event, cmd) { } else if (cmd === "edit-title") { const $title = $noteBox.find(".title a"); - const title = await promptDialog.ask("Enter new note title:", $title.text()); + const title = await promptDialog.ask({ message: "Enter new note title:", defaultValue: $title.text() }); if (!title) { return; diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index 91551b50b..6c3560d0f 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -95,8 +95,11 @@ async function setNoteTypeMime(req) { async function getRelationMap(req) { const noteIds = req.body.noteIds; const resp = { + // noteId => title noteTitles: {}, - relations: [] + relations: [], + // relation name => mirror relation name + mirrorRelations: {} }; if (noteIds.length === 0) { @@ -105,19 +108,26 @@ async function getRelationMap(req) { const questionMarks = noteIds.map(noteId => '?').join(','); - (await repository.getEntities(`SELECT * FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds)) - .forEach(note => resp.noteTitles[note.noteId] = note.title); + const notes = await repository.getEntities(`SELECT * FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); - // FIXME: this actually doesn't take into account inherited relations! But maybe it is better this way? - resp.relations = (await repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'relation' AND noteId IN (${questionMarks})`, noteIds)) - .map(relation => { return { - attributeId: relation.attributeId, - sourceNoteId: relation.noteId, - targetNoteId: relation.value, - name: relation.name - }; }) - // both sourceNoteId and targetNoteId has to be in the included notes, but source was already checked in the SQL query - .filter(relation => noteIds.includes(relation.targetNoteId)); + for (const note of notes) { + resp.noteTitles[note.noteId] = note.title; + + resp.relations = resp.relations.concat((await note.getRelations()) + .filter(relation => noteIds.includes(relation.value)) + .map(relation => { return { + attributeId: relation.attributeId, + sourceNoteId: relation.noteId, + targetNoteId: relation.value, + name: relation.name + }; })); + + for (const relationDefinition of await note.getRelationDefinitions()) { + if (relationDefinition.value.mirrorRelation) { + resp.mirrorRelations[relationDefinition.name] = relationDefinition.value.mirrorRelation; + } + } + } return resp; }