diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-05-24 13:30:40 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-24 13:30:40 -0400 | 
| commit | c61a87b152b91bdebe01eefdbc3fa00670a3071d (patch) | |
| tree | 63a94eacdc437da1e166a72a9b4d4794df294f22 /ext/mixed/js | |
| parent | 83a577fa569e5a6d468e3b304313106bba3e1e49 (diff) | |
API refactor (#532)
* Convert api.js into a class instance
* Use new api.* functions
* Fix missing binds
* Group functions with progress callbacks together
* Change style
* Fix API override not working
Diffstat (limited to 'ext/mixed/js')
| -rw-r--r-- | ext/mixed/js/api.js | 572 | ||||
| -rw-r--r-- | ext/mixed/js/display-generator.js | 4 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 30 | ||||
| -rw-r--r-- | ext/mixed/js/dynamic-loader.js | 4 | ||||
| -rw-r--r-- | ext/mixed/js/media-loader.js | 4 | 
5 files changed, 310 insertions, 304 deletions
| diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 0bc91759..e09a0db6 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -15,307 +15,321 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +const api = (() => { +    class API { +        constructor() { +            this._forwardLogsToBackendEnabled = false; +        } + +        forwardLogsToBackend() { +            if (this._forwardLogsToBackendEnabled) { return; } +            this._forwardLogsToBackendEnabled = true; + +            yomichan.on('log', async ({error, level, context}) => { +                try { +                    await this.log(errorToJson(error), level, context); +                } catch (e) { +                    // NOP +                } +            }); +        } + +        // Invoke functions + +        optionsSchemaGet() { +            return this._invoke('optionsSchemaGet'); +        } + +        optionsGet(optionsContext) { +            return this._invoke('optionsGet', {optionsContext}); +        } + +        optionsGetFull() { +            return this._invoke('optionsGetFull'); +        } + +        optionsSave(source) { +            return this._invoke('optionsSave', {source}); +        } + +        termsFind(text, details, optionsContext) { +            return this._invoke('termsFind', {text, details, optionsContext}); +        } + +        textParse(text, optionsContext) { +            return this._invoke('textParse', {text, optionsContext}); +        } + +        kanjiFind(text, optionsContext) { +            return this._invoke('kanjiFind', {text, optionsContext}); +        } + +        definitionAdd(definition, mode, context, details, optionsContext) { +            return this._invoke('definitionAdd', {definition, mode, context, details, optionsContext}); +        } + +        definitionsAddable(definitions, modes, context, optionsContext) { +            return this._invoke('definitionsAddable', {definitions, modes, context, optionsContext}); +        } + +        noteView(noteId) { +            return this._invoke('noteView', {noteId}); +        } + +        templateRender(template, data) { +            return this._invoke('templateRender', {data, template}); +        } + +        audioGetUri(definition, source, details) { +            return this._invoke('audioGetUri', {definition, source, details}); +        } + +        commandExec(command, params) { +            return this._invoke('commandExec', {command, params}); +        } + +        screenshotGet(options) { +            return this._invoke('screenshotGet', {options}); +        } + +        sendMessageToFrame(frameId, action, params) { +            return this._invoke('sendMessageToFrame', {frameId, action, params}); +        } + +        broadcastTab(action, params) { +            return this._invoke('broadcastTab', {action, params}); +        } -function apiOptionsSchemaGet() { -    return _apiInvoke('optionsSchemaGet'); -} +        frameInformationGet() { +            return this._invoke('frameInformationGet'); +        } -function apiOptionsGet(optionsContext) { -    return _apiInvoke('optionsGet', {optionsContext}); -} +        injectStylesheet(type, value) { +            return this._invoke('injectStylesheet', {type, value}); +        } -function apiOptionsGetFull() { -    return _apiInvoke('optionsGetFull'); -} +        getEnvironmentInfo() { +            return this._invoke('getEnvironmentInfo'); +        } -function apiOptionsSave(source) { -    return _apiInvoke('optionsSave', {source}); -} +        clipboardGet() { +            return this._invoke('clipboardGet'); +        } -function apiTermsFind(text, details, optionsContext) { -    return _apiInvoke('termsFind', {text, details, optionsContext}); -} +        getDisplayTemplatesHtml() { +            return this._invoke('getDisplayTemplatesHtml'); +        } -function apiTextParse(text, optionsContext) { -    return _apiInvoke('textParse', {text, optionsContext}); -} +        getQueryParserTemplatesHtml() { +            return this._invoke('getQueryParserTemplatesHtml'); +        } -function apiKanjiFind(text, optionsContext) { -    return _apiInvoke('kanjiFind', {text, optionsContext}); -} +        getZoom() { +            return this._invoke('getZoom'); +        } -function apiDefinitionAdd(definition, mode, context, details, optionsContext) { -    return _apiInvoke('definitionAdd', {definition, mode, context, details, optionsContext}); -} +        getDefaultAnkiFieldTemplates() { +            return this._invoke('getDefaultAnkiFieldTemplates'); +        } -function apiDefinitionsAddable(definitions, modes, context, optionsContext) { -    return _apiInvoke('definitionsAddable', {definitions, modes, context, optionsContext}); -} +        getAnkiDeckNames() { +            return this._invoke('getAnkiDeckNames'); +        } -function apiNoteView(noteId) { -    return _apiInvoke('noteView', {noteId}); -} +        getAnkiModelNames() { +            return this._invoke('getAnkiModelNames'); +        } -function apiTemplateRender(template, data) { -    return _apiInvoke('templateRender', {data, template}); -} +        getAnkiModelFieldNames(modelName) { +            return this._invoke('getAnkiModelFieldNames', {modelName}); +        } -function apiAudioGetUri(definition, source, details) { -    return _apiInvoke('audioGetUri', {definition, source, details}); -} +        getDictionaryInfo() { +            return this._invoke('getDictionaryInfo'); +        } -function apiCommandExec(command, params) { -    return _apiInvoke('commandExec', {command, params}); -} - -function apiScreenshotGet(options) { -    return _apiInvoke('screenshotGet', {options}); -} - -function apiSendMessageToFrame(frameId, action, params) { -    return _apiInvoke('sendMessageToFrame', {frameId, action, params}); -} - -function apiBroadcastTab(action, params) { -    return _apiInvoke('broadcastTab', {action, params}); -} - -function apiFrameInformationGet() { -    return _apiInvoke('frameInformationGet'); -} - -function apiInjectStylesheet(type, value) { -    return _apiInvoke('injectStylesheet', {type, value}); -} - -function apiGetEnvironmentInfo() { -    return _apiInvoke('getEnvironmentInfo'); -} - -function apiClipboardGet() { -    return _apiInvoke('clipboardGet'); -} - -function apiGetDisplayTemplatesHtml() { -    return _apiInvoke('getDisplayTemplatesHtml'); -} - -function apiGetQueryParserTemplatesHtml() { -    return _apiInvoke('getQueryParserTemplatesHtml'); -} - -function apiGetZoom() { -    return _apiInvoke('getZoom'); -} - -function apiGetDefaultAnkiFieldTemplates() { -    return _apiInvoke('getDefaultAnkiFieldTemplates'); -} - -function apiGetAnkiDeckNames() { -    return _apiInvoke('getAnkiDeckNames'); -} - -function apiGetAnkiModelNames() { -    return _apiInvoke('getAnkiModelNames'); -} - -function apiGetAnkiModelFieldNames(modelName) { -    return _apiInvoke('getAnkiModelFieldNames', {modelName}); -} - -function apiGetDictionaryInfo() { -    return _apiInvoke('getDictionaryInfo'); -} - -function apiGetDictionaryCounts(dictionaryNames, getTotal) { -    return _apiInvoke('getDictionaryCounts', {dictionaryNames, getTotal}); -} - -function apiPurgeDatabase() { -    return _apiInvoke('purgeDatabase'); -} - -function apiGetMedia(targets) { -    return _apiInvoke('getMedia', {targets}); -} - -function apiLog(error, level, context) { -    return _apiInvoke('log', {error, level, context}); -} - -function apiLogIndicatorClear() { -    return _apiInvoke('logIndicatorClear'); -} - -function apiImportDictionaryArchive(archiveContent, details, onProgress) { -    return _apiInvokeWithProgress('importDictionaryArchive', {archiveContent, details}, onProgress); -} - -function apiDeleteDictionary(dictionaryName, onProgress) { -    return _apiInvokeWithProgress('deleteDictionary', {dictionaryName}, onProgress); -} - -function apiModifySettings(targets, source) { -    return _apiInvoke('modifySettings', {targets, source}); -} - -function _apiCreateActionPort(timeout=5000) { -    return new Promise((resolve, reject) => { -        let timer = null; -        let portNameResolve; -        let portNameReject; -        const portNamePromise = new Promise((resolve2, reject2) => { -            portNameResolve = resolve2; -            portNameReject = reject2; -        }); - -        const onConnect = async (port) => { -            try { -                const portName = await portNamePromise; -                if (port.name !== portName || timer === null) { return; } -            } catch (e) { -                return; -            } - -            clearTimeout(timer); -            timer = null; - -            chrome.runtime.onConnect.removeListener(onConnect); -            resolve(port); -        }; - -        const onError = (e) => { -            if (timer !== null) { -                clearTimeout(timer); -                timer = null; -            } -            chrome.runtime.onConnect.removeListener(onConnect); -            portNameReject(e); -            reject(e); -        }; - -        timer = setTimeout(() => onError(new Error('Timeout')), timeout); - -        chrome.runtime.onConnect.addListener(onConnect); -        _apiInvoke('createActionPort').then(portNameResolve, onError); -    }); -} - -function _apiInvokeWithProgress(action, params, onProgress, timeout=5000) { -    return new Promise((resolve, reject) => { -        let timer = null; -        let port = null; - -        if (typeof onProgress !== 'function') { -            onProgress = () => {}; -        } - -        const onMessage = (message) => { -            switch (message.type) { -                case 'ack': +        getDictionaryCounts(dictionaryNames, getTotal) { +            return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal}); +        } + +        purgeDatabase() { +            return this._invoke('purgeDatabase'); +        } + +        getMedia(targets) { +            return this._invoke('getMedia', {targets}); +        } + +        log(error, level, context) { +            return this._invoke('log', {error, level, context}); +        } + +        logIndicatorClear() { +            return this._invoke('logIndicatorClear'); +        } + +        modifySettings(targets, source) { +            return this._invoke('modifySettings', {targets, source}); +        } + +        // Invoke functions with progress + +        importDictionaryArchive(archiveContent, details, onProgress) { +            return this._invokeWithProgress('importDictionaryArchive', {archiveContent, details}, onProgress); +        } + +        deleteDictionary(dictionaryName, onProgress) { +            return this._invokeWithProgress('deleteDictionary', {dictionaryName}, onProgress); +        } + +        // Utilities + +        _createActionPort(timeout=5000) { +            return new Promise((resolve, reject) => { +                let timer = null; +                let portNameResolve; +                let portNameReject; +                const portNamePromise = new Promise((resolve2, reject2) => { +                    portNameResolve = resolve2; +                    portNameReject = reject2; +                }); + +                const onConnect = async (port) => { +                    try { +                        const portName = await portNamePromise; +                        if (port.name !== portName || timer === null) { return; } +                    } catch (e) { +                        return; +                    } + +                    clearTimeout(timer); +                    timer = null; + +                    chrome.runtime.onConnect.removeListener(onConnect); +                    resolve(port); +                }; + +                const onError = (e) => {                      if (timer !== null) {                          clearTimeout(timer);                          timer = null;                      } -                    break; -                case 'progress': -                    try { -                        onProgress(...message.data); -                    } catch (e) { -                        // NOP +                    chrome.runtime.onConnect.removeListener(onConnect); +                    portNameReject(e); +                    reject(e); +                }; + +                timer = setTimeout(() => onError(new Error('Timeout')), timeout); + +                chrome.runtime.onConnect.addListener(onConnect); +                this._invoke('createActionPort').then(portNameResolve, onError); +            }); +        } + +        _invokeWithProgress(action, params, onProgress, timeout=5000) { +            return new Promise((resolve, reject) => { +                let timer = null; +                let port = null; + +                if (typeof onProgress !== 'function') { +                    onProgress = () => {}; +                } + +                const onMessage = (message) => { +                    switch (message.type) { +                        case 'ack': +                            if (timer !== null) { +                                clearTimeout(timer); +                                timer = null; +                            } +                            break; +                        case 'progress': +                            try { +                                onProgress(...message.data); +                            } catch (e) { +                                // NOP +                            } +                            break; +                        case 'complete': +                            cleanup(); +                            resolve(message.data); +                            break; +                        case 'error': +                            cleanup(); +                            reject(jsonToError(message.data)); +                            break;                      } -                    break; -                case 'complete': +                }; + +                const onDisconnect = () => {                      cleanup(); -                    resolve(message.data); -                    break; -                case 'error': +                    reject(new Error('Disconnected')); +                }; + +                const cleanup = () => { +                    if (timer !== null) { +                        clearTimeout(timer); +                        timer = null; +                    } +                    if (port !== null) { +                        port.onMessage.removeListener(onMessage); +                        port.onDisconnect.removeListener(onDisconnect); +                        port.disconnect(); +                        port = null; +                    } +                    onProgress = null; +                }; + +                timer = setTimeout(() => {                      cleanup(); -                    reject(jsonToError(message.data)); -                    break; -            } -        }; - -        const onDisconnect = () => { -            cleanup(); -            reject(new Error('Disconnected')); -        }; - -        const cleanup = () => { -            if (timer !== null) { -                clearTimeout(timer); -                timer = null; -            } -            if (port !== null) { -                port.onMessage.removeListener(onMessage); -                port.onDisconnect.removeListener(onDisconnect); -                port.disconnect(); -                port = null; -            } -            onProgress = null; -        }; - -        timer = setTimeout(() => { -            cleanup(); -            reject(new Error('Timeout')); -        }, timeout); - -        (async () => { -            try { -                port = await _apiCreateActionPort(timeout); -                port.onMessage.addListener(onMessage); -                port.onDisconnect.addListener(onDisconnect); -                port.postMessage({action, params}); -            } catch (e) { -                cleanup(); -                reject(e); -            } finally { -                action = null; -                params = null; -            } -        })(); -    }); -} - -function _apiInvoke(action, params={}) { -    const data = {action, params}; -    return new Promise((resolve, reject) => { -        try { -            chrome.runtime.sendMessage(data, (response) => { -                _apiCheckLastError(chrome.runtime.lastError); -                if (response !== null && typeof response === 'object') { -                    if (typeof response.error !== 'undefined') { -                        reject(jsonToError(response.error)); -                    } else { -                        resolve(response.result); +                    reject(new Error('Timeout')); +                }, timeout); + +                (async () => { +                    try { +                        port = await this._createActionPort(timeout); +                        port.onMessage.addListener(onMessage); +                        port.onDisconnect.addListener(onDisconnect); +                        port.postMessage({action, params}); +                    } catch (e) { +                        cleanup(); +                        reject(e); +                    } finally { +                        action = null; +                        params = null;                      } -                } else { -                    const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`; -                    reject(new Error(`${message} (${JSON.stringify(data)})`)); +                })(); +            }); +        } + +        _invoke(action, params={}) { +            const data = {action, params}; +            return new Promise((resolve, reject) => { +                try { +                    chrome.runtime.sendMessage(data, (response) => { +                        this._checkLastError(chrome.runtime.lastError); +                        if (response !== null && typeof response === 'object') { +                            if (typeof response.error !== 'undefined') { +                                reject(jsonToError(response.error)); +                            } else { +                                resolve(response.result); +                            } +                        } else { +                            const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`; +                            reject(new Error(`${message} (${JSON.stringify(data)})`)); +                        } +                    }); +                } catch (e) { +                    reject(e); +                    yomichan.triggerOrphaned(e);                  }              }); -        } catch (e) { -            reject(e); -            yomichan.triggerOrphaned(e); -        } -    }); -} - -function _apiCheckLastError() { -    // NOP -} - -let _apiForwardLogsToBackendEnabled = false; -function apiForwardLogsToBackend() { -    if (_apiForwardLogsToBackendEnabled) { return; } -    _apiForwardLogsToBackendEnabled = true; - -    yomichan.on('log', async ({error, level, context}) => { -        try { -            await apiLog(errorToJson(error), level, context); -        } catch (e) { +        } + +        _checkLastError() {              // NOP          } -    }); -} +    } + +    return new API(); +})(); diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js index a2b2b139..3f3a155e 100644 --- a/ext/mixed/js/display-generator.js +++ b/ext/mixed/js/display-generator.js @@ -17,7 +17,7 @@  /* global   * TemplateHandler - * apiGetDisplayTemplatesHtml + * api   * jp   */ @@ -29,7 +29,7 @@ class DisplayGenerator {      }      async prepare() { -        const html = await apiGetDisplayTemplatesHtml(); +        const html = await api.getDisplayTemplatesHtml();          this._templateHandler = new TemplateHandler(html);      } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 2e59b4ff..380134ad 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -22,15 +22,7 @@   * DisplayGenerator   * MediaLoader   * WindowScroll - * apiAudioGetUri - * apiBroadcastTab - * apiDefinitionAdd - * apiDefinitionsAddable - * apiKanjiFind - * apiNoteView - * apiOptionsGet - * apiScreenshotGet - * apiTermsFind + * api   * docRangeFromPoint   * docSentenceExtract   */ @@ -49,7 +41,7 @@ class Display {          this.audioSystem = new AudioSystem({              audioUriBuilder: {                  getUri: async (definition, source, details) => { -                    return await apiAudioGetUri(definition, source, details); +                    return await api.audioGetUri(definition, source, details);                  }              },              useCache: true @@ -212,7 +204,7 @@ class Display {                  url: this.context.get('url')              }; -            const definitions = await apiKanjiFind(link.textContent, this.getOptionsContext()); +            const definitions = await api.kanjiFind(link.textContent, this.getOptionsContext());              this.setContent('kanji', {definitions, context});          } catch (error) {              this.onError(error); @@ -290,7 +282,7 @@ class Display {              try {                  textSource.setEndOffset(this.options.scanning.length); -                ({definitions, length} = await apiTermsFind(textSource.text(), {}, this.getOptionsContext())); +                ({definitions, length} = await api.termsFind(textSource.text(), {}, this.getOptionsContext()));                  if (definitions.length === 0) {                      return false;                  } @@ -334,7 +326,7 @@ class Display {      onNoteView(e) {          e.preventDefault();          const link = e.currentTarget; -        apiNoteView(link.dataset.noteId); +        api.noteView(link.dataset.noteId);      }      onKeyDown(e) { @@ -379,7 +371,7 @@ class Display {      }      async updateOptions() { -        this.options = await apiOptionsGet(this.getOptionsContext()); +        this.options = await api.optionsGet(this.getOptionsContext());          this.updateDocumentOptions(this.options);          this.updateTheme(this.options.general.popupTheme);          this.setCustomCss(this.options.general.customPopupCss); @@ -746,7 +738,7 @@ class Display {      noteTryView() {          const button = this.viewerButtonFind(this.index);          if (button !== null && !button.classList.contains('disabled')) { -            apiNoteView(button.dataset.noteId); +            api.noteView(button.dataset.noteId);          }      } @@ -763,7 +755,7 @@ class Display {              }              const context = await this._getNoteContext(); -            const noteId = await apiDefinitionAdd(definition, mode, context, details, this.getOptionsContext()); +            const noteId = await api.definitionAdd(definition, mode, context, details, this.getOptionsContext());              if (noteId) {                  const index = this.definitions.indexOf(definition);                  const adderButton = this.adderButtonFind(index, mode); @@ -857,7 +849,7 @@ class Display {              await promiseTimeout(1); // Wait for popup to be hidden.              const {format, quality} = this.options.anki.screenshot; -            const dataUrl = await apiScreenshotGet({format, quality}); +            const dataUrl = await api.screenshotGet({format, quality});              if (!dataUrl || dataUrl.error) { return; }              return {dataUrl, format}; @@ -871,7 +863,7 @@ class Display {      }      setPopupVisibleOverride(visible) { -        return apiBroadcastTab('popupSetVisibleOverride', {visible}); +        return api.broadcastTab('popupSetVisibleOverride', {visible});      }      setSpinnerVisible(visible) { @@ -933,7 +925,7 @@ class Display {      async getDefinitionsAddable(definitions, modes) {          try {              const context = await this._getNoteContext(); -            return await apiDefinitionsAddable(definitions, modes, context, this.getOptionsContext()); +            return await api.definitionsAddable(definitions, modes, context, this.getOptionsContext());          } catch (e) {              return [];          } diff --git a/ext/mixed/js/dynamic-loader.js b/ext/mixed/js/dynamic-loader.js index ce946109..37f85112 100644 --- a/ext/mixed/js/dynamic-loader.js +++ b/ext/mixed/js/dynamic-loader.js @@ -16,7 +16,7 @@   */  /* global - * apiInjectStylesheet + * api   */  const dynamicLoader = (() => { @@ -45,7 +45,7 @@ const dynamicLoader = (() => {              }              injectedStylesheets.set(id, null); -            await apiInjectStylesheet(type, value); +            await api.injectStylesheet(type, value);              return null;          } diff --git a/ext/mixed/js/media-loader.js b/ext/mixed/js/media-loader.js index 64ccd715..fc6e93d1 100644 --- a/ext/mixed/js/media-loader.js +++ b/ext/mixed/js/media-loader.js @@ -16,7 +16,7 @@   */  /* global - * apiGetMedia + * api   */  class MediaLoader { @@ -84,7 +84,7 @@ class MediaLoader {      async _getMediaData(path, dictionaryName, cachedData) {          const token = this._token; -        const data = (await apiGetMedia([{path, dictionaryName}]))[0]; +        const data = (await api.getMedia([{path, dictionaryName}]))[0];          if (token === this._token && data !== null) {              const contentArrayBuffer = this._base64ToArrayBuffer(data.content);              const blob = new Blob([contentArrayBuffer], {type: data.mediaType}); |