From 1e254fd1d4423b984e176547ef36a14383bbd7f5 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 28 Dec 2023 22:17:38 -0500 Subject: Event dispatcher refactor (#463) * Refactor EventDispatcher template type * Update core types * Update log * Update clipboard monitor * Update application events * Update popup events * Update text scanner * Update cross frame API * Update display events * Type updates * Update display history * Update query parser * Update search persistent state controller * Update panel element * Update popup menu * Update audio system * Update hotkey handler * Update settings controller * Update audio controller * Update types * Update types * Update types * Add event handler types * Update type * Fix issues * Remove error suppression * Fix typo --- ext/js/app/popup-proxy.js | 4 +- ext/js/app/popup-window.js | 2 +- ext/js/app/popup.js | 8 ++-- ext/js/background/backend.js | 2 +- ext/js/comm/clipboard-monitor.js | 4 +- ext/js/comm/cross-frame-api.js | 4 +- ext/js/core.js | 44 ++++++++++++++-------- ext/js/display/display-anki.js | 6 +-- ext/js/display/display-audio.js | 8 ++-- ext/js/display/display-history.js | 4 +- ext/js/display/display.js | 32 +++++----------- ext/js/display/query-parser.js | 8 ++-- ext/js/display/search-display-controller.js | 4 +- .../display/search-persistent-state-controller.js | 2 +- ext/js/dom/panel-element.js | 15 ++++---- ext/js/dom/popup-menu.js | 4 +- ext/js/input/hotkey-handler.js | 22 +++++------ ext/js/language/text-scanner.js | 4 +- ext/js/media/audio-system.js | 2 +- ext/js/pages/settings/anki-controller.js | 4 +- ext/js/pages/settings/anki-templates-controller.js | 2 +- ext/js/pages/settings/audio-controller.js | 6 +-- ext/js/pages/settings/backup-controller.js | 4 +- .../settings/collapsible-dictionary-controller.js | 2 +- ext/js/pages/settings/dictionary-controller.js | 6 +-- .../pages/settings/dictionary-import-controller.js | 2 +- .../extension-keyboard-shortcuts-controller.js | 2 +- .../pages/settings/keyboard-mouse-input-field.js | 6 +-- .../settings/keyboard-shortcuts-controller.js | 4 +- ext/js/pages/settings/nested-popups-controller.js | 2 +- .../settings/permissions-origin-controller.js | 2 +- .../settings/permissions-toggle-controller.js | 4 +- .../settings/persistent-storage-controller.js | 2 +- ext/js/pages/settings/popup-preview-frame.js | 2 +- ext/js/pages/settings/profile-conditions-ui.js | 33 +++++++--------- ext/js/pages/settings/profile-controller.js | 2 +- .../settings/recommended-permissions-controller.js | 2 +- ext/js/pages/settings/scan-inputs-controller.js | 10 ++--- .../settings/scan-inputs-simple-controller.js | 6 +-- .../secondary-search-dictionary-controller.js | 2 +- .../sentence-termination-characters-controller.js | 2 +- ext/js/pages/settings/settings-controller.js | 12 ++---- .../sort-frequency-dictionary-controller.js | 2 +- .../translation-text-replacements-controller.js | 2 +- ext/js/yomitan.js | 14 ++++++- 45 files changed, 154 insertions(+), 162 deletions(-) (limited to 'ext') diff --git a/ext/js/app/popup-proxy.js b/ext/js/app/popup-proxy.js index 924175e2..d141d35b 100644 --- a/ext/js/app/popup-proxy.js +++ b/ext/js/app/popup-proxy.js @@ -22,7 +22,7 @@ import {yomitan} from '../yomitan.js'; /** * This class is a proxy for a Popup that is hosted in a different frame. * It effectively forwards all API calls to the underlying Popup. - * @augments EventDispatcher + * @augments EventDispatcher */ export class PopupProxy extends EventDispatcher { /** @@ -361,7 +361,7 @@ export class PopupProxy extends EventDispatcher { } else { this._frameOffsetX = 0; this._frameOffsetY = 0; - this.trigger('offsetNotFound'); + this.trigger('offsetNotFound', {}); return; } this._frameOffsetUpdatedAt = now; diff --git a/ext/js/app/popup-window.js b/ext/js/app/popup-window.js index 9a0f8011..0b083d80 100644 --- a/ext/js/app/popup-window.js +++ b/ext/js/app/popup-window.js @@ -21,7 +21,7 @@ import {yomitan} from '../yomitan.js'; /** * This class represents a popup that is hosted in a new native window. - * @augments EventDispatcher + * @augments EventDispatcher */ export class PopupWindow extends EventDispatcher { /** diff --git a/ext/js/app/popup.js b/ext/js/app/popup.js index a0712604..4f2368d4 100644 --- a/ext/js/app/popup.js +++ b/ext/js/app/popup.js @@ -26,7 +26,7 @@ import {ThemeController} from './theme-controller.js'; /** * This class is the container which hosts the display of search results. - * @augments EventDispatcher + * @augments EventDispatcher */ export class Popup extends EventDispatcher { /** @@ -360,9 +360,7 @@ export class Popup extends EventDispatcher { parentNode = this._shadow; } const node = await loadStyle('yomitan-popup-outer-user-stylesheet', 'code', css, useWebExtensionApi, parentNode); - /** @type {import('popup').CustomOuterCssChangedEvent} */ - const event = {node, useWebExtensionApi, inShadow}; - this.trigger('customOuterCssChanged', event); + this.trigger('customOuterCssChanged', {node, useWebExtensionApi, inShadow}); } /** @@ -653,7 +651,7 @@ export class Popup extends EventDispatcher { } /** - * @param {import('dynamic-property').ChangeEventDetails} event + * @param {import('dynamic-property').EventArgument} event */ _onVisibleChange({value}) { if (this._visibleValue === value) { return; } diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 68d4e0c8..b523cd6e 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -315,7 +315,7 @@ export class Backend { // Event handlers /** - * @param {{text: string}} params + * @param {import('clipboard-monitor').EventArgument<'change'>} details */ async _onClipboardTextChange({text}) { const {clipboard: {maximumSearchLength}} = this._getProfileOptions({current: true}, false); diff --git a/ext/js/comm/clipboard-monitor.js b/ext/js/comm/clipboard-monitor.js index 3b3a56a9..d717dff2 100644 --- a/ext/js/comm/clipboard-monitor.js +++ b/ext/js/comm/clipboard-monitor.js @@ -19,7 +19,7 @@ import {EventDispatcher} from '../core.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class ClipboardMonitor extends EventDispatcher { /** @@ -71,7 +71,7 @@ export class ClipboardMonitor extends EventDispatcher { ) { this._previousText = text; if (canChange && this._japaneseUtil.isStringPartiallyJapanese(text)) { - this.trigger('change', /** @type {import('clipboard-monitor').ChangeEvent} */ ({text})); + this.trigger('change', {text}); } } diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js index 023ca18e..14e410a8 100644 --- a/ext/js/comm/cross-frame-api.js +++ b/ext/js/comm/cross-frame-api.js @@ -22,9 +22,9 @@ import {parseJson} from '../core/json.js'; import {yomitan} from '../yomitan.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ -class CrossFrameAPIPort extends EventDispatcher { +export class CrossFrameAPIPort extends EventDispatcher { /** * @param {number} otherTabId * @param {number} otherFrameId diff --git a/ext/js/core.js b/ext/js/core.js index 63c2f527..c9c989ac 100644 --- a/ext/js/core.js +++ b/ext/js/core.js @@ -353,22 +353,30 @@ export function invokeMessageHandler(handler, params, callback, ...extraArgs) { } /** - * @template {string} TEventName + * The following typedef is required because the JSDoc `implements` tag doesn't work with `import()`. + * https://github.com/microsoft/TypeScript/issues/49905 + * @typedef {import('core').EventDispatcherOffGeneric} EventDispatcherOffGeneric + */ + +/** * Base class controls basic event dispatching. + * @template {import('core').EventSurface} TSurface + * @implements {EventDispatcherOffGeneric} */ export class EventDispatcher { /** * Creates a new instance. */ constructor() { - /** @type {Map void)[]>} */ + /** @type {Map, import('core').EventHandlerAny[]>} */ this._eventMap = new Map(); } /** * Triggers an event with the given name and specified argument. - * @param {TEventName} eventName The string representing the event's name. - * @param {unknown} [details] The argument passed to the callback functions. + * @template {import('core').EventNames} TName + * @param {TName} eventName The string representing the event's name. + * @param {import('core').EventArgument} details The argument passed to the callback functions. * @returns {boolean} `true` if any callbacks were registered, `false` otherwise. */ trigger(eventName, details) { @@ -383,8 +391,9 @@ export class EventDispatcher { /** * Adds a single event listener to a specific event. - * @param {TEventName} eventName The string representing the event's name. - * @param {(details: import('core').SafeAny) => void} callback The event listener callback to add. + * @template {import('core').EventNames} TName + * @param {TName} eventName The string representing the event's name. + * @param {import('core').EventHandler} callback The event listener callback to add. */ on(eventName, callback) { let callbacks = this._eventMap.get(eventName); @@ -397,8 +406,9 @@ export class EventDispatcher { /** * Removes a single event listener from a specific event. - * @param {TEventName} eventName The string representing the event's name. - * @param {(details: import('core').SafeAny) => void} callback The event listener callback to add. + * @template {import('core').EventNames} TName + * @param {TName} eventName The string representing the event's name. + * @param {import('core').EventHandler} callback The event listener callback to add. * @returns {boolean} `true` if the callback was removed, `false` otherwise. */ off(eventName, callback) { @@ -420,7 +430,8 @@ export class EventDispatcher { /** * Checks if an event has any listeners. - * @param {TEventName} eventName The string representing the event's name. + * @template {import('core').EventNames} TName + * @param {TName} eventName The string representing the event's name. * @returns {boolean} `true` if the event has listeners, `false` otherwise. */ hasListeners(eventName) { @@ -476,10 +487,11 @@ export class EventListenerCollection { /** * Adds an event listener using `object.on`. The listener will later be removed using `object.off`. - * @template {string} TEventName - * @param {EventDispatcher} target The object to add the event listener to. - * @param {TEventName} eventName The string representing the event's name. - * @param {(details: import('core').SafeAny) => void} callback The event listener callback to add. + * @template {import('core').EventSurface} TSurface + * @template {import('core').EventNames} TName + * @param {EventDispatcher} target The object to add the event listener to. + * @param {TName} eventName The string representing the event's name. + * @param {import('core').EventHandler} callback The event listener callback to add. */ on(target, eventName, callback) { target.on(eventName, callback); @@ -512,7 +524,7 @@ export class EventListenerCollection { * Class representing a generic value with an override stack. * Changes can be observed by listening to the 'change' event. * @template [T=unknown] - * @augments EventDispatcher + * @augments EventDispatcher> */ export class DynamicProperty extends EventDispatcher { /** @@ -615,14 +627,14 @@ export class DynamicProperty extends EventDispatcher { const value = this._overrides.length > 0 ? this._overrides[0].value : this._defaultValue; if (this._value === value) { return; } this._value = value; - this.trigger('change', /** @type {import('dynamic-property').ChangeEventDetails} */ ({value})); + this.trigger('change', {value}); } } /** * This class handles logging of messages to the console and triggering * an event for log calls. - * @augments EventDispatcher + * @augments EventDispatcher */ export class Logger extends EventDispatcher { /** diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js index 423cdfdc..759998c4 100644 --- a/ext/js/display/display-anki.js +++ b/ext/js/display/display-anki.js @@ -182,7 +182,7 @@ export class DisplayAnki { // Private /** - * @param {import('display').OptionsUpdatedEvent} details + * @param {import('display').EventArgument<'optionsUpdated'>} details */ _onOptionsUpdated({options}) { const { @@ -238,7 +238,7 @@ export class DisplayAnki { } /** - * @param {import('display').ContentUpdateEntryEvent} details + * @param {import('display').EventArgument<'contentUpdateEntry'>} details */ _onContentUpdateEntry({element}) { const eventListeners = this._eventListeners; @@ -261,7 +261,7 @@ export class DisplayAnki { } /** - * @param {import('display').LogDictionaryEntryDataEvent} details + * @param {import('display').EventArgument<'logDictionaryEntryData'>} details */ _onLogDictionaryEntryData({dictionaryEntry, promises}) { promises.push(this.getLogData(dictionaryEntry)); diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js index 30cf2b92..cba9c43c 100644 --- a/ext/js/display/display-audio.js +++ b/ext/js/display/display-audio.js @@ -162,7 +162,7 @@ export class DisplayAudio { // Private /** - * @param {import('display').OptionsUpdatedEvent} details + * @param {import('display').EventArgument<'optionsUpdated'>} details */ _onOptionsUpdated({options}) { const {enabled, autoPlay, volume, sources} = options.audio; @@ -201,7 +201,7 @@ export class DisplayAudio { } /** - * @param {import('display').ContentUpdateEntryEvent} details + * @param {import('display').EventArgument<'contentUpdateEntry'>} details */ _onContentUpdateEntry({element}) { const eventListeners = this._eventListeners; @@ -237,7 +237,7 @@ export class DisplayAudio { } /** - * @param {import('display').FrameVisibilityChangeEvent} details + * @param {import('display').EventArgument<'frameVisibilityChange'>} details */ _onFrameVisibilityChange({value}) { if (!value) { @@ -779,7 +779,7 @@ export class DisplayAudio { } /** - * @param {import('popup-menu').MenuCloseEventDetails} details + * @param {import('popup-menu').EventArgument<'close'>} details */ _onPopupMenuClose({menu}) { this._openMenus.delete(menu); diff --git a/ext/js/display/display-history.js b/ext/js/display/display-history.js index af6d734e..30bc3eec 100644 --- a/ext/js/display/display-history.js +++ b/ext/js/display/display-history.js @@ -19,7 +19,7 @@ import {EventDispatcher, generateId, isObject} from '../core.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class DisplayHistory extends EventDispatcher { /** @@ -161,7 +161,7 @@ export class DisplayHistory extends EventDispatcher { * @param {boolean} synthetic */ _triggerStateChanged(synthetic) { - this.trigger('stateChanged', /** @type {import('display-history').StateChangedEvent} */ ({synthetic})); + this.trigger('stateChanged', {synthetic}); } /** diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 79cf79a8..08f640d0 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -34,7 +34,7 @@ import {OptionToggleHotkeyHandler} from './option-toggle-hotkey-handler.js'; import {QueryParser} from './query-parser.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class Display extends EventDispatcher { /** @@ -449,9 +449,7 @@ export class Display extends EventDispatcher { this._updateNestedFrontend(options); this._updateContentTextScanner(options); - /** @type {import('display').OptionsUpdatedEvent} */ - const event = {options}; - this.trigger('optionsUpdated', event); + this.trigger('optionsUpdated', {options}); } /** @@ -716,9 +714,7 @@ export class Display extends EventDispatcher { */ _onMessageVisibilityChanged({value}) { this._frameVisible = value; - /** @type {import('display').FrameVisibilityChangeEvent} */ - const event = {value}; - this.trigger('frameVisibilityChange', event); + this.trigger('frameVisibilityChange', {value}); } /** */ @@ -796,7 +792,7 @@ export class Display extends EventDispatcher { } /** - * @param {import('display').QueryParserSearchedEvent} details + * @param {import('query-parser').EventArgument<'searched'>} details */ _onQueryParserSearch({type, dictionaryEntries, sentence, inputInfo: {eventType}, textSource, optionsContext, sentenceOffset}) { const query = textSource.text(); @@ -869,7 +865,7 @@ export class Display extends EventDispatcher { } /** - * @param {import('dynamic-property').ChangeEventDetails} details + * @param {import('dynamic-property').EventArgument} details */ _onProgressIndicatorVisibleChanged({value}) { if (this._progressIndicatorTimer !== null) { @@ -1646,7 +1642,7 @@ export class Display extends EventDispatcher { /** */ _closePopups() { - yomitan.trigger('closePopups'); + yomitan.triggerClosePopups(); } /** @@ -2011,9 +2007,7 @@ export class Display extends EventDispatcher { /** @type {Promise[]} */ const promises = []; - /** @type {import('display').LogDictionaryEntryDataEvent} */ - const event = {dictionaryEntry, promises}; - this.trigger('logDictionaryEntryData', event); + this.trigger('logDictionaryEntryData', {dictionaryEntry, promises}); if (promises.length > 0) { for (const result2 of await Promise.all(promises)) { Object.assign(result, result2); @@ -2031,9 +2025,7 @@ export class Display extends EventDispatcher { /** */ _triggerContentUpdateStart() { - /** @type {import('display').ContentUpdateStartEvent} */ - const event = {type: this._contentType, query: this._query}; - this.trigger('contentUpdateStart', event); + this.trigger('contentUpdateStart', {type: this._contentType, query: this._query}); } /** @@ -2042,15 +2034,11 @@ export class Display extends EventDispatcher { * @param {number} index */ _triggerContentUpdateEntry(dictionaryEntry, element, index) { - /** @type {import('display').ContentUpdateEntryEvent} */ - const event = {dictionaryEntry, element, index}; - this.trigger('contentUpdateEntry', event); + this.trigger('contentUpdateEntry', {dictionaryEntry, element, index}); } /** */ _triggerContentUpdateComplete() { - /** @type {import('display').ContentUpdateCompleteEvent} */ - const event = {type: this._contentType}; - this.trigger('contentUpdateComplete', event); + this.trigger('contentUpdateComplete', {type: this._contentType}); } } diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index 0e7e1e1a..11c5a4ec 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -22,7 +22,7 @@ import {TextScanner} from '../language/text-scanner.js'; import {yomitan} from '../yomitan.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class QueryParser extends EventDispatcher { /** @@ -160,8 +160,7 @@ export class QueryParser extends EventDispatcher { } = e; if (type === null || dictionaryEntries === null || sentence === null || optionsContext === null) { return; } - /** @type {import('display').QueryParserSearchedEvent} */ - const event2 = { + this.trigger('searched', { textScanner, type, dictionaryEntries, @@ -170,8 +169,7 @@ export class QueryParser extends EventDispatcher { textSource, optionsContext, sentenceOffset: this._getSentenceOffset(e.textSource) - }; - this.trigger('searched', event2); + }); } /** diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js index 6767d201..3df3a332 100644 --- a/ext/js/display/search-display-controller.js +++ b/ext/js/display/search-display-controller.js @@ -183,7 +183,7 @@ export class SearchDisplayController { } /** - * @param {import('display').OptionsUpdatedEvent} details + * @param {import('display').EventArgument<'optionsUpdated'>} details */ _onDisplayOptionsUpdated({options}) { this._clipboardMonitorEnabled = options.clipboard.enableSearchPageMonitor; @@ -195,7 +195,7 @@ export class SearchDisplayController { } /** - * @param {import('display').ContentUpdateStartEvent} details + * @param {import('display').EventArgument<'contentUpdateStart'>} details */ _onContentUpdateStart({type, query}) { let animate = false; diff --git a/ext/js/display/search-persistent-state-controller.js b/ext/js/display/search-persistent-state-controller.js index d92ddf68..1bd32f5b 100644 --- a/ext/js/display/search-persistent-state-controller.js +++ b/ext/js/display/search-persistent-state-controller.js @@ -19,7 +19,7 @@ import {EventDispatcher} from '../core.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class SearchPersistentStateController extends EventDispatcher { constructor() { diff --git a/ext/js/dom/panel-element.js b/ext/js/dom/panel-element.js index d4cb28fd..959ca420 100644 --- a/ext/js/dom/panel-element.js +++ b/ext/js/dom/panel-element.js @@ -19,7 +19,7 @@ import {EventDispatcher} from '../core.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class PanelElement extends EventDispatcher { /** @@ -84,9 +84,9 @@ export class PanelElement extends EventDispatcher { } /** - * @param {import('panel-element').EventType} eventName - * @param {(details: import('core').SafeAny) => void} callback - * @returns {void} + * @template {import('core').EventNames} TName + * @param {TName} eventName + * @param {(details: import('core').EventArgument) => void} callback */ on(eventName, callback) { if (eventName === 'visibilityChanged') { @@ -100,12 +100,13 @@ export class PanelElement extends EventDispatcher { }); } } - return super.on(eventName, callback); + super.on(eventName, callback); } /** - * @param {import('panel-element').EventType} eventName - * @param {(details: import('core').SafeAny) => void} callback + * @template {import('core').EventNames} TName + * @param {TName} eventName + * @param {(details: import('core').EventArgument) => void} callback * @returns {boolean} */ off(eventName, callback) { diff --git a/ext/js/dom/popup-menu.js b/ext/js/dom/popup-menu.js index 72df82a0..61f20ba8 100644 --- a/ext/js/dom/popup-menu.js +++ b/ext/js/dom/popup-menu.js @@ -20,7 +20,7 @@ import {EventDispatcher, EventListenerCollection} from '../core.js'; import {querySelectorNotNull} from './query-selector.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class PopupMenu extends EventDispatcher { /** @@ -247,7 +247,7 @@ export class PopupMenu extends EventDispatcher { {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false} ); - /** @type {import('popup-menu').MenuCloseEventDetails} */ + /** @type {import('popup-menu').EventArgument<'close'>} */ const detail = { menu: this, item, diff --git a/ext/js/input/hotkey-handler.js b/ext/js/input/hotkey-handler.js index da763662..48c2de57 100644 --- a/ext/js/input/hotkey-handler.js +++ b/ext/js/input/hotkey-handler.js @@ -22,7 +22,7 @@ import {yomitan} from '../yomitan.js'; /** * Class which handles hotkey events and actions. - * @augments EventDispatcher + * @augments EventDispatcher */ export class HotkeyHandler extends EventDispatcher { /** @@ -120,25 +120,21 @@ export class HotkeyHandler extends EventDispatcher { } /** - * Adds a single event listener to a specific event. - * @template [TEventDetails=unknown] - * @param {import('hotkey-handler').EventType} eventName The string representing the event's name. - * @param {(details: TEventDetails) => void} callback The event listener callback to add. - * @returns {void} + * @template {import('core').EventNames} TName + * @param {TName} eventName + * @param {(details: import('core').EventArgument) => void} callback */ on(eventName, callback) { - const result = super.on(eventName, callback); + super.on(eventName, callback); this._updateHasEventListeners(); this._updateEventHandlers(); - return result; } /** - * Removes a single event listener from a specific event. - * @template [TEventDetails=unknown] - * @param {import('hotkey-handler').EventType} eventName The string representing the event's name. - * @param {(details: TEventDetails) => void} callback The event listener callback to add. - * @returns {boolean} `true` if the callback was removed, `false` otherwise. + * @template {import('core').EventNames} TName + * @param {TName} eventName + * @param {(details: import('core').EventArgument) => void} callback + * @returns {boolean} */ off(eventName, callback) { const result = super.off(eventName, callback); diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 164e150e..9c254e44 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -22,7 +22,7 @@ import {TextSourceElement} from '../dom/text-source-element.js'; import {yomitan} from '../yomitan.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class TextScanner extends EventDispatcher { /** @@ -1597,7 +1597,7 @@ export class TextScanner extends EventDispatcher { } /** - * @param {string} reason + * @param {import('text-scanner').ClearReason} reason */ _triggerClear(reason) { this.trigger('clear', {reason}); diff --git a/ext/js/media/audio-system.js b/ext/js/media/audio-system.js index 1e8f1be2..c311b96c 100644 --- a/ext/js/media/audio-system.js +++ b/ext/js/media/audio-system.js @@ -20,7 +20,7 @@ import {EventDispatcher} from '../core.js'; import {TextToSpeechAudio} from './text-to-speech-audio.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class AudioSystem extends EventDispatcher { constructor() { diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index aea94b65..d64034a5 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -221,7 +221,7 @@ export class AnkiController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ async _onOptionsChanged({options: {anki}}) { /** @type {?string} */ @@ -964,7 +964,7 @@ class AnkiCardController { } /** - * @param {import('settings-controller').PermissionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'permissionsChanged'>} details */ _onPermissionsChanged({permissions: {permissions}}) { const permissionsSet = new Set(permissions); diff --git a/ext/js/pages/settings/anki-templates-controller.js b/ext/js/pages/settings/anki-templates-controller.js index ac0cc9c2..56e992b0 100644 --- a/ext/js/pages/settings/anki-templates-controller.js +++ b/ext/js/pages/settings/anki-templates-controller.js @@ -94,7 +94,7 @@ export class AnkiTemplatesController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { let templates = options.anki.fieldTemplates; diff --git a/ext/js/pages/settings/audio-controller.js b/ext/js/pages/settings/audio-controller.js index af05ee70..2b46455f 100644 --- a/ext/js/pages/settings/audio-controller.js +++ b/ext/js/pages/settings/audio-controller.js @@ -21,7 +21,7 @@ import {querySelectorNotNull} from '../../dom/query-selector.js'; import {AudioSystem} from '../../media/audio-system.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class AudioController extends EventDispatcher { /** @@ -117,7 +117,7 @@ export class AudioController extends EventDispatcher { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { for (const entry of this._audioSourceEntries) { @@ -163,7 +163,7 @@ export class AudioController extends EventDispatcher { ); voices.sort(this._textToSpeechVoiceCompare.bind(this)); this._voices = voices; - this.trigger('voicesUpdated'); + this.trigger('voicesUpdated', {}); } /** diff --git a/ext/js/pages/settings/backup-controller.js b/ext/js/pages/settings/backup-controller.js index c0e56e96..2ae52925 100644 --- a/ext/js/pages/settings/backup-controller.js +++ b/ext/js/pages/settings/backup-controller.js @@ -290,7 +290,7 @@ export class BackupController { modal.setVisible(false); }; /** - * @param {import('panel-element').VisibilityChangedEvent} details + * @param {import('panel-element').EventArgument<'visibilityChanged'>} details */ const onModalVisibilityChanged = ({visible}) => { if (visible) { return; } @@ -644,7 +644,7 @@ export class BackupController { await yomitan.api.purgeDatabase(); await Dexie.import(file, {progressCallback: this._databaseImportProgressCallback}); yomitan.api.triggerDatabaseUpdated('dictionary', 'import'); - yomitan.trigger('storageChanged'); + yomitan.triggerStorageChanged(); } /** */ diff --git a/ext/js/pages/settings/collapsible-dictionary-controller.js b/ext/js/pages/settings/collapsible-dictionary-controller.js index cff3ad20..341522cf 100644 --- a/ext/js/pages/settings/collapsible-dictionary-controller.js +++ b/ext/js/pages/settings/collapsible-dictionary-controller.js @@ -70,7 +70,7 @@ export class CollapsibleDictionaryController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { this._eventListeners.removeAllEventListeners(); diff --git a/ext/js/pages/settings/dictionary-controller.js b/ext/js/pages/settings/dictionary-controller.js index db6a73d4..0132fe9e 100644 --- a/ext/js/pages/settings/dictionary-controller.js +++ b/ext/js/pages/settings/dictionary-controller.js @@ -473,7 +473,7 @@ export class DictionaryController { value: dictionaries }]); - /** @type {import('settings-controller').DictionarySettingsReorderedEvent} */ + /** @type {import('settings-controller').EventArgument<'dictionarySettingsReordered'>} */ const event = {source: this}; this._settingsController.trigger('dictionarySettingsReordered', event); @@ -577,7 +577,7 @@ export class DictionaryController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { this._updateDictionariesEnabledWarnings(options); @@ -930,7 +930,7 @@ export class DictionaryController { /** */ _triggerStorageChanged() { - yomitan.trigger('storageChanged'); + yomitan.triggerStorageChanged(); } /** */ diff --git a/ext/js/pages/settings/dictionary-import-controller.js b/ext/js/pages/settings/dictionary-import-controller.js index 79a62d32..eadfcb91 100644 --- a/ext/js/pages/settings/dictionary-import-controller.js +++ b/ext/js/pages/settings/dictionary-import-controller.js @@ -398,6 +398,6 @@ export class DictionaryImportController { /** */ _triggerStorageChanged() { - yomitan.trigger('storageChanged'); + yomitan.triggerStorageChanged(); } } diff --git a/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js b/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js index e92d9e93..e3d84ac2 100644 --- a/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/extension-keyboard-shortcuts-controller.js @@ -313,7 +313,7 @@ class ExtensionKeyboardShortcutHotkeyEntry { // Private /** - * @param {import('keyboard-mouse-input-field').ChangeEvent} e + * @param {import('keyboard-mouse-input-field').EventArgument<'change'>} e */ _onInputFieldChange(e) { const {key, modifiers} = e; diff --git a/ext/js/pages/settings/keyboard-mouse-input-field.js b/ext/js/pages/settings/keyboard-mouse-input-field.js index f50ca112..0628d065 100644 --- a/ext/js/pages/settings/keyboard-mouse-input-field.js +++ b/ext/js/pages/settings/keyboard-mouse-input-field.js @@ -21,7 +21,7 @@ import {DocumentUtil} from '../../dom/document-util.js'; import {HotkeyUtil} from '../../input/hotkey-util.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class KeyboardMouseInputField extends EventDispatcher { /** @@ -308,9 +308,7 @@ export class KeyboardMouseInputField extends EventDispatcher { this._updateDisplayString(); if (changed) { - /** @type {import('keyboard-mouse-input-field').ChangeEvent} */ - const event = {modifiers: this._modifiers, key: this._key}; - this.trigger('change', event); + this.trigger('change', {modifiers: this._modifiers, key: this._key}); } } diff --git a/ext/js/pages/settings/keyboard-shortcuts-controller.js b/ext/js/pages/settings/keyboard-shortcuts-controller.js index cbdae77d..b45c656a 100644 --- a/ext/js/pages/settings/keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/keyboard-shortcuts-controller.js @@ -160,7 +160,7 @@ export class KeyboardShortcutController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { for (const entry of this._entries) { @@ -395,7 +395,7 @@ class KeyboardShortcutHotkeyEntry { } /** - * @param {import('keyboard-mouse-input-field').ChangeEvent} details + * @param {import('keyboard-mouse-input-field').EventArgument<'change'>} details */ _onInputFieldChange({key, modifiers}) { /** @type {import('input').ModifierKey[]} */ diff --git a/ext/js/pages/settings/nested-popups-controller.js b/ext/js/pages/settings/nested-popups-controller.js index 7eb78148..4f0aa761 100644 --- a/ext/js/pages/settings/nested-popups-controller.js +++ b/ext/js/pages/settings/nested-popups-controller.js @@ -50,7 +50,7 @@ export class NestedPopupsController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { this._updatePopupNestingMaxDepth(options.scanning.popupNestingMaxDepth); diff --git a/ext/js/pages/settings/permissions-origin-controller.js b/ext/js/pages/settings/permissions-origin-controller.js index 6dacced8..3a9db602 100644 --- a/ext/js/pages/settings/permissions-origin-controller.js +++ b/ext/js/pages/settings/permissions-origin-controller.js @@ -60,7 +60,7 @@ export class PermissionsOriginController { // Private /** - * @param {import('settings-controller').PermissionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'permissionsChanged'>} details */ _onPermissionsChanged({permissions}) { this._eventListeners.removeAllEventListeners(); diff --git a/ext/js/pages/settings/permissions-toggle-controller.js b/ext/js/pages/settings/permissions-toggle-controller.js index 85752a7e..055ce1f4 100644 --- a/ext/js/pages/settings/permissions-toggle-controller.js +++ b/ext/js/pages/settings/permissions-toggle-controller.js @@ -47,7 +47,7 @@ export class PermissionsToggleController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { let accessor = null; @@ -104,7 +104,7 @@ export class PermissionsToggleController { } /** - * @param {import('settings-controller').PermissionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'permissionsChanged'>} details */ _onPermissionsChanged({permissions}) { const permissions2 = permissions.permissions; diff --git a/ext/js/pages/settings/persistent-storage-controller.js b/ext/js/pages/settings/persistent-storage-controller.js index 7386edd7..70c9a177 100644 --- a/ext/js/pages/settings/persistent-storage-controller.js +++ b/ext/js/pages/settings/persistent-storage-controller.js @@ -82,7 +82,7 @@ export class PersistentStorageController { const node = document.querySelector('#storage-persistent-fail-warning'); if (node !== null) { node.hidden = isStoragePeristent; } - yomitan.trigger('storageChanged'); + yomitan.triggerStorageChanged(); } /** diff --git a/ext/js/pages/settings/popup-preview-frame.js b/ext/js/pages/settings/popup-preview-frame.js index 7828a025..609710ba 100644 --- a/ext/js/pages/settings/popup-preview-frame.js +++ b/ext/js/pages/settings/popup-preview-frame.js @@ -140,7 +140,7 @@ export class PopupPreviewFrame { } /** - * @param {import('popup').CustomOuterCssChangedEvent} details + * @param {import('popup').EventArgument<'customOuterCssChanged'>} details */ _onCustomOuterCssChanged({node, inShadow}) { if (node === null || inShadow) { return; } diff --git a/ext/js/pages/settings/profile-conditions-ui.js b/ext/js/pages/settings/profile-conditions-ui.js index 29e7460f..22e47a9b 100644 --- a/ext/js/pages/settings/profile-conditions-ui.js +++ b/ext/js/pages/settings/profile-conditions-ui.js @@ -22,7 +22,7 @@ import {querySelectorNotNull} from '../../dom/query-selector.js'; import {KeyboardMouseInputField} from './keyboard-mouse-input-field.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class ProfileConditionsUI extends EventDispatcher { /** @@ -440,9 +440,7 @@ export class ProfileConditionsUI extends EventDispatcher { * @param {number} count */ _triggerConditionGroupCountChanged(count) { - /** @type {import('profile-conditions-ui').ConditionGroupCountChangedEvent} */ - const event = {count, profileIndex: this._profileIndex}; - this.trigger('conditionGroupCountChanged', event); + this.trigger('conditionGroupCountChanged', {count, profileIndex: this._profileIndex}); } } @@ -747,7 +745,7 @@ class ProfileConditionUI { /** * @param {import('profile-conditions-ui').InputData} details - * @param {import('keyboard-mouse-input-field').ChangeEvent} event + * @param {import('keyboard-mouse-input-field').EventArgument<'change'>} event */ _onModifierInputChange({validate, normalize}, event) { const modifiers = this._joinModifiers(event.modifiers); @@ -863,10 +861,6 @@ class ProfileConditionUI { let inputValue = value; let inputStep = null; let showMouseButton = false; - /** @type {import('event-listener-collection').AddEventListenerArgs[]} */ - const events1 = []; - /** @type {import('event-listener-collection').OnArgs[]} */ - const events2 = []; /** @type {import('profile-conditions-ui').InputData} */ const inputData = {validate, normalize}; const node = this._valueInput; @@ -875,7 +869,6 @@ class ProfileConditionUI { case 'integer': inputType = 'number'; inputStep = '1'; - events1.push([node, 'change', this._onValueInputChange.bind(this, inputData), false]); break; case 'modifierKeys': case 'modifierInputs': @@ -883,10 +876,6 @@ class ProfileConditionUI { showMouseButton = (type === 'modifierInputs'); this._kbmInputField = this._parent.parent.createKeyboardMouseInputField(node, this._mouseButton); this._kbmInputField.prepare(null, this._splitModifiers(value), showMouseButton, false); - events2.push([this._kbmInputField, 'change', this._onModifierInputChange.bind(this, inputData)]); - break; - default: // 'string' - events1.push([node, 'change', this._onValueInputChange.bind(this, inputData), false]); break; } @@ -902,11 +891,17 @@ class ProfileConditionUI { node.removeAttribute('step'); } this._mouseButtonContainer.hidden = !showMouseButton; - for (const args of events1) { - this._inputEventListeners.addEventListener(...args); - } - for (const args of events2) { - this._inputEventListeners.on(...args); + + switch (type) { + case 'modifierKeys': + case 'modifierInputs': + if (this._kbmInputField !== null) { + this._inputEventListeners.on(this._kbmInputField, 'change', this._onModifierInputChange.bind(this, inputData)); + } + break; + default: // 'integer', 'string' + this._inputEventListeners.addEventListener(node, 'change', this._onValueInputChange.bind(this, inputData), false); + break; } return this._validateValue(value, validate); diff --git a/ext/js/pages/settings/profile-controller.js b/ext/js/pages/settings/profile-controller.js index c54bfe73..54a41058 100644 --- a/ext/js/pages/settings/profile-controller.js +++ b/ext/js/pages/settings/profile-controller.js @@ -475,7 +475,7 @@ export class ProfileController { } /** - * @param {import('profile-conditions-ui').ConditionGroupCountChangedEvent} details + * @param {import('profile-conditions-ui').EventArgument<'conditionGroupCountChanged'>} details */ _onConditionGroupCountChanged({count, profileIndex}) { if (profileIndex >= 0 && profileIndex < this._profileEntryList.length) { diff --git a/ext/js/pages/settings/recommended-permissions-controller.js b/ext/js/pages/settings/recommended-permissions-controller.js index b19311aa..a870de50 100644 --- a/ext/js/pages/settings/recommended-permissions-controller.js +++ b/ext/js/pages/settings/recommended-permissions-controller.js @@ -48,7 +48,7 @@ export class RecommendedPermissionsController { // Private /** - * @param {import('settings-controller').PermissionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'permissionsChanged'>} details */ _onPermissionsChanged({permissions}) { this._eventListeners.removeAllEventListeners(); diff --git a/ext/js/pages/settings/scan-inputs-controller.js b/ext/js/pages/settings/scan-inputs-controller.js index eb526863..4854c28f 100644 --- a/ext/js/pages/settings/scan-inputs-controller.js +++ b/ext/js/pages/settings/scan-inputs-controller.js @@ -110,7 +110,7 @@ export class ScanInputsController { // Private /** - * @param {import('settings-controller').ScanInputsChangedEvent} details + * @param {import('settings-controller').EventArgument<'scanInputsChanged'>} details */ _onScanInputsChanged({source}) { if (source === this) { return; } @@ -118,7 +118,7 @@ export class ScanInputsController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { const {inputs} = options.scanning; @@ -190,7 +190,7 @@ export class ScanInputsController { /** */ _triggerScanInputsChanged() { - /** @type {import('settings-controller').ScanInputsChangedEvent} */ + /** @type {import('settings-controller').EventArgument<'scanInputsChanged'>} */ const event = {source: this}; this._settingsController.trigger('scanInputsChanged', event); } @@ -312,7 +312,7 @@ class ScanInputField { // Private /** - * @param {import('keyboard-mouse-input-field').ChangeEvent} details + * @param {import('keyboard-mouse-input-field').EventArgument<'change'>} details */ _onIncludeValueChange({modifiers}) { const modifiers2 = this._joinModifiers(modifiers); @@ -320,7 +320,7 @@ class ScanInputField { } /** - * @param {import('keyboard-mouse-input-field').ChangeEvent} details + * @param {import('keyboard-mouse-input-field').EventArgument<'change'>} details */ _onExcludeValueChange({modifiers}) { const modifiers2 = this._joinModifiers(modifiers); diff --git a/ext/js/pages/settings/scan-inputs-simple-controller.js b/ext/js/pages/settings/scan-inputs-simple-controller.js index ddb68825..f0255595 100644 --- a/ext/js/pages/settings/scan-inputs-simple-controller.js +++ b/ext/js/pages/settings/scan-inputs-simple-controller.js @@ -67,7 +67,7 @@ export class ScanInputsSimpleController { // Private /** - * @param {import('settings-controller').ScanInputsChangedEvent} details + * @param {import('settings-controller').EventArgument<'scanInputsChanged'>} details */ _onScanInputsChanged({source}) { if (source === this) { return; } @@ -75,7 +75,7 @@ export class ScanInputsSimpleController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { const {scanning: {inputs}} = options; @@ -236,7 +236,7 @@ export class ScanInputsSimpleController { */ async _modifyProfileSettings(targets) { await this._settingsController.modifyProfileSettings(targets); - /** @type {import('settings-controller').ScanInputsChangedEvent} */ + /** @type {import('settings-controller').EventArgument<'scanInputsChanged'>} */ const event = {source: this}; this._settingsController.trigger('scanInputsChanged', event); } diff --git a/ext/js/pages/settings/secondary-search-dictionary-controller.js b/ext/js/pages/settings/secondary-search-dictionary-controller.js index f24f6ea3..7f0882b8 100644 --- a/ext/js/pages/settings/secondary-search-dictionary-controller.js +++ b/ext/js/pages/settings/secondary-search-dictionary-controller.js @@ -66,7 +66,7 @@ export class SecondarySearchDictionaryController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { this._eventListeners.removeAllEventListeners(); diff --git a/ext/js/pages/settings/sentence-termination-characters-controller.js b/ext/js/pages/settings/sentence-termination-characters-controller.js index 7fd90b28..f7793943 100644 --- a/ext/js/pages/settings/sentence-termination-characters-controller.js +++ b/ext/js/pages/settings/sentence-termination-characters-controller.js @@ -105,7 +105,7 @@ export class SentenceTerminationCharactersController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { for (const entry of this._entries) { diff --git a/ext/js/pages/settings/settings-controller.js b/ext/js/pages/settings/settings-controller.js index 1b46c745..52b777a3 100644 --- a/ext/js/pages/settings/settings-controller.js +++ b/ext/js/pages/settings/settings-controller.js @@ -23,7 +23,7 @@ import {HtmlTemplateCollection} from '../../dom/html-template-collection.js'; import {yomitan} from '../../yomitan.js'; /** - * @augments EventDispatcher + * @augments EventDispatcher */ export class SettingsController extends EventDispatcher { constructor() { @@ -234,7 +234,7 @@ export class SettingsController extends EventDispatcher { */ _setProfileIndex(value, canUpdateProfileIndex) { this._profileIndex = value; - this.trigger('optionsContextChanged'); + this.trigger('optionsContextChanged', {}); this._onOptionsUpdatedInternal(canUpdateProfileIndex); } @@ -253,9 +253,7 @@ export class SettingsController extends EventDispatcher { const optionsContext = this.getOptionsContext(); try { const options = await this.getOptions(); - /** @type {import('settings-controller').OptionsChangedEvent} */ - const event = {options, optionsContext}; - this.trigger('optionsChanged', event); + this.trigger('optionsChanged', {options, optionsContext}); } catch (e) { if (canUpdateProfileIndex) { this._setProfileIndex(0, false); @@ -339,9 +337,7 @@ export class SettingsController extends EventDispatcher { if (!this.hasListeners(eventName)) { return; } const permissions = await this._permissionsUtil.getAllPermissions(); - /** @type {import('settings-controller').PermissionsChangedEvent} */ - const event = {permissions}; - this.trigger(eventName, event); + this.trigger(eventName, {permissions}); } /** diff --git a/ext/js/pages/settings/sort-frequency-dictionary-controller.js b/ext/js/pages/settings/sort-frequency-dictionary-controller.js index 2c56f023..3fdd66c7 100644 --- a/ext/js/pages/settings/sort-frequency-dictionary-controller.js +++ b/ext/js/pages/settings/sort-frequency-dictionary-controller.js @@ -68,7 +68,7 @@ export class SortFrequencyDictionaryController { } /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { const {sortFrequencyDictionary, sortFrequencyDictionaryOrder} = options.general; diff --git a/ext/js/pages/settings/translation-text-replacements-controller.js b/ext/js/pages/settings/translation-text-replacements-controller.js index ebd1b58d..9f3fda00 100644 --- a/ext/js/pages/settings/translation-text-replacements-controller.js +++ b/ext/js/pages/settings/translation-text-replacements-controller.js @@ -110,7 +110,7 @@ export class TranslationTextReplacementsController { // Private /** - * @param {import('settings-controller').OptionsChangedEvent} details + * @param {import('settings-controller').EventArgument<'optionsChanged'>} details */ _onOptionsChanged({options}) { for (const entry of this._entries) { diff --git a/ext/js/yomitan.js b/ext/js/yomitan.js index 621e9cf0..8980c589 100644 --- a/ext/js/yomitan.js +++ b/ext/js/yomitan.js @@ -50,7 +50,7 @@ if (checkChromeNotAvailable()) { /** * The Yomitan class is a core component through which various APIs are handled and invoked. - * @augments EventDispatcher + * @augments EventDispatcher */ export class Yomitan extends EventDispatcher { /** @@ -208,12 +208,22 @@ export class Yomitan extends EventDispatcher { if (this._isTriggeringExtensionUnloaded) { return; } try { this._isTriggeringExtensionUnloaded = true; - this.trigger('extensionUnloaded'); + this.trigger('extensionUnloaded', {}); } finally { this._isTriggeringExtensionUnloaded = false; } } + /** */ + triggerStorageChanged() { + this.trigger('storageChanged', {}); + } + + /** */ + triggerClosePopups() { + this.trigger('closePopups', {}); + } + // Private /** -- cgit v1.2.3