aboutsummaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2024-02-27 07:23:42 -0500
committerGitHub <noreply@github.com>2024-02-27 12:23:42 +0000
commite47a0f488f3d9bbcb76ebcf4f5afe203c1ee06c0 (patch)
tree35a4a7411dd3154b19316b330c4976f291f021d2 /ext/js
parente74fadc5a411e907da088729ea13e23e6f5aa58d (diff)
Object utilities (#729)
* Create utilities * Rename old isObject * Use new isObject * Remove old function * Add additional function * Simplify for now * Rename function for clarity * Rename function * Expand type * Update eslint
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/background/backend.js15
-rw-r--r--ext/js/background/offscreen-proxy.js4
-rw-r--r--ext/js/comm/frame-client.js7
-rw-r--r--ext/js/core/object-utilities.js32
-rw-r--r--ext/js/core/utilities.js9
-rw-r--r--ext/js/data/anki-util.js4
-rw-r--r--ext/js/data/options-util.js7
-rw-r--r--ext/js/display/display-generator.js3
-rw-r--r--ext/js/display/display-history.js9
-rw-r--r--ext/js/input/hotkey-help-controller.js4
-rw-r--r--ext/js/pages/settings/backup-controller.js16
-rw-r--r--ext/js/pages/settings/extension-keyboard-shortcuts-controller.js4
-rw-r--r--ext/js/pages/settings/persistent-storage-controller.js4
-rw-r--r--ext/js/pages/settings/settings-controller.js5
14 files changed, 75 insertions, 48 deletions
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);
}
}