diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-07-31 12:30:31 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-31 12:30:31 -0400 | 
| commit | 00c5ae79833a641ccc5f7d31b6eea3e91db4cb71 (patch) | |
| tree | e21cb14527e2bf6e74b36eeab75e994d52df37b6 | |
| parent | 2d57d69b9ed3adf1241074f7eb29a588bec817a2 (diff) | |
DictionaryImporterMediaLoader (#1860)
* Rename param for consistency
* Move media loading functionality into DictionaryImporterMediaLoader
* Create test class for media loading
* Remove unnecessary Blob/Image/URL functionality
| -rw-r--r-- | dev/database-vm.js | 69 | ||||
| -rw-r--r-- | dev/translator-vm.js | 5 | ||||
| -rw-r--r-- | dev/vm.js | 18 | ||||
| -rw-r--r-- | ext/js/language/dictionary-importer-media-loader.js | 57 | ||||
| -rw-r--r-- | ext/js/language/dictionary-importer.js | 41 | ||||
| -rw-r--r-- | ext/js/pages/settings/dictionary-import-controller.js | 4 | ||||
| -rw-r--r-- | ext/settings.html | 1 | ||||
| -rw-r--r-- | ext/welcome.html | 1 | ||||
| -rw-r--r-- | test/test-database.js | 14 | 
9 files changed, 91 insertions, 119 deletions
| diff --git a/dev/database-vm.js b/dev/database-vm.js index 07d9bd5a..ebde5a2a 100644 --- a/dev/database-vm.js +++ b/dev/database-vm.js @@ -30,63 +30,6 @@ const chrome = {      }  }; -class Image { -    constructor() { -        this._src = ''; -        this._loadCallbacks = []; -    } - -    get src() { -        return this._src; -    } - -    set src(value) { -        this._src = value; -        this._delayTriggerLoad(); -    } - -    get naturalWidth() { -        return 100; -    } - -    get naturalHeight() { -        return 100; -    } - -    addEventListener(eventName, callback) { -        if (eventName === 'load') { -            this._loadCallbacks.push(callback); -        } -    } - -    removeEventListener(eventName, callback) { -        if (eventName === 'load') { -            const index = this._loadCallbacks.indexOf(callback); -            if (index >= 0) { -                this._loadCallbacks.splice(index, 1); -            } -        } -    } - -    removeAttribute() { -        // NOP -    } - -    async _delayTriggerLoad() { -        await Promise.resolve(); -        for (const callback of this._loadCallbacks) { -            callback(); -        } -    } -} - -class Blob { -    constructor(array, options) { -        this._array = array; -        this._options = options; -    } -} -  async function fetch(url2) {      const extDir = path.join(__dirname, '..', 'ext');      let filePath; @@ -114,8 +57,6 @@ class DatabaseVM extends VM {      constructor(globals={}) {          super(Object.assign({              chrome, -            Image, -            Blob,              fetch,              indexedDB: global.indexedDB,              IDBKeyRange: global.IDBKeyRange, @@ -127,6 +68,14 @@ class DatabaseVM extends VM {      }  } +class DatabaseVMDictionaryImporterMediaLoader { +    async getImageResolution() { +        // Placeholder values +        return {width: 100, height: 100}; +    } +} +  module.exports = { -    DatabaseVM +    DatabaseVM, +    DatabaseVMDictionaryImporterMediaLoader  }; diff --git a/dev/translator-vm.js b/dev/translator-vm.js index 3c3886ba..d616afc5 100644 --- a/dev/translator-vm.js +++ b/dev/translator-vm.js @@ -18,7 +18,7 @@  const fs = require('fs');  const path = require('path');  const assert = require('assert'); -const {DatabaseVM} = require('./database-vm'); +const {DatabaseVM, DatabaseVMDictionaryImporterMediaLoader} = require('./database-vm');  const {createDictionaryArchive} = require('./util');  function clone(value) { @@ -75,7 +75,8 @@ class TranslatorVM extends DatabaseVM {          const testDictionaryContent = await testDictionary.generateAsync({type: 'arraybuffer'});          // Setup database -        const dictionaryImporter = new DictionaryImporter(); +        const dictionaryImporterMediaLoader = new DatabaseVMDictionaryImporterMediaLoader(); +        const dictionaryImporter = new DictionaryImporter(dictionaryImporterMediaLoader);          const dictionaryDatabase = new DictionaryDatabase();          await dictionaryDatabase.prepare(); @@ -116,7 +116,7 @@ function deepStrictEqual(actual, expected) {  } -function createURLClass(urlMap) { +function createURLClass() {      const BaseURL = URL;      const result = function URL(url) {          const u = new BaseURL(url); @@ -133,23 +133,13 @@ function createURLClass(urlMap) {          this.searchParams = u.searchParams;          this.username = u.username;      }; -    result.createObjectURL = (object) => { -        const id = crypto.randomBytes(16).toString('hex'); -        const url = `blob:${id}`; -        urlMap.set(url, object); -        return url; -    }; -    result.revokeObjectURL = (url) => { -        urlMap.delete(url); -    };      return result;  }  class VM {      constructor(context={}) { -        this._urlMap = new Map(); -        context.URL = createURLClass(this._urlMap); +        context.URL = createURLClass();          context.crypto = {              getRandomValues: (array) => {                  const buffer = crypto.randomBytes(array.byteLength); @@ -205,10 +195,6 @@ class VM {          return single ? results[0] : results;      } - -    getUrlObject(url) { -        return this._urlMap.get(url); -    }  } diff --git a/ext/js/language/dictionary-importer-media-loader.js b/ext/js/language/dictionary-importer-media-loader.js new file mode 100644 index 00000000..bbcc5476 --- /dev/null +++ b/ext/js/language/dictionary-importer-media-loader.js @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021  Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +/* global + * MediaUtil + */ + +/** + * Class used for loading and validating media during the dictionary import process. + */ +class DictionaryImporterMediaLoader { +    /** +     * Attempts to load an image using a base64 encoded content and a media type +     * and returns its resolution. +     * @param mediaType The media type for the image content. +     * @param content The binary content for the image, encoded in base64. +     * @returns A Promise which resolves with {width, height} on success, +     *   otherwise an error is thrown. +     */ +    getImageResolution(mediaType, content) { +        return new Promise((resolve, reject) => { +            const image = new Image(); +            const eventListeners = new EventListenerCollection(); +            const cleanup = () => { +                image.removeAttribute('src'); +                URL.revokeObjectURL(url); +                eventListeners.removeAllEventListeners(); +            }; +            eventListeners.addEventListener(image, 'load', () => { +                const {naturalWidth: width, naturalHeight: height} = image; +                cleanup(); +                resolve({width, height}); +            }, false); +            eventListeners.addEventListener(image, 'error', () => { +                cleanup(); +                reject(new Error('Image failed to load')); +            }, false); +            const blob = MediaUtil.createBlobFromBase64Content(content, mediaType); +            const url = URL.createObjectURL(blob); +            image.src = url; +        }); +    } +} diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index 27b3c44e..b931c929 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -22,11 +22,12 @@   */  class DictionaryImporter { -    constructor() { +    constructor(mediaLoader) { +        this._mediaLoader = mediaLoader;          this._schemas = new Map();      } -    async importDictionary(dictionaryDatabase, archiveSource, details, onProgress) { +    async importDictionary(dictionaryDatabase, archiveContent, details, onProgress) {          if (!dictionaryDatabase) {              throw new Error('Invalid database');          } @@ -37,7 +38,7 @@ class DictionaryImporter {          const hasOnProgress = (typeof onProgress === 'function');          // Read archive -        const archive = await JSZip.loadAsync(archiveSource); +        const archive = await JSZip.loadAsync(archiveContent);          // Read and validate index          const indexFileName = 'index.json'; @@ -469,7 +470,7 @@ class DictionaryImporter {          let width;          let height;          try { -            ({width, height} = await this._getImageResolution(mediaType, content)); +            ({width, height} = await this._mediaLoader.getImageResolution(mediaType, content));          } catch (e) {              throw createError('Could not load image');          } @@ -502,36 +503,4 @@ class DictionaryImporter {          }          return await response.json();      } - -    /** -     * Attempts to load an image using a base64 encoded content and a media type -     * and returns its resolution. -     * @param mediaType The media type for the image content. -     * @param content The binary content for the image, encoded in base64. -     * @returns A Promise which resolves with {width, height} on success, -     *   otherwise an error is thrown. -     */ -    _getImageResolution(mediaType, content) { -        return new Promise((resolve, reject) => { -            const image = new Image(); -            const eventListeners = new EventListenerCollection(); -            const cleanup = () => { -                image.removeAttribute('src'); -                URL.revokeObjectURL(url); -                eventListeners.removeAllEventListeners(); -            }; -            eventListeners.addEventListener(image, 'load', () => { -                const {naturalWidth: width, naturalHeight: height} = image; -                cleanup(); -                resolve({width, height}); -            }, false); -            eventListeners.addEventListener(image, 'error', () => { -                cleanup(); -                reject(new Error('Image failed to load')); -            }, false); -            const blob = MediaUtil.createBlobFromBase64Content(content, mediaType); -            const url = URL.createObjectURL(blob); -            image.src = url; -        }); -    }  } diff --git a/ext/js/pages/settings/dictionary-import-controller.js b/ext/js/pages/settings/dictionary-import-controller.js index db9a1a50..128e18cb 100644 --- a/ext/js/pages/settings/dictionary-import-controller.js +++ b/ext/js/pages/settings/dictionary-import-controller.js @@ -19,6 +19,7 @@   * DictionaryController   * DictionaryDatabase   * DictionaryImporter + * DictionaryImporterMediaLoader   */  class DictionaryImportController { @@ -184,7 +185,8 @@ class DictionaryImportController {      async _importDictionary(file, importDetails, onProgress) {          const dictionaryDatabase = await this._getPreparedDictionaryDatabase();          try { -            const dictionaryImporter = new DictionaryImporter(); +            const dictionaryImporterMediaLoader = new DictionaryImporterMediaLoader(); +            const dictionaryImporter = new DictionaryImporter(dictionaryImporterMediaLoader);              const archiveContent = await this._readFile(file);              const {result, errors} = await dictionaryImporter.importDictionary(dictionaryDatabase, archiveContent, importDetails, onProgress);              yomichan.api.triggerDatabaseUpdated('dictionary', 'import'); diff --git a/ext/settings.html b/ext/settings.html index 9d2d9791..cd2376d6 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -3451,6 +3451,7 @@  <script src="/js/input/hotkey-util.js"></script>  <script src="/js/language/dictionary-database.js"></script>  <script src="/js/language/dictionary-importer.js"></script> +<script src="/js/language/dictionary-importer-media-loader.js"></script>  <script src="/js/language/sandbox/dictionary-data-util.js"></script>  <script src="/js/language/sandbox/japanese-util.js"></script>  <script src="/js/media/audio-system.js"></script> diff --git a/ext/welcome.html b/ext/welcome.html index 85022e2c..5c9f4469 100644 --- a/ext/welcome.html +++ b/ext/welcome.html @@ -409,6 +409,7 @@  <script src="/js/input/hotkey-util.js"></script>  <script src="/js/language/dictionary-database.js"></script>  <script src="/js/language/dictionary-importer.js"></script> +<script src="/js/language/dictionary-importer-media-loader.js"></script>  <script src="/js/media/media-util.js"></script>  <script src="/js/pages/settings/dictionary-controller.js"></script>  <script src="/js/pages/settings/dictionary-import-controller.js"></script> diff --git a/test/test-database.js b/test/test-database.js index 19ac49e2..ac7e825b 100644 --- a/test/test-database.js +++ b/test/test-database.js @@ -18,7 +18,7 @@  const path = require('path');  const assert = require('assert');  const {createDictionaryArchive, testMain} = require('../dev/util'); -const {DatabaseVM} = require('../dev/database-vm'); +const {DatabaseVM, DatabaseVMDictionaryImporterMediaLoader} = require('../dev/database-vm');  const vm = new DatabaseVM(); @@ -41,6 +41,12 @@ function createTestDictionaryArchive(dictionary, dictionaryName) {  } +function createDictionaryImporter() { +    const dictionaryImporterMediaLoader = new DatabaseVMDictionaryImporterMediaLoader(); +    return new DictionaryImporter(dictionaryImporterMediaLoader); +} + +  function countDictionaryDatabaseEntriesWithTerm(dictionaryDatabaseEntries, term) {      return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.term === term ? 1 : 0)), 0);  } @@ -125,7 +131,7 @@ async function testDatabase1() {      ];      // Setup database -    const dictionaryImporter = new DictionaryImporter(); +    const dictionaryImporter = createDictionaryImporter();      const dictionaryDatabase = new DictionaryDatabase();      await dictionaryDatabase.prepare(); @@ -775,7 +781,7 @@ async function testDatabase2() {      ]);      // Setup database -    const dictionaryImporter = new DictionaryImporter(); +    const dictionaryImporter = createDictionaryImporter();      const dictionaryDatabase = new DictionaryDatabase();      // Error: not prepared @@ -817,7 +823,7 @@ async function testDatabase3() {      ];      // Setup database -    const dictionaryImporter = new DictionaryImporter(); +    const dictionaryImporter = createDictionaryImporter();      const dictionaryDatabase = new DictionaryDatabase();      await dictionaryDatabase.prepare(); |