diff options
| -rw-r--r-- | ext/css/settings.css | 17 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 14 | ||||
| -rw-r--r-- | ext/js/background/environment.js | 14 | ||||
| -rw-r--r-- | ext/js/media/audio-system.js | 18 | ||||
| -rw-r--r-- | ext/js/pages/action-popup-main.js | 9 | ||||
| -rw-r--r-- | ext/js/pages/settings/audio-controller.js | 4 | ||||
| -rw-r--r-- | ext/js/pages/settings/settings-main.js | 27 | ||||
| -rw-r--r-- | ext/settings.html | 19 | 
8 files changed, 101 insertions, 21 deletions
| diff --git a/ext/css/settings.css b/ext/css/settings.css index c0e75fc2..6bc377c2 100644 --- a/ext/css/settings.css +++ b/ext/css/settings.css @@ -134,7 +134,7 @@ ul+p,  ul+ol,  ul+ul,  li { -    margin: 0.425em 0; +    margin: 0.425em 0 0;  }  a {      color: var(--link-color); @@ -2152,6 +2152,13 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {      margin-left: 0.375em;  } +.page-loading-stalled-notification { +    border: 1px solid var(--danger-color); +} +:root:not([data-loading-stalled=true]) .page-loading-stalled-notification { +    display: none; +} +  /* Generic layouts */  .margin-above { @@ -2233,19 +2240,25 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {  /* Environment-specific display */ +:root[data-browser=unknown] [data-show-for-browser], +:root[data-browser=unknown] [data-hide-for-browser],  :root[data-browser=edge] [data-show-for-browser]:not([data-show-for-browser~=edge]),  :root[data-browser=edge-legacy] [data-show-for-browser]:not([data-show-for-browser~=edge-legacy]),  :root[data-browser=chrome] [data-show-for-browser]:not([data-show-for-browser~=chrome]), +:root[data-browser=safari] [data-show-for-browser]:not([data-show-for-browser~=safari]),  :root[data-browser=firefox] [data-show-for-browser]:not([data-show-for-browser~=firefox]),  :root[data-browser=firefox-mobile] [data-show-for-browser]:not([data-show-for-browser~=firefox-mobile]),  :root[data-browser=edge] [data-hide-for-browser~=edge],  :root[data-browser=edge-legacy] [data-hide-for-browser~=edge-legacy],  :root[data-browser=chrome] [data-hide-for-browser~=chrome], +:root[data-browser=safari] [data-hide-for-browser~=safari],  :root[data-browser=firefox] [data-hide-for-browser~=firefox],  :root[data-browser=firefox-mobile] [data-hide-for-browser~=firefox-mobile] {      display: none;  } +:root[data-os=unknown] [data-show-for-os], +:root[data-os=unknown] [data-hide-for-os],  :root[data-os=mac] [data-show-for-os]:not([data-show-for-os~=mac]),  :root[data-os=win] [data-show-for-os]:not([data-show-for-os~=win]),  :root[data-os=android] [data-show-for-os]:not([data-show-for-os~=android]), @@ -2261,6 +2274,8 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {      display: none;  } +:root[data-manifest-version=unknown] [data-show-for-manifest-version], +:root[data-manifest-version=unknown] [data-hide-for-manifest-version],  :root[data-manifest-version='2'] [data-show-for-manifest-version]:not([data-show-for-manifest-version~='2']),  :root[data-manifest-version='3'] [data-show-for-manifest-version]:not([data-show-for-manifest-version~='3']),  :root[data-manifest-version='2'] [data-hide-for-manifest-version~='2'], diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a6b9c0dc..21b18e99 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -112,7 +112,7 @@ class Backend {              ['getDisplayTemplatesHtml',      {async: true,  contentScript: true,  handler: this._onApiGetDisplayTemplatesHtml.bind(this)}],              ['getZoom',                      {async: true,  contentScript: true,  handler: this._onApiGetZoom.bind(this)}],              ['getDefaultAnkiFieldTemplates', {async: false, contentScript: true,  handler: this._onApiGetDefaultAnkiFieldTemplates.bind(this)}], -            ['getDictionaryInfo',            {async: true,  contentScript: false, handler: this._onApiGetDictionaryInfo.bind(this)}], +            ['getDictionaryInfo',            {async: true,  contentScript: true,  handler: this._onApiGetDictionaryInfo.bind(this)}],              ['getDictionaryCounts',          {async: true,  contentScript: false, handler: this._onApiGetDictionaryCounts.bind(this)}],              ['purgeDatabase',                {async: true,  contentScript: false, handler: this._onApiPurgeDatabase.bind(this)}],              ['getMedia',                     {async: true,  contentScript: true,  handler: this._onApiGetMedia.bind(this)}], @@ -1288,10 +1288,14 @@ class Backend {      }      _validatePrivilegedMessageSender(sender) { -        const url = sender.url; -        if (!(typeof url === 'string' && yomichan.isExtensionUrl(url))) { -            throw new Error('Invalid message sender'); -        } +        let {url} = sender; +        if (typeof url === 'string' && yomichan.isExtensionUrl(url)) { return; } +        const {tab} = url; +        if (typeof tab === 'object' && tab !== null) { +            ({url} = tab); +            if (typeof url === 'string' && yomichan.isExtensionUrl(url)) { return; } +        } +        throw new Error('Invalid message sender');      }      _getBrowserIconTitle() { diff --git a/ext/js/background/environment.js b/ext/js/background/environment.js index 04099ca1..8111741f 100644 --- a/ext/js/background/environment.js +++ b/ext/js/background/environment.js @@ -83,9 +83,23 @@ class Environment {              } catch (e) {                  // NOP              } +            if (this._isSafari()) { +                return 'safari'; +            }              return 'firefox';          } else {              return 'chrome';          }      } + +    _isSafari() { +        const {vendor, userAgent} = navigator; +        return ( +            typeof vendor === 'string' && +            typeof userAgent === 'string' && +            vendor.includes('Apple') && +            !userAgent.includes('CriOS') && +            !userAgent.includes('FxiOS') +        ); +    }  } diff --git a/ext/js/media/audio-system.js b/ext/js/media/audio-system.js index cf63511f..cc2bcfc0 100644 --- a/ext/js/media/audio-system.js +++ b/ext/js/media/audio-system.js @@ -19,18 +19,20 @@   * TextToSpeechAudio   */ -class AudioSystem { +class AudioSystem extends EventDispatcher {      constructor() { +        super();          this._fallbackAudio = null;      }      prepare() {          // speechSynthesis.getVoices() will not be populated unless some API call is made. -        if (typeof speechSynthesis === 'undefined') { return; } - -        const eventListeners = new EventListenerCollection(); -        const onVoicesChanged = () => { eventListeners.removeAllEventListeners(); }; -        eventListeners.addEventListener(speechSynthesis, 'voiceschanged', onVoicesChanged, false); +        if ( +            typeof speechSynthesis !== 'undefined' && +            typeof speechSynthesis.addEventListener === 'function' +        ) { +            speechSynthesis.addEventListener('voiceschanged', this._onVoicesChanged.bind(this), false); +        }      }      getFallbackAudio() { @@ -64,6 +66,10 @@ class AudioSystem {      // Private +    _onVoicesChanged(e) { +        this.trigger('voiceschanged', e); +    } +      _isAudioValid(audio, source) {          switch (source) {              case 'jpod101': diff --git a/ext/js/pages/action-popup-main.js b/ext/js/pages/action-popup-main.js index 2de986da..4934802b 100644 --- a/ext/js/pages/action-popup-main.js +++ b/ext/js/pages/action-popup-main.js @@ -103,6 +103,10 @@ class DisplayController {                      let tab;                      try {                          tab = await this._getCurrentTab(); +                        // Safari assigns a tab object to the popup, other browsers do not +                        if (tab && await this._isSafari()) { +                            tab = void 0; +                        }                      } catch (e) {                          // NOP                      } @@ -220,6 +224,11 @@ class DisplayController {              node.hidden = false;          }      } + +    async _isSafari() { +        const {browser} = await yomichan.api.getEnvironmentInfo(); +        return browser === 'safari'; +    }  }  (async () => { diff --git a/ext/js/pages/settings/audio-controller.js b/ext/js/pages/settings/audio-controller.js index e62383a8..6b1ce0b5 100644 --- a/ext/js/pages/settings/audio-controller.js +++ b/ext/js/pages/settings/audio-controller.js @@ -39,9 +39,7 @@ class AudioController {          this._audioSourceAddButton.addEventListener('click', this._onAddAudioSource.bind(this), false); -        if (typeof speechSynthesis !== 'undefined') { -            speechSynthesis.addEventListener('voiceschanged', this._updateTextToSpeechVoices.bind(this), false); -        } +        this._audioSystem.on('voiceschanged', this._updateTextToSpeechVoices.bind(this), false);          this._updateTextToSpeechVoices();          document.querySelector('#text-to-speech-voice-test').addEventListener('click', this._onTestTextToSpeech.bind(this), false); diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js index 2560685c..6f3e2a58 100644 --- a/ext/js/pages/settings/settings-main.js +++ b/ext/js/pages/settings/settings-main.js @@ -24,6 +24,7 @@   * DictionaryController   * DictionaryImportController   * DocumentFocusController + * Environment   * ExtensionKeyboardShortcutController   * GenericSettingController   * KeyboardShortcutController @@ -47,11 +48,16 @@   */  async function setupEnvironmentInfo() { +    const {dataset} = document.documentElement;      const {manifest_version: manifestVersion} = chrome.runtime.getManifest(); -    const {browser, platform} = await yomichan.api.getEnvironmentInfo(); -    document.documentElement.dataset.browser = browser; -    document.documentElement.dataset.os = platform.os; -    document.documentElement.dataset.manifestVersion = `${manifestVersion}`; +    dataset.manifestVersion = `${manifestVersion}`; + +    const environment = new Environment(); +    await environment.prepare(); +    const {browser, platform} = environment.getInfo(); + +    dataset.browser = browser; +    dataset.os = platform.os;  }  async function setupGenericSettingsController(genericSettingController) { @@ -67,9 +73,20 @@ async function setupGenericSettingsController(genericSettingController) {          const statusFooter = new StatusFooter(document.querySelector('.status-footer-container'));          statusFooter.prepare(); +        setupEnvironmentInfo(); + +        let prepareTimer = setTimeout(() => { +            prepareTimer = null; +            document.documentElement.dataset.loadingStalled = 'true'; +        }, 1000); +          await yomichan.prepare(); -        setupEnvironmentInfo(); +        if (prepareTimer !== null) { +            clearTimeout(prepareTimer); +            prepareTimer = null; +        } +        delete document.documentElement.dataset.loadingStalled;          const optionsFull = await yomichan.api.optionsGetFull(); diff --git a/ext/settings.html b/ext/settings.html index d629e6a6..732a96e2 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -1,5 +1,5 @@  <!DOCTYPE html> -<html lang="en"> +<html lang="en" data-browser="unknown" data-os="unknown" data-manifest-version="unknown">  <head>      <meta charset="UTF-8">      <meta name="viewport" content="width=device-width,initial-scale=1"> @@ -59,6 +59,23 @@      <h1>Yomichan Settings</h1> +    <!-- Notifications --> +    <div class="settings-group settings-group-top-margin page-loading-stalled-notification"> +        <div class="settings-item"><div class="settings-item-inner settings-item-inner-wrappable"> +            <div class="settings-item-left"> +                <div class="settings-item-label"> +                    <p> +                        This page is taking longer than expected to load. +                    </p> +                    <p data-show-for-browser="safari"> +                        Due to a bug in Safari, it may be necessary to click the <img src="/images/yomichan-icon.svg" class="inline-icon" alt=""> <em>Yomichan</em> +                        button in the browser bar to fully load the page +                    </p> +                </div> +            </div> +        </div></div> +    </div> +      <!-- Profile -->      <div class="heading-container">          <div class="heading-container-icon"><span class="icon" data-icon="profile"></span></div> |