From 0d1b36e970c49e6e6cfc72dcac4e2114d88de833 Mon Sep 17 00:00:00 2001
From: Khai Truong <56820749+khaitruong922@users.noreply.github.com>
Date: Sat, 5 Oct 2024 08:04:11 +0700
Subject: [PATCH] Create HTML templates for modals (#1425)

* reuse modals

* refactor

* only load templates when needed

* lint

* shared-modals

* lint

* type

* lint

* lint
---
 ext/js/display/search-main.js             |    4 +-
 ext/js/pages/permissions-main.js          |    4 +-
 ext/js/pages/settings/modal-controller.js |   20 +-
 ext/js/pages/settings/settings-main.js    |    4 +-
 ext/js/pages/welcome-main.js              |    4 +-
 ext/settings.html                         | 1476 --------------------
 ext/templates-modals.html                 | 1477 +++++++++++++++++++++
 ext/welcome.html                          |  333 -----
 8 files changed, 1503 insertions(+), 1819 deletions(-)
 create mode 100644 ext/templates-modals.html

diff --git a/ext/js/display/search-main.js b/ext/js/display/search-main.js
index 050fe167bb..377953f68f 100644
--- a/ext/js/display/search-main.js
+++ b/ext/js/display/search-main.js
@@ -54,8 +54,8 @@ await Application.main(true, async (application) => {
     const searchDisplayController = new SearchDisplayController(display, displayAudio, searchPersistentStateController);
     await searchDisplayController.prepare();
 
-    const modalController = new ModalController();
-    modalController.prepare();
+    const modalController = new ModalController([]);
+    await modalController.prepare();
 
     const settingsController = new SettingsController(application);
     await settingsController.prepare();
diff --git a/ext/js/pages/permissions-main.js b/ext/js/pages/permissions-main.js
index 9f37f11bfd..cc31f8d6ba 100644
--- a/ext/js/pages/permissions-main.js
+++ b/ext/js/pages/permissions-main.js
@@ -87,8 +87,8 @@ function setupPermissionsToggles() {
 }
 
 await Application.main(true, async (application) => {
-    const modalController = new ModalController();
-    modalController.prepare();
+    const modalController = new ModalController([]);
+    await modalController.prepare();
 
     const settingsController = new SettingsController(application);
     await settingsController.prepare();
diff --git a/ext/js/pages/settings/modal-controller.js b/ext/js/pages/settings/modal-controller.js
index 36e8505542..fb3a0dbc14 100644
--- a/ext/js/pages/settings/modal-controller.js
+++ b/ext/js/pages/settings/modal-controller.js
@@ -16,18 +16,34 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+import {HtmlTemplateCollection} from '../../dom/html-template-collection.js';
 import {Modal} from './modal.js';
 
 export class ModalController {
-    constructor() {
+    /**
+     * @param {string[]} templateNames
+     */
+    constructor(templateNames) {
         /** @type {Modal[]} */
         this._modals = [];
         /** @type {Map<string|Element, Modal>} */
         this._modalMap = new Map();
+        /** @type {HtmlTemplateCollection} */
+        this._templates = new HtmlTemplateCollection();
+        /** @type {string[]} */
+        this._templateNames = templateNames;
     }
 
     /** */
-    prepare() {
+    async prepare() {
+        if (this._templateNames.length > 0) {
+            await this._templates.loadFromFiles(['/templates-modals.html']);
+            for (const name of this._templateNames) {
+                const template = this._templates.getTemplateContent(name);
+                document.body.appendChild(template);
+            }
+        }
+
         const idSuffix = '-modal';
         for (const node of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('.modal'))) {
             let {id} = node;
diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js
index ebb51276c3..f070c03876 100644
--- a/ext/js/pages/settings/settings-main.js
+++ b/ext/js/pages/settings/settings-main.js
@@ -85,8 +85,8 @@ await Application.main(true, async (application) => {
 
     const preparePromises = [];
 
-    const modalController = new ModalController();
-    modalController.prepare();
+    const modalController = new ModalController(['shared-modals', 'settings-modals']);
+    await modalController.prepare();
 
     const settingsController = new SettingsController(application);
     await settingsController.prepare();
diff --git a/ext/js/pages/welcome-main.js b/ext/js/pages/welcome-main.js
index 349b09ac49..ae00c0c637 100644
--- a/ext/js/pages/welcome-main.js
+++ b/ext/js/pages/welcome-main.js
@@ -60,8 +60,8 @@ async function checkNeedsCustomTemplatesWarning() {
 }
 
 await Application.main(true, async (application) => {
-    const modalController = new ModalController();
-    modalController.prepare();
+    const modalController = new ModalController(['shared-modals']);
+    await modalController.prepare();
 
     const settingsController = new SettingsController(application);
     await settingsController.prepare();
diff --git a/ext/settings.html b/ext/settings.html
index e62558f798..5d17659b75 100644
--- a/ext/settings.html
+++ b/ext/settings.html
@@ -2376,1481 +2376,5 @@ <h1>Yomitan Settings</h1>
 
 <div id="popup-menus"></div>
 
-
-<!-- Profile modals -->
-<div id="profiles-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Profiles</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="profile-entry-header">
-            <div class="profile-entry-cell"></div>
-            <div class="profile-entry-cell"><span class="profile-entry-header-text">Default</span></div>
-            <div class="profile-entry-cell"><span class="profile-entry-header-text">Name</span></div>
-            <div class="profile-entry-cell"><span class="profile-entry-header-text">Conditions</span></div>
-            <div class="profile-entry-cell"></div>
-        </div>
-        <div class="profile-entry-list generic-list" id="profile-entry-list"></div>
-        <div class="profile-add-button-container">
-            <button type="button" class="low-emphasis" id="profile-add-button">Add</button>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="profile-conditions-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Profile Conditions</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Conditions for profile <em id="profile-conditions-profile-name"></em>:
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <a tabindex="0" class="more-toggle more-only" data-parent-distance="3">Info&hellip;</a>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                <p>
-                    Profile usage conditions are used to automatically select certain profiles based on context.
-                    For example, different profiles can be used depending on the nested level of the popup, or based on the website's URL.
-                </p>
-                <p>
-                    Conditions are organized into groups corresponding to the order in which they are checked.
-                    If all of the conditions in any group of a profile are met, then that profile will be used for that context.
-                </p>
-                <p>
-                    If no conditions are specified, the profile will only be used if it is selected as the default profile.
-                </p>
-                <p>
-                    <a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a>
-                </p>
-            </div>
-        </div>
-        <div class="profile-condition-groups" id="profile-condition-groups"></div>
-        <div class="profile-condition-group-list-info">
-            <div class="profile-condition-groups-empty-info"><em>No conditions set up.</em></div>
-            <div class="profile-condition-group-list-info-space"></div>
-            <button type="button" class="low-emphasis" id="profile-add-condition-group">Add Group</button>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="profile-copy-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header">
-        <div class="modal-title">Copy Profile</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>Select which profile to copy options from:</p>
-        <select class="form-control" id="profile-copy-source-select"></select>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" id="profile-copy-confirm-button">Copy Profile</button>
-    </div>
-</div></div>
-
-<div id="profile-remove-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Profile Deletion</div></div>
-    <div class="modal-body">
-        <p>
-            Are you sure you want to delete the profile <em id="profile-remove-name"></em>?
-        </p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" id="profile-remove-confirm-button">Remove Profile</button>
-    </div>
-</div></div>
-
-
-<!-- Dictionary modals -->
-<div id="dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Enable support for prefix wildcard searches
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" data-setting="global.database.prefixWildcardsSupported" data-scope="global"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                <p>
-                    In order for dictionaries to support searches using prefix wildcards on the search page,
-                    some additional data must be stored in the database.
-                    Enabling this option will include this extra data for any new dictionaries that are imported.
-                </p>
-                <p class="warning-text">
-                    This option will not change any dictionaries that are already imported;
-                    they must be re-imported for the option to take effect.
-                </p>
-                <p>
-                    <a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a>
-                </p>
-            </div>
-        </div>
-
-        <div class="warning-text margin-above no-dictionaries-installed-warning" hidden>
-            No dictionaries have been installed yet.
-            Visit the <a href="https://github.com/themoeway/yomitan/blob/master/docs/dictionaries.md#dictionaries" target="_blank" rel="noopener noreferrer">Yomitan homepage</a>
-            for a list of free dictionaries or click the <em>Import</em> button below to select a dictionary file to import.
-        </div>
-        <div id="dictionary-error" class="danger-text margin-above" hidden></div>
-        <div id="dictionary-list" class="dictionary-list generic-list" data-count="0">
-            <div class="dictionary-item-top"></div>
-            <label class="dictionary-item-top toggle dictionary-item-enabled-toggle-container"><input type="checkbox" id="all-dictionaries-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-            <div class="dictionary-item-top dictionary-item-title-container">All</div>
-            <div class="dictionary-item-top advanced-only">Priority</div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-        </div>
-
-        <div hidden><input type="file" id="dictionary-import-file-input" accept=".zip,application/zip" multiple></div>
-    </div>
-    <div class="modal-body-addon dictionary-delete-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
-    </div>
-    <div class="modal-body-addon dictionary-import-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis danger dictionary-database-mutating-input" id="dictionary-delete-all-button">Delete All</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input debug-only" id="dictionary-check-integrity">Check Integrity</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-check-updates">Check for Updates</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-button">Import</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-import-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-medium">
-    <div class="modal-header"><div class="modal-title">Import Dictionaries</div></div>
-    <div class="modal-body">
-        <div id="dictionary-drop-file-zone">
-            <div id="dictionary-drag-drop-text">
-                <span class="icon" data-icon="book"></span>
-                <h1>Drag and drop dictionaries (.zip)</h1>
-                <h5>or click here to upload</h5>
-            </div>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide" class="basic-only">Close</button>
-    </div>
-    <div class="modal-body advanced-only">
-        <p>Import dictionaries from URLs:</p>
-        <textarea type="text" id="dictionary-import-url-text"></textarea>
-    </div>
-    <div class="modal-footer advanced-only">
-        <button type="button" data-modal-action="hide" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-url-button">Import from URLs</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-delete-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to delete the dictionary:</p>
-        <p><strong id="dictionary-confirm-delete-name"></strong>?</p>
-        <p class="danger-text">This action cannot be undone.</p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-button">Delete</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-delete-all-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to delete <strong>all dictionaries</strong>?</p>
-        <p class="danger-text">This action cannot be undone.</p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-all-button">Delete</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-update-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Update</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to update the dictionary:</p>
-        <p><strong id="dictionary-confirm-update-name"></strong>?</p>
-        <section>
-            Updating a dictionary involves:
-            <ul>
-                <li>Deleting the installed version</li>
-                <li>Downloading the latest version </li>
-                <li>Importing the latest version</li>
-            </ul>
-            <p class="warning-text">Especially for large dictionaries, this process can take a while, and downloading will use your network.</p>
-        </section>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-update-button">Update</button>
-    </div>
-</div></div>
-
-<div id="secondary-search-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Secondary Search Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>
-            These dictionaries will be used to search for definitions of the related terms when the grouping mode is
-            <em>Group related terms</em>.
-        </p>
-        <div id="secondary-search-dictionary-list" class="secondary-search-dictionary-list"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="collapsible-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Collapsible Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body-addon">
-        <p>
-            Dictionary definitions can be collapsed if they exceed a certain line count,
-            which may be useful for dictionaries with long definitions.
-            The appearance can be customized using custom CSS.
-            <a tabindex="0" data-modal-action="show,collapsible-dictionaries-info">More&hellip;</a>
-        </p>
-    </div>
-    <div class="modal-body">
-        <div id="collapsible-dictionary-list" class="collapsible-dictionary-list"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="collapsible-dictionaries-info-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Collapsible Dictionary Info</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>
-            Dictionary definitions can be collapsed if they exceed a certain line count,
-            which may be useful for dictionaries with long definitions.
-            There are five different modes:
-        </p>
-        <ul>
-            <li>
-                <strong>Not collapsible</strong> -
-                Definitions will not be collapsed.
-            </li>
-            <li>
-                <strong>Collapsed</strong> -
-                Definitions will show a collapse button if their size exceeds the max height,
-                and they will be collapsed by default.
-            </li>
-            <li>
-                <strong>Expanded</strong> -
-                Definitions will show a collapse button if their size exceeds the max height,
-                and they will be expanded by default.
-            </li>
-            <li>
-                <strong>Force collapsed</strong> -
-                Definitions will always show a collapse button,
-                and they will be collapsed by default.
-            </li>
-            <li>
-                <strong>Force expanded</strong> -
-                Definitions will always show a collapse button,
-                and they will be expanded by default.
-            </li>
-        </ul>
-        <p>
-            By default, the number of lines shown for a definition is 3.
-            This can be configured by adjusting the <a tabindex="0" data-modal-action="show,custom-css">custom CSS</a>;
-            the value can be a unitless integer or decimal number.
-        </p>
-        <div class="code margin-above">/* Globally set the line count */
-:root {
-    --collapsible-definition-line-count: 2;
-}
-
-/* Set the line count for a specific dictionary */
-.definition-item[data-dictionary='JMdict'] {
-    --collapsible-definition-line-count: 2;
-}
-
-/* Spoiler-like functionality, use with <em>Force collapsed</em> mode */
-.definition-item[data-dictionary='JMdict'] .definition-item-inner.collapsible.collapsed {
-    color: #000000;
-    background-color: #000000;
-}
-</div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-details-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title"><strong class="dictionary-title"></strong> <span class="light dictionary-revision"></span></div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item dictionary-outdated-notification" hidden><div class="settings-item-children danger-text">
-            This dictionary is outdated and may not support new extension features.
-            Re-import the dictionary to enable support for the latest features.
-        </div></div>
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Prefix wildcard searches supported
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-prefix-wildcard-searches-supported" disabled><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                <p class="warning-text">
-                    Changing this value requires the dictionary to be re-imported.
-                </p>
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <div class="settings-item dictionary-parts-of-speech-filter-setting" hidden>
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Part of speech filtering
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-parts-of-speech-filter-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                When deinflecting words, only dictionary entries whose POS matches that expected by the deinflector will be shown.
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <div class="settings-item dictionary-use-deinflections-setting" hidden>
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Use deinflections
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-use-deinflections-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                Deinflections from this dictionary will be used.
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <hr>
-        <div class="settings-item"><div class="settings-item-children">
-            <div class="dictionary-details-table"></div>
-            <div class="dictionary-counts"></div>
-        </div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-extra-data-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">
-            <strong class="dictionary-title">Unassociated Data</strong> <span class="light dictionary-total-count"></span>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p class="warning-text">
-            The database contains extra data which is not associated with any installed dictionary.
-            Purging the database can fix this issue.
-        </p>
-        <div class="dictionary-counts"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-move-location-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Move Dictionary Options</div></div>
-    <div class="modal-body">
-        <p>Input the location the dictionary <strong class="dictionary-title"></strong> should be moved to:</p>
-        <div class="margin-above">
-            <input type="number" id="dictionary-move-location" min="1" step="1">
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" data-modal-action="hide" id="dictionary-move-button">Move</button>
-    </div>
-</div></div>
-
-<div id="dictionary-set-alias-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Rename</div></div>
-    <div class="modal-body">
-        <p>Input the display name for <strong class="dictionary-title"></strong> dictionary:</p>
-        <div class="margin-above">
-            <input type="text" id="dictionary-alias-input">
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" id="dictionary-reset-alias-button">Reset</button>
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" data-modal-action="hide" id="dictionary-set-alias-button">Save</button>
-    </div>
-</div></div>
-
-<!-- Custom CSS modal -->
-<div id="custom-css-modal" class="modal modal-left" tabindex="-1" role="dialog" hidden>
-    <div class="modal-content-container">
-        <div class="modal-content-dimmer"></div>
-        <div class="modal-content">
-            <div class="modal-header">
-                <div class="modal-title">Custom CSS</div>
-                <div class="modal-header-button-container">
-                    <div class="modal-header-button-group">
-                        <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                        <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-                    </div>
-                </div>
-            </div>
-
-            <div class="modal-body custom-popup-css-container">
-                <div class="custom-popup-css-header">Popup CSS</div>
-                <textarea class="no-wrap" autocomplete="off" spellcheck="false" id="custom-popup-css" data-setting="general.customPopupCss" data-tab-action="indent,4"></textarea>
-                <div class="custom-popup-css-header margin-above">Popup outer CSS</div>
-                <textarea class="no-wrap" autocomplete="off" spellcheck="false" id="custom-popup-outer-css" data-setting="general.customPopupOuterCss" data-tab-action="indent,4"></textarea>
-            </div>
-            <div class="modal-footer">
-                <button type="button" data-modal-action="hide">Close</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-
-<!-- Audio sources modal -->
-<div id="audio-sources-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Audio Sources</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        When searching for audio, the sources are checked in order until the first
-                        valid source is found. This allows for selecting a fallback source if the
-                        first choice is not available.
-                    </div>
-                </div>
-            </div>
-            <div class="settings-item-children">
-                <div id="audio-source-list" class="generic-list"></div>
-                <div id="audio-source-list-empty">
-                    No audio sources enabled
-                </div>
-            </div>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" id="audio-source-add" class="low-emphasis">Add</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="audio-source-help-custom-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header">
-        <div class="modal-title">Audio Source - Custom URL</div>
-    </div>
-    <div class="modal-body">
-        <p>
-            A custom URL can be used to play audio from any URL.
-            The replacement tags <code data-select-on-click="">{term}</code> and <code data-select-on-click="">{reading}</code>
-            can be used to specify which term and reading is being looked up.<br>
-        </p>
-        <p>
-            Example:<br>
-            <a tabindex="0" data-select-on-click="">http://localhost/audio.mp3?term={term}&amp;reading={reading}</a>
-        </p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="audio-source-help-custom-json-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header">
-        <div class="modal-title">Audio Source - Custom URL (JSON)</div>
-    </div>
-    <div class="modal-body">
-        <p>
-            A custom URL to a JSON file which lists one or more audio URLs for a given term.
-            The format of the JSON file is described in <a href="/data/schemas/custom-audio-list-schema.json" target="_blank" rel="noopener noreferrer">this schema file</a>.
-        </p>
-        <p>
-            Example:<br>
-            <a tabindex="0" data-select-on-click="">http://localhost/audio.json?term={term}&amp;reading={reading}</a>
-        </p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="audio-source-help-text-to-speech-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header">
-        <div class="modal-title">Audio Source - Text-to-speech</div>
-    </div>
-    <div class="modal-body">
-        <p>
-            A synthesized voice will speak the given text, using either the term text or the reading.
-        </p>
-        <div class="horizontal-flex margin-above">
-            <input type="text" value="よみたん" id="text-to-speech-voice-test-text" autocomplete="off" lang="ja">
-            <button type="button" id="text-to-speech-voice-test">Test</button>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Scanning inputs modal -->
-<div id="scanning-inputs-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Scanning Inputs</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div>
-            <p>
-                Scanning inputs are used to define when text scanning should occur.
-                <a tabindex="0" class="more-toggle more-only" data-parent-distance="2">More&hellip;</a>
-            </p>
-            <div class="margin-above more" hidden>
-                <p>
-                    Text scanning is performed when a pointer is moved and certain inputs are either pressed or not pressed.
-                    The <em>Required inputs</em> field is used to define which inputs <em>must</em> be pressed, and
-                    the <em>Excluded inputs</em> field is used to define which inputs <em>must not</em> be pressed.
-                    If the <em>Required inputs</em> field is empty, text will be scanned whenever the pointer is moved.
-                </p>
-                <p>
-                    The <em>Input types</em> group is used to define which types of pointer input that the
-                    keyboard and button inputs are applied to.
-                    Supported pointer types include the mouse cursor, touchscreen touches, and pen devices.
-                    When using the <em>Pen</em> option, the defined inputs will correspond to buttons on the pen device.
-                </p>
-                <p>
-                    Some additional scanning and search options can be configured by clicking the menu button and selecting
-                    <em>Show advanced options</em>.
-                </p>
-                <ul>
-                    <li>To assign keyboard keys, select the input field and press modifier keys on the keyboard.</li>
-                    <li>To assign mouse or pen buttons, click on the button with the mouse icon using the desired button.</li>
-                    <li>
-                        To clear inputs, select the input field and press the <em>Escape</em> button,
-                        or use the <em>Clear inputs</em> menu option.
-                    </li>
-                </ul>
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Less&hellip;</a></p>
-            </div>
-        </div>
-        <div class="scan-input-list generic-list margin-above" id="scan-input-list"></div>
-        <div class="warning-text margin-above generic-list-empty-indicator">
-            No scanning inputs have been defined yet.
-            Click the <em>Add</em> button to add a new input.
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" id="scan-input-add">Add</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Input action prevention modal -->
-<div id="input-action-prevention-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Input Action Prevention</div></div>
-    <div class="modal-body">
-        <div>
-            Prevent middle mouse button actions on:
-            <a tabindex="0" class="more-toggle more-only" data-parent-distance="2">(?)</a>
-        </div>
-        <div class="more" hidden>
-            <p>
-                This option is used to disable the default action of the middle mouse button in different contexts.
-                This can be useful for preventing the scroll action that the middle mouse button is typically mapped to,
-                which is otherwise difficult to disable inside extension pages via other means.
-            </p>
-            <p>
-                <a tabindex="0" class="more-toggle" data-parent-distance="3">Less&hellip;</a>
-            </p>
-        </div>
-        <div class="input-prevention-option-list">
-            <label class="input-prevention-option-list-item">
-                <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onWebPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
-                <span>Webpages</span>
-            </label>
-            <label class="input-prevention-option-list-item">
-                <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onPopupPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
-                <span>Popups</span>
-            </label>
-            <label class="input-prevention-option-list-item">
-                <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onSearchPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
-                <span>Search page</span>
-            </label>
-            <label class="input-prevention-option-list-item">
-                <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onSearchQuery"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
-                <span>Search query</span>
-            </label>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Anki cards modal -->
-<div id="anki-cards-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Anki Cards</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div>
-        <div class="tabs-container">
-            <div class="tabs">
-                <label class="tab">
-                    <input type="radio" name="anki-card-primary-type" data-value="terms" data-anki-card-menu="anki-card-terms-field-menu" checked>
-                    <div class="tab-inner"><span class="tab-label">Terms</span></div>
-                </label>
-                <label class="tab">
-                    <input type="radio" name="anki-card-primary-type" data-value="kanji" data-anki-card-menu="anki-card-kanji-field-menu">
-                    <div class="tab-inner"><span class="tab-label">Kanji</span></div>
-                </label>
-            </div>
-            <div class="tabs-right" hidden>
-                <button type="button" class="icon-button" data-menu-position="below left" id="anki-card-primary-type-menu-button"><span class="icon-button-inner"><span class="icon" data-icon="kebab-menu"></span></span></button>
-            </div>
-        </div>
-        <div class="modal-separator-line"></div>
-    </div>
-    <div class="modal-body anki-card" id="anki-card-primary" data-anki-card-type="terms" data-anki-card-menu="anki-card-terms-field-menu">
-        <div class="settings-item"><div class="settings-item-inner">
-            <div class="settings-item-left">
-                <div class="settings-item-label">Deck</div>
-            </div>
-            <div class="settings-item-right">
-                <select class="anki-card-deck"></select>
-            </div>
-        </div></div>
-        <div class="settings-item"><div class="settings-item-inner">
-            <div class="settings-item-left">
-                <div class="settings-item-label">Model</div>
-            </div>
-            <div class="settings-item-right">
-                <select class="anki-card-model"></select>
-            </div>
-        </div></div>
-        <div class="anki-card-fields">
-            <div class="anki-card-field-name-header" data-persistent="true">Field</div>
-            <div class="anki-card-field-input-header" data-persistent="true">Value</div>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="show,anki-cards-info">Help</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="anki-cards-info-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
-    <div class="modal-header">
-        <div class="modal-title">Anki Card Information</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>
-            Anki card fields can be populated with information about a term or kanji character by using field markers.
-            When a card is being generated, field markers are replaced with information about the term or kanji by using the installed dictionaries.
-            Several preset markers are available, which are described below.
-            Markers can be customized by adjusting the <a tabindex="0" data-modal-action="show,anki-card-templates">Anki card templates</a>.
-        </p>
-        <p>
-            Anki requires the first field in a model to be unique for a card;
-            therefore, it is recommended to use <code class="anki-field-marker">{expression}</code> as the marker for the first field of term cards,
-            or <code class="anki-field-marker">{character}</code> for kanji cards.
-        </p>
-        <table class="anki-field-marker-info-table margin-above">
-            <tbody>
-                <tr class="anki-field-marker-info-table-heading">
-                    <td>Marker (for terms)</td>
-                    <td>Description</td>
-                </tr>
-
-                <tr>
-                    <td><code class="anki-field-marker">{audio}</code></td>
-                    <td>Audio of the term's pronunciation from one of the audio sources (if available).</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{cloze-body-kana}</code></td>
-                    <td>Kana reading for <code class="anki-field-marker">{cloze-body}</code>.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{conjugation}</code></td>
-                    <td>Conjugation path from the raw inflected term to the source term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{expression}</code></td>
-                    <td>Term expressed using kanji. If kanji expression is not available, kana is used.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{furigana}</code></td>
-                    <td>
-                        Term expressed as kanji with furigana displayed above it.
-                        Example: <ruby>日本語<rt>にほんご</rt></ruby>.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{furigana-plain}</code></td>
-                    <td>
-                        Term expressed as kanji with furigana displayed next to it in brackets.
-                        Example: 日本語[にほんご].
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary}</code></td>
-                    <td>List of definitions for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary-brief}</code></td>
-                    <td>List of definitions for the term in a more compact format.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary-no-dictionary}</code></td>
-                    <td>List of definitions for the term, except the dictionary tag is omitted.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary-first}</code></td>
-                    <td>First definition for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary-first-brief}</code></td>
-                    <td>First definition for the term in a more compact format.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary-first-no-dictionary}</code></td>
-                    <td>First definition for the term, except the dictionary tag is omitted.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{part-of-speech}</code></td>
-                    <td>Part of speech information for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{phonetic-transcriptions}</code></td>
-                    <td>List of phonetic transcriptions for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{pitch-accents}</code></td>
-                    <td>List of pitch accent downstep notations for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{pitch-accent-graphs}</code></td>
-                    <td>List of pitch accent graphs for the term.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{pitch-accent-graphs-jj}</code></td>
-                    <td>List of pitch accent graphs for the term (styled after Jidoujisho).</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{pitch-accent-positions}</code></td>
-                    <td>List of accent downstep positions for the term as a number.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{pitch-accent-categories}</code></td>
-                    <td>List of pitch accent categories for the term (e.g. heiban, kifuku, atamadaka, odaka, nakadaka).</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{reading}</code></td>
-                    <td>Kana reading for the term, or empty for terms where the expression is the reading.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{single-glossary-DICT-NAME}</code></td>
-                    <td>
-                        Same as <code class="anki-field-marker">{glossary}</code>, but with entries from only a single dictionary.
-                        The dictionary name will likely be modified, use the options from the ▼ dropdown.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{single-glossary-DICT-NAME-brief}</code></td>
-                    <td>
-                        See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-brief}</code>.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{single-glossary-DICT-NAME-no-dictionary}</code></td>
-                    <td>
-                        See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-no-dictionary}</code>.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{tags}</code></td>
-                    <td>Grammar and usage tags providing information about the term.</td>
-                </tr>
-
-                <tr class="anki-field-marker-info-table-heading">
-                    <td>Marker (for kanji)</td>
-                    <td>Description</td>
-                </tr>
-
-                <tr>
-                    <td><code class="anki-field-marker">{character}</code></td>
-                    <td>Unicode glyph representing the current kanji.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{glossary}</code></td>
-                    <td>List of definitions for the kanji.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{kunyomi}</code></td>
-                    <td>Kunyomi (Japanese reading) for the kanji, expressed as hiragana.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{onyomi}</code></td>
-                    <td>Onyomi (Chinese reading) for the kanji, expressed as katakana.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{onyomi-hiragana}</code></td>
-                    <td>Onyomi (Chinese reading) for the kanji, expressed as hiragana.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{stroke-count}</code></td>
-                    <td>Number of strokes that the kanji character has.</td>
-                </tr>
-
-                <tr class="anki-field-marker-info-table-heading">
-                    <td>Marker (for both)</td>
-                    <td>Description</td>
-                </tr>
-
-                <tr>
-                    <td><code class="anki-field-marker">{clipboard-image}</code></td>
-                    <td>An image which is stored in the system clipboard, if available.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{clipboard-text}</code></td>
-                    <td>Text which is stored in the system clipboard, if available.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{cloze-body}</code></td>
-                    <td>Original inflected term as it appeared before being reduced to dictionary form by Yomitan.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{cloze-prefix}</code></td>
-                    <td>Fragment of the containing <code class="anki-field-marker">{sentence}</code> starting at the beginning of <code class="anki-field-marker">{sentence}</code> until the beginning of <code class="anki-field-marker">{cloze-body}</code>.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{cloze-suffix}</code></td>
-                    <td>Fragment of the containing <code class="anki-field-marker">{sentence}</code> starting at the end of <code class="anki-field-marker">{cloze-body}</code> until the end of <code class="anki-field-marker">{sentence}</code>.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{dictionary}</code></td>
-                    <td>Original name of the dictionary from which the card is being created.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{dictionary-alias}</code></td>
-                    <td>Display name of the dictionary from which the card is being created.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{document-title}</code></td>
-                    <td>Title of the web page that the term or kanji appeared in.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{frequencies}</code></td>
-                    <td>
-                        Frequency information for the term or kanji.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{frequency-harmonic-rank}</code></td>
-                    <td>
-                        The harmonic mean of frequency data for the current term or kanji.<br>
-                        Defaults to rank 9999999 when frequency data is not found, indicating extremely low rank-based term or kanji usage.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{frequency-harmonic-occurrence}</code></td>
-                    <td>
-                        The harmonic mean of frequency data for the current term or kanji.<br>
-                        Defaults to 0 occurrences when frequency data is not found, the lowest possible occurrence-based term or kanji usage.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{frequency-average-rank}</code></td>
-                    <td>
-                        The average of frequency data for the current term or kanji.<br>
-                        Defaults to rank 9999999 when frequency data is not found, indicating extremely low rank-based term or kanji usage.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{frequency-average-occurrence}</code></td>
-                    <td>
-                        The average of frequency data for the current term or kanji.<br>
-                        Defaults to 0 occurrences when frequency data is not found, the lowest possible occurrence-based term or kanji usage.
-                    </td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{screenshot}</code></td>
-                    <td>Screenshot of the web page taken at the time the term or kanji was added.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{search-query}</code></td>
-                    <td>The full search query shown on the search page.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{popup-selection-text}</code></td>
-                    <td>The selected text on the search page or popup.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{sentence}</code></td>
-                    <td>Sentence, quote, or phrase that the term or kanji appears in from the source content.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{sentence-furigana}</code></td>
-                    <td>Sentence, quote, or phrase that the term or kanji appears in from the source content, with furigana added.</td>
-                </tr>
-                <tr>
-                    <td><code class="anki-field-marker">{url}</code></td>
-                    <td>Address of the web page in which the term or kanji appeared in.</td>
-                </tr>
-            </tbody>
-        </table>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Anki field template modals -->
-<div id="anki-card-templates-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
-    <div class="modal-header"><div class="modal-title">Anki Card Templates</div></div>
-    <div class="modal-body anki-card-templates-layout">
-        <div class="anki-card-templates-info">
-            <p>
-                Anki card fields are formatted using the <a href="https://handlebarsjs.com/" target="_blank" rel="noopener noreferrer">Handlebars.js</a>
-                template rendering engine.
-                Advanced users can modify these templates for full control over what information is included in Anki cards.
-            </p>
-            <p>
-                Consider copy-pasting the source into a code editor that supports syntax highlighting for easier editing.
-            </p>
-        </div>
-        <textarea autocomplete="off" spellcheck="false" id="anki-card-templates-textarea" class="no-wrap margin-above" data-tab-action="indent,4"></textarea>
-        <div id="anki-card-templates-compile-result" class="code danger-text margin-above" hidden></div>
-        <div class="anki-card-templates-test-container margin-above">
-            <p>
-                Card templates can be tested using the inputs below.
-            </p>
-            <div class="anki-card-templates-test-table margin-above">
-                <div class="anki-card-templates-test-table-header">Scanned text</div>
-                <div class="anki-card-templates-test-table-header">Card field</div>
-                <div></div>
-                <input type="text" id="anki-card-templates-test-text-input" class="form-control" value="読め" placeholder="Preview text" autocomplete="off" lang="ja">
-                <div class="anki-card-templates-test-input-container input-group">
-                    <input type="text" id="anki-card-templates-test-field-input" value="{expression}" placeholder="{marker}" autocomplete="off" spellcheck="false">
-                    <button type="button" class="input-suffix input-suffix-icon-button light-icon" id="anki-card-templates-test-field-menu-button" data-menu="anki-card-all-field-menu" data-menu-position="below left"><span class="icon" data-icon="material-down-arrow"></span></button>
-                </div>
-                <button type="button" id="anki-card-templates-test-render-button">Test</button>
-            </div>
-        </div>
-        <div class="code margin-above" id="anki-card-templates-render-result"><em>Card render result</em></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="danger" id="anki-card-templates-reset-button">Reset Templates</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="anki-card-templates-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Reset Anki Card Templates</div></div>
-    <div class="modal-body">
-        <p class="danger-text">
-            Are you sure you want to reset the card templates to their default value?
-            Any changes you made will be lost.
-        </p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" id="anki-card-templates-reset-button-confirm">Reset Templates</button>
-    </div>
-</div></div>
-
-<!-- Generate anki deck modal -->
-<div id="generate-anki-notes-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
-    <div class="modal-header"><div class="modal-title">Anki Note Generator</div></div>
-    <div class="modal-body generate-anki-notes-layout">
-        <div class="generate-anki-notes-info">
-            <p class="warning-text">
-                WARNING: This feature is experimental!
-            </p>
-            <p>
-                Enter a newline separated list of terms below to send notes directly to an Anki deck or export to an Anki deck file in <code>Notes in plain text (.txt)</code> format.
-            </p>
-            <p>
-                For more information check the <a href="https://github.com/themoeway/yomitan/blob/master/docs/anki-integration.md#anki-note-generation">documentation</a>.
-            </p>
-        </div>
-        <textarea autocomplete="off" spellcheck="false" id="generate-anki-notes-textarea" class="no-wrap margin-above" data-tab-action="indent,4"></textarea>
-        <div class="generate-anki-notes-test-container margin-above">
-            <p>
-                Active Anki deck: <code id="generate-anki-notes-active-deck"></code><br>
-                Active Anki model: <code id="generate-anki-notes-active-model"></code>
-            </p>
-            <div class="generate-anki-notes-test-table margin-above">
-                <div class="generate-anki-notes-test-table-header">Test word</div>
-                <div></div>
-                <input type="text" id="generate-anki-notes-test-text-input" class="form-control" value="読め" placeholder="Preview text" autocomplete="off" lang="ja">
-                <button type="button" id="generate-anki-notes-test-render-button">Preview Card</button>
-            </div>
-        </div>
-        <div class="code margin-above" id="generate-anki-notes-render-result"><em>Card render result</em></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" id="generate-anki-notes-send-to-anki-button">Send to Anki</button>
-        <button type="button" class="low-emphasis" id="generate-anki-notes-export-button">Export to File</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="generate-anki-notes-send-to-anki-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Send Notes to Anki</div></div>
-    <div class="modal-body">
-        <p>
-            Are you sure you want to send <strong id="generate-anki-notes-send-wordcount"></strong> terms to <code id="generate-anki-notes-active-deck-confirm"></code>? This action cannot be undone.
-        </p>
-        <div class="settings-item margin-above"><div class="settings-item-inner">
-            <div class="settings-item-left">
-                <div class="settings-item-label">
-                    Add media to notes
-                </div>
-                <div class="settings-item-description">
-                    Adding media increases processing time.
-                </div>
-            </div>
-            <div class="settings-item-right">
-                <label class="toggle"><input type="checkbox" id="generate-anki-notes-add-media"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-            </div>
-        </div></div>
-        <div class="settings-item margin-above"><div class="settings-item-inner">
-            <div class="settings-item-left">
-                <div class="settings-item-label">
-                    Prevent sending duplicate notes
-                </div>
-                <div class="settings-item-description">
-                    Checking for duplicates increases processing time.
-                </div>
-            </div>
-            <div class="settings-item-right">
-                <label class="toggle"><input type="checkbox" id="generate-anki-notes-disallow-duplicates"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-            </div>
-        </div></div>
-    </div>
-    <div class="modal-body-addon generate-anki-notes-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide" id="generate-anki-notes-send-to-anki-cancel-button">Cancel</button>
-        <button type="button" class="danger" id="generate-anki-notes-send-button-confirm">Send to Anki</button>
-    </div>
-</div></div>
-
-<div id="generate-anki-notes-export-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Export Notes to File</div></div>
-    <div class="modal-body">
-        <p>
-            Are you sure you want to export <strong id="generate-anki-notes-export-wordcount"></strong> terms to <code>Notes in plain text (.txt)</code> format?
-        </p>
-    </div>
-    <div class="modal-body-addon generate-anki-notes-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide" id="generate-anki-notes-export-cancel-button">Cancel</button>
-        <button type="button" class="danger" id="generate-anki-notes-export-button-confirm">Export to File</button>
-    </div>
-</div></div>
-
-
-<!-- Import/export modals -->
-<div id="settings-import-error-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Import Error</div></div>
-    <div class="modal-body">
-        <p>An error occurred while trying to import the settings file:</p>
-        <p class="danger-text" id="settings-import-error-message"></p>
-        <p>Additional info can be found in the developer console.</p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="settings-import-warning-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Import Security Warning</div></div>
-    <div class="modal-body">
-        <p>
-            Settings file contains settings which may pose a security risk.
-            Only import settings from sources you trust.
-        </p>
-        <ul class="danger-text" id="settings-import-warning-message"></ul>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger settings-import-warning-import-button">Import</button>
-        <button type="button" class="settings-import-warning-import-button" data-import-sanitize="true">Sanitize and Import</button>
-    </div>
-</div></div>
-
-<div id="settings-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Reset Settings</div></div>
-    <div class="modal-body">
-        <p class="danger-text">
-            You are about to reset all Yomitan settings back to their default values.
-            This will delete all custom profiles you may have created.
-            <strong>This action cannot be undone.</strong>
-        </p>
-        <p>
-            Consider making a backup using the <em>Export Settings</em> button before resetting
-            if you want to be able to revert.
-        </p>
-        <p>
-            Dictionary data will not be deleted, but any installed dictionaries
-            will need to be re-enabled.
-        </p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" id="settings-reset-confirm-button">Reset All Settings</button>
-    </div>
-</div></div>
-
-
-<!-- Translation modals -->
-<div id="translation-text-replacement-patterns-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Custom Text Replacement Patterns</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item"><div class="settings-item-inner"><div class="settings-item-left"><div class="settings-item-label">
-            Text replacement patterns are used to modify or remove text that matches certain patterns.
-            Patterns are defined using
-            <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" target="_blank" rel="noreferrer noopener">regular expression syntax</a>,
-            and the replacement text can use certain
-            <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter" target="_blank" rel="noreferrer noopener">special replacement patterns</a>.
-        </div></div></div></div>
-        <div class="settings-item"><div class="settings-item-inner">
-            <div class="settings-item-left">
-                <div class="settings-item-label">
-                    Search original text
-                </div>
-                <div class="settings-item-description">
-                    The original unmodified text will also be searched for definitions.
-                </div>
-            </div>
-            <div class="settings-item-right">
-                <label class="toggle"><input type="checkbox" data-setting="translation.textReplacements.searchOriginal"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-            </div>
-        </div></div>
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">Text replacement patterns</div>
-                </div>
-            </div>
-            <div class="settings-item-children">
-                <div id="translation-text-replacement-list" class="generic-list"></div>
-                <div class="generic-list-empty-indicator"><em>None defined</em></div>
-            </div>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" id="translation-text-replacement-add" class="low-emphasis">Add</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Sentence parsing modal -->
-<div id="sentence-termination-characters-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Sentence Termination Characters</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>
-            Sentences are terminated by punctuation and quotation marks, which can both be configured below.
-        </p>
-        <table class="sentence-termination-character-list-table" id="sentence-termination-character-list-table" hidden>
-            <thead><tr>
-                <td>#</td>
-                <td>Enabled</td>
-                <td>Type</td>
-                <td>Character 1</td>
-                <td>Character 2</td>
-                <td>Include character in sentence</td>
-                <td></td>
-            </tr></thead>
-            <tbody class="sentence-termination-character-list generic-list" id="sentence-termination-character-list"></tbody>
-        </table>
-        <div id="sentence-termination-character-list-empty" hidden>
-            No terminators defined.
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis danger" id="sentence-termination-character-list-reset">Reset</button>
-        <button type="button" class="low-emphasis" id="sentence-termination-character-list-add">Add</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-
-<!-- Keyboard shortcuts modal -->
-<div id="keyboard-shortcuts-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
-    <div class="modal-header">
-        <div class="modal-title">Keyboard Shortcuts</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="hotkey-list generic-list" id="hotkey-list"></div>
-        <div class="hotkey-list-empty" id="hotkey-list-empty" hidden>
-            No keyboard shortcuts defined.
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis danger" data-modal-action="show,keyboard-shortcuts-reset">Reset</button>
-        <button type="button" class="low-emphasis" id="hotkey-list-add">Add</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="extension-keyboard-shortcuts-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Native Keyboard Shortcuts</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div>
-            <p data-show-for-browser="chrome edge">
-                The native keyboard shortcuts are listed below,
-                but cannot be configured from within the extension on this browser.
-                To configure these shortcuts:
-            </p>
-
-            <p data-show-for-browser="firefox">
-                The native keyboard shortcuts can be configured below on this browser,
-                or by doing the following:
-            </p>
-
-            <ul data-show-for-browser="chrome">
-                <li>Open <a tabindex="0" data-special-url="chrome://extensions/shortcuts">chrome://extensions/shortcuts</a> in a new tab.</li>
-                <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
-            </ul>
-
-            <ul data-show-for-browser="edge">
-                <li>Open <a tabindex="0" data-special-url="edge://extensions/shortcuts">edge://extensions/shortcuts</a> in a new tab.</li>
-                <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
-            </ul>
-
-            <ul data-show-for-browser="firefox">
-                <li>Open the extensions page (<a tabindex="0" data-select-on-click="">about:addons</a>)</li>
-                <li>Click the button on the right with the gear icon, then click <em>Manage Extension Shortcuts</em>.</li>
-                <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
-            </ul>
-        </div>
-        <div class="modal-separator-line"></div>
-        <div class="modal-settings-group" id="extension-hotkey-list"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis danger" id="extension-hotkey-list-reset-all">Reset All</button>
-        <button type="button" class="low-emphasis danger" id="extension-hotkey-list-clear-all">Clear All</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="keyboard-shortcuts-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Keyboard Shortcuts Reset</div></div>
-    <div class="modal-body">
-        Are you sure you want to reset all keyboard shortcuts to their defaults?
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" id="hotkey-list-reset" data-modal-action="hide">Reset All</button>
-    </div>
-</div></div>
-
-<!-- Recommended dictionary modals -->
-<div id="recommended-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Recommended Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div id="recommended-term-dictionaries" hidden>
-            <h1 class="modal-title">Term Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-kanji-dictionaries" hidden>
-            <h1 class="modal-title">Kanji Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-frequency-dictionaries" hidden>
-            <h1 class="modal-title">Frequency Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-grammar-dictionaries" hidden>
-            <h1 class="modal-title">Grammar Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-pronunciation-dictionaries" hidden>
-            <h1 class="modal-title">Pronunciation Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-    </div>
-    <div class="modal-body-addon dictionary-delete-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
-    </div>
-    <div class="modal-body-addon dictionary-import-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
 </body>
 </html>
diff --git a/ext/templates-modals.html b/ext/templates-modals.html
new file mode 100644
index 0000000000..09e4eb997c
--- /dev/null
+++ b/ext/templates-modals.html
@@ -0,0 +1,1477 @@
+<!DOCTYPE html><html><head><title>Templates</title></head><body>
+
+<template id="shared-modals-template">
+    <div id="dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+    <div class="modal-header">
+            <div class="modal-title">Dictionaries</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="settings-item">
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            Enable support for prefix wildcard searches
+                            <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
+                        </div>
+                    </div>
+                    <div class="settings-item-right">
+                        <label class="toggle"><input type="checkbox" data-setting="global.database.prefixWildcardsSupported" data-scope="global"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                    </div>
+                </div>
+                <div class="settings-item-children more" hidden>
+                    <p>
+                        In order for dictionaries to support searches using prefix wildcards on the search page,
+                        some additional data must be stored in the database.
+                        Enabling this option will include this extra data for any new dictionaries that are imported.
+                    </p>
+                    <p class="warning-text">
+                        This option will not change any dictionaries that are already imported;
+                        they must be re-imported for the option to take effect.
+                    </p>
+                    <p>
+                        <a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a>
+                    </p>
+                </div>
+            </div>
+
+            <div class="warning-text margin-above no-dictionaries-installed-warning" hidden>
+                No dictionaries have been installed yet.
+                Visit the <a href="https://github.com/themoeway/yomitan/blob/master/docs/dictionaries.md#dictionaries" target="_blank" rel="noopener noreferrer">Yomitan homepage</a>
+                for a list of free dictionaries or click the <em>Import</em> button below to select a dictionary file to import.
+            </div>
+            <div id="dictionary-error" class="danger-text margin-above" hidden></div>
+            <div id="dictionary-list" class="dictionary-list generic-list" data-count="0">
+                <div class="dictionary-item-top"></div>
+                <label class="dictionary-item-top toggle dictionary-item-enabled-toggle-container"><input type="checkbox" id="all-dictionaries-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                <div class="dictionary-item-top dictionary-item-title-container">All</div>
+                <div class="dictionary-item-top advanced-only">Priority</div>
+                <div class="dictionary-item-top dictionary-item-button-height"></div>
+                <div class="dictionary-item-top dictionary-item-button-height"></div>
+                <div class="dictionary-item-top dictionary-item-button-height"></div>
+            </div>
+
+            <div hidden><input type="file" id="dictionary-import-file-input" accept=".zip,application/zip" multiple></div>
+        </div>
+        <div class="modal-body-addon dictionary-delete-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
+        </div>
+        <div class="modal-body-addon dictionary-import-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar"></div></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis danger dictionary-database-mutating-input" id="dictionary-delete-all-button">Delete All</button>
+            <button type="button" class="low-emphasis dictionary-database-mutating-input debug-only" id="dictionary-check-integrity">Check Integrity</button>
+            <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-check-updates">Check for Updates</button>
+            <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-button">Import</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-import-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-medium">
+        <div class="modal-header"><div class="modal-title">Import Dictionaries</div></div>
+        <div class="modal-body">
+            <div id="dictionary-drop-file-zone">
+                <div id="dictionary-drag-drop-text">
+                    <span class="icon" data-icon="book"></span>
+                    <h1>Drag and drop dictionaries (.zip)</h1>
+                    <h5>or click here to upload</h5>
+                </div>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide" class="basic-only">Close</button>
+        </div>
+        <div class="modal-body advanced-only">
+            <p>Import dictionaries from URLs:</p>
+            <textarea type="text" id="dictionary-import-url-text"></textarea>
+        </div>
+        <div class="modal-footer advanced-only">
+            <button type="button" data-modal-action="hide" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-url-button">Import from URLs</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-confirm-delete-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
+        <div class="modal-body">
+            <p>Are you sure you want to delete the dictionary:</p>
+            <p><strong id="dictionary-confirm-delete-name"></strong>?</p>
+            <p class="danger-text">This action cannot be undone.</p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-button">Delete</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-confirm-delete-all-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
+        <div class="modal-body">
+            <p>Are you sure you want to delete <strong>all dictionaries</strong>?</p>
+            <p class="danger-text">This action cannot be undone.</p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-all-button">Delete</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-confirm-update-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Confirm Dictionary Update</div></div>
+        <div class="modal-body">
+            <p>Are you sure you want to update the dictionary:</p>
+            <p><strong id="dictionary-confirm-update-name"></strong>?</p>
+            <section>
+                Updating a dictionary involves:
+                <ul>
+                    <li>Deleting the installed version</li>
+                    <li>Downloading the latest version </li>
+                    <li>Importing the latest version</li>
+                </ul>
+                <p class="warning-text">Especially for large dictionaries, this process can take a while, and downloading will use your network.</p>
+            </section>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-update-button">Update</button>
+        </div>
+    </div></div>
+
+    <div id="secondary-search-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Secondary Search Dictionaries</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p>
+                These dictionaries will be used to search for definitions of the related terms when the grouping mode is
+                <em>Group related terms</em>.
+            </p>
+            <div id="secondary-search-dictionary-list" class="secondary-search-dictionary-list"></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-details-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title"><strong class="dictionary-title"></strong> <span class="light dictionary-revision"></span></div>
+        </div>
+        <div class="modal-body">
+            <div class="settings-item dictionary-outdated-notification" hidden><div class="settings-item-children danger-text">
+                This dictionary is outdated and may not support new extension features.
+                Re-import the dictionary to enable support for the latest features.
+            </div></div>
+            <div class="settings-item">
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            Prefix wildcard searches supported
+                            <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
+                        </div>
+                    </div>
+                    <div class="settings-item-right">
+                        <label class="toggle"><input type="checkbox" class="dictionary-prefix-wildcard-searches-supported" disabled><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                    </div>
+                </div>
+                <div class="settings-item-children more" hidden>
+                    <p class="warning-text">
+                        Changing this value requires the dictionary to be re-imported.
+                    </p>
+                    <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
+                </div>
+            </div>
+            <div class="settings-item dictionary-parts-of-speech-filter-setting" hidden>
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            Part of speech filtering
+                            <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
+                        </div>
+                    </div>
+                    <div class="settings-item-right">
+                        <label class="toggle"><input type="checkbox" class="dictionary-parts-of-speech-filter-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                    </div>
+                </div>
+                <div class="settings-item-children more" hidden>
+                    When deinflecting words, only dictionary entries whose POS matches that expected by the deinflector will be shown.
+                    <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
+                </div>
+            </div>
+            <div class="settings-item dictionary-use-deinflections-setting" hidden>
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            Use deinflections
+                            <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
+                        </div>
+                    </div>
+                    <div class="settings-item-right">
+                        <label class="toggle"><input type="checkbox" class="dictionary-use-deinflections-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                    </div>
+                </div>
+                <div class="settings-item-children more" hidden>
+                    Deinflections from this dictionary will be used.
+                    <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
+                </div>
+            </div>
+            <hr>
+            <div class="settings-item"><div class="settings-item-children">
+                <div class="dictionary-details-table"></div>
+                <div class="dictionary-counts"></div>
+            </div></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-extra-data-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">
+                <strong class="dictionary-title">Unassociated Data</strong> <span class="light dictionary-total-count"></span>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p class="warning-text">
+                The database contains extra data which is not associated with any installed dictionary.
+                Purging the database can fix this issue.
+            </p>
+            <div class="dictionary-counts"></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-move-location-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Move Dictionary Options</div></div>
+        <div class="modal-body">
+            <p>Input the location the dictionary <strong class="dictionary-title"></strong> should be moved to:</p>
+            <div class="margin-above">
+                <input type="number" id="dictionary-move-location" min="1" step="1">
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" data-modal-action="hide" id="dictionary-move-button">Move</button>
+        </div>
+    </div></div>
+
+    <div id="dictionary-set-alias-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Rename</div></div>
+        <div class="modal-body">
+            <p>Input the display name for <strong class="dictionary-title"></strong> dictionary:</p>
+            <div class="margin-above">
+                <input type="text" id="dictionary-alias-input">
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" id="dictionary-reset-alias-button">Reset</button>
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" data-modal-action="hide" id="dictionary-set-alias-button">Save</button>
+        </div>
+    </div></div>
+
+    <div id="recommended-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Recommended Dictionaries</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div id="recommended-term-dictionaries" hidden>
+                <h1 class="modal-title">Term Dictionaries</h1>
+                <div class="recommended-dictionary-list"></div>
+            </div>
+            <div id="recommended-kanji-dictionaries" hidden>
+                <h1 class="modal-title">Kanji Dictionaries</h1>
+                <div class="recommended-dictionary-list"></div>
+            </div>
+            <div id="recommended-frequency-dictionaries" hidden>
+                <h1 class="modal-title">Frequency Dictionaries</h1>
+                <div class="recommended-dictionary-list"></div>
+            </div>
+            <div id="recommended-grammar-dictionaries" hidden>
+                <h1 class="modal-title">Grammar Dictionaries</h1>
+                <div class="recommended-dictionary-list"></div>
+            </div>
+            <div id="recommended-pronunciation-dictionaries" hidden>
+                <h1 class="modal-title">Pronunciation Dictionaries</h1>
+                <div class="recommended-dictionary-list"></div>
+            </div>
+        </div>
+        <div class="modal-body-addon dictionary-delete-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
+        </div>
+        <div class="modal-body-addon dictionary-import-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar"></div></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+</template>
+
+<template id="settings-modals-template">
+    <div id="profiles-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Profiles</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="profile-entry-header">
+                <div class="profile-entry-cell"></div>
+                <div class="profile-entry-cell"><span class="profile-entry-header-text">Default</span></div>
+                <div class="profile-entry-cell"><span class="profile-entry-header-text">Name</span></div>
+                <div class="profile-entry-cell"><span class="profile-entry-header-text">Conditions</span></div>
+                <div class="profile-entry-cell"></div>
+            </div>
+            <div class="profile-entry-list generic-list" id="profile-entry-list"></div>
+            <div class="profile-add-button-container">
+                <button type="button" class="low-emphasis" id="profile-add-button">Add</button>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="profile-conditions-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Profile Conditions</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="settings-item">
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            Conditions for profile <em id="profile-conditions-profile-name"></em>:
+                        </div>
+                    </div>
+                    <div class="settings-item-right">
+                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="3">Info&hellip;</a>
+                    </div>
+                </div>
+                <div class="settings-item-children more" hidden>
+                    <p>
+                        Profile usage conditions are used to automatically select certain profiles based on context.
+                        For example, different profiles can be used depending on the nested level of the popup, or based on the website's URL.
+                    </p>
+                    <p>
+                        Conditions are organized into groups corresponding to the order in which they are checked.
+                        If all of the conditions in any group of a profile are met, then that profile will be used for that context.
+                    </p>
+                    <p>
+                        If no conditions are specified, the profile will only be used if it is selected as the default profile.
+                    </p>
+                    <p>
+                        <a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a>
+                    </p>
+                </div>
+            </div>
+            <div class="profile-condition-groups" id="profile-condition-groups"></div>
+            <div class="profile-condition-group-list-info">
+                <div class="profile-condition-groups-empty-info"><em>No conditions set up.</em></div>
+                <div class="profile-condition-group-list-info-space"></div>
+                <button type="button" class="low-emphasis" id="profile-add-condition-group">Add Group</button>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="profile-copy-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header">
+            <div class="modal-title">Copy Profile</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p>Select which profile to copy options from:</p>
+            <select class="form-control" id="profile-copy-source-select"></select>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" id="profile-copy-confirm-button">Copy Profile</button>
+        </div>
+    </div></div>
+
+    <div id="profile-remove-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Confirm Profile Deletion</div></div>
+        <div class="modal-body">
+            <p>
+                Are you sure you want to delete the profile <em id="profile-remove-name"></em>?
+            </p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" id="profile-remove-confirm-button">Remove Profile</button>
+        </div>
+    </div></div>
+
+    <div id="collapsible-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Collapsible Dictionaries</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body-addon">
+            <p>
+                Dictionary definitions can be collapsed if they exceed a certain line count,
+                which may be useful for dictionaries with long definitions.
+                The appearance can be customized using custom CSS.
+                <a tabindex="0" data-modal-action="show,collapsible-dictionaries-info">More&hellip;</a>
+            </p>
+        </div>
+        <div class="modal-body">
+            <div id="collapsible-dictionary-list" class="collapsible-dictionary-list"></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="collapsible-dictionaries-info-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Collapsible Dictionary Info</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p>
+                Dictionary definitions can be collapsed if they exceed a certain line count,
+                which may be useful for dictionaries with long definitions.
+                There are five different modes:
+            </p>
+            <ul>
+                <li>
+                    <strong>Not collapsible</strong> -
+                    Definitions will not be collapsed.
+                </li>
+                <li>
+                    <strong>Collapsed</strong> -
+                    Definitions will show a collapse button if their size exceeds the max height,
+                    and they will be collapsed by default.
+                </li>
+                <li>
+                    <strong>Expanded</strong> -
+                    Definitions will show a collapse button if their size exceeds the max height,
+                    and they will be expanded by default.
+                </li>
+                <li>
+                    <strong>Force collapsed</strong> -
+                    Definitions will always show a collapse button,
+                    and they will be collapsed by default.
+                </li>
+                <li>
+                    <strong>Force expanded</strong> -
+                    Definitions will always show a collapse button,
+                    and they will be expanded by default.
+                </li>
+            </ul>
+            <p>
+                By default, the number of lines shown for a definition is 3.
+                This can be configured by adjusting the <a tabindex="0" data-modal-action="show,custom-css">custom CSS</a>;
+                the value can be a unitless integer or decimal number.
+            </p>
+            <div class="code margin-above">/* Globally set the line count */
+    :root {
+        --collapsible-definition-line-count: 2;
+    }
+
+    /* Set the line count for a specific dictionary */
+    .definition-item[data-dictionary='JMdict'] {
+        --collapsible-definition-line-count: 2;
+    }
+
+    /* Spoiler-like functionality, use with <em>Force collapsed</em> mode */
+    .definition-item[data-dictionary='JMdict'] .definition-item-inner.collapsible.collapsed {
+        color: #000000;
+        background-color: #000000;
+    }
+    </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <!-- Custom CSS modal -->
+    <div id="custom-css-modal" class="modal modal-left" tabindex="-1" role="dialog" hidden>
+        <div class="modal-content-container">
+            <div class="modal-content-dimmer"></div>
+            <div class="modal-content">
+                <div class="modal-header">
+                    <div class="modal-title">Custom CSS</div>
+                    <div class="modal-header-button-container">
+                        <div class="modal-header-button-group">
+                            <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                            <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="modal-body custom-popup-css-container">
+                    <div class="custom-popup-css-header">Popup CSS</div>
+                    <textarea class="no-wrap" autocomplete="off" spellcheck="false" id="custom-popup-css" data-setting="general.customPopupCss" data-tab-action="indent,4"></textarea>
+                    <div class="custom-popup-css-header margin-above">Popup outer CSS</div>
+                    <textarea class="no-wrap" autocomplete="off" spellcheck="false" id="custom-popup-outer-css" data-setting="general.customPopupOuterCss" data-tab-action="indent,4"></textarea>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" data-modal-action="hide">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- Audio sources modal -->
+    <div id="audio-sources-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Audio Sources</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="settings-item">
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">
+                            When searching for audio, the sources are checked in order until the first
+                            valid source is found. This allows for selecting a fallback source if the
+                            first choice is not available.
+                        </div>
+                    </div>
+                </div>
+                <div class="settings-item-children">
+                    <div id="audio-source-list" class="generic-list"></div>
+                    <div id="audio-source-list-empty">
+                        No audio sources enabled
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" id="audio-source-add" class="low-emphasis">Add</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="audio-source-help-custom-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header">
+            <div class="modal-title">Audio Source - Custom URL</div>
+        </div>
+        <div class="modal-body">
+            <p>
+                A custom URL can be used to play audio from any URL.
+                The replacement tags <code data-select-on-click="">{term}</code> and <code data-select-on-click="">{reading}</code>
+                can be used to specify which term and reading is being looked up.<br>
+            </p>
+            <p>
+                Example:<br>
+                <a tabindex="0" data-select-on-click="">http://localhost/audio.mp3?term={term}&amp;reading={reading}</a>
+            </p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="audio-source-help-custom-json-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header">
+            <div class="modal-title">Audio Source - Custom URL (JSON)</div>
+        </div>
+        <div class="modal-body">
+            <p>
+                A custom URL to a JSON file which lists one or more audio URLs for a given term.
+                The format of the JSON file is described in <a href="/data/schemas/custom-audio-list-schema.json" target="_blank" rel="noopener noreferrer">this schema file</a>.
+            </p>
+            <p>
+                Example:<br>
+                <a tabindex="0" data-select-on-click="">http://localhost/audio.json?term={term}&amp;reading={reading}</a>
+            </p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="audio-source-help-text-to-speech-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header">
+            <div class="modal-title">Audio Source - Text-to-speech</div>
+        </div>
+        <div class="modal-body">
+            <p>
+                A synthesized voice will speak the given text, using either the term text or the reading.
+            </p>
+            <div class="horizontal-flex margin-above">
+                <input type="text" value="よみたん" id="text-to-speech-voice-test-text" autocomplete="off" lang="ja">
+                <button type="button" id="text-to-speech-voice-test">Test</button>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Scanning inputs modal -->
+    <div id="scanning-inputs-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Scanning Inputs</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div>
+                <p>
+                    Scanning inputs are used to define when text scanning should occur.
+                    <a tabindex="0" class="more-toggle more-only" data-parent-distance="2">More&hellip;</a>
+                </p>
+                <div class="margin-above more" hidden>
+                    <p>
+                        Text scanning is performed when a pointer is moved and certain inputs are either pressed or not pressed.
+                        The <em>Required inputs</em> field is used to define which inputs <em>must</em> be pressed, and
+                        the <em>Excluded inputs</em> field is used to define which inputs <em>must not</em> be pressed.
+                        If the <em>Required inputs</em> field is empty, text will be scanned whenever the pointer is moved.
+                    </p>
+                    <p>
+                        The <em>Input types</em> group is used to define which types of pointer input that the
+                        keyboard and button inputs are applied to.
+                        Supported pointer types include the mouse cursor, touchscreen touches, and pen devices.
+                        When using the <em>Pen</em> option, the defined inputs will correspond to buttons on the pen device.
+                    </p>
+                    <p>
+                        Some additional scanning and search options can be configured by clicking the menu button and selecting
+                        <em>Show advanced options</em>.
+                    </p>
+                    <ul>
+                        <li>To assign keyboard keys, select the input field and press modifier keys on the keyboard.</li>
+                        <li>To assign mouse or pen buttons, click on the button with the mouse icon using the desired button.</li>
+                        <li>
+                            To clear inputs, select the input field and press the <em>Escape</em> button,
+                            or use the <em>Clear inputs</em> menu option.
+                        </li>
+                    </ul>
+                    <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Less&hellip;</a></p>
+                </div>
+            </div>
+            <div class="scan-input-list generic-list margin-above" id="scan-input-list"></div>
+            <div class="warning-text margin-above generic-list-empty-indicator">
+                No scanning inputs have been defined yet.
+                Click the <em>Add</em> button to add a new input.
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" id="scan-input-add">Add</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Input action prevention modal -->
+    <div id="input-action-prevention-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Input Action Prevention</div></div>
+        <div class="modal-body">
+            <div>
+                Prevent middle mouse button actions on:
+                <a tabindex="0" class="more-toggle more-only" data-parent-distance="2">(?)</a>
+            </div>
+            <div class="more" hidden>
+                <p>
+                    This option is used to disable the default action of the middle mouse button in different contexts.
+                    This can be useful for preventing the scroll action that the middle mouse button is typically mapped to,
+                    which is otherwise difficult to disable inside extension pages via other means.
+                </p>
+                <p>
+                    <a tabindex="0" class="more-toggle" data-parent-distance="3">Less&hellip;</a>
+                </p>
+            </div>
+            <div class="input-prevention-option-list">
+                <label class="input-prevention-option-list-item">
+                    <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onWebPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
+                    <span>Webpages</span>
+                </label>
+                <label class="input-prevention-option-list-item">
+                    <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onPopupPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
+                    <span>Popups</span>
+                </label>
+                <label class="input-prevention-option-list-item">
+                    <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onSearchPages"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
+                    <span>Search page</span>
+                </label>
+                <label class="input-prevention-option-list-item">
+                    <label class="checkbox"><input type="checkbox" data-setting="scanning.preventMiddleMouse.onSearchQuery"><span class="checkbox-body"><span class="checkbox-fill"></span><span class="checkbox-border"></span><span class="checkbox-check"></span></span></label>
+                    <span>Search query</span>
+                </label>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Anki cards modal -->
+    <div id="anki-cards-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Anki Cards</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div>
+            <div class="tabs-container">
+                <div class="tabs">
+                    <label class="tab">
+                        <input type="radio" name="anki-card-primary-type" data-value="terms" data-anki-card-menu="anki-card-terms-field-menu" checked>
+                        <div class="tab-inner"><span class="tab-label">Terms</span></div>
+                    </label>
+                    <label class="tab">
+                        <input type="radio" name="anki-card-primary-type" data-value="kanji" data-anki-card-menu="anki-card-kanji-field-menu">
+                        <div class="tab-inner"><span class="tab-label">Kanji</span></div>
+                    </label>
+                </div>
+                <div class="tabs-right" hidden>
+                    <button type="button" class="icon-button" data-menu-position="below left" id="anki-card-primary-type-menu-button"><span class="icon-button-inner"><span class="icon" data-icon="kebab-menu"></span></span></button>
+                </div>
+            </div>
+            <div class="modal-separator-line"></div>
+        </div>
+        <div class="modal-body anki-card" id="anki-card-primary" data-anki-card-type="terms" data-anki-card-menu="anki-card-terms-field-menu">
+            <div class="settings-item"><div class="settings-item-inner">
+                <div class="settings-item-left">
+                    <div class="settings-item-label">Deck</div>
+                </div>
+                <div class="settings-item-right">
+                    <select class="anki-card-deck"></select>
+                </div>
+            </div></div>
+            <div class="settings-item"><div class="settings-item-inner">
+                <div class="settings-item-left">
+                    <div class="settings-item-label">Model</div>
+                </div>
+                <div class="settings-item-right">
+                    <select class="anki-card-model"></select>
+                </div>
+            </div></div>
+            <div class="anki-card-fields">
+                <div class="anki-card-field-name-header" data-persistent="true">Field</div>
+                <div class="anki-card-field-input-header" data-persistent="true">Value</div>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="show,anki-cards-info">Help</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="anki-cards-info-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
+        <div class="modal-header">
+            <div class="modal-title">Anki Card Information</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p>
+                Anki card fields can be populated with information about a term or kanji character by using field markers.
+                When a card is being generated, field markers are replaced with information about the term or kanji by using the installed dictionaries.
+                Several preset markers are available, which are described below.
+                Markers can be customized by adjusting the <a tabindex="0" data-modal-action="show,anki-card-templates">Anki card templates</a>.
+            </p>
+            <p>
+                Anki requires the first field in a model to be unique for a card;
+                therefore, it is recommended to use <code class="anki-field-marker">{expression}</code> as the marker for the first field of term cards,
+                or <code class="anki-field-marker">{character}</code> for kanji cards.
+            </p>
+            <table class="anki-field-marker-info-table margin-above">
+                <tbody>
+                    <tr class="anki-field-marker-info-table-heading">
+                        <td>Marker (for terms)</td>
+                        <td>Description</td>
+                    </tr>
+
+                    <tr>
+                        <td><code class="anki-field-marker">{audio}</code></td>
+                        <td>Audio of the term's pronunciation from one of the audio sources (if available).</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{cloze-body-kana}</code></td>
+                        <td>Kana reading for <code class="anki-field-marker">{cloze-body}</code>.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{conjugation}</code></td>
+                        <td>Conjugation path from the raw inflected term to the source term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{expression}</code></td>
+                        <td>Term expressed using kanji. If kanji expression is not available, kana is used.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{furigana}</code></td>
+                        <td>
+                            Term expressed as kanji with furigana displayed above it.
+                            Example: <ruby>日本語<rt>にほんご</rt></ruby>.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{furigana-plain}</code></td>
+                        <td>
+                            Term expressed as kanji with furigana displayed next to it in brackets.
+                            Example: 日本語[にほんご].
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary}</code></td>
+                        <td>List of definitions for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary-brief}</code></td>
+                        <td>List of definitions for the term in a more compact format.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary-no-dictionary}</code></td>
+                        <td>List of definitions for the term, except the dictionary tag is omitted.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary-first}</code></td>
+                        <td>First definition for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary-first-brief}</code></td>
+                        <td>First definition for the term in a more compact format.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary-first-no-dictionary}</code></td>
+                        <td>First definition for the term, except the dictionary tag is omitted.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{part-of-speech}</code></td>
+                        <td>Part of speech information for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{phonetic-transcriptions}</code></td>
+                        <td>List of phonetic transcriptions for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{pitch-accents}</code></td>
+                        <td>List of pitch accent downstep notations for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{pitch-accent-graphs}</code></td>
+                        <td>List of pitch accent graphs for the term.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{pitch-accent-graphs-jj}</code></td>
+                        <td>List of pitch accent graphs for the term (styled after Jidoujisho).</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{pitch-accent-positions}</code></td>
+                        <td>List of accent downstep positions for the term as a number.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{pitch-accent-categories}</code></td>
+                        <td>List of pitch accent categories for the term (e.g. heiban, kifuku, atamadaka, odaka, nakadaka).</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{reading}</code></td>
+                        <td>Kana reading for the term, or empty for terms where the expression is the reading.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{single-glossary-DICT-NAME}</code></td>
+                        <td>
+                            Same as <code class="anki-field-marker">{glossary}</code>, but with entries from only a single dictionary.
+                            The dictionary name will likely be modified, use the options from the ▼ dropdown.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{single-glossary-DICT-NAME-brief}</code></td>
+                        <td>
+                            See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-brief}</code>.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{single-glossary-DICT-NAME-no-dictionary}</code></td>
+                        <td>
+                            See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-no-dictionary}</code>.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{tags}</code></td>
+                        <td>Grammar and usage tags providing information about the term.</td>
+                    </tr>
+
+                    <tr class="anki-field-marker-info-table-heading">
+                        <td>Marker (for kanji)</td>
+                        <td>Description</td>
+                    </tr>
+
+                    <tr>
+                        <td><code class="anki-field-marker">{character}</code></td>
+                        <td>Unicode glyph representing the current kanji.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{glossary}</code></td>
+                        <td>List of definitions for the kanji.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{kunyomi}</code></td>
+                        <td>Kunyomi (Japanese reading) for the kanji, expressed as hiragana.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{onyomi}</code></td>
+                        <td>Onyomi (Chinese reading) for the kanji, expressed as katakana.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{onyomi-hiragana}</code></td>
+                        <td>Onyomi (Chinese reading) for the kanji, expressed as hiragana.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{stroke-count}</code></td>
+                        <td>Number of strokes that the kanji character has.</td>
+                    </tr>
+
+                    <tr class="anki-field-marker-info-table-heading">
+                        <td>Marker (for both)</td>
+                        <td>Description</td>
+                    </tr>
+
+                    <tr>
+                        <td><code class="anki-field-marker">{clipboard-image}</code></td>
+                        <td>An image which is stored in the system clipboard, if available.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{clipboard-text}</code></td>
+                        <td>Text which is stored in the system clipboard, if available.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{cloze-body}</code></td>
+                        <td>Original inflected term as it appeared before being reduced to dictionary form by Yomitan.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{cloze-prefix}</code></td>
+                        <td>Fragment of the containing <code class="anki-field-marker">{sentence}</code> starting at the beginning of <code class="anki-field-marker">{sentence}</code> until the beginning of <code class="anki-field-marker">{cloze-body}</code>.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{cloze-suffix}</code></td>
+                        <td>Fragment of the containing <code class="anki-field-marker">{sentence}</code> starting at the end of <code class="anki-field-marker">{cloze-body}</code> until the end of <code class="anki-field-marker">{sentence}</code>.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{dictionary}</code></td>
+                        <td>Original name of the dictionary from which the card is being created.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{dictionary-alias}</code></td>
+                        <td>Display name of the dictionary from which the card is being created.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{document-title}</code></td>
+                        <td>Title of the web page that the term or kanji appeared in.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{frequencies}</code></td>
+                        <td>
+                            Frequency information for the term or kanji.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{frequency-harmonic-rank}</code></td>
+                        <td>
+                            The harmonic mean of frequency data for the current term or kanji.<br>
+                            Defaults to rank 9999999 when frequency data is not found, indicating extremely low rank-based term or kanji usage.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{frequency-harmonic-occurrence}</code></td>
+                        <td>
+                            The harmonic mean of frequency data for the current term or kanji.<br>
+                            Defaults to 0 occurrences when frequency data is not found, the lowest possible occurrence-based term or kanji usage.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{frequency-average-rank}</code></td>
+                        <td>
+                            The average of frequency data for the current term or kanji.<br>
+                            Defaults to rank 9999999 when frequency data is not found, indicating extremely low rank-based term or kanji usage.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{frequency-average-occurrence}</code></td>
+                        <td>
+                            The average of frequency data for the current term or kanji.<br>
+                            Defaults to 0 occurrences when frequency data is not found, the lowest possible occurrence-based term or kanji usage.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{screenshot}</code></td>
+                        <td>Screenshot of the web page taken at the time the term or kanji was added.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{search-query}</code></td>
+                        <td>The full search query shown on the search page.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{popup-selection-text}</code></td>
+                        <td>The selected text on the search page or popup.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{sentence}</code></td>
+                        <td>Sentence, quote, or phrase that the term or kanji appears in from the source content.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{sentence-furigana}</code></td>
+                        <td>Sentence, quote, or phrase that the term or kanji appears in from the source content, with furigana added.</td>
+                    </tr>
+                    <tr>
+                        <td><code class="anki-field-marker">{url}</code></td>
+                        <td>Address of the web page in which the term or kanji appeared in.</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="modal-footer">
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Anki field template modals -->
+    <div id="anki-card-templates-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
+        <div class="modal-header"><div class="modal-title">Anki Card Templates</div></div>
+        <div class="modal-body anki-card-templates-layout">
+            <div class="anki-card-templates-info">
+                <p>
+                    Anki card fields are formatted using the <a href="https://handlebarsjs.com/" target="_blank" rel="noopener noreferrer">Handlebars.js</a>
+                    template rendering engine.
+                    Advanced users can modify these templates for full control over what information is included in Anki cards.
+                </p>
+                <p>
+                    Consider copy-pasting the source into a code editor that supports syntax highlighting for easier editing.
+                </p>
+            </div>
+            <textarea autocomplete="off" spellcheck="false" id="anki-card-templates-textarea" class="no-wrap margin-above" data-tab-action="indent,4"></textarea>
+            <div id="anki-card-templates-compile-result" class="code danger-text margin-above" hidden></div>
+            <div class="anki-card-templates-test-container margin-above">
+                <p>
+                    Card templates can be tested using the inputs below.
+                </p>
+                <div class="anki-card-templates-test-table margin-above">
+                    <div class="anki-card-templates-test-table-header">Scanned text</div>
+                    <div class="anki-card-templates-test-table-header">Card field</div>
+                    <div></div>
+                    <input type="text" id="anki-card-templates-test-text-input" class="form-control" value="読め" placeholder="Preview text" autocomplete="off" lang="ja">
+                    <div class="anki-card-templates-test-input-container input-group">
+                        <input type="text" id="anki-card-templates-test-field-input" value="{expression}" placeholder="{marker}" autocomplete="off" spellcheck="false">
+                        <button type="button" class="input-suffix input-suffix-icon-button light-icon" id="anki-card-templates-test-field-menu-button" data-menu="anki-card-all-field-menu" data-menu-position="below left"><span class="icon" data-icon="material-down-arrow"></span></button>
+                    </div>
+                    <button type="button" id="anki-card-templates-test-render-button">Test</button>
+                </div>
+            </div>
+            <div class="code margin-above" id="anki-card-templates-render-result"><em>Card render result</em></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="danger" id="anki-card-templates-reset-button">Reset Templates</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="anki-card-templates-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Reset Anki Card Templates</div></div>
+        <div class="modal-body">
+            <p class="danger-text">
+                Are you sure you want to reset the card templates to their default value?
+                Any changes you made will be lost.
+            </p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" id="anki-card-templates-reset-button-confirm">Reset Templates</button>
+        </div>
+    </div></div>
+
+    <!-- Generate anki deck modal -->
+    <div id="generate-anki-notes-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
+        <div class="modal-header"><div class="modal-title">Anki Note Generator</div></div>
+        <div class="modal-body generate-anki-notes-layout">
+            <div class="generate-anki-notes-info">
+                <p class="warning-text">
+                    WARNING: This feature is experimental!
+                </p>
+                <p>
+                    Enter a newline separated list of terms below to send notes directly to an Anki deck or export to an Anki deck file in <code>Notes in plain text (.txt)</code> format.
+                </p>
+                <p>
+                    For more information check the <a href="https://github.com/themoeway/yomitan/blob/master/docs/anki-integration.md#anki-note-generation">documentation</a>.
+                </p>
+            </div>
+            <textarea autocomplete="off" spellcheck="false" id="generate-anki-notes-textarea" class="no-wrap margin-above" data-tab-action="indent,4"></textarea>
+            <div class="generate-anki-notes-test-container margin-above">
+                <p>
+                    Active Anki deck: <code id="generate-anki-notes-active-deck"></code><br>
+                    Active Anki model: <code id="generate-anki-notes-active-model"></code>
+                </p>
+                <div class="generate-anki-notes-test-table margin-above">
+                    <div class="generate-anki-notes-test-table-header">Test word</div>
+                    <div></div>
+                    <input type="text" id="generate-anki-notes-test-text-input" class="form-control" value="読め" placeholder="Preview text" autocomplete="off" lang="ja">
+                    <button type="button" id="generate-anki-notes-test-render-button">Preview Card</button>
+                </div>
+            </div>
+            <div class="code margin-above" id="generate-anki-notes-render-result"><em>Card render result</em></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" id="generate-anki-notes-send-to-anki-button">Send to Anki</button>
+            <button type="button" class="low-emphasis" id="generate-anki-notes-export-button">Export to File</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="generate-anki-notes-send-to-anki-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Send Notes to Anki</div></div>
+        <div class="modal-body">
+            <p>
+                Are you sure you want to send <strong id="generate-anki-notes-send-wordcount"></strong> terms to <code id="generate-anki-notes-active-deck-confirm"></code>? This action cannot be undone.
+            </p>
+            <div class="settings-item margin-above"><div class="settings-item-inner">
+                <div class="settings-item-left">
+                    <div class="settings-item-label">
+                        Add media to notes
+                    </div>
+                    <div class="settings-item-description">
+                        Adding media increases processing time.
+                    </div>
+                </div>
+                <div class="settings-item-right">
+                    <label class="toggle"><input type="checkbox" id="generate-anki-notes-add-media"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                </div>
+            </div></div>
+            <div class="settings-item margin-above"><div class="settings-item-inner">
+                <div class="settings-item-left">
+                    <div class="settings-item-label">
+                        Prevent sending duplicate notes
+                    </div>
+                    <div class="settings-item-description">
+                        Checking for duplicates increases processing time.
+                    </div>
+                </div>
+                <div class="settings-item-right">
+                    <label class="toggle"><input type="checkbox" id="generate-anki-notes-disallow-duplicates"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                </div>
+            </div></div>
+        </div>
+        <div class="modal-body-addon generate-anki-notes-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar"></div></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide" id="generate-anki-notes-send-to-anki-cancel-button">Cancel</button>
+            <button type="button" class="danger" id="generate-anki-notes-send-button-confirm">Send to Anki</button>
+        </div>
+    </div></div>
+
+    <div id="generate-anki-notes-export-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Export Notes to File</div></div>
+        <div class="modal-body">
+            <p>
+                Are you sure you want to export <strong id="generate-anki-notes-export-wordcount"></strong> terms to <code>Notes in plain text (.txt)</code> format?
+            </p>
+        </div>
+        <div class="modal-body-addon generate-anki-notes-progress" hidden>
+            <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
+            <div class="progress-bar-track"><div class="progress-bar"></div></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide" id="generate-anki-notes-export-cancel-button">Cancel</button>
+            <button type="button" class="danger" id="generate-anki-notes-export-button-confirm">Export to File</button>
+        </div>
+    </div></div>
+
+
+    <!-- Import/export modals -->
+    <div id="settings-import-error-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Import Error</div></div>
+        <div class="modal-body">
+            <p>An error occurred while trying to import the settings file:</p>
+            <p class="danger-text" id="settings-import-error-message"></p>
+            <p>Additional info can be found in the developer console.</p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="settings-import-warning-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Import Security Warning</div></div>
+        <div class="modal-body">
+            <p>
+                Settings file contains settings which may pose a security risk.
+                Only import settings from sources you trust.
+            </p>
+            <ul class="danger-text" id="settings-import-warning-message"></ul>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger settings-import-warning-import-button">Import</button>
+            <button type="button" class="settings-import-warning-import-button" data-import-sanitize="true">Sanitize and Import</button>
+        </div>
+    </div></div>
+
+    <div id="settings-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Reset Settings</div></div>
+        <div class="modal-body">
+            <p class="danger-text">
+                You are about to reset all Yomitan settings back to their default values.
+                This will delete all custom profiles you may have created.
+                <strong>This action cannot be undone.</strong>
+            </p>
+            <p>
+                Consider making a backup using the <em>Export Settings</em> button before resetting
+                if you want to be able to revert.
+            </p>
+            <p>
+                Dictionary data will not be deleted, but any installed dictionaries
+                will need to be re-enabled.
+            </p>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" id="settings-reset-confirm-button">Reset All Settings</button>
+        </div>
+    </div></div>
+
+
+    <!-- Translation modals -->
+    <div id="translation-text-replacement-patterns-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Custom Text Replacement Patterns</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="settings-item"><div class="settings-item-inner"><div class="settings-item-left"><div class="settings-item-label">
+                Text replacement patterns are used to modify or remove text that matches certain patterns.
+                Patterns are defined using
+                <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" target="_blank" rel="noreferrer noopener">regular expression syntax</a>,
+                and the replacement text can use certain
+                <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter" target="_blank" rel="noreferrer noopener">special replacement patterns</a>.
+            </div></div></div></div>
+            <div class="settings-item"><div class="settings-item-inner">
+                <div class="settings-item-left">
+                    <div class="settings-item-label">
+                        Search original text
+                    </div>
+                    <div class="settings-item-description">
+                        The original unmodified text will also be searched for definitions.
+                    </div>
+                </div>
+                <div class="settings-item-right">
+                    <label class="toggle"><input type="checkbox" data-setting="translation.textReplacements.searchOriginal"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+                </div>
+            </div></div>
+            <div class="settings-item">
+                <div class="settings-item-inner">
+                    <div class="settings-item-left">
+                        <div class="settings-item-label">Text replacement patterns</div>
+                    </div>
+                </div>
+                <div class="settings-item-children">
+                    <div id="translation-text-replacement-list" class="generic-list"></div>
+                    <div class="generic-list-empty-indicator"><em>None defined</em></div>
+                </div>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" id="translation-text-replacement-add" class="low-emphasis">Add</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Sentence parsing modal -->
+    <div id="sentence-termination-characters-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Sentence Termination Characters</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <p>
+                Sentences are terminated by punctuation and quotation marks, which can both be configured below.
+            </p>
+            <table class="sentence-termination-character-list-table" id="sentence-termination-character-list-table" hidden>
+                <thead><tr>
+                    <td>#</td>
+                    <td>Enabled</td>
+                    <td>Type</td>
+                    <td>Character 1</td>
+                    <td>Character 2</td>
+                    <td>Include character in sentence</td>
+                    <td></td>
+                </tr></thead>
+                <tbody class="sentence-termination-character-list generic-list" id="sentence-termination-character-list"></tbody>
+            </table>
+            <div id="sentence-termination-character-list-empty" hidden>
+                No terminators defined.
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis danger" id="sentence-termination-character-list-reset">Reset</button>
+            <button type="button" class="low-emphasis" id="sentence-termination-character-list-add">Add</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+
+    <!-- Keyboard shortcuts modal -->
+    <div id="keyboard-shortcuts-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-full">
+        <div class="modal-header">
+            <div class="modal-title">Keyboard Shortcuts</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div class="hotkey-list generic-list" id="hotkey-list"></div>
+            <div class="hotkey-list-empty" id="hotkey-list-empty" hidden>
+                No keyboard shortcuts defined.
+            </div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis danger" data-modal-action="show,keyboard-shortcuts-reset">Reset</button>
+            <button type="button" class="low-emphasis" id="hotkey-list-add">Add</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="extension-keyboard-shortcuts-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
+        <div class="modal-header">
+            <div class="modal-title">Native Keyboard Shortcuts</div>
+            <div class="modal-header-button-container">
+                <div class="modal-header-button-group">
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
+                    <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
+                </div>
+            </div>
+        </div>
+        <div class="modal-body">
+            <div>
+                <p data-show-for-browser="chrome edge">
+                    The native keyboard shortcuts are listed below,
+                    but cannot be configured from within the extension on this browser.
+                    To configure these shortcuts:
+                </p>
+
+                <p data-show-for-browser="firefox">
+                    The native keyboard shortcuts can be configured below on this browser,
+                    or by doing the following:
+                </p>
+
+                <ul data-show-for-browser="chrome">
+                    <li>Open <a tabindex="0" data-special-url="chrome://extensions/shortcuts">chrome://extensions/shortcuts</a> in a new tab.</li>
+                    <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
+                </ul>
+
+                <ul data-show-for-browser="edge">
+                    <li>Open <a tabindex="0" data-special-url="edge://extensions/shortcuts">edge://extensions/shortcuts</a> in a new tab.</li>
+                    <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
+                </ul>
+
+                <ul data-show-for-browser="firefox">
+                    <li>Open the extensions page (<a tabindex="0" data-select-on-click="">about:addons</a>)</li>
+                    <li>Click the button on the right with the gear icon, then click <em>Manage Extension Shortcuts</em>.</li>
+                    <li>Find the <em>Yomitan</em> section and configure the shortcuts.</li>
+                </ul>
+            </div>
+            <div class="modal-separator-line"></div>
+            <div class="modal-settings-group" id="extension-hotkey-list"></div>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis danger" id="extension-hotkey-list-reset-all">Reset All</button>
+            <button type="button" class="low-emphasis danger" id="extension-hotkey-list-clear-all">Clear All</button>
+            <button type="button" data-modal-action="hide">Close</button>
+        </div>
+    </div></div>
+
+    <div id="keyboard-shortcuts-reset-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
+        <div class="modal-header"><div class="modal-title">Confirm Keyboard Shortcuts Reset</div></div>
+        <div class="modal-body">
+            Are you sure you want to reset all keyboard shortcuts to their defaults?
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
+            <button type="button" class="danger" id="hotkey-list-reset" data-modal-action="hide">Reset All</button>
+        </div>
+    </div></div>
+</template>
+
+</body></html>
diff --git a/ext/welcome.html b/ext/welcome.html
index 30ba352118..2d292df53d 100644
--- a/ext/welcome.html
+++ b/ext/welcome.html
@@ -199,338 +199,5 @@ <h2>Basic customization</h2>
 <div id="popup-menus"></div>
 
 
-<!-- Dictionary modals -->
-<div id="dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Enable support for prefix wildcard searches
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" data-setting="global.database.prefixWildcardsSupported" data-scope="global"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                <p>
-                    In order for dictionaries to support searches using prefix wildcards on the search page,
-                    some additional data must be stored in the database.
-                    Enabling this option will include this extra data for any new dictionaries that are imported.
-                </p>
-                <p class="warning-text">
-                    This option will not change any dictionaries that are already imported;
-                    they must be re-imported for the option to take effect.
-                </p>
-                <p>
-                    <a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a>
-                </p>
-            </div>
-        </div>
-
-        <div class="warning-text margin-above no-dictionaries-installed-warning" hidden>
-            No dictionaries have been installed yet.
-            Visit the <a href="https://github.com/themoeway/yomitan/blob/master/docs/dictionaries.md#dictionaries" target="_blank" rel="noopener noreferrer">Yomitan homepage</a>
-            for a list of free dictionaries or click the <em>Import</em> button below to select a dictionary file to import.
-        </div>
-        <div id="dictionary-error" class="danger-text margin-above" hidden></div>
-        <div id="dictionary-list" class="dictionary-list generic-list" data-count="0">
-            <div class="dictionary-item-top"></div>
-            <label class="dictionary-item-top toggle dictionary-item-enabled-toggle-container"><input type="checkbox" id="all-dictionaries-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-            <div class="dictionary-item-top dictionary-item-title-container">All</div>
-            <div class="dictionary-item-top advanced-only">Priority</div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-            <div class="dictionary-item-top dictionary-item-button-height"></div>
-        </div>
-
-        <div hidden><input type="file" id="dictionary-import-file-input" accept=".zip,application/zip" multiple></div>
-    </div>
-    <div class="modal-body-addon dictionary-delete-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
-    </div>
-    <div class="modal-body-addon dictionary-import-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis danger dictionary-database-mutating-input" id="dictionary-delete-all-button">Delete All</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input debug-only" id="dictionary-check-integrity">Check Integrity</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-check-updates">Check for Updates</button>
-        <button type="button" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-button">Import</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-import-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-medium">
-    <div class="modal-header"><div class="modal-title">Import Dictionaries</div></div>
-    <div class="modal-body">
-        <div id="dictionary-drop-file-zone">
-            <div id="dictionary-drag-drop-text">
-                <span class="icon" data-icon="book"></span>
-                <h1>Drag and drop dictionaries (.zip)</h1>
-                <h5>or click here to upload</h5>
-            </div>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide" class="basic-only">Close</button>
-    </div>
-    <div class="modal-body advanced-only">
-        <p>Import dictionaries from URLs:</p>
-        <textarea type="text" id="dictionary-import-url-text"></textarea>
-    </div>
-    <div class="modal-footer advanced-only">
-        <button type="button" data-modal-action="hide" class="low-emphasis dictionary-database-mutating-input" id="dictionary-import-url-button">Import from URLs</button>
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-delete-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to delete the dictionary:</p>
-        <p><strong id="dictionary-confirm-delete-name"></strong>?</p>
-        <p class="danger-text">This action cannot be undone.</p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-button">Delete</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-delete-all-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Deletion</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to delete <strong>all dictionaries</strong>?</p>
-        <p class="danger-text">This action cannot be undone.</p>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-delete-all-button">Delete</button>
-    </div>
-</div></div>
-
-<div id="dictionary-confirm-update-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Confirm Dictionary Update</div></div>
-    <div class="modal-body">
-        <p>Are you sure you want to update the dictionary:</p>
-        <p><strong id="dictionary-confirm-update-name"></strong>?</p>
-        <section>
-            Updating a dictionary involves:
-            <ul>
-                <li>Deleting the installed version</li>
-                <li>Downloading the latest version </li>
-                <li>Importing the latest version</li>
-            </ul>
-            <p class="warning-text">Especially for large dictionaries, this process can take a while, and downloading will use your network.</p>
-        </section>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" class="danger" data-modal-action="hide" id="dictionary-confirm-update-button">Update</button>
-    </div>
-</div></div>
-
-<div id="secondary-search-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Secondary Search Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p>
-            These dictionaries will be used to search for definitions of the related terms when the grouping mode is
-            <em>Group related terms</em>.
-        </p>
-        <div id="secondary-search-dictionary-list" class="secondary-search-dictionary-list"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-details-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title"><strong class="dictionary-title"></strong> <span class="light dictionary-revision"></span></div>
-    </div>
-    <div class="modal-body">
-        <div class="settings-item dictionary-outdated-notification" hidden><div class="settings-item-children danger-text">
-            This dictionary is outdated and may not support new extension features.
-            Re-import the dictionary to enable support for the latest features.
-        </div></div>
-        <div class="settings-item">
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Prefix wildcard searches supported
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-prefix-wildcard-searches-supported" disabled><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                <p class="warning-text">
-                    Changing this value requires the dictionary to be re-imported.
-                </p>
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <div class="settings-item dictionary-parts-of-speech-filter-setting" hidden>
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Part of speech filtering
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-parts-of-speech-filter-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                When deinflecting words, only dictionary entries whose POS matches that expected by the deinflector will be shown.
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <div class="settings-item dictionary-use-deinflections-setting" hidden>
-            <div class="settings-item-inner">
-                <div class="settings-item-left">
-                    <div class="settings-item-label">
-                        Use deinflections
-                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a>
-                    </div>
-                </div>
-                <div class="settings-item-right">
-                    <label class="toggle"><input type="checkbox" class="dictionary-use-deinflections-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
-                </div>
-            </div>
-            <div class="settings-item-children more" hidden>
-                Deinflections from this dictionary will be used.
-                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide&hellip;</a></p>
-            </div>
-        </div>
-        <hr>
-        <div class="settings-item"><div class="settings-item-children">
-            <div class="dictionary-details-table"></div>
-            <div class="dictionary-counts"></div>
-        </div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-extra-data-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">
-            <strong class="dictionary-title">Unassociated Data</strong> <span class="light dictionary-total-count"></span>
-        </div>
-    </div>
-    <div class="modal-body">
-        <p class="warning-text">
-            The database contains extra data which is not associated with any installed dictionary.
-            Purging the database can fix this issue.
-        </p>
-        <div class="dictionary-counts"></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
-<div id="dictionary-move-location-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Move Dictionary Options</div></div>
-    <div class="modal-body">
-        <p>Input the location the dictionary <strong class="dictionary-title"></strong> should be moved to:</p>
-        <div class="margin-above">
-            <input type="number" id="dictionary-move-location" min="1" step="1">
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" data-modal-action="hide" id="dictionary-move-button">Move</button>
-    </div>
-</div></div>
-
-<div id="dictionary-set-alias-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content modal-content-small">
-    <div class="modal-header"><div class="modal-title">Rename</div></div>
-    <div class="modal-body">
-        <p>Input the display name for <strong class="dictionary-title"></strong> dictionary:</p>
-        <div class="margin-above">
-            <input type="text" id="dictionary-alias-input">
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" class="low-emphasis" id="dictionary-reset-alias-button">Reset</button>
-        <button type="button" class="low-emphasis" data-modal-action="hide">Cancel</button>
-        <button type="button" data-modal-action="hide" id="dictionary-set-alias-button">Save</button>
-    </div>
-</div></div>
-
-<!-- Recommended dictionary modals -->
-<div id="recommended-dictionaries-modal" class="modal" tabindex="-1" role="dialog" hidden><div class="modal-content">
-    <div class="modal-header">
-        <div class="modal-title">Recommended Dictionaries</div>
-        <div class="modal-header-button-container">
-            <div class="modal-header-button-group">
-                <button type="button" class="icon-button modal-header-button" data-modal-action="expand"><span class="icon-button-inner"><span class="icon" data-icon="expand"></span></span></button>
-                <button type="button" class="icon-button modal-header-button" data-modal-action="collapse"><span class="icon-button-inner"><span class="icon" data-icon="collapse"></span></span></button>
-            </div>
-        </div>
-    </div>
-    <div class="modal-body">
-        <div id="recommended-term-dictionaries" hidden>
-            <h1 class="modal-title">Term Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-kanji-dictionaries" hidden>
-            <h1 class="modal-title">Kanji Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-frequency-dictionaries" hidden>
-            <h1 class="modal-title">Frequency Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-grammar-dictionaries" hidden>
-            <h1 class="modal-title">Grammar Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-        <div id="recommended-pronunciation-dictionaries" hidden>
-            <h1 class="modal-title">Pronunciation Dictionaries</h1>
-            <div class="recommended-dictionary-list"></div>
-        </div>
-    </div>
-    <div class="modal-body-addon dictionary-delete-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar danger"></div></div>
-    </div>
-    <div class="modal-body-addon dictionary-import-progress" hidden>
-        <div class="progress-labels"><div class="progress-info"></div><div class="progress-status"></div></div>
-        <div class="progress-bar-track"><div class="progress-bar"></div></div>
-    </div>
-    <div class="modal-footer">
-        <button type="button" data-modal-action="hide">Close</button>
-    </div>
-</div></div>
-
 </body>
 </html>