@@ -170,14 +170,7 @@ const TPL = `
- Other notes with this label
-
-
-
-
-
-
-
+
@@ -275,6 +268,10 @@ export default class NoteAttributesWidget extends TabAwareWidget {
this.$attrExtrasTitle = this.$widget.find('.attr-extras-title');
this.$attrExtrasList = this.$widget.find('.attr-extras-list');
this.$attrExtrasMoreNotes = this.$widget.find('.attr-extras-more-notes');
+ this.$attrEditName = this.$attrExtras.find('.attr-edit-name');
+ this.$attrEditValue = this.$attrExtras.find('.attr-edit-value');
+ this.$attrEditInheritable = this.$attrExtras.find('.attr-edit-inheritable');
+
this.initialized = this.initEditor();
this.$attrDisplay = this.$widget.find('.attr-display');
@@ -319,11 +316,11 @@ export default class NoteAttributesWidget extends TabAwareWidget {
this.$attrExtras.hide();
});
- // this.$editor.on('blur', () => {
- // this.save();
- //
- // this.$attrExtras.hide();
- // });
+ this.$editor.on('blur', () => {
+ this.save();
+
+ this.$attrExtras.hide();
+ });
return this.$widget;
}
@@ -375,18 +372,10 @@ export default class NoteAttributesWidget extends TabAwareWidget {
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
let matchedAttr = null;
- let matchedPart = null;
for (const attr of parsedAttrs) {
- if (clickIndex >= attr.nameStartIndex && clickIndex <= attr.nameEndIndex) {
+ if (clickIndex >= attr.startIndex && clickIndex <= attr.endIndex) {
matchedAttr = attr;
- matchedPart = 'name';
- break;
- }
-
- if (clickIndex >= attr.valueStartIndex && clickIndex <= attr.valueEndIndex) {
- matchedAttr = attr;
- matchedPart = 'value';
break;
}
}
@@ -397,13 +386,7 @@ export default class NoteAttributesWidget extends TabAwareWidget {
return;
}
- const searchString = this.formatAttrForSearch(matchedAttr);
-
- let {count, results} = await server.get('search/' + encodeURIComponent(searchString), {
- type: matchedAttr.type,
- name: matchedAttr.name,
- value: matchedPart === 'value' ? matchedAttr.value : undefined
- });
+ let {results, count} = await server.post('search-related', matchedAttr);
for (const res of results) {
res.noteId = res.notePathArray[res.notePathArray.length - 1];
@@ -412,22 +395,12 @@ export default class NoteAttributesWidget extends TabAwareWidget {
results = results.filter(({noteId}) => noteId !== this.noteId);
if (results.length === 0) {
- this.$attrExtrasTitle.text(
- `There are no other notes with ${matchedAttr.type} name "${matchedAttr.name}"`
- // not displaying value since it can be long
- + (matchedPart === 'value' ? " and matching value" : ""));
+ this.$attrExtrasTitle.hide();
}
else {
- this.$attrExtrasTitle.text(
- `Notes with ${matchedAttr.type} name "${matchedAttr.name}"`
- // not displaying value since it can be long
- + (matchedPart === 'value' ? " and matching value" : "")
- + ":"
- );
+ this.$attrExtrasTitle.text(`Other notes with ${matchedAttr.type} name "${matchedAttr.name}"`);
}
- this.$attrExtrasTitle.hide();
-
this.$attrExtrasList.empty();
const displayedResults = results.length <= DISPLAYED_NOTES ? results : results.slice(0, DISPLAYED_NOTES);
@@ -449,6 +422,9 @@ export default class NoteAttributesWidget extends TabAwareWidget {
this.$attrExtrasMoreNotes.hide();
}
+ this.$attrEditName.val(matchedAttr.name);
+ this.$attrEditValue.val(matchedAttr.value);
+
this.$attrExtras.css("left", e.pageX - this.$attrExtras.width() / 2);
this.$attrExtras.css("top", e.pageY + 30);
this.$attrExtras.show();
@@ -510,6 +486,11 @@ export default class NoteAttributesWidget extends TabAwareWidget {
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
+ // disable spellcheck for attribute editor
+ this.textEditor.editing.view.change( writer => {
+ writer.setAttribute( 'spellcheck', 'false', this.textEditor.editing.view.document.getRoot() );
+ } );
+
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
//CKEditorInspector.attach(this.textEditor);
}
@@ -616,29 +597,6 @@ export default class NoteAttributesWidget extends TabAwareWidget {
}
}
- formatAttrForSearch(attr) {
- let searchStr = '';
-
- if (attr.type === 'label') {
- searchStr += '#';
- }
- else if (attr.type === 'relation') {
- searchStr += '~';
- }
- else {
- throw new Error(`Unrecognized attribute type ${JSON.stringify(attr)}`);
- }
-
- searchStr += attr.name;
-
- if (attr.value) {
- searchStr += '=';
- searchStr += this.formatValue(attr.value);
- }
-
- return searchStr;
- }
-
async focusOnAttributesEvent({tabId}) {
if (this.tabContext.tabId === tabId) {
this.$editor.trigger('focus');
diff --git a/src/routes/api/search.js b/src/routes/api/search.js
index c5ed5a095..866f9678b 100644
--- a/src/routes/api/search.js
+++ b/src/routes/api/search.js
@@ -108,7 +108,75 @@ function searchFromRelation(note, relationName) {
return typeof result[0] === 'string' ? result : result.map(item => item.noteId);
}
+function getRelatedNotes(req) {
+ const attr = req.body;
+
+ const matchingNameAndValue = searchService.searchNotes(formatAttrForSearch(attr, true));
+ const matchingName = searchService.searchNotes(formatAttrForSearch(attr, false));
+
+ const results = [];
+
+ for (const record of matchingNameAndValue.concat(matchingName)) {
+ if (results.length >= 20) {
+ break;
+ }
+
+ if (results.find(res => res.noteId === record.noteId)) {
+ continue;
+ }
+
+ results.push(record);
+ }
+
+ return {
+ count: matchingName.length,
+ results
+ };
+}
+
+function formatAttrForSearch(attr, searchWithValue) {
+ let searchStr = '';
+
+ if (attr.type === 'label') {
+ searchStr += '#';
+ }
+ else if (attr.type === 'relation') {
+ searchStr += '~';
+ }
+ else {
+ throw new Error(`Unrecognized attribute type ${JSON.stringify(attr)}`);
+ }
+
+ searchStr += attr.name;
+
+ if (searchWithValue && attr.value) {
+ searchStr += '=';
+ searchStr += formatValue(attr.value);
+ }
+
+ return searchStr;
+}
+
+function formatValue(val) {
+ if (!/[^\w_-]/.test(val)) {
+ return val;
+ }
+ else if (!val.includes('"')) {
+ return '"' + val + '"';
+ }
+ else if (!val.includes("'")) {
+ return "'" + val + "'";
+ }
+ else if (!val.includes("`")) {
+ return "`" + val + "`";
+ }
+ else {
+ return '"' + val.replace(/"/g, '\\"') + '"';
+ }
+}
+
module.exports = {
searchNotes,
- searchFromNote
+ searchFromNote,
+ getRelatedNotes
};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 0cde69324..e8b34d505 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -256,6 +256,7 @@ function register(app) {
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
apiRoute(GET, '/api/search-note/:noteId', searchRoute.searchFromNote);
+ apiRoute(POST, '/api/search-related', searchRoute.getRelatedNotes);
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|