From ade1b705d2370be9222ba4164f79bbdfae590bc1 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:20:02 -0400 Subject: Mark internals as private --- ext/bg/js/anki.js | 70 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 34 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index c7f7c0cc..021cb4c4 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -25,82 +25,84 @@ class AnkiConnect { constructor(server) { - this.server = server; - this.localVersion = 2; - this.remoteVersion = 0; + this._server = server; + this._localVersion = 2; + this._remoteVersion = 0; } async addNote(note) { - await this.checkVersion(); - return await this.ankiInvoke('addNote', {note}); + await this._checkVersion(); + return await this._ankiInvoke('addNote', {note}); } async canAddNotes(notes) { - await this.checkVersion(); - return await this.ankiInvoke('canAddNotes', {notes}); + await this._checkVersion(); + return await this._ankiInvoke('canAddNotes', {notes}); } async getDeckNames() { - await this.checkVersion(); - return await this.ankiInvoke('deckNames'); + await this._checkVersion(); + return await this._ankiInvoke('deckNames'); } async getModelNames() { - await this.checkVersion(); - return await this.ankiInvoke('modelNames'); + await this._checkVersion(); + return await this._ankiInvoke('modelNames'); } async getModelFieldNames(modelName) { - await this.checkVersion(); - return await this.ankiInvoke('modelFieldNames', {modelName}); + await this._checkVersion(); + return await this._ankiInvoke('modelFieldNames', {modelName}); } async guiBrowse(query) { - await this.checkVersion(); - return await this.ankiInvoke('guiBrowse', {query}); + await this._checkVersion(); + return await this._ankiInvoke('guiBrowse', {query}); } async storeMediaFile(filename, dataBase64) { - await this.checkVersion(); - return await this.ankiInvoke('storeMediaFile', {filename, data: dataBase64}); - } - - async checkVersion() { - if (this.remoteVersion < this.localVersion) { - this.remoteVersion = await this.ankiInvoke('version'); - if (this.remoteVersion < this.localVersion) { - throw new Error('Extension and plugin versions incompatible'); - } - } + await this._checkVersion(); + return await this._ankiInvoke('storeMediaFile', {filename, data: dataBase64}); } async findNoteIds(notes) { - await this.checkVersion(); + await this._checkVersion(); const actions = notes.map((note) => ({ action: 'findNotes', params: { - query: `deck:"${AnkiConnect.escapeQuery(note.deckName)}" ${AnkiConnect.fieldsToQuery(note.fields)}` + query: `deck:"${this._escapeQuery(note.deckName)}" ${this._fieldsToQuery(note.fields)}` } })); - return await this.ankiInvoke('multi', {actions}); + return await this._ankiInvoke('multi', {actions}); + } + + // Private + + async _checkVersion() { + if (this._remoteVersion < this._localVersion) { + this._remoteVersion = await this._ankiInvoke('version'); + if (this._remoteVersion < this._localVersion) { + throw new Error('Extension and plugin versions incompatible'); + } + } } - ankiInvoke(action, params) { - return requestJson(this.server, 'POST', {action, params, version: this.localVersion}); + _ankiInvoke(action, params) { + return requestJson(this._server, 'POST', {action, params, version: this._localVersion}); } - static escapeQuery(text) { + _escapeQuery(text) { return text.replace(/"/g, ''); } - static fieldsToQuery(fields) { + _fieldsToQuery(fields) { const fieldNames = Object.keys(fields); if (fieldNames.length === 0) { return ''; } const key = fieldNames[0]; - return `${key.toLowerCase()}:"${AnkiConnect.escapeQuery(fields[key])}"`; + return `${key.toLowerCase()}:"${this._escapeQuery(fields[key])}"`; } } -- cgit v1.2.3 From c41c7252aeb5f10fca7403a19740d869743a38a5 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:37:13 -0400 Subject: Add enabled checks --- ext/bg/js/anki.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index 021cb4c4..f7a24291 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -25,47 +25,74 @@ class AnkiConnect { constructor(server) { + this._enabled = true; this._server = server; this._localVersion = 2; this._remoteVersion = 0; } + setServer(server) { + this._server = server; + } + + getServer() { + return this._server; + } + + setEnabled(enabled) { + this._enabled = enabled; + } + + isEnabled() { + return this._enabled; + } + async addNote(note) { + if (!this._enabled) { return null; } await this._checkVersion(); return await this._ankiInvoke('addNote', {note}); } async canAddNotes(notes) { + if (!this._enabled) { return []; } await this._checkVersion(); return await this._ankiInvoke('canAddNotes', {notes}); } async getDeckNames() { + if (!this._enabled) { return []; } await this._checkVersion(); return await this._ankiInvoke('deckNames'); } async getModelNames() { + if (!this._enabled) { return []; } await this._checkVersion(); return await this._ankiInvoke('modelNames'); } async getModelFieldNames(modelName) { + if (!this._enabled) { return []; } await this._checkVersion(); return await this._ankiInvoke('modelFieldNames', {modelName}); } async guiBrowse(query) { + if (!this._enabled) { return []; } await this._checkVersion(); return await this._ankiInvoke('guiBrowse', {query}); } async storeMediaFile(filename, dataBase64) { + if (!this._enabled) { + return {result: null, error: 'AnkiConnect not enabled'}; + } await this._checkVersion(); return await this._ankiInvoke('storeMediaFile', {filename, data: dataBase64}); } async findNoteIds(notes) { + if (!this._enabled) { return []; } await this._checkVersion(); const actions = notes.map((note) => ({ action: 'findNotes', -- cgit v1.2.3 From 4c2ca82a2937fa4e1d0f3f6744f4e1a7b88692a1 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:38:33 -0400 Subject: Use single instance of AnkiConnect --- ext/bg/js/anki.js | 2 +- ext/bg/js/backend.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index f7a24291..dd802424 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -25,7 +25,7 @@ class AnkiConnect { constructor(server) { - this._enabled = true; + this._enabled = false; this._server = server; this._localVersion = 2; this._remoteVersion = 0; diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index be8ea322..24a16199 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -47,7 +47,7 @@ class Backend { this.database = new Database(); this.dictionaryImporter = new DictionaryImporter(); this.translator = new Translator(this.database); - this.anki = new AnkiNull(); + this.anki = new AnkiConnect(); this.mecab = new Mecab(); this.clipboardMonitor = new ClipboardMonitor({getClipboard: this._onApiClipboardGet.bind(this)}); this.options = null; @@ -210,7 +210,8 @@ class Backend { this.setExtensionBadgeText(''); } - this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull(); + this.anki.setServer(options.anki.server); + this.anki.setEnabled(options.anki.enable); if (options.parsing.enableMecabParser) { this.mecab.startListener(); -- cgit v1.2.3 From cc5e4294223f9d0106ddec1d561b29ac449b1115 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:38:52 -0400 Subject: Remove AnkiNull and redundant comment --- ext/bg/js/anki.js | 39 --------------------------------------- 1 file changed, 39 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index dd802424..27590311 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -19,10 +19,6 @@ * requestJson */ -/* - * AnkiConnect - */ - class AnkiConnect { constructor(server) { this._enabled = false; @@ -132,38 +128,3 @@ class AnkiConnect { return `${key.toLowerCase()}:"${this._escapeQuery(fields[key])}"`; } } - - -/* - * AnkiNull - */ - -class AnkiNull { - async addNote() { - return null; - } - - async canAddNotes() { - return []; - } - - async getDeckNames() { - return []; - } - - async getModelNames() { - return []; - } - - async getModelFieldNames() { - return []; - } - - async guiBrowse() { - return []; - } - - async findNoteIds() { - return []; - } -} -- cgit v1.2.3 From 3c335e68cdd41860d791ffe85dd07abf8932d3ce Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:43:24 -0400 Subject: Throw errors in returned by invocation --- ext/bg/js/anki.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index 27590311..c07af462 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -81,7 +81,7 @@ class AnkiConnect { async storeMediaFile(filename, dataBase64) { if (!this._enabled) { - return {result: null, error: 'AnkiConnect not enabled'}; + throw new Error('AnkiConnect not enabled'); } await this._checkVersion(); return await this._ankiInvoke('storeMediaFile', {filename, data: dataBase64}); @@ -110,8 +110,19 @@ class AnkiConnect { } } - _ankiInvoke(action, params) { - return requestJson(this._server, 'POST', {action, params, version: this._localVersion}); + async _ankiInvoke(action, params) { + const result = await requestJson(this._server, 'POST', {action, params, version: this._localVersion}); + if ( + result !== null && + typeof result === 'object' && + !Array.isArray(result) + ) { + const error = result.error; + if (typeof error !== 'undefined') { + throw new Error(`AnkiConnect error: ${error}`); + } + } + return result; } _escapeQuery(text) { -- cgit v1.2.3 From 37c374fb633a5b2f224348a8e5490f0275d348e7 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 12 Apr 2020 12:44:33 -0400 Subject: Rename _ankiInvoke to _invoke to remove redundancy --- ext/bg/js/anki.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index c07af462..928b5159 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -46,37 +46,37 @@ class AnkiConnect { async addNote(note) { if (!this._enabled) { return null; } await this._checkVersion(); - return await this._ankiInvoke('addNote', {note}); + return await this._invoke('addNote', {note}); } async canAddNotes(notes) { if (!this._enabled) { return []; } await this._checkVersion(); - return await this._ankiInvoke('canAddNotes', {notes}); + return await this._invoke('canAddNotes', {notes}); } async getDeckNames() { if (!this._enabled) { return []; } await this._checkVersion(); - return await this._ankiInvoke('deckNames'); + return await this._invoke('deckNames'); } async getModelNames() { if (!this._enabled) { return []; } await this._checkVersion(); - return await this._ankiInvoke('modelNames'); + return await this._invoke('modelNames'); } async getModelFieldNames(modelName) { if (!this._enabled) { return []; } await this._checkVersion(); - return await this._ankiInvoke('modelFieldNames', {modelName}); + return await this._invoke('modelFieldNames', {modelName}); } async guiBrowse(query) { if (!this._enabled) { return []; } await this._checkVersion(); - return await this._ankiInvoke('guiBrowse', {query}); + return await this._invoke('guiBrowse', {query}); } async storeMediaFile(filename, dataBase64) { @@ -84,7 +84,7 @@ class AnkiConnect { throw new Error('AnkiConnect not enabled'); } await this._checkVersion(); - return await this._ankiInvoke('storeMediaFile', {filename, data: dataBase64}); + return await this._invoke('storeMediaFile', {filename, data: dataBase64}); } async findNoteIds(notes) { @@ -96,21 +96,21 @@ class AnkiConnect { query: `deck:"${this._escapeQuery(note.deckName)}" ${this._fieldsToQuery(note.fields)}` } })); - return await this._ankiInvoke('multi', {actions}); + return await this._invoke('multi', {actions}); } // Private async _checkVersion() { if (this._remoteVersion < this._localVersion) { - this._remoteVersion = await this._ankiInvoke('version'); + this._remoteVersion = await this._invoke('version'); if (this._remoteVersion < this._localVersion) { throw new Error('Extension and plugin versions incompatible'); } } } - async _ankiInvoke(action, params) { + async _invoke(action, params) { const result = await requestJson(this._server, 'POST', {action, params, version: this._localVersion}); if ( result !== null && -- cgit v1.2.3 From 018913d03fff627fb7d34f594340c47a607e8839 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 17 Apr 2020 19:25:07 -0400 Subject: Use isObject --- ext/bg/js/anki.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index 928b5159..38823431 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -112,11 +112,7 @@ class AnkiConnect { async _invoke(action, params) { const result = await requestJson(this._server, 'POST', {action, params, version: this._localVersion}); - if ( - result !== null && - typeof result === 'object' && - !Array.isArray(result) - ) { + if (isObject(result)) { const error = result.error; if (typeof error !== 'undefined') { throw new Error(`AnkiConnect error: ${error}`); -- cgit v1.2.3 From 0956634d61ef2b6202645ec4b502239573c2e743 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Apr 2020 18:10:59 -0400 Subject: Add duplicateScope: 'deck' option (#476) * Add duplicateScope: 'deck' option * Add option to control duplicate scope * Use duplicateScope for findNoteIds * Update location of quotes --- ext/bg/data/options-schema.json | 6 ++++++ ext/bg/js/anki-note-builder.js | 5 ++++- ext/bg/js/anki.js | 15 +++++++-------- ext/bg/js/backend.js | 2 +- ext/bg/js/options.js | 1 + ext/bg/js/settings/main.js | 2 ++ ext/bg/settings.html | 8 ++++++++ 7 files changed, 29 insertions(+), 10 deletions(-) (limited to 'ext/bg/js/anki.js') diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index 4f9e694d..8622f16b 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -492,6 +492,7 @@ "screenshot", "terms", "kanji", + "duplicateScope", "fieldTemplates" ], "properties": { @@ -587,6 +588,11 @@ } } }, + "duplicateScope": { + "type": "string", + "default": "collection", + "enum": ["collection", "deck"] + }, "fieldTemplates": { "type": ["string", "null"], "default": null diff --git a/ext/bg/js/anki-note-builder.js b/ext/bg/js/anki-note-builder.js index 9bab095d..dc1e9427 100644 --- a/ext/bg/js/anki-note-builder.js +++ b/ext/bg/js/anki-note-builder.js @@ -32,7 +32,10 @@ class AnkiNoteBuilder { fields: {}, tags, deckName: modeOptions.deck, - modelName: modeOptions.model + modelName: modeOptions.model, + options: { + duplicateScope: options.anki.duplicateScope + } }; for (const [fieldName, fieldValue] of modeOptionsFieldEntries) { diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index 38823431..0d38837c 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -87,15 +87,14 @@ class AnkiConnect { return await this._invoke('storeMediaFile', {filename, data: dataBase64}); } - async findNoteIds(notes) { + async findNoteIds(notes, duplicateScope) { if (!this._enabled) { return []; } await this._checkVersion(); - const actions = notes.map((note) => ({ - action: 'findNotes', - params: { - query: `deck:"${this._escapeQuery(note.deckName)}" ${this._fieldsToQuery(note.fields)}` - } - })); + const actions = notes.map((note) => { + let query = (duplicateScope === 'deck' ? `"deck:${this._escapeQuery(note.deckName)}" ` : ''); + query += this._fieldsToQuery(note.fields); + return {action: 'findNotes', params: {query}}; + }); return await this._invoke('multi', {actions}); } @@ -132,6 +131,6 @@ class AnkiConnect { } const key = fieldNames[0]; - return `${key.toLowerCase()}:"${this._escapeQuery(fields[key])}"`; + return `"${key.toLowerCase()}:${this._escapeQuery(fields[key])}"`; } } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 3c47b14e..dd1fd8e9 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -555,7 +555,7 @@ class Backend { } if (cannotAdd.length > 0) { - const noteIdsArray = await this.anki.findNoteIds(cannotAdd.map((e) => e[0])); + const noteIdsArray = await this.anki.findNoteIds(cannotAdd.map((e) => e[0]), options.anki.duplicateScope); for (let i = 0, ii = Math.min(cannotAdd.length, noteIdsArray.length); i < ii; ++i) { const noteIds = noteIdsArray[i]; if (noteIds.length > 0) { diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index da26b628..8e1814ed 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -201,6 +201,7 @@ function profileOptionsCreateDefaults() { screenshot: {format: 'png', quality: 92}, terms: {deck: '', model: '', fields: {}}, kanji: {deck: '', model: '', fields: {}}, + duplicateScope: 'collection', fieldTemplates: null } }; diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index f03cc631..cf75d629 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -131,6 +131,7 @@ async function formRead(options) { options.anki.tags = utilBackgroundIsolate($('#card-tags').val().split(/[,; ]+/)); options.anki.sentenceExt = parseInt($('#sentence-detection-extent').val(), 10); options.anki.server = $('#interface-server').val(); + options.anki.duplicateScope = $('#duplicate-scope').val(); options.anki.screenshot.format = $('#screenshot-format').val(); options.anki.screenshot.quality = parseInt($('#screenshot-quality').val(), 10); @@ -212,6 +213,7 @@ async function formWrite(options) { $('#card-tags').val(options.anki.tags.join(' ')); $('#sentence-detection-extent').val(options.anki.sentenceExt); $('#interface-server').val(options.anki.server); + $('#duplicate-scope').val(options.anki.duplicateScope); $('#screenshot-format').val(options.anki.screenshot.format); $('#screenshot-quality').val(options.anki.screenshot.quality); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index f0236193..b6120b5f 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -820,6 +820,14 @@ +
+ + +
+