aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/js/settings/anki-templates-controller.js8
-rw-r--r--ext/bg/js/settings/backup-controller.js29
-rw-r--r--ext/bg/js/settings/dictionary-controller.js19
-rw-r--r--ext/bg/js/settings/dictionary-import-controller.js12
-rw-r--r--ext/bg/js/settings/modal.js65
-rw-r--r--ext/bg/js/settings/profile-controller.js17
-rw-r--r--ext/bg/settings.html1
-rw-r--r--ext/mixed/js/core.js5
8 files changed, 115 insertions, 41 deletions
diff --git a/ext/bg/js/settings/anki-templates-controller.js b/ext/bg/js/settings/anki-templates-controller.js
index c980bfa2..f2e5be43 100644
--- a/ext/bg/js/settings/anki-templates-controller.js
+++ b/ext/bg/js/settings/anki-templates-controller.js
@@ -17,6 +17,7 @@
/* global
* AnkiNoteBuilder
+ * Modal
* TemplateRendererProxy
* api
*/
@@ -28,12 +29,15 @@ class AnkiTemplatesController {
this._cachedDefinitionValue = null;
this._cachedDefinitionText = null;
this._defaultFieldTemplates = null;
+ this._fieldTemplateResetModal = null;
this._templateRenderer = new TemplateRendererProxy();
}
async prepare() {
this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates();
+ this._fieldTemplateResetModal = new Modal(document.querySelector('#field-template-reset-modal'));
+
const markers = new Set([
...this._ankiController.getFieldMarkers('terms'),
...this._ankiController.getFieldMarkers('kanji')
@@ -69,13 +73,13 @@ class AnkiTemplatesController {
_onReset(e) {
e.preventDefault();
- $('#field-template-reset-modal').modal('show');
+ this._fieldTemplateResetModal.setVisible(true);
}
_onResetConfirm(e) {
e.preventDefault();
- $('#field-template-reset-modal').modal('hide');
+ this._fieldTemplateResetModal.setVisible(false);
const value = this._defaultFieldTemplates;
diff --git a/ext/bg/js/settings/backup-controller.js b/ext/bg/js/settings/backup-controller.js
index 08ee7070..0676d451 100644
--- a/ext/bg/js/settings/backup-controller.js
+++ b/ext/bg/js/settings/backup-controller.js
@@ -16,6 +16,7 @@
*/
/* global
+ * Modal
* OptionsUtil
* api
*/
@@ -26,12 +27,19 @@ class BackupController {
this._settingsExportToken = null;
this._settingsExportRevoke = null;
this._currentVersion = 0;
+ this._settingsResetModal = null;
+ this._settingsImportErrorModal = null;
+ this._settingsImportWarningModal = null;
this._optionsUtil = new OptionsUtil();
}
async prepare() {
await this._optionsUtil.prepare();
+ this._settingsResetModal = new Modal(document.querySelector('#settings-reset-modal'));
+ this._settingsImportErrorModal = new Modal(document.querySelector('#settings-import-error-modal'));
+ this._settingsImportWarningModal = new Modal(document.querySelector('#settings-import-warning-modal'));
+
document.querySelector('#settings-export').addEventListener('click', this._onSettingsExportClick.bind(this), false);
document.querySelector('#settings-import').addEventListener('click', this._onSettingsImportClick.bind(this), false);
document.querySelector('#settings-import-file').addEventListener('change', this._onSettingsImportFileChange.bind(this), false);
@@ -153,14 +161,14 @@ class BackupController {
_showSettingsImportError(error) {
yomichan.logError(error);
document.querySelector('#settings-import-error-modal-message').textContent = `${error}`;
- $('#settings-import-error-modal').modal('show');
+ this._settingsImportErrorModal.setVisible(true);
}
async _showSettingsImportWarnings(warnings) {
- const modalNode = $('#settings-import-warning-modal');
+ const modal = this._settingsImportWarningModal;
const buttons = document.querySelectorAll('.settings-import-warning-modal-import-button');
const messageContainer = document.querySelector('#settings-import-warning-modal-message');
- if (modalNode.length === 0 || buttons.length === 0 || messageContainer === null) {
+ if (buttons.length === 0 || messageContainer === null) {
return {result: false};
}
@@ -175,7 +183,7 @@ class BackupController {
messageContainer.appendChild(fragment);
// Show modal
- modalNode.modal('show');
+ modal.setVisible(true);
// Wait for modal to close
return new Promise((resolve) => {
@@ -185,9 +193,10 @@ class BackupController {
result: true,
sanitize: e.currentTarget.dataset.importSanitize === 'true'
});
- modalNode.modal('hide');
+ modal.setVisible(false);
};
- const onModalHide = () => {
+ const onModalVisibilityChanged = ({visible}) => {
+ if (visible) { return; }
complete({result: false});
};
@@ -196,7 +205,7 @@ class BackupController {
if (completed) { return; }
completed = true;
- modalNode.off('hide.bs.modal', onModalHide);
+ modal.off('visibilityChanged', onModalVisibilityChanged);
for (const button of buttons) {
button.removeEventListener('click', onButtonClick, false);
}
@@ -205,7 +214,7 @@ class BackupController {
};
// Hook events
- modalNode.on('hide.bs.modal', onModalHide);
+ modal.on('visibilityChanged', onModalVisibilityChanged);
for (const button of buttons) {
button.addEventListener('click', onButtonClick, false);
}
@@ -368,11 +377,11 @@ class BackupController {
// Resetting
_onSettingsResetClick() {
- $('#settings-reset-modal').modal('show');
+ this._settingsResetModal.setVisible(true);
}
async _onSettingsResetConfirmClick() {
- $('#settings-reset-modal').modal('hide');
+ this._settingsResetModal.setVisible(false);
// Get default options
const optionsFull = this._optionsUtil.getDefault();
diff --git a/ext/bg/js/settings/dictionary-controller.js b/ext/bg/js/settings/dictionary-controller.js
index 75022d1f..afc198e2 100644
--- a/ext/bg/js/settings/dictionary-controller.js
+++ b/ext/bg/js/settings/dictionary-controller.js
@@ -16,6 +16,7 @@
*/
/* global
+ * Modal
* ObjectPropertyAccessor
* api
*/
@@ -164,7 +165,7 @@ class DictionaryController {
this._checkIntegrityButton = document.querySelector('#dict-check-integrity');
this._dictionaryEntryContainer = document.querySelector('#dict-groups');
this._integrityExtraInfoContainer = document.querySelector('#dict-groups-extra');
- this._deleteDictionaryModal = document.querySelector('#dict-delete-modal');
+ this._deleteDictionaryModal = new Modal(document.querySelector('#dict-delete-modal'));
yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this));
@@ -177,9 +178,9 @@ class DictionaryController {
deleteDictionary(dictionaryTitle) {
if (this._isDeleting) { return; }
const modal = this._deleteDictionaryModal;
- modal.dataset.dictionaryTitle = dictionaryTitle;
- modal.querySelector('#dict-remove-modal-dict-name').textContent = dictionaryTitle;
- this._setModalVisible(modal, true);
+ modal.node.dataset.dictionaryTitle = dictionaryTitle;
+ modal.node.querySelector('#dict-remove-modal-dict-name').textContent = dictionaryTitle;
+ modal.setVisible(true);
}
// Private
@@ -209,11 +210,11 @@ class DictionaryController {
e.preventDefault();
const modal = this._deleteDictionaryModal;
- this._setModalVisible(modal, false);
+ modal.setVisible(false);
- const title = modal.dataset.dictionaryTitle;
+ const title = modal.node.dataset.dictionaryTitle;
if (typeof title !== 'string') { return; }
- delete modal.dataset.dictionaryTitle;
+ delete modal.node.dataset.dictionaryTitle;
this._deleteDictionary(title);
}
@@ -223,10 +224,6 @@ class DictionaryController {
this._checkIntegrity();
}
- _setModalVisible(node, visible) {
- $(node).modal(visible ? 'show' : 'hide');
- }
-
_updateMainDictionarySelectOptions(dictionaries) {
const fragment = document.createDocumentFragment();
diff --git a/ext/bg/js/settings/dictionary-import-controller.js b/ext/bg/js/settings/dictionary-import-controller.js
index dd4889dc..a78378e8 100644
--- a/ext/bg/js/settings/dictionary-import-controller.js
+++ b/ext/bg/js/settings/dictionary-import-controller.js
@@ -18,6 +18,7 @@
/* global
* DictionaryDatabase
* DictionaryImporter
+ * Modal
* ObjectPropertyAccessor
* api
*/
@@ -53,7 +54,7 @@ class DictionaryImportController {
this._purgeConfirmButton = document.querySelector('#dict-purge-confirm');
this._importFileButton = document.querySelector('#dict-file-button');
this._importFileInput = document.querySelector('#dict-file');
- this._purgeConfirmModal = document.querySelector('#dict-purge-modal');
+ this._purgeConfirmModal = new Modal(document.querySelector('#dict-purge-modal'));
this._errorContainer = document.querySelector('#dict-error');
this._spinner = document.querySelector('#dict-spinner');
this._progressContainer = document.querySelector('#dict-import-progress');
@@ -75,12 +76,12 @@ class DictionaryImportController {
_onPurgeButtonClick(e) {
e.preventDefault();
- this._setPurgeModalVisible(true);
+ this._purgeConfirmModal.setVisible(true);
}
_onPurgeConfirmButtonClick(e) {
e.preventDefault();
- this._setPurgeModalVisible(false);
+ this._purgeConfirmModal.setVisible(false);
this._purgeDatabase();
}
@@ -220,11 +221,6 @@ class DictionaryImportController {
return await this._modifyGlobalSettings(targets);
}
- _setPurgeModalVisible(visible) {
- const node = $(this._purgeConfirmModal);
- node.modal(visible ? 'show' : 'hide');
- }
-
_setSpinnerVisible(visible) {
this._spinner.hidden = !visible;
}
diff --git a/ext/bg/js/settings/modal.js b/ext/bg/js/settings/modal.js
new file mode 100644
index 00000000..42a511ca
--- /dev/null
+++ b/ext/bg/js/settings/modal.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 Yomichan 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/>.
+ */
+
+class Modal extends EventDispatcher {
+ constructor(node) {
+ super();
+ this._node = node;
+ this._eventListeners = new EventListenerCollection();
+ }
+
+ get node() {
+ return this._node;
+ }
+
+ setVisible(value) {
+ this._getWrappedNode().modal(value ? 'show' : 'hide');
+ }
+
+ on(eventName, callback) {
+ if (eventName === 'visibilityChanged') {
+ if (this._eventListeners.size === 0) {
+ const wrappedNode = this._getWrappedNode();
+ this._eventListeners.on(wrappedNode, 'hidden.bs.modal', this._onModalHide.bind(this));
+ this._eventListeners.on(wrappedNode, 'shown.bs.modal', this._onModalShow.bind(this));
+ }
+ }
+ return super.on(eventName, callback);
+ }
+
+ off(eventName, callback) {
+ const result = super.off(eventName, callback);
+ if (eventName === 'visibilityChanged' && !this.hasListeners(eventName)) {
+ this._eventListeners.removeAllEventListeners();
+ }
+ return result;
+ }
+
+ // Private
+
+ _onModalHide() {
+ this.trigger('visibilityChanged', {visible: false});
+ }
+
+ _onModalShow() {
+ this.trigger('visibilityChanged', {visible: true});
+ }
+
+ _getWrappedNode() {
+ return $(this._node);
+ }
+}
diff --git a/ext/bg/js/settings/profile-controller.js b/ext/bg/js/settings/profile-controller.js
index fd7137be..7c6dfae5 100644
--- a/ext/bg/js/settings/profile-controller.js
+++ b/ext/bg/js/settings/profile-controller.js
@@ -16,6 +16,7 @@
*/
/* global
+ * Modal
* ProfileConditionsUI
* api
*/
@@ -51,8 +52,8 @@ class ProfileController {
this._profileCopyButton = document.querySelector('#profile-copy');
this._profileMoveUpButton = document.querySelector('#profile-move-up');
this._profileMoveDownButton = document.querySelector('#profile-move-down');
- this._profileRemoveModal = document.querySelector('#profile-remove-modal');
- this._profileCopyModal = document.querySelector('#profile-copy-modal');
+ this._profileRemoveModal = new Modal(document.querySelector('#profile-remove-modal'));
+ this._profileCopyModal = new Modal(document.querySelector('#profile-copy-modal'));
this._profileActiveSelect.addEventListener('change', this._onProfileActiveChange.bind(this), false);
this._profileTargetSelect.addEventListener('change', this._onProfileTargetChange.bind(this), false);
@@ -135,11 +136,11 @@ class ProfileController {
const profileIndex = this._settingsController.profileIndex;
const profile = this._optionsFull.profiles[profileIndex];
this._removeProfileNameElement.textContent = profile.name;
- this._setModalVisible(this._profileRemoveModal, true);
+ this._profileRemoveModal.setVisible(true);
}
_onRemoveConfirm() {
- this._setModalVisible(this._profileRemoveModal, false);
+ this._profileRemoveModal.setVisible(false);
if (this._optionsFull.profiles.length <= 1) { return; }
const profileIndex = this._settingsController.profileIndex;
this._removeProfile(profileIndex);
@@ -160,11 +161,11 @@ class ProfileController {
}
this._profileCopySourceSelect.value = `${copyFromIndex}`;
- this._setModalVisible(this._profileCopyModal, true);
+ this._profileCopyModal.setVisible(true);
}
_onCopyConfirm() {
- this._setModalVisible(this._profileCopyModal, false);
+ this._profileCopyModal.setVisible(false);
const profileIndex = this._settingsController.profileIndex;
const max = this._optionsFull.profiles.length;
@@ -265,10 +266,6 @@ class ProfileController {
return null;
}
- _setModalVisible(node, visible) {
- $(node).modal(visible ? 'show' : 'hide');
- }
-
async _addProfile() {
const profileIndex = this._settingsController.profileIndex;
const profiles = this._optionsFull.profiles;
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 7141776c..9c8621f7 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -1216,6 +1216,7 @@
<script src="/bg/js/template-renderer-proxy.js"></script>
<script src="/bg/js/settings/keyboard-mouse-input-field.js"></script>
+ <script src="/bg/js/settings/modal.js"></script>
<script src="/bg/js/settings/profile-conditions-ui.js"></script>
<script src="/bg/js/settings/anki-controller.js"></script>
diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js
index 351d9371..7eb21659 100644
--- a/ext/mixed/js/core.js
+++ b/ext/mixed/js/core.js
@@ -325,6 +325,11 @@ class EventDispatcher {
}
return false;
}
+
+ hasListeners(eventName) {
+ const callbacks = this._eventMap.get(eventName);
+ return (typeof callbacks !== 'undefined' && callbacks.length > 0);
+ }
}
class EventListenerCollection {