diff --git a/db/migrations/0146__add_spell_check_options.sql b/db/migrations/0146__add_spell_check_options.sql
new file mode 100644
index 000000000..7f2378f39
--- /dev/null
+++ b/db/migrations/0146__add_spell_check_options.sql
@@ -0,0 +1,5 @@
+INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
+VALUES ('spellCheckEnabled', 'true', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0);
+
+INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
+VALUES ('spellCheckLanguageCode', 'en-US', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0);
\ No newline at end of file
diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html
index 803a7e61d..e4dc8c372 100644
--- a/docs/backend_api/BackendScriptApi.html
+++ b/docs/backend_api/BackendScriptApi.html
@@ -396,7 +396,7 @@ the backend.
Source:
@@ -1533,7 +1533,7 @@ the backend.
Source:
@@ -1997,7 +1997,7 @@ the backend.
Source:
@@ -2765,7 +2765,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3418,7 +3418,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3596,7 +3596,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3751,7 +3751,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3901,7 +3901,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4427,7 +4427,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
Source:
@@ -4560,7 +4560,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
Source:
@@ -4935,7 +4935,7 @@ transactional by default.
Source:
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html
index 9afc0aea4..52e7f3546 100644
--- a/docs/backend_api/services_backend_script_api.js.html
+++ b/docs/backend_api/services_backend_script_api.js.html
@@ -237,9 +237,11 @@ function BackendScriptApi(currentNote, apiParams) {
* @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/
this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
- await noteService.createNote(parentNoteId, title, content, extraOptions);
+ const ret = await noteService.createNote(parentNoteId, title, content, extraOptions);
ws.refreshTree();
+
+ return ret;
};
/**
@@ -343,7 +345,8 @@ function BackendScriptApi(currentNote, apiParams) {
this.getAppInfo = () => appInfo
}
-module.exports = BackendScriptApi;
+module.exports = BackendScriptApi;
+
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html
index 58f9fcc86..8df157743 100644
--- a/docs/frontend_api/FrontendScriptApi.html
+++ b/docs/frontend_api/FrontendScriptApi.html
@@ -131,6 +131,116 @@
+ $container
+
+
+
+
+
+
+
+
+
+
+ Properties:
+
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ container
+
+
+
+
+
+jQuery
+
+
+
+
+
+
+
+
+
+ of all the rendered script content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
currentNote
@@ -223,7 +333,7 @@
Source:
@@ -336,7 +446,7 @@
Source:
@@ -442,7 +552,7 @@
Source:
@@ -552,7 +662,7 @@
Source:
@@ -661,7 +771,7 @@
Source:
@@ -790,7 +900,7 @@
Source:
@@ -945,7 +1055,7 @@
Source:
@@ -1100,7 +1210,7 @@
Source:
@@ -1280,7 +1390,7 @@
Source:
@@ -1413,7 +1523,7 @@
Source:
@@ -1519,7 +1629,7 @@
Source:
@@ -1625,7 +1735,7 @@
Source:
@@ -1784,7 +1894,7 @@
Source:
@@ -1891,7 +2001,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2046,7 +2156,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2202,7 +2312,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2403,7 +2513,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2509,7 +2619,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2664,7 +2774,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2773,7 +2883,7 @@ note.
Source:
@@ -2928,7 +3038,7 @@ note.
Source:
@@ -3061,7 +3171,7 @@ note.
Source:
@@ -3167,7 +3277,7 @@ note.
Source:
@@ -3255,7 +3365,7 @@ note.
Source:
@@ -3406,7 +3516,7 @@ note.
Source:
@@ -3539,7 +3649,7 @@ note.
Source:
@@ -3700,7 +3810,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -3860,7 +3970,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4016,7 +4126,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4167,7 +4277,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4304,7 +4414,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4441,7 +4551,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html
index 6514efb81..11675a2c8 100644
--- a/docs/frontend_api/NoteShort.html
+++ b/docs/frontend_api/NoteShort.html
@@ -30,7 +30,10 @@
NoteShort()
- This note's representation is used in note tree and is kept in TreeCache.
+ FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache.
+Attribute cache should be limited to "transaction".
+
+This note's representation is used in note tree and is kept in TreeCache.
@@ -93,7 +96,7 @@
Source:
@@ -183,7 +186,7 @@
Source:
@@ -241,7 +244,7 @@
Source:
@@ -299,7 +302,7 @@
Source:
@@ -357,7 +360,7 @@
Source:
@@ -415,7 +418,7 @@
Source:
@@ -473,7 +476,7 @@
Source:
@@ -531,7 +534,7 @@
Source:
@@ -679,7 +682,7 @@
Source:
@@ -846,7 +849,7 @@
Source:
@@ -1020,7 +1023,7 @@
Source:
@@ -1126,7 +1129,7 @@
Source:
@@ -1228,7 +1231,7 @@
Source:
@@ -1330,7 +1333,7 @@
Source:
@@ -1432,7 +1435,7 @@
Source:
@@ -1583,7 +1586,7 @@
Source:
@@ -1750,7 +1753,7 @@
Source:
@@ -1917,7 +1920,7 @@
Source:
@@ -2072,7 +2075,7 @@
Source:
@@ -2178,7 +2181,7 @@
Source:
@@ -2280,7 +2283,7 @@
Source:
@@ -2431,7 +2434,7 @@
Source:
@@ -2598,7 +2601,7 @@
Source:
@@ -2765,7 +2768,7 @@
Source:
@@ -2920,7 +2923,7 @@
Source:
@@ -3090,7 +3093,7 @@
Source:
@@ -3241,7 +3244,7 @@
Source:
@@ -3351,7 +3354,7 @@
Source:
@@ -3525,7 +3528,7 @@
Source:
@@ -3631,7 +3634,7 @@
Source:
@@ -3782,7 +3785,7 @@
Source:
@@ -3937,7 +3940,7 @@
Source:
@@ -4048,7 +4051,7 @@ Cache is note instance scoped.
Source:
@@ -4132,7 +4135,7 @@ Cache is note instance scoped.
Source:
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html
index 7e0281d3b..a61030a29 100644
--- a/docs/frontend_api/entities_note_short.js.html
+++ b/docs/frontend_api/entities_note_short.js.html
@@ -35,6 +35,9 @@ const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition';
/**
+ * FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache.
+ * Attribute cache should be limited to "transaction".
+ *
* This note's representation is used in note tree and is kept in TreeCache.
*/
class NoteShort {
diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html
index 0a422518d..6e4c07a95 100644
--- a/docs/frontend_api/global.html
+++ b/docs/frontend_api/global.html
@@ -303,7 +303,7 @@
Source:
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index 74d1bb234..203bd234e 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -44,9 +44,12 @@ import StandardWidget from '../widgets/standard_widget.js';
* @constructor
* @hideconstructor
*/
-function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null) {
+function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null, $container = null) {
const $pluginButtons = $("#plugin-buttons");
+ /** @property {jQuery} container of all the rendered script content */
+ this.$container = $container;
+
/** @property {object} note where script started executing */
this.startNote = startNote;
/** @property {object} note where script is currently executing */
@@ -322,7 +325,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {boolean} returns true if the original note is still loaded, false if user switched to another
*/
this.isNoteStillActive = () => {
- return this.originEntity.noteId === tabContext.noteId;
+ return tabContext.note && this.originEntity.noteId === tabContext.note.noteId;
};
/**
diff --git a/electron.js b/electron.js
index 693532e58..ab70a1e41 100644
--- a/electron.js
+++ b/electron.js
@@ -10,7 +10,6 @@ const port = require('./src/services/port');
const env = require('./src/services/env');
const appIconService = require('./src/services/app_icon');
const windowStateKeeper = require('electron-window-state');
-const contextMenu = require('electron-context-menu');
// Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
@@ -22,25 +21,25 @@ let mainWindow;
require('electron-dl')({ saveAs: true });
-contextMenu({
- menu: (actions, params, browserWindow) => [
- actions.cut(),
- actions.copy(),
- actions.copyLink(),
- actions.paste(),
- {
- label: 'Search DuckDuckGo for “{selection}”',
- // Only show it when right-clicking text
- visible: params.selectionText.trim().length > 0,
- click: () => {
- const {shell} = require('electron');
-
- shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`);
- }
- },
- actions.inspect()
- ]
-});
+// contextMenu({
+// menu: (actions, params, browserWindow) => [
+// actions.cut(),
+// actions.copy(),
+// actions.copyLink(),
+// actions.paste(),
+// {
+// label: 'Search DuckDuckGo for “{selection}”',
+// // Only show it when right-clicking text
+// visible: params.selectionText.trim().length > 0,
+// click: () => {
+// const {shell} = require('electron');
+//
+// shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`);
+// }
+// },
+// actions.inspect()
+// ]
+// });
function onClosed() {
// Dereference the window
diff --git a/package-lock.json b/package-lock.json
index abd302069..fe2405188 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,34 @@
"integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==",
"dev": true
},
+ "@aabuhijleh/electron-remote": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@aabuhijleh/electron-remote/-/electron-remote-1.4.0.tgz",
+ "integrity": "sha512-EG4ZXxqbFY4lpX55vctwz14mFrEOcOHFCMLH5z5lOl6fiviTqscy86tSlKwEE3/o3ExtdPr2tECgCogYYL7d+g==",
+ "requires": {
+ "debug": "^2.5.1",
+ "hashids": "^1.1.1",
+ "lodash.get": "^4.4.2",
+ "pify": "^2.3.0",
+ "rxjs": "^5.0.0-beta.12",
+ "xmlhttprequest": "^1.8.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
+ }
+ },
"@babel/code-frame": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
@@ -233,6 +261,14 @@
}
}
},
+ "@felixrieseberg/spellchecker": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/@felixrieseberg/spellchecker/-/spellchecker-4.0.10.tgz",
+ "integrity": "sha512-b+BlHcBXjx+W7yGNAtoVpAv8dvmAQ8Tp2YhNjqxIgocb6Wq1nKLl4jfu9DG60UWC0hTNvvQ74ny9ojiUFNqGSA==",
+ "requires": {
+ "nan": "^2.13.2"
+ }
+ },
"@jimp/bmp": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.8.4.tgz",
@@ -1202,6 +1238,11 @@
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
},
+ "bcp47": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
+ "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4="
+ },
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -1955,6 +1996,54 @@
}
}
},
+ "cld": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/cld/-/cld-2.5.1.tgz",
+ "integrity": "sha512-DwdvvcFVizwDdPCocoPPReFk3BwLEaTZ3RzFgJ4jLzsBzJKUC3cTna0ZmAZG4tFtMmQdl0ciso3+ijkH3OPZPA==",
+ "requires": {
+ "glob": "^5.0.10",
+ "nan": "^2.9.2",
+ "rimraf": "^2.4.0",
+ "underscore": "^1.6.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ }
+ }
+ },
"clean-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
@@ -4139,6 +4228,45 @@
}
}
},
+ "electron-spellchecker": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/electron-spellchecker/-/electron-spellchecker-2.2.0.tgz",
+ "integrity": "sha512-QkOVgjmjx6bDkqNshRTfVzEz9ctjiKVPZw77YLS0sQReP320QNtTXAKyo+01TORWk58RFT/LdxPZ/aejLdPmOA==",
+ "requires": {
+ "@aabuhijleh/electron-remote": "^1.4.0",
+ "@felixrieseberg/spellchecker": "^4.0.10",
+ "bcp47": "^1.1.2",
+ "cld": "^2.5.1",
+ "debug": "^4.1.1",
+ "keyboard-layout": "^2.0.16",
+ "lru-cache": "^5.1.1",
+ "mkdirp": "^0.5.1",
+ "pify": "^4.0.1",
+ "rxjs": "^5.0.1",
+ "rxjs-serial-subscription": "^0.1.1",
+ "spawn-rx": "^2.0.7"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ }
+ }
+ },
"electron-window-state": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz",
@@ -5089,6 +5217,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
+ "event-kit": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.5.3.tgz",
+ "integrity": "sha512-b7Qi1JNzY4BfAYfnIRanLk0DOD1gdkWHT4GISIn8Q2tAf3LpU8SP2CMwWaq40imYoKWbtN4ZhbSRxvsnikooZQ=="
+ },
"exec-buffer": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz",
@@ -6721,6 +6854,11 @@
"integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
"dev": true
},
+ "hashids": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/hashids/-/hashids-1.2.2.tgz",
+ "integrity": "sha512-dEHCG2LraR6PNvSGxosZHIRgxF5sNLOIBFEHbj8lfP9WWmu/PWPMzsip1drdVSOFi51N2pU7gZavrgn7sbGFuw=="
+ },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -7991,6 +8129,15 @@
"resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz",
"integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ=="
},
+ "keyboard-layout": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.16.tgz",
+ "integrity": "sha512-eGrxmlV6jbm/mbPEOpYGuH53XEC7wIUj9ZxKcT2z9QHJ/RwrT9iVkvxka9zRxqHZHwQzcffgsa5OxoVAKnhK9w==",
+ "requires": {
+ "event-kit": "^2.0.0",
+ "nan": "^2.13.2"
+ }
+ },
"keyboardevent-from-electron-accelerator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-1.1.0.tgz",
@@ -8203,8 +8350,7 @@
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
- "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
- "dev": true
+ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
},
"lodash.camelcase": {
"version": "4.3.0",
@@ -8229,8 +8375,7 @@
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
- "dev": true
+ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isarguments": {
"version": "3.1.0",
@@ -11793,11 +11938,18 @@
"version": "5.5.12",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz",
"integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==",
- "dev": true,
"requires": {
"symbol-observable": "1.0.1"
}
},
+ "rxjs-serial-subscription": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/rxjs-serial-subscription/-/rxjs-serial-subscription-0.1.1.tgz",
+ "integrity": "sha1-pCsdsL8QlLCSMRkeJ3jKP8+e0Uc=",
+ "requires": {
+ "rxjs": "^5.0.0-beta.12"
+ }
+ },
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -12293,7 +12445,6 @@
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz",
"integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==",
- "dev": true,
"requires": {
"debug": "^2.5.1",
"lodash.assign": "^4.2.0",
@@ -12304,7 +12455,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -12667,8 +12817,7 @@
"symbol-observable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
- "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
- "dev": true
+ "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
},
"symbol-tree": {
"version": "3.2.2",
@@ -13194,8 +13343,7 @@
"underscore": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
- "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==",
- "dev": true
+ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
},
"unescape": {
"version": "1.0.1",
@@ -13893,6 +14041,11 @@
"integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
"dev": true
},
+ "xmlhttprequest": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+ "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
+ },
"xo": {
"version": "0.25.3",
"resolved": "https://registry.npmjs.org/xo/-/xo-0.25.3.tgz",
diff --git a/package.json b/package.json
index fa148f97b..1fc2c5d1b 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"electron-debug": "3.0.1",
"electron-dl": "1.14.0",
"electron-find": "1.0.6",
+ "electron-spellchecker": "^2.2.0",
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.16.2",
diff --git a/src/app.js b/src/app.js
index 26b5f439d..b25bf7dd2 100644
--- a/src/app.js
+++ b/src/app.js
@@ -88,7 +88,10 @@ app.use((req, res, next) => {
// error handler
app.use((err, req, res, next) => {
- if (err && err.message && err.message.includes("Invalid package")) {
+ if (err && err.message && (
+ err.message.includes("Invalid package")
+ || (err.message.includes("Router not found for request") && err.message.includes("node_modules"))
+ )) {
// electron 6 outputs a lot of such errors which do not seem important
}
else {
diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js
index 30ef34d6b..5f33fd985 100644
--- a/src/public/javascripts/desktop.js
+++ b/src/public/javascripts/desktop.js
@@ -177,3 +177,7 @@ entrypoints.registerEntrypoints();
noteTooltipService.setupGlobalTooltip();
noteAutocompleteService.init();
+
+if (utils.isElectron()) {
+ import("./services/spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck());
+}
\ No newline at end of file
diff --git a/src/public/javascripts/dialogs/options.js b/src/public/javascripts/dialogs/options.js
index 2e5b991fc..e2835a35c 100644
--- a/src/public/javascripts/dialogs/options.js
+++ b/src/public/javascripts/dialogs/options.js
@@ -19,8 +19,7 @@ export async function showDialog() {
import('./options/appearance.js'),
import('./options/code_notes.js'),
import('./options/change_password.js'),
- import('./options/note_revisions.js'),
- import('./options/protected_session.js'),
+ import('./options/other.js'),
import('./options/sidebar.js'),
import('./options/sync.js'),
]))
diff --git a/src/public/javascripts/dialogs/options/note_revisions.js b/src/public/javascripts/dialogs/options/note_revisions.js
deleted file mode 100644
index 17d3ff010..000000000
--- a/src/public/javascripts/dialogs/options/note_revisions.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import server from "../../services/server.js";
-import infoService from "../../services/info.js";
-
-export default class NoteRevisionsOptions {
- constructor() {
- this.$form = $("#note-revision-snapshot-time-interval-form");
- this.$timeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
-
- this.$form.submit(() => {
- const opts = { 'noteRevisionSnapshotTimeInterval': this.$timeInterval.val() };
- server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
-
- return false;
- });
- }
-
- optionsLoaded(options) {
- this.$timeInterval.val(options['noteRevisionSnapshotTimeInterval']);
- }
-}
\ No newline at end of file
diff --git a/src/public/javascripts/dialogs/options/other.js b/src/public/javascripts/dialogs/options/other.js
new file mode 100644
index 000000000..98eaafd0a
--- /dev/null
+++ b/src/public/javascripts/dialogs/options/other.js
@@ -0,0 +1,54 @@
+import optionsService from "../../services/options.js";
+import server from "../../services/server.js";
+import infoService from "../../services/info.js";
+
+export default class ProtectedSessionOptions {
+ constructor() {
+ this.$spellCheckEnabled = $("#spell-check-enabled");
+ this.$spellCheckLanguageCode = $("#spell-check-language-code");
+
+ this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
+ this.$noteRevisionsTimeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
+
+ this.$spellCheckEnabled.change(() => {
+ const opts = { 'spellCheckEnabled': this.$spellCheckEnabled.is(":checked") ? "true" : "false" };
+ server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
+
+ return false;
+ });
+
+ this.$spellCheckLanguageCode.change(() => {
+ const opts = { 'spellCheckLanguageCode': this.$spellCheckLanguageCode.val() };
+ server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
+
+ return false;
+ });
+
+ this.$protectedSessionTimeout.change(() => {
+ const protectedSessionTimeout = this.$protectedSessionTimeout.val();
+
+ server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => {
+ optionsService.reloadOptions();
+
+ infoService.showMessage("Options change have been saved.");
+ });
+
+ return false;
+ });
+
+ this.$noteRevisionsTimeInterval.change(() => {
+ const opts = { 'noteRevisionSnapshotTimeInterval': this.$noteRevisionsTimeInterval.val() };
+ server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
+
+ return false;
+ });
+ }
+
+ optionsLoaded(options) {
+ this.$spellCheckEnabled.prop("checked", options['spellCheckEnabled'] === 'true');
+ this.$spellCheckLanguageCode.val(options['spellCheckLanguageCode']);
+
+ this.$protectedSessionTimeout.val(options['protectedSessionTimeout']);
+ this.$noteRevisionsTimeInterval.val(options['noteRevisionSnapshotTimeInterval']);
+ }
+}
\ No newline at end of file
diff --git a/src/public/javascripts/dialogs/options/protected_session.js b/src/public/javascripts/dialogs/options/protected_session.js
deleted file mode 100644
index 5db0fe126..000000000
--- a/src/public/javascripts/dialogs/options/protected_session.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import optionsService from "../../services/options.js";
-import server from "../../services/server.js";
-import infoService from "../../services/info.js";
-
-export default class ProtectedSessionOptions {
- constructor() {
- this.$form = $("#protected-session-timeout-form");
- this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
-
- this.$form.submit(() => this.save());
- }
-
- optionsLoaded(options) {
- this.$protectedSessionTimeout.val(options['protectedSessionTimeout']);
- }
-
- save() {
- const protectedSessionTimeout = this.$protectedSessionTimeout.val();
-
- server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => {
- optionsService.reloadOptions();
-
- infoService.showMessage("Options change have been saved.");
- });
-
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/public/javascripts/services/options.js b/src/public/javascripts/services/options.js
index 11e34efbf..c67190710 100644
--- a/src/public/javascripts/services/options.js
+++ b/src/public/javascripts/services/options.js
@@ -51,7 +51,11 @@ function reloadOptions() {
return optionsReady;
}
-/** just waits for some options without triggering reload */
+/**
+ * just waits for some options without triggering reload
+ *
+ * @return {Options}
+ */
async function waitForOptions() {
return await optionsReady;
}
diff --git a/src/public/javascripts/services/spell_check.js b/src/public/javascripts/services/spell_check.js
new file mode 100644
index 000000000..db1546b3d
--- /dev/null
+++ b/src/public/javascripts/services/spell_check.js
@@ -0,0 +1,43 @@
+import optionsService from "./options.js";
+
+export async function initSpellCheck() {
+ const options = await optionsService.waitForOptions();
+
+ if (!options.is('spellCheckEnabled')) {
+ return;
+ }
+
+ const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
+ const {remote, shell} = require('electron');
+
+ const spellCheckHandler = new SpellCheckHandler();
+ spellCheckHandler.attachToInput();
+
+ spellCheckHandler.switchLanguage(options.get('spellCheckLanguageCode'));
+
+ spellCheckHandler.currentSpellcheckerChanged.subscribe(() => {
+ console.debug(`Detected language is ${spellCheckHandler.currentSpellcheckerLanguage}`);
+ });
+
+ const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler, null, true, (menu, menuInfo) => {
+ // There's no menu.remove(id) so this is a convoluted way of removing the 'Search with Google' menu item
+ const oldItems = menu.items;
+ menu.clear();
+ oldItems.forEach(oldItem => {
+ if (!oldItem.label.includes('Google')) {
+ menu.append(oldItem);
+ } else {
+ menu.append(new remote.MenuItem({
+ label: 'Search with DuckDuckGo',
+ click: () => {
+ shell.openExternal(`https://duckduckgo.com/?q=${encodeURIComponent(menuInfo.selectionText)}`);
+ }
+ }));
+ }
+ });
+ });
+
+ new ContextMenuListener(async (info) => {
+ await contextMenuBuilder.showPopupMenu(info);
+ });
+}
\ No newline at end of file
diff --git a/src/routes/api/options.js b/src/routes/api/options.js
index 5d956cd3e..3533ae9de 100644
--- a/src/routes/api/options.js
+++ b/src/routes/api/options.js
@@ -32,7 +32,9 @@ const ALLOWED_OPTIONS = [
'similarNotesWidget',
'editedNotesWidget',
'calendarWidget',
- 'codeNotesMimeTypes'
+ 'codeNotesMimeTypes',
+ 'spellCheckEnabled',
+ 'spellCheckLanguageCode'
];
async function getOptions() {
diff --git a/src/services/app_info.js b/src/services/app_info.js
index ee1db9289..477fb83c3 100644
--- a/src/services/app_info.js
+++ b/src/services/app_info.js
@@ -4,7 +4,7 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
-const APP_DB_VERSION = 145;
+const APP_DB_VERSION = 146;
const SYNC_VERSION = 10;
const CLIPPER_PROTOCOL_VERSION = "1.0";
diff --git a/src/services/options_init.js b/src/services/options_init.js
index d1dcf3e78..2ebb2b9de 100644
--- a/src/services/options_init.js
+++ b/src/services/options_init.js
@@ -79,6 +79,9 @@ async function initNotSyncedOptions(initialized, startNotePath = 'root', opts =
await optionService.createOption('similarNotesWidget', '{"enabled":true,"expanded":true,"position":600}', false);
await optionService.createOption('initialized', initialized ? 'true' : 'false', false);
+
+ await optionService.createOption('spellCheckEnabled', 'true', false);
+ await optionService.createOption('spellCheckLanguageCode', 'en-US', false);
}
module.exports = {
diff --git a/src/views/dialogs/options.ejs b/src/views/dialogs/options.ejs
index 17e110f9c..b551975b2 100644
--- a/src/views/dialogs/options.ejs
+++ b/src/views/dialogs/options.ejs
@@ -22,15 +22,12 @@
Change password
-
- Protected session
-
-
- Note revisions
-
Sync
+
+ Other
+
Advanced
@@ -41,8 +38,7 @@
<% include options/sidebar.ejs %>
<% include options/code_notes.ejs %>
<% include options/change_password.ejs %>
- <% include options/protected_session.ejs %>
- <% include options/note_revisions.ejs %>
+ <% include options/other.ejs %>
<% include options/sync.ejs %>
<% include options/advanced.ejs %>
diff --git a/src/views/dialogs/options/note_revisions.ejs b/src/views/dialogs/options/note_revisions.ejs
deleted file mode 100644
index 376bdd085..000000000
--- a/src/views/dialogs/options/note_revisions.ejs
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
Note revisions snapshot interval
-
-
Note revision snapshot time interval is time in seconds after which new note revision will be created for the note.
-
-
-
\ No newline at end of file
diff --git a/src/views/dialogs/options/other.ejs b/src/views/dialogs/options/other.ejs
new file mode 100644
index 000000000..6cb0b6b93
--- /dev/null
+++ b/src/views/dialogs/options/other.ejs
@@ -0,0 +1,44 @@
+
+
+
Spell check
+
+
These options apply only for desktop builds, browsers will use their own native spell check.
+
+
+
+ Enable spellcheck
+
+
+
+
+
+ Language code
+
+
+
+
Changes to the spell check options will take effect after application restart.
+
+
+
+
Protected session timeout
+
+
Protected session timeout is a time period after which the protected session is wiped out from
+ browser's memory. This is measured from the last interaction with protected notes. See wiki for more info.
+
+
+ Protected session timeout (in seconds)
+
+
+
+
+
+
Note revisions snapshot interval
+
+
Note revision snapshot time interval is time in seconds after which new note revision will be created for the note. See wiki for more info.
+
+
+ Note revision snapshot time interval (in seconds)
+
+
+
+
\ No newline at end of file
diff --git a/src/views/dialogs/options/protected_session.ejs b/src/views/dialogs/options/protected_session.ejs
deleted file mode 100644
index da8c212ad..000000000
--- a/src/views/dialogs/options/protected_session.ejs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
Protected session timeout
-
-
Protected session timeout is a time period after which the protected session is wiped out from
- browser's memory. This is measured from the last interaction with protected notes.
-
-
-
\ No newline at end of file