diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-09-08 11:01:08 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-09-08 11:01:08 -0400 | 
| commit | f7093b4c1abf73363e65dba54a098b76d0f2ba50 (patch) | |
| tree | c54fc65cb559d21450a1c2a3596ec222e40fc9ba /ext/bg/js | |
| parent | 36fc5abae543840484b3d8f7abff85f57de66ada (diff) | |
Anki note clipboard marker (#780)
* Update fields reference
* Add support for adding clipboard images to anki notes
* Add handlebars templates
* Add markers
* Add markers to readme
Diffstat (limited to 'ext/bg/js')
| -rw-r--r-- | ext/bg/js/anki-note-builder.js | 38 | ||||
| -rw-r--r-- | ext/bg/js/backend.js | 14 | ||||
| -rw-r--r-- | ext/bg/js/options.js | 6 | ||||
| -rw-r--r-- | ext/bg/js/settings/anki-templates.js | 5 | ||||
| -rw-r--r-- | ext/bg/js/settings/anki.js | 2 | 
5 files changed, 58 insertions, 7 deletions
| diff --git a/ext/bg/js/anki-note-builder.js b/ext/bg/js/anki-note-builder.js index 041e6dcd..d69a4fea 100644 --- a/ext/bg/js/anki-note-builder.js +++ b/ext/bg/js/anki-note-builder.js @@ -20,10 +20,11 @@   */  class AnkiNoteBuilder { -    constructor({anki, audioSystem, renderTemplate}) { +    constructor({anki, audioSystem, renderTemplate, getClipboardImage=null}) {          this._anki = anki;          this._audioSystem = audioSystem;          this._renderTemplate = renderTemplate; +        this._getClipboardImage = getClipboardImage;      }      async createNote(definition, mode, context, options, templates) { @@ -138,6 +139,31 @@ class AnkiNoteBuilder {          definition.screenshotFileName = fileName;      } +    async injectClipboardImage(definition, fields) { +        if (!this._containsMarker(fields, 'clipboard-image')) { return; } + +        const reading = definition.reading; +        const now = new Date(Date.now()); + +        try { +            const dataUrl = await this._getClipboardImage(); +            if (dataUrl === null) { return; } + +            const extension = this._getImageExtensionFromDataUrl(dataUrl); +            if (extension === null) { return; } + +            let fileName = `yomichan_clipboard_image_${reading}_${this._dateToString(now)}.${extension}`; +            fileName = AnkiNoteBuilder.replaceInvalidFileNameCharacters(fileName); +            const data = dataUrl.replace(/^data:[\w\W]*?,/, ''); + +            await this._anki.storeMediaFile(fileName, data); + +            definition.clipboardImageFileName = fileName; +        } catch (e) { +            // NOP +        } +    } +      _createInjectedAudioFileName(definition) {          const {reading, expression} = definition;          if (!reading && !expression) { return null; } @@ -170,6 +196,16 @@ class AnkiNoteBuilder {          return false;      } +    _getImageExtensionFromDataUrl(dataUrl) { +        const match = /^data:([^;]*);/.exec(dataUrl); +        if (match === null) { return null; } +        switch (match[1].toLowerCase()) { +            case 'image/png': return 'png'; +            case 'image/jpeg': return 'jpeg'; +            default: return null; +        } +    } +      static replaceInvalidFileNameCharacters(fileName) {          // eslint-disable-next-line no-control-regex          return fileName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '-'); diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 047044df..e9f3fbec 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -60,7 +60,8 @@ class Backend {          this._ankiNoteBuilder = new AnkiNoteBuilder({              anki: this._anki,              audioSystem: this._audioSystem, -            renderTemplate: this._renderTemplate.bind(this) +            renderTemplate: this._renderTemplate.bind(this), +            getClipboardImage: this._onApiClipboardImageGet.bind(this)          });          this._templateRenderer = new TemplateRenderer(); @@ -444,21 +445,28 @@ class Backend {      async _onApiDefinitionAdd({definition, mode, context, details, optionsContext}) {          const options = this.getOptions(optionsContext);          const templates = this._getTemplates(options); +        const fields = ( +            mode === 'kanji' ? +            options.anki.kanji.fields : +            options.anki.terms.fields +        );          if (mode !== 'kanji') {              const {customSourceUrl} = options.audio;              await this._ankiNoteBuilder.injectAudio(                  definition, -                options.anki.terms.fields, +                fields,                  options.audio.sources,                  customSourceUrl              );          } +        await this._ankiNoteBuilder.injectClipboardImage(definition, fields); +          if (details && details.screenshot) {              await this._ankiNoteBuilder.injectScreenshot(                  definition, -                options.anki.terms.fields, +                fields,                  details.screenshot              );          } diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index fc2403e1..9dc0c166 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -432,7 +432,7 @@ class OptionsUtil {                  update: this._updateVersion3.bind(this)              },              { -                async: false, +                async: true,                  update: this._updateVersion4.bind(this)              }          ]; @@ -468,10 +468,11 @@ class OptionsUtil {          return options;      } -    static _updateVersion4(options) { +    static async _updateVersion4(options) {          // Version 4 changes:          //  Options conditions converted to string representations.          //  Added usePopupWindow. +        //  Updated handlebars templates to include "clipboard-image" definition.          for (const {conditionGroups} of options.profiles) {              for (const {conditions} of conditionGroups) {                  for (const condition of conditions) { @@ -487,6 +488,7 @@ class OptionsUtil {          for (const {options: profileOptions} of options.profiles) {              profileOptions.general.usePopupWindow = false;          } +        await this._addFieldTemplatesToOptions(options, '/bg/data/anki-field-templates-upgrade-v4.handlebars');          return options;      }  } diff --git a/ext/bg/js/settings/anki-templates.js b/ext/bg/js/settings/anki-templates.js index 4e004308..fb03ef14 100644 --- a/ext/bg/js/settings/anki-templates.js +++ b/ext/bg/js/settings/anki-templates.js @@ -143,7 +143,10 @@ class AnkiTemplatesController {                  };                  let templates = options.anki.fieldTemplates;                  if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; } -                const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: api.templateRender.bind(api)}); +                const ankiNoteBuilder = new AnkiNoteBuilder({ +                    renderTemplate: api.templateRender.bind(api), +                    getClipboardImage: api.clipboardGetImage.bind(api) +                });                  const data = ankiNoteBuilder.createNoteData(definition, mode, context, options);                  result = await ankiNoteBuilder.formatField(field, data, templates, exceptions);              } diff --git a/ext/bg/js/settings/anki.js b/ext/bg/js/settings/anki.js index ac4c5455..0965e633 100644 --- a/ext/bg/js/settings/anki.js +++ b/ext/bg/js/settings/anki.js @@ -44,6 +44,7 @@ class AnkiController {              case 'terms':                  return [                      'audio', +                    'clipboard-image',                      'cloze-body',                      'cloze-prefix',                      'cloze-suffix', @@ -66,6 +67,7 @@ class AnkiController {              case 'kanji':                  return [                      'character', +                    'clipboard-image',                      'cloze-body',                      'cloze-prefix',                      'cloze-suffix', |