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:
+
+
+
+
+
+
+ | Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+ 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:
+
+
+
+
+
+
+ | Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+ 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(" ");
+ }
+
+ 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;
}