diff options
| -rw-r--r-- | .eslintrc.json | 1 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 15 | ||||
| -rw-r--r-- | ext/js/background/offscreen-proxy.js | 4 | ||||
| -rw-r--r-- | ext/js/comm/frame-client.js | 7 | ||||
| -rw-r--r-- | ext/js/core/object-utilities.js | 32 | ||||
| -rw-r--r-- | ext/js/core/utilities.js | 9 | ||||
| -rw-r--r-- | ext/js/data/anki-util.js | 4 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 7 | ||||
| -rw-r--r-- | ext/js/display/display-generator.js | 3 | ||||
| -rw-r--r-- | ext/js/display/display-history.js | 9 | ||||
| -rw-r--r-- | ext/js/input/hotkey-help-controller.js | 4 | ||||
| -rw-r--r-- | ext/js/pages/settings/backup-controller.js | 16 | ||||
| -rw-r--r-- | ext/js/pages/settings/extension-keyboard-shortcuts-controller.js | 4 | ||||
| -rw-r--r-- | ext/js/pages/settings/persistent-storage-controller.js | 4 | ||||
| -rw-r--r-- | ext/js/pages/settings/settings-controller.js | 5 | 
15 files changed, 76 insertions, 48 deletions
| diff --git a/.eslintrc.json b/.eslintrc.json index 86fa06e0..d7357173 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -622,6 +622,7 @@                  "ext/js/core/json.js",                  "ext/js/core/log-utilities.js",                  "ext/js/core/log.js", +                "ext/js/core/object-utilities.js",                  "ext/js/core/to-error.js",                  "ext/js/core/utilities.js",                  "ext/js/data/anki-util.js", diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index dbcbdd62..d19c3b45 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -26,7 +26,8 @@ import {ExtensionError} from '../core/extension-error.js';  import {fetchJson, fetchText} from '../core/fetch-utilities.js';  import {logErrorLevelToNumber} from '../core/log-utilities.js';  import {log} from '../core/log.js'; -import {clone, deferPromise, isObject, promiseTimeout} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js'; +import {clone, deferPromise, promiseTimeout} from '../core/utilities.js';  import {isNoteDataValid} from '../data/anki-util.js';  import {OptionsUtil} from '../data/options-util.js';  import {getAllPermissions, hasPermissions, hasRequiredPermissionsForOptions} from '../data/permissions-util.js'; @@ -222,12 +223,12 @@ export class Backend {       * @returns {void}       */      _prepareInternalSync() { -        if (isObject(chrome.commands) && isObject(chrome.commands.onCommand)) { +        if (isObjectNotArray(chrome.commands) && isObjectNotArray(chrome.commands.onCommand)) {              const onCommand = this._onWebExtensionEventWrapper(this._onCommand.bind(this));              chrome.commands.onCommand.addListener(onCommand);          } -        if (isObject(chrome.tabs) && isObject(chrome.tabs.onZoomChange)) { +        if (isObjectNotArray(chrome.tabs) && isObjectNotArray(chrome.tabs.onZoomChange)) {              const onZoomChange = this._onWebExtensionEventWrapper(this._onZoomChange.bind(this));              chrome.tabs.onZoomChange.addListener(onZoomChange);          } @@ -1094,7 +1095,7 @@ export class Backend {          }          // chrome.windows not supported (e.g. on Firefox mobile) -        if (!isObject(chrome.windows)) { +        if (!isObjectNotArray(chrome.windows)) {              throw new Error('Window creation not supported');          } @@ -1561,7 +1562,7 @@ export class Backend {       */      _getBrowserIconTitle() {          return ( -            isObject(chrome.action) && +            isObjectNotArray(chrome.action) &&              typeof chrome.action.getTitle === 'function' ?                  new Promise((resolve) => { chrome.action.getTitle({}, resolve); }) :                  Promise.resolve('') @@ -1573,7 +1574,7 @@ export class Backend {       */      _updateBadge() {          let title = this._defaultBrowserActionTitle; -        if (title === null || !isObject(chrome.action)) { +        if (title === null || !isObjectNotArray(chrome.action)) {              // Not ready or invalid              return;          } @@ -2580,7 +2581,7 @@ export class Backend {       * @returns {boolean}       */      _canObservePermissionsChanges() { -        return isObject(chrome.permissions) && isObject(chrome.permissions.onAdded) && isObject(chrome.permissions.onRemoved); +        return isObjectNotArray(chrome.permissions) && isObjectNotArray(chrome.permissions.onAdded) && isObjectNotArray(chrome.permissions.onRemoved);      }      /** diff --git a/ext/js/background/offscreen-proxy.js b/ext/js/background/offscreen-proxy.js index 102a9eed..59d1291e 100644 --- a/ext/js/background/offscreen-proxy.js +++ b/ext/js/background/offscreen-proxy.js @@ -17,7 +17,7 @@   */  import {ExtensionError} from '../core/extension-error.js'; -import {isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js';  import {base64ToArrayBuffer} from '../data/sandbox/array-buffer-util.js';  /** @@ -123,7 +123,7 @@ export class OffscreenProxy {          if (typeof runtimeError !== 'undefined') {              throw new Error(runtimeError.message);          } -        if (!isObject(response)) { +        if (!isObjectNotArray(response)) {              throw new Error('Offscreen document did not respond');          }          const responseError = response.error; diff --git a/ext/js/comm/frame-client.js b/ext/js/comm/frame-client.js index 3a41ab0f..a30efa29 100644 --- a/ext/js/comm/frame-client.js +++ b/ext/js/comm/frame-client.js @@ -16,7 +16,8 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import {deferPromise, generateId, isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js'; +import {deferPromise, generateId} from '../core/utilities.js';  export class FrameClient {      constructor() { @@ -121,9 +122,9 @@ export class FrameClient {               */              const onMessageInner = async (message) => {                  try { -                    if (!isObject(message)) { return; } +                    if (!isObjectNotArray(message)) { return; }                      const {action, params} = message; -                    if (!isObject(params)) { return; } +                    if (!isObjectNotArray(params)) { return; }                      await frameLoadedPromise;                      if (timer === null) { return; } // Done diff --git a/ext/js/core/object-utilities.js b/ext/js/core/object-utilities.js new file mode 100644 index 00000000..8cbba7cd --- /dev/null +++ b/ext/js/core/object-utilities.js @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024  Yomitan 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/>. + */ + +/** + * @param {unknown} value + * @returns {value is Record<string, unknown>} + */ +export function isObjectNotArray(value) { +    return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +/** + * @param {unknown} value + * @returns {value is Record<string|number|symbol, unknown>} + */ +export function isObject(value) { +    return typeof value === 'object' && value !== null; +} diff --git a/ext/js/core/utilities.js b/ext/js/core/utilities.js index 1b785e79..1428a744 100644 --- a/ext/js/core/utilities.js +++ b/ext/js/core/utilities.js @@ -17,15 +17,6 @@   */  /** - * Checks whether a given value is a non-array object. - * @param {unknown} value The value to check. - * @returns {boolean} `true` if the value is an object and not an array, `false` otherwise. - */ -export function isObject(value) { -    return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -/**   * Converts any string into a form that can be passed into the RegExp constructor.   * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions   * @param {string} string The string to convert to a valid regular expression. diff --git a/ext/js/data/anki-util.js b/ext/js/data/anki-util.js index 123e5d2f..eea3fb8b 100644 --- a/ext/js/data/anki-util.js +++ b/ext/js/data/anki-util.js @@ -16,7 +16,7 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import {isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js';  /** @type {RegExp} @readonly */  const markerPattern = /\{([\w-]+)\}/g; @@ -74,7 +74,7 @@ export function cloneFieldMarkerPattern(global) {   * @returns {boolean} `true` if the note is valid, `false` otherwise.   */  export function isNoteDataValid(note) { -    if (!isObject(note)) { return false; } +    if (!isObjectNotArray(note)) { return false; }      const {fields, deckName, modelName} = note;      return (          typeof deckName === 'string' && diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index de30f52a..f2d085fe 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -18,7 +18,8 @@  import {fetchJson, fetchText} from '../core/fetch-utilities.js';  import {parseJson} from '../core/json.js'; -import {escapeRegExp, isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js'; +import {escapeRegExp} from '../core/utilities.js';  import {TemplatePatcher} from '../templates/template-patcher.js';  import {JsonSchema} from './json-schema.js'; @@ -70,7 +71,7 @@ export class OptionsUtil {          // Remove invalid profiles          const profiles = /** @type {unknown[]} */ (options.profiles);          for (let i = profiles.length - 1; i >= 0; --i) { -            if (!isObject(profiles[i])) { +            if (!isObjectNotArray(profiles[i])) {                  profiles.splice(i, 1);              }          } @@ -1102,7 +1103,7 @@ export class OptionsUtil {              }          } -        if (customTemplates && isObject(chrome.storage)) { +        if (customTemplates && isObjectNotArray(chrome.storage)) {              void chrome.storage.session.set({needsCustomTemplatesWarning: true});              await this._createTab(chrome.runtime.getURL('/welcome.html'));              void chrome.storage.session.set({openedWelcomePage: true}); diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index 0b3236e9..0caf4d71 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -17,7 +17,6 @@   */  import {ExtensionError} from '../core/extension-error.js'; -import {isObject} from '../core/utilities.js';  import {getDisambiguations, getGroupedPronunciations, getTermFrequency, groupKanjiFrequencies, groupTermFrequencies, groupTermTags, isNonNounVerbOrAdjective} from '../dictionary/dictionary-data-util.js';  import {HtmlTemplateCollection} from '../dom/html-template-collection.js';  import {distributeFurigana, getKanaMorae, getPitchCategory, isCodePointKanji} from '../language/ja/japanese.js'; @@ -257,7 +256,7 @@ export class DisplayGenerator {              if (error instanceof DocumentFragment || error instanceof Node) {                  div.appendChild(error);              } else { -                let message = isObject(error) && typeof error.message === 'string' ? error.message : `${error}`; +                let message = error.message;                  let link = null;                  if (error instanceof ExtensionError && error.data !== null && typeof error.data === 'object') {                      const {referenceUrl} = /** @type {import('core').UnknownObject} */ (error.data); diff --git a/ext/js/display/display-history.js b/ext/js/display/display-history.js index bc4f1539..183368d0 100644 --- a/ext/js/display/display-history.js +++ b/ext/js/display/display-history.js @@ -17,7 +17,8 @@   */  import {EventDispatcher} from '../core/event-dispatcher.js'; -import {generateId, isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js'; +import {generateId} from '../core/utilities.js';  /**   * @augments EventDispatcher<import('display-history').Events> @@ -38,12 +39,12 @@ export class DisplayHistory extends EventDispatcher {          const historyState = history.state;          const {id, state} = ( -            typeof historyState === 'object' && historyState !== null ? +            isObjectNotArray(historyState) ?              historyState :              {id: null, state: null}          );          /** @type {?import('display-history').EntryState} */ -        const stateObject = typeof state === 'object' || state === null ? state : null; +        const stateObject = isObjectNotArray(state) ? state : null;          /** @type {import('display-history').Entry} */          this._current = this._createHistoryEntry(id, location.href, stateObject, null, null);      } @@ -189,7 +190,7 @@ export class DisplayHistory extends EventDispatcher {      _updateStateFromHistory() {          let state = history.state;          let id = null; -        if (isObject(state)) { +        if (isObjectNotArray(state)) {              id = state.id;              if (typeof id === 'string') {                  const entry = this._historyMap.get(id); diff --git a/ext/js/input/hotkey-help-controller.js b/ext/js/input/hotkey-help-controller.js index 1fa9372f..16f3d26f 100644 --- a/ext/js/input/hotkey-help-controller.js +++ b/ext/js/input/hotkey-help-controller.js @@ -17,7 +17,7 @@   */  import {parseJson} from '../core/json.js'; -import {isObject} from '../core/utilities.js'; +import {isObjectNotArray} from '../core/object-utilities.js';  import {HotkeyUtil} from './hotkey-util.js';  export class HotkeyHelpController { @@ -93,7 +93,7 @@ export class HotkeyHelpController {       */      _getAllCommands() {          return new Promise((resolve, reject) => { -            if (!(isObject(chrome.commands) && typeof chrome.commands.getAll === 'function')) { +            if (!(isObjectNotArray(chrome.commands) && typeof chrome.commands.getAll === 'function')) {                  resolve([]);                  return;              } diff --git a/ext/js/pages/settings/backup-controller.js b/ext/js/pages/settings/backup-controller.js index 2a0706e4..dd739d39 100644 --- a/ext/js/pages/settings/backup-controller.js +++ b/ext/js/pages/settings/backup-controller.js @@ -19,8 +19,8 @@  import {Dexie} from '../../../lib/dexie.js';  import {parseJson} from '../../core/json.js';  import {log} from '../../core/log.js'; +import {isObjectNotArray} from '../../core/object-utilities.js';  import {toError} from '../../core/to-error.js'; -import {isObject} from '../../core/utilities.js';  import {OptionsUtil} from '../../data/options-util.js';  import {getAllPermissions} from '../../data/permissions-util.js';  import {arrayBufferUtf8Decode} from '../../data/sandbox/array-buffer-util.js'; @@ -354,7 +354,7 @@ export class BackupController {          const warnings = [];          const anki = options.anki; -        if (isObject(anki)) { +        if (isObjectNotArray(anki)) {              const fieldTemplates = anki.fieldTemplates;              if (typeof fieldTemplates === 'string') {                  warnings.push('anki.fieldTemplates contains a non-default value'); @@ -372,12 +372,12 @@ export class BackupController {          }          const audio = options.audio; -        if (isObject(audio)) { +        if (isObjectNotArray(audio)) {              const sources = audio.sources;              if (Array.isArray(sources)) {                  for (let i = 0, ii = sources.length; i < ii; ++i) {                      const source = sources[i]; -                    if (!isObject(source)) { continue; } +                    if (!isObjectNotArray(source)) { continue; }                      const {url} = source;                      if (typeof url === 'string' && url.length > 0 && !this._isLocalhostUrl(url)) {                          warnings.push(`audio.sources[${i}].url uses a non-localhost URL`); @@ -403,9 +403,9 @@ export class BackupController {          const profiles = optionsFull.profiles;          if (Array.isArray(profiles)) {              for (const profile of profiles) { -                if (!isObject(profile)) { continue; } +                if (!isObjectNotArray(profile)) { continue; }                  const options = profile.options; -                if (!isObject(options)) { continue; } +                if (!isObjectNotArray(options)) { continue; }                  const warnings2 = this._settingsImportSanitizeProfileOptions(options, dryRun);                  for (const warning of warnings2) { @@ -428,7 +428,7 @@ export class BackupController {          const data = parseJson(dataString);          // Type check -        if (!isObject(data)) { +        if (!isObjectNotArray(data)) {              throw new Error(`Invalid data type: ${typeof data}`);          } @@ -451,7 +451,7 @@ export class BackupController {          // Verify options exists          let optionsFull = data.options; -        if (!isObject(optionsFull)) { +        if (!isObjectNotArray(optionsFull)) {              throw new Error(`Invalid options type: ${typeof optionsFull}`);          } diff --git a/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js b/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js index 1f55f391..f19a75fa 100644 --- a/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js @@ -17,7 +17,7 @@   */  import {EventListenerCollection} from '../../core/event-listener-collection.js'; -import {isObject} from '../../core/utilities.js'; +import {isObjectNotArray} from '../../core/object-utilities.js';  import {querySelectorNotNull} from '../../dom/query-selector.js';  import {HotkeyUtil} from '../../input/hotkey-util.js';  import {KeyboardMouseInputField} from './keyboard-mouse-input-field.js'; @@ -149,7 +149,7 @@ export class ExtensionKeyboardShortcutController {       */      _getCommands() {          return new Promise((resolve, reject) => { -            if (!(isObject(chrome.commands) && typeof chrome.commands.getAll === 'function')) { +            if (!(isObjectNotArray(chrome.commands) && typeof chrome.commands.getAll === 'function')) {                  resolve([]);                  return;              } diff --git a/ext/js/pages/settings/persistent-storage-controller.js b/ext/js/pages/settings/persistent-storage-controller.js index e0ff75ee..c2508d90 100644 --- a/ext/js/pages/settings/persistent-storage-controller.js +++ b/ext/js/pages/settings/persistent-storage-controller.js @@ -16,7 +16,7 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import {isObject} from '../../core/utilities.js'; +import {isObjectNotArray} from '../../core/object-utilities.js';  import {querySelectorNotNull} from '../../dom/query-selector.js';  export class PersistentStorageController { @@ -98,7 +98,7 @@ export class PersistentStorageController {       * @returns {boolean}       */      _isPersistentStorageSupported() { -        return isObject(navigator.storage) && typeof navigator.storage.persist === 'function'; +        return isObjectNotArray(navigator.storage) && typeof navigator.storage.persist === 'function';      }      /** diff --git a/ext/js/pages/settings/settings-controller.js b/ext/js/pages/settings/settings-controller.js index a9f3a026..ee44f875 100644 --- a/ext/js/pages/settings/settings-controller.js +++ b/ext/js/pages/settings/settings-controller.js @@ -18,7 +18,8 @@  import {EventDispatcher} from '../../core/event-dispatcher.js';  import {EventListenerCollection} from '../../core/event-listener-collection.js'; -import {generateId, isObject} from '../../core/utilities.js'; +import {isObjectNotArray} from '../../core/object-utilities.js'; +import {generateId} from '../../core/utilities.js';  import {OptionsUtil} from '../../data/options-util.js';  import {getAllPermissions} from '../../data/permissions-util.js';  import {HtmlTemplateCollection} from '../../dom/html-template-collection.js'; @@ -353,6 +354,6 @@ export class SettingsController extends EventDispatcher {       * @returns {boolean}       */      _canObservePermissionsChanges() { -        return isObject(chrome.permissions) && isObject(chrome.permissions.onAdded) && isObject(chrome.permissions.onRemoved); +        return isObjectNotArray(chrome.permissions) && isObjectNotArray(chrome.permissions.onAdded) && isObjectNotArray(chrome.permissions.onRemoved);      }  } |