summaryrefslogtreecommitdiff
path: root/ext/js/pages/settings/settings-display-controller.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/pages/settings/settings-display-controller.js')
-rw-r--r--ext/js/pages/settings/settings-display-controller.js152
1 files changed, 116 insertions, 36 deletions
diff --git a/ext/js/pages/settings/settings-display-controller.js b/ext/js/pages/settings/settings-display-controller.js
index e23e355d..16e6cfae 100644
--- a/ext/js/pages/settings/settings-display-controller.js
+++ b/ext/js/pages/settings/settings-display-controller.js
@@ -21,44 +21,54 @@ import {PopupMenu} from '../../dom/popup-menu.js';
import {SelectorObserver} from '../../dom/selector-observer.js';
export class SettingsDisplayController {
+ /**
+ * @param {import('./settings-controller.js').SettingsController} settingsController
+ * @param {import('./modal-controller.js').ModalController} modalController
+ */
constructor(settingsController, modalController) {
+ /** @type {import('./settings-controller.js').SettingsController} */
this._settingsController = settingsController;
+ /** @type {import('./modal-controller.js').ModalController} */
this._modalController = modalController;
+ /** @type {?HTMLElement} */
this._contentNode = null;
+ /** @type {?HTMLElement} */
this._menuContainer = null;
- this._onMoreToggleClickBind = null;
- this._onMenuButtonClickBind = null;
+ /** @type {(event: MouseEvent) => void} */
+ this._onMoreToggleClickBind = this._onMoreToggleClick.bind(this);
+ /** @type {(event: MouseEvent) => void} */
+ this._onMenuButtonClickBind = this._onMenuButtonClick.bind(this);
}
+ /** */
prepare() {
- this._contentNode = document.querySelector('.content');
- this._menuContainer = document.querySelector('#popup-menus');
+ this._contentNode = /** @type {HTMLElement} */ (document.querySelector('.content'));
+ this._menuContainer = /** @type {HTMLElement} */ (document.querySelector('#popup-menus'));
const onFabButtonClick = this._onFabButtonClick.bind(this);
- for (const fabButton of document.querySelectorAll('.fab-button')) {
+ for (const fabButton of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('.fab-button'))) {
fabButton.addEventListener('click', onFabButtonClick, false);
}
const onModalAction = this._onModalAction.bind(this);
- for (const node of document.querySelectorAll('[data-modal-action]')) {
+ for (const node of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('[data-modal-action]'))) {
node.addEventListener('click', onModalAction, false);
}
const onSelectOnClickElementClick = this._onSelectOnClickElementClick.bind(this);
- for (const node of document.querySelectorAll('[data-select-on-click]')) {
+ for (const node of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('[data-select-on-click]'))) {
node.addEventListener('click', onSelectOnClickElementClick, false);
}
const onInputTabActionKeyDown = this._onInputTabActionKeyDown.bind(this);
- for (const node of document.querySelectorAll('[data-tab-action]')) {
+ for (const node of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('[data-tab-action]'))) {
node.addEventListener('keydown', onInputTabActionKeyDown, false);
}
- for (const node of document.querySelectorAll('.defer-load-iframe')) {
+ for (const node of /** @type {NodeListOf<HTMLIFrameElement>} */ (document.querySelectorAll('.defer-load-iframe'))) {
this._setupDeferLoadIframe(node);
}
- this._onMoreToggleClickBind = this._onMoreToggleClick.bind(this);
const moreSelectorObserver = new SelectorObserver({
selector: '.more-toggle',
onAdded: this._onMoreSetup.bind(this),
@@ -66,7 +76,6 @@ export class SettingsDisplayController {
});
moreSelectorObserver.observe(document.documentElement, false);
- this._onMenuButtonClickBind = this._onMenuButtonClick.bind(this);
const menuSelectorObserver = new SelectorObserver({
selector: '[data-menu]',
onAdded: this._onMenuSetup.bind(this),
@@ -81,32 +90,54 @@ export class SettingsDisplayController {
// Private
+ /**
+ * @param {Element} element
+ * @returns {null}
+ */
_onMoreSetup(element) {
- element.addEventListener('click', this._onMoreToggleClickBind, false);
+ /** @type {HTMLElement} */ (element).addEventListener('click', this._onMoreToggleClickBind, false);
return null;
}
+ /**
+ * @param {Element} element
+ */
_onMoreCleanup(element) {
- element.removeEventListener('click', this._onMoreToggleClickBind, false);
+ /** @type {HTMLElement} */ (element).removeEventListener('click', this._onMoreToggleClickBind, false);
}
+ /**
+ * @param {Element} element
+ * @returns {null}
+ */
_onMenuSetup(element) {
- element.addEventListener('click', this._onMenuButtonClickBind, false);
+ /** @type {HTMLElement} */ (element).addEventListener('click', this._onMenuButtonClickBind, false);
return null;
}
+ /**
+ * @param {Element} element
+ */
_onMenuCleanup(element) {
- element.removeEventListener('click', this._onMenuButtonClickBind, false);
+ /** @type {HTMLElement} */ (element).removeEventListener('click', this._onMenuButtonClickBind, false);
}
+ /**
+ * @param {MouseEvent} e
+ */
_onMenuButtonClick(e) {
- const element = e.currentTarget;
+ const element = /** @type {HTMLElement} */ (e.currentTarget);
const {menu} = element.dataset;
+ if (typeof menu === 'undefined') { return; }
this._showMenu(element, menu);
}
+ /**
+ * @param {MouseEvent} e
+ */
_onFabButtonClick(e) {
- const action = e.currentTarget.dataset.action;
+ const element = /** @type {HTMLElement} */ (e.currentTarget);
+ const action = element.dataset.action;
switch (action) {
case 'toggle-sidebar':
document.body.classList.toggle('sidebar-visible');
@@ -117,16 +148,20 @@ export class SettingsDisplayController {
}
}
+ /**
+ * @param {MouseEvent} e
+ */
_onMoreToggleClick(e) {
- const container = this._getMoreContainer(e.currentTarget);
+ const node = /** @type {HTMLElement} */ (e.currentTarget);
+ const container = this._getMoreContainer(node);
if (container === null) { return; }
- const more = container.querySelector('.more');
+ const more = /** @type {?HTMLElement} */ (container.querySelector('.more'));
if (more === null) { return; }
const moreVisible = more.hidden;
more.hidden = !moreVisible;
- for (const moreToggle of container.querySelectorAll('.more-toggle')) {
+ for (const moreToggle of /** @type {NodeListOf<HTMLElement>} */ (container.querySelectorAll('.more-toggle'))) {
const container2 = this._getMoreContainer(moreToggle);
if (container2 === null) { continue; }
@@ -137,13 +172,16 @@ export class SettingsDisplayController {
}
e.preventDefault();
- return false;
}
+ /** */
_onPopState() {
this._updateScrollTarget();
}
+ /**
+ * @param {KeyboardEvent} e
+ */
_onKeyDown(e) {
switch (e.code) {
case 'Escape':
@@ -155,12 +193,18 @@ export class SettingsDisplayController {
}
}
+ /**
+ * @param {MouseEvent} e
+ */
_onModalAction(e) {
- const node = e.currentTarget;
+ const node = /** @type {HTMLElement} */ (e.currentTarget);
const {modalAction} = node.dataset;
if (typeof modalAction !== 'string') { return; }
- let [action, target] = modalAction.split(',');
+ const modalActionArray = modalAction.split(',');
+ const action = modalActionArray[0];
+ /** @type {string|Element|undefined} */
+ let target = modalActionArray[1];
if (typeof target === 'undefined') {
const currentModal = node.closest('.modal');
if (currentModal === null) { return; }
@@ -185,26 +229,33 @@ export class SettingsDisplayController {
e.preventDefault();
}
+ /**
+ * @param {MouseEvent} e
+ */
_onSelectOnClickElementClick(e) {
if (e.button !== 0) { return; }
- const node = e.currentTarget;
+ const node = /** @type {HTMLElement} */ (e.currentTarget);
const range = document.createRange();
range.selectNode(node);
const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
+ if (selection !== null) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
e.preventDefault();
e.stopPropagation();
- return false;
}
+ /**
+ * @param {KeyboardEvent} e
+ */
_onInputTabActionKeyDown(e) {
if (e.key !== 'Tab' || e.ctrlKey) { return; }
- const node = e.currentTarget;
+ const node = /** @type {HTMLElement} */ (e.currentTarget);
const {tabAction} = node.dataset;
if (typeof tabAction !== 'string') { return; }
@@ -220,6 +271,7 @@ export class SettingsDisplayController {
}
}
+ /** */
_updateScrollTarget() {
const hash = window.location.hash;
if (!hash.startsWith('#!')) { return; }
@@ -233,18 +285,25 @@ export class SettingsDisplayController {
content.scrollTop += rect2.top - rect1.top;
}
+ /**
+ * @param {HTMLElement} link
+ * @returns {?Element}
+ */
_getMoreContainer(link) {
const v = link.dataset.parentDistance;
const distance = v ? parseInt(v, 10) : 1;
if (Number.isNaN(distance)) { return null; }
+ /** @type {?Element} */
+ let result = link;
for (let i = 0; i < distance; ++i) {
- link = link.parentNode;
- if (link === null) { break; }
+ if (result === null) { break; }
+ result = /** @type {?Element} */ (result.parentNode);
}
- return link;
+ return result;
}
+ /** */
_closeTopMenuOrModal() {
for (const popupMenu of PopupMenu.openMenus) {
popupMenu.close();
@@ -257,17 +316,27 @@ export class SettingsDisplayController {
}
}
+ /**
+ * @param {HTMLElement} element
+ * @param {string} menuName
+ */
_showMenu(element, menuName) {
- const menu = this._settingsController.instantiateTemplate(menuName);
- if (menu === null) { return; }
+ const menu = /** @type {HTMLElement} */ (this._settingsController.instantiateTemplate(menuName));
- this._menuContainer.appendChild(menu);
+ /** @type {HTMLElement} */ (this._menuContainer).appendChild(menu);
const popupMenu = new PopupMenu(element, menu);
popupMenu.prepare();
}
+ /**
+ * @param {KeyboardEvent} e
+ * @param {HTMLElement} node
+ * @param {string[]} args
+ */
_indentInput(e, node, args) {
+ if (!(node instanceof HTMLTextAreaElement)) { return; }
+
let indent = '\t';
if (args.length > 1) {
const count = parseInt(args[1], 10);
@@ -276,7 +345,8 @@ export class SettingsDisplayController {
const {selectionStart: start, selectionEnd: end, value} = node;
const lineStart = value.substring(0, start).lastIndexOf('\n') + 1;
- const lineWhitespace = /^[ \t]*/.exec(value.substring(lineStart))[0];
+ const lineWhitespaceMatch = /^[ \t]*/.exec(value.substring(lineStart));
+ const lineWhitespace = lineWhitespaceMatch !== null ? lineWhitespaceMatch[0] : '';
if (e.shiftKey) {
const whitespaceLength = Math.max(0, Math.floor((lineWhitespace.length - 1) / 4) * 4);
@@ -298,17 +368,23 @@ export class SettingsDisplayController {
}
}
+ /**
+ * @param {HTMLIFrameElement} element
+ */
_setupDeferLoadIframe(element) {
const parent = this._getMoreContainer(element);
if (parent === null) { return; }
+ /** @type {?MutationObserver} */
let mutationObserver = null;
const callback = () => {
if (!this._isElementVisible(element)) { return false; }
const src = element.dataset.src;
delete element.dataset.src;
- element.src = src;
+ if (typeof src === 'string') {
+ element.src = src;
+ }
if (mutationObserver === null) { return true; }
@@ -323,6 +399,10 @@ export class SettingsDisplayController {
mutationObserver.observe(parent, {attributes: true});
}
+ /**
+ * @param {HTMLElement} element
+ * @returns {boolean}
+ */
_isElementVisible(element) {
return (element.offsetParent !== null);
}