aboutsummaryrefslogtreecommitdiff
path: root/ext/js/display
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-12-16 19:55:02 -0500
committerGitHub <noreply@github.com>2023-12-17 00:55:02 +0000
commit95ad1ae1ef4a53802c12eab4c9b1545af0333aa1 (patch)
treec08016ccb3265a5931baf413cd9d78fcbf600ae9 /ext/js/display
parent77d27113d347b4724302f1c72de1f238e04aeead (diff)
Safer query selector (#364)
* Add querySelectorNotNull helper function * Use querySelectorNotNull * Updates * Update settings * Remove unused * Update * Update function calls * More updates * Update types * Remove obsolete code
Diffstat (limited to 'ext/js/display')
-rw-r--r--ext/js/display/display-anki.js18
-rw-r--r--ext/js/display/display-audio.js18
-rw-r--r--ext/js/display/display-notification.js5
-rw-r--r--ext/js/display/display-profile-selection.js15
-rw-r--r--ext/js/display/display.js40
-rw-r--r--ext/js/display/query-parser.js7
-rw-r--r--ext/js/display/search-display-controller.js13
7 files changed, 71 insertions, 45 deletions
diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js
index 574e90ee..81791285 100644
--- a/ext/js/display/display-anki.js
+++ b/ext/js/display/display-anki.js
@@ -20,6 +20,7 @@ import {EventListenerCollection, deferPromise} from '../core.js';
import {AnkiNoteBuilder} from '../data/anki-note-builder.js';
import {AnkiUtil} from '../data/anki-util.js';
import {PopupMenu} from '../dom/popup-menu.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';
export class DisplayAnki {
@@ -91,7 +92,7 @@ export class DisplayAnki {
['term', ['term-kanji', 'term-kana']]
]);
/** @type {HTMLElement} */
- this._menuContainer = /** @type {HTMLElement} */ (document.querySelector('#popup-menus'));
+ this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {(event: MouseEvent) => void} */
this._onShowTagsBind = this._onShowTags.bind(this);
/** @type {(event: MouseEvent) => void} */
@@ -827,7 +828,8 @@ export class DisplayAnki {
button.hidden = disabled;
button.dataset.noteIds = allNoteIds.join(' ');
- const badge = /** @type {?HTMLElement} */ (button.querySelector('.action-button-badge'));
+ /** @type {?HTMLElement} */
+ const badge = button.querySelector('.action-button-badge');
if (badge !== null) {
const badgeData = badge.dataset;
if (allNoteIds.length > 1) {
@@ -866,13 +868,17 @@ export class DisplayAnki {
const noteIds = this._getNodeNoteIds(node);
if (noteIds.length === 0) { return; }
- const menuContainerNode = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu'));
- const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
+ /** @type {HTMLElement} */
+ const menuContainerNode = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu');
+ /** @type {HTMLElement} */
+ const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');
for (let i = 0, ii = noteIds.length; i < ii; ++i) {
const noteId = noteIds[i];
- const item = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu-item'));
- const label = /** @type {Element} */ (item.querySelector('.popup-menu-item-label'));
+ /** @type {HTMLElement} */
+ const item = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu-item');
+ /** @type {Element} */
+ const label = querySelectorNotNull(item, '.popup-menu-item-label');
label.textContent = `Note ${i + 1}: ${noteId}`;
item.dataset.menuAction = 'viewNote';
item.dataset.noteIds = `${noteId}`;
diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js
index 3576decb..1f279030 100644
--- a/ext/js/display/display-audio.js
+++ b/ext/js/display/display-audio.js
@@ -18,6 +18,7 @@
import {EventListenerCollection} from '../core.js';
import {PopupMenu} from '../dom/popup-menu.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {AudioSystem} from '../media/audio-system.js';
import {yomitan} from '../yomitan.js';
@@ -45,7 +46,7 @@ export class DisplayAudio {
/** @type {Map<string, import('display-audio').CacheItem>} */
this._cache = new Map();
/** @type {Element} */
- this._menuContainer = /** @type {Element} */ (document.querySelector('#popup-menus'));
+ this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {import('core').TokenObject} */
this._entriesToken = {};
/** @type {Set<PopupMenu>} */
@@ -715,7 +716,8 @@ export class DisplayAudio {
button.dataset.potentialAvailableAudioCount = `${potentialAvailableAudioCount}`;
}
- const badge = /** @type {?HTMLElement} */ (button.querySelector('.action-button-badge'));
+ /** @type {?HTMLElement} */
+ const badge = button.querySelector('.action-button-badge');
if (badge === null) { return; }
const badgeData = badge.dataset;
@@ -804,7 +806,8 @@ export class DisplayAudio {
_createMenu(sourceButton, term, reading) {
// Create menu
const menuContainerNode = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('audio-button-popup-menu'));
- const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
+ /** @type {HTMLElement} */
+ const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');
menuContainerNode.dataset.term = term;
menuContainerNode.dataset.reading = reading;
@@ -837,7 +840,8 @@ export class DisplayAudio {
const existingNode = this._getOrCreateMenuItem(currentItems, index, subIndex);
const node = existingNode !== null ? existingNode : /** @type {HTMLElement} */ (displayGenerator.instantiateTemplate('audio-button-popup-menu-item'));
- const labelNode = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-audio-button .popup-menu-item-label'));
+ /** @type {HTMLElement} */
+ const labelNode = querySelectorNotNull(node, '.popup-menu-item-audio-button .popup-menu-item-label');
let label = name;
if (!nameUnique) {
label = `${label} ${nameIndex + 1}`;
@@ -847,11 +851,13 @@ export class DisplayAudio {
if (typeof subName === 'string' && subName.length > 0) { label += `: ${subName}`; }
labelNode.textContent = label;
- const cardButton = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-set-primary-audio-button'));
+ /** @type {HTMLElement} */
+ const cardButton = querySelectorNotNull(node, '.popup-menu-item-set-primary-audio-button');
cardButton.hidden = !downloadable;
if (valid !== null) {
- const icon = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-audio-button .popup-menu-item-icon'));
+ /** @type {HTMLElement} */
+ const icon = querySelectorNotNull(node, '.popup-menu-item-audio-button .popup-menu-item-icon');
icon.dataset.icon = valid ? 'checkmark' : 'cross';
showIcons = true;
}
diff --git a/ext/js/display/display-notification.js b/ext/js/display/display-notification.js
index d1cfa537..a0e9e35c 100644
--- a/ext/js/display/display-notification.js
+++ b/ext/js/display/display-notification.js
@@ -17,6 +17,7 @@
*/
import {EventListenerCollection} from '../core.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
export class DisplayNotification {
/**
@@ -29,9 +30,9 @@ export class DisplayNotification {
/** @type {HTMLElement} */
this._node = node;
/** @type {HTMLElement} */
- this._body = /** @type {HTMLElement} */ (node.querySelector('.footer-notification-body'));
+ this._body = querySelectorNotNull(node, '.footer-notification-body');
/** @type {HTMLElement} */
- this._closeButton = /** @type {HTMLElement} */ (node.querySelector('.footer-notification-close-button'));
+ this._closeButton = querySelectorNotNull(node, '.footer-notification-close-button');
/** @type {EventListenerCollection} */
this._eventListeners = new EventListenerCollection();
/** @type {?import('core').Timeout} */
diff --git a/ext/js/display/display-profile-selection.js b/ext/js/display/display-profile-selection.js
index c5cb7d06..f2ebd3e4 100644
--- a/ext/js/display/display-profile-selection.js
+++ b/ext/js/display/display-profile-selection.js
@@ -18,6 +18,7 @@
import {EventListenerCollection, generateId} from '../core.js';
import {PanelElement} from '../dom/panel-element.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';
export class DisplayProfileSelection {
@@ -28,12 +29,14 @@ export class DisplayProfileSelection {
/** @type {import('./display.js').Display} */
this._display = display;
/** @type {HTMLElement} */
- this._profielList = /** @type {HTMLElement} */ (document.querySelector('#profile-list'));
+ this._profielList = querySelectorNotNull(document, '#profile-list');
/** @type {HTMLButtonElement} */
- this._profileButton = /** @type {HTMLButtonElement} */ (document.querySelector('#profile-button'));
+ this._profileButton = querySelectorNotNull(document, '#profile-button');
+ /** @type {HTMLElement} */
+ const profilePanelElement = querySelectorNotNull(document, '#profile-panel');
/** @type {PanelElement} */
this._profilePanel = new PanelElement({
- node: /** @type {HTMLElement} */ (document.querySelector('#profile-panel')),
+ node: profilePanelElement,
closingAnimationDuration: 375 // Milliseconds; includes buffer
});
/** @type {boolean} */
@@ -98,9 +101,11 @@ export class DisplayProfileSelection {
for (let i = 0, ii = profiles.length; i < ii; ++i) {
const {name} = profiles[i];
const entry = displayGenerator.createProfileListItem();
- const radio = /** @type {HTMLInputElement} */ (entry.querySelector('.profile-entry-is-default-radio'));
+ /** @type {HTMLInputElement} */
+ const radio = querySelectorNotNull(entry, '.profile-entry-is-default-radio');
radio.checked = (i === profileCurrent);
- const nameNode = /** @type {Element} */ (entry.querySelector('.profile-list-item-name'));
+ /** @type {Element} */
+ const nameNode = querySelectorNotNull(entry, '.profile-list-item-name');
nameNode.textContent = name;
fragment.appendChild(entry);
this._eventListeners.addEventListener(radio, 'change', this._onProfileRadioChange.bind(this, i), false);
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index 9c2d97f2..b6a818ba 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -22,6 +22,7 @@ import {ThemeController} from '../app/theme-controller.js';
import {FrameEndpoint} from '../comm/frame-endpoint.js';
import {DynamicProperty, EventDispatcher, EventListenerCollection, clone, deepEqual, invokeMessageHandler, log, promiseTimeout} from '../core.js';
import {PopupMenu} from '../dom/popup-menu.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {ScrollElement} from '../dom/scroll-element.js';
import {HotkeyHelpController} from '../input/hotkey-help-controller.js';
import {TextScanner} from '../language/text-scanner.js';
@@ -62,7 +63,7 @@ export class Display extends EventDispatcher {
/** @type {import('../input/hotkey-handler.js').HotkeyHandler} */
this._hotkeyHandler = hotkeyHandler;
/** @type {HTMLElement} */
- this._container = /** @type {HTMLElement} */ (document.querySelector('#dictionary-entries'));
+ this._container = querySelectorNotNull(document, '#dictionary-entries');
/** @type {import('dictionary').DictionaryEntry[]} */
this._dictionaryEntries = [];
/** @type {HTMLElement[]} */
@@ -116,7 +117,7 @@ export class Display extends EventDispatcher {
/** @type {number} */
this._queryOffset = 0;
/** @type {HTMLElement} */
- this._progressIndicator = /** @type {HTMLElement} */ (document.querySelector('#progress-indicator'));
+ this._progressIndicator = querySelectorNotNull(document, '#progress-indicator');
/** @type {?import('core').Timeout} */
this._progressIndicatorTimer = null;
/** @type {DynamicProperty<boolean>} */
@@ -126,24 +127,24 @@ export class Display extends EventDispatcher {
/** @type {?boolean} */
this._queryParserVisibleOverride = null;
/** @type {HTMLElement} */
- this._queryParserContainer = /** @type {HTMLElement} */ (document.querySelector('#query-parser-container'));
+ this._queryParserContainer = querySelectorNotNull(document, '#query-parser-container');
/** @type {QueryParser} */
this._queryParser = new QueryParser({
getSearchContext: this._getSearchContext.bind(this),
japaneseUtil
});
/** @type {HTMLElement} */
- this._contentScrollElement = /** @type {HTMLElement} */ (document.querySelector('#content-scroll'));
+ this._contentScrollElement = querySelectorNotNull(document, '#content-scroll');
/** @type {HTMLElement} */
- this._contentScrollBodyElement = /** @type {HTMLElement} */ (document.querySelector('#content-body'));
+ this._contentScrollBodyElement = querySelectorNotNull(document, '#content-body');
/** @type {ScrollElement} */
this._windowScroll = new ScrollElement(this._contentScrollElement);
- /** @type {HTMLButtonElement} */
- this._closeButton = /** @type {HTMLButtonElement} */ (document.querySelector('#close-button'));
- /** @type {HTMLButtonElement} */
- this._navigationPreviousButton = /** @type {HTMLButtonElement} */ (document.querySelector('#navigate-previous-button'));
- /** @type {HTMLButtonElement} */
- this._navigationNextButton = /** @type {HTMLButtonElement} */ (document.querySelector('#navigate-next-button'));
+ /** @type {?HTMLButtonElement} */
+ this._closeButton = document.querySelector('#close-button');
+ /** @type {?HTMLButtonElement} */
+ this._navigationPreviousButton = document.querySelector('#navigate-previous-button');
+ /** @type {?HTMLButtonElement} */
+ this._navigationNextButton = document.querySelector('#navigate-next-button');
/** @type {?Frontend} */
this._frontend = null;
/** @type {?Promise<void>} */
@@ -171,7 +172,7 @@ export class Display extends EventDispatcher {
/** @type {?import('./display-notification.js').DisplayNotification} */
this._tagNotification = null;
/** @type {HTMLElement} */
- this._footerNotificationContainer = /** @type {HTMLElement} */ (document.querySelector('#content-footer'));
+ this._footerNotificationContainer = querySelectorNotNull(document, '#content-footer');
/** @type {OptionToggleHotkeyHandler} */
this._optionToggleHotkeyHandler = new OptionToggleHotkeyHandler(this);
/** @type {ElementOverflowController} */
@@ -179,7 +180,7 @@ export class Display extends EventDispatcher {
/** @type {boolean} */
this._frameVisible = (pageType === 'search');
/** @type {HTMLElement} */
- this._menuContainer = /** @type {HTMLElement} */ (document.querySelector('#popup-menus'));
+ this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {(event: MouseEvent) => void} */
this._onEntryClickBind = this._onEntryClick.bind(this);
/** @type {(event: MouseEvent) => void} */
@@ -1041,7 +1042,8 @@ export class Display extends EventDispatcher {
const node = /** @type {HTMLElement} */ (e.currentTarget);
const menuContainerNode = /** @type {HTMLElement} */ (this._displayGenerator.instantiateTemplate('dictionary-entry-popup-menu'));
- const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
+ /** @type {HTMLElement} */
+ const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');
/**
* @param {string} menuAction
@@ -1049,7 +1051,9 @@ export class Display extends EventDispatcher {
*/
const addItem = (menuAction, label) => {
const item = /** @type {HTMLElement} */ (this._displayGenerator.instantiateTemplate('dictionary-entry-popup-menu-item'));
- /** @type {HTMLElement} */ (item.querySelector('.popup-menu-item-label')).textContent = label;
+ /** @type {HTMLElement} */
+ const labelElement = querySelectorNotNull(item, '.popup-menu-item-label');
+ labelElement.textContent = label;
item.dataset.menuAction = menuAction;
menuBodyNode.appendChild(item);
};
@@ -1291,7 +1295,8 @@ export class Display extends EventDispatcher {
/** */
_setContentExtensionUnloaded() {
- const errorExtensionUnloaded = /** @type {?HTMLElement} */ (document.querySelector('#error-extension-unloaded'));
+ /** @type {?HTMLElement} */
+ const errorExtensionUnloaded = document.querySelector('#error-extension-unloaded');
if (this._container !== null) {
this._container.hidden = true;
@@ -1323,7 +1328,8 @@ export class Display extends EventDispatcher {
* @param {boolean} visible
*/
_setNoContentVisible(visible) {
- const noResults = /** @type {?HTMLElement} */ (document.querySelector('#no-results'));
+ /** @type {?HTMLElement} */
+ const noResults = document.querySelector('#no-results');
if (noResults !== null) {
noResults.hidden = !visible;
diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js
index 03b54fd5..e71c7251 100644
--- a/ext/js/display/query-parser.js
+++ b/ext/js/display/query-parser.js
@@ -17,6 +17,7 @@
*/
import {EventDispatcher, log} from '../core.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {TextScanner} from '../language/text-scanner.js';
import {yomitan} from '../yomitan.js';
@@ -50,11 +51,11 @@ export class QueryParser extends EventDispatcher {
/** @type {import('api').ParseTextResult} */
this._parseResults = [];
/** @type {HTMLElement} */
- this._queryParser = /** @type {HTMLElement} */ (document.querySelector('#query-parser-content'));
+ this._queryParser = querySelectorNotNull(document, '#query-parser-content');
/** @type {HTMLElement} */
- this._queryParserModeContainer = /** @type {HTMLElement} */ (document.querySelector('#query-parser-mode-container'));
+ this._queryParserModeContainer = querySelectorNotNull(document, '#query-parser-mode-container');
/** @type {HTMLSelectElement} */
- this._queryParserModeSelect = /** @type {HTMLSelectElement} */ (document.querySelector('#query-parser-mode-select'));
+ this._queryParserModeSelect = querySelectorNotNull(document, '#query-parser-mode-select');
/** @type {TextScanner} */
this._textScanner = new TextScanner({
node: this._queryParser,
diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js
index 2778c4cd..b512a16d 100644
--- a/ext/js/display/search-display-controller.js
+++ b/ext/js/display/search-display-controller.js
@@ -19,6 +19,7 @@
import * as wanakana from '../../lib/wanakana.js';
import {ClipboardMonitor} from '../comm/clipboard-monitor.js';
import {EventListenerCollection, invokeMessageHandler} from '../core.js';
+import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';
export class SearchDisplayController {
@@ -42,17 +43,17 @@ export class SearchDisplayController {
/** @type {import('./search-persistent-state-controller.js').SearchPersistentStateController} */
this._searchPersistentStateController = searchPersistentStateController;
/** @type {HTMLButtonElement} */
- this._searchButton = /** @type {HTMLButtonElement} */ (document.querySelector('#search-button'));
+ this._searchButton = querySelectorNotNull(document, '#search-button');
/** @type {HTMLButtonElement} */
- this._searchBackButton = /** @type {HTMLButtonElement} */ (document.querySelector('#search-back-button'));
+ this._searchBackButton = querySelectorNotNull(document, '#search-back-button');
/** @type {HTMLTextAreaElement} */
- this._queryInput = /** @type {HTMLTextAreaElement} */ (document.querySelector('#search-textbox'));
+ this._queryInput = querySelectorNotNull(document, '#search-textbox');
/** @type {HTMLElement} */
- this._introElement = /** @type {HTMLElement} */ (document.querySelector('#intro'));
+ this._introElement = querySelectorNotNull(document, '#intro');
/** @type {HTMLInputElement} */
- this._clipboardMonitorEnableCheckbox = /** @type {HTMLInputElement} */ (document.querySelector('#clipboard-monitor-enable'));
+ this._clipboardMonitorEnableCheckbox = querySelectorNotNull(document, '#clipboard-monitor-enable');
/** @type {HTMLInputElement} */
- this._wanakanaEnableCheckbox = /** @type {HTMLInputElement} */ (document.querySelector('#wanakana-enable'));
+ this._wanakanaEnableCheckbox = querySelectorNotNull(document, '#wanakana-enable');
/** @type {EventListenerCollection} */
this._queryInputEvents = new EventListenerCollection();
/** @type {boolean} */