aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-11-08 12:34:23 -0500
committerGitHub <noreply@github.com>2020-11-08 12:34:23 -0500
commit8cf10d685d52d5ae75d65c4aea4adcb6d101e9a4 (patch)
tree3a1874942370917e328976854c5a5c7352d9e085
parentdb9ec4c4495404ad3ebc9ce56866c3631cedf8dc (diff)
Search and display style updates (#1005)
* Use same selectors * Use consistent stylesheet declaration * Fix query parser not being cleared * Set property upon load * Don't focus for Enter key press * Update search page styles * Update indent and nodes * Support dark style * Add missing var
-rw-r--r--ext/bg/js/search-main.js1
-rw-r--r--ext/bg/js/search.js91
-rw-r--r--ext/bg/search.html166
-rw-r--r--ext/fg/float.html131
-rw-r--r--ext/fg/js/float.js2
-rw-r--r--ext/mixed/css/display.css17
-rw-r--r--ext/mixed/css/search.css326
-rw-r--r--ext/mixed/js/display.js11
8 files changed, 532 insertions, 213 deletions
diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js
index cf9282fd..07130631 100644
--- a/ext/bg/js/search-main.js
+++ b/ext/bg/js/search-main.js
@@ -28,6 +28,7 @@
const displaySearch = new DisplaySearch();
await displaySearch.prepare();
+ document.documentElement.dataset.loaded = 'true';
yomichan.ready();
} catch (e) {
yomichan.logError(e);
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index af88bb76..bd840981 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -25,12 +25,14 @@
class DisplaySearch extends Display {
constructor() {
- super(document.querySelector('#spinner'), document.querySelector('#content'));
- this._searchButton = document.querySelector('#search');
- this._queryInput = document.querySelector('#query');
+ super();
+ this._searchButton = document.querySelector('#search-button');
+ this._queryInput = document.querySelector('#search-textbox');
this._introElement = document.querySelector('#intro');
this._clipboardMonitorEnableCheckbox = document.querySelector('#clipboard-monitor-enable');
this._wanakanaEnableCheckbox = document.querySelector('#wanakana-enable');
+ this._queryInputEvents = new EventListenerCollection();
+ this._wanakanaEnabled = false;
this._isPrepared = false;
this._introVisible = true;
this._introAnimationTimer = null;
@@ -42,7 +44,7 @@ class DisplaySearch extends Display {
});
this._onKeyDownIgnoreKeys = new Map([
['ANY_MOD', new Set([
- 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'PageDown', 'PageUp', 'Home', 'End',
+ 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'PageDown', 'PageUp', 'Home', 'End', 'Enter',
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10',
'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20',
'F21', 'F22', 'F23', 'F24'
@@ -71,22 +73,16 @@ class DisplaySearch extends Display {
this.queryParserVisible = true;
this.setHistorySettings({useBrowserHistory: true});
- const options = this.getOptions();
- if (options.general.enableWanakana === true) {
- this._wanakanaEnableCheckbox.checked = true;
- wanakana.bind(this._queryInput);
- } else {
- this._wanakanaEnableCheckbox.checked = false;
- }
+ const enableWanakana = !!this.getOptions().general.enableWanakana;
+ this._wanakanaEnableCheckbox.checked = enableWanakana;
+ this._setWanakanaEnabled(enableWanakana);
this._searchButton.addEventListener('click', this._onSearch.bind(this), false);
- this._queryInput.addEventListener('input', this._onSearchInput.bind(this), false);
this._wanakanaEnableCheckbox.addEventListener('change', this._onWanakanaEnableChange.bind(this));
window.addEventListener('copy', this._onCopy.bind(this));
this._clipboardMonitor.on('change', this._onExternalSearchUpdate.bind(this));
this._clipboardMonitorEnableCheckbox.addEventListener('change', this._onClipboardMonitorEnableChange.bind(this));
- this._updateSearchButton();
this._onModeChange();
await this._prepareNestedPopups();
@@ -141,7 +137,7 @@ class DisplaySearch extends Display {
}
postProcessQuery(query) {
- if (this._isWanakanaEnabled()) {
+ if (this._wanakanaEnabled) {
try {
query = wanakana.toKana(query);
} catch (e) {
@@ -173,28 +169,27 @@ class DisplaySearch extends Display {
if (typeof source !== 'string') { source = ''; }
this._queryInput.value = source;
+ this._updateSearchHeight();
this._setIntroVisible(!valid, animate);
- this._updateSearchButton();
}
_onSearchInput() {
- this._updateSearchButton();
-
- const queryElementRect = this._queryInput.getBoundingClientRect();
- if (queryElementRect.top < 0 || queryElementRect.bottom > window.innerHeight) {
- this._queryInput.scrollIntoView();
- }
+ this._updateSearchHeight();
}
- _onSearch(e) {
- if (this._queryInput === null) {
- return;
- }
+ _onSearchKeydown(e) {
+ if (e.code !== 'Enter' || e.shiftKey) { return; }
+ // Search
e.preventDefault();
+ e.stopImmediatePropagation();
+ e.currentTarget.blur();
+ this._search();
+ }
- const query = this._queryInput.value;
- this._onSearchQueryUpdated(query, true);
+ _onSearch(e) {
+ e.preventDefault();
+ this._search();
}
_onCopy() {
@@ -228,11 +223,7 @@ class DisplaySearch extends Display {
_onWanakanaEnableChange(e) {
const value = e.target.checked;
- if (value) {
- wanakana.bind(this._queryInput);
- } else {
- wanakana.unbind(this._queryInput);
- }
+ this._setWanakanaEnabled(value);
api.modifySettings([{
action: 'set',
path: 'general.enableWanakana',
@@ -254,8 +245,22 @@ class DisplaySearch extends Display {
this._updateClipboardMonitorEnabled();
}
- _isWanakanaEnabled() {
- return this._wanakanaEnableCheckbox !== null && this._wanakanaEnableCheckbox.checked;
+ _setWanakanaEnabled(enabled) {
+ const input = this._queryInput;
+ this._queryInputEvents.removeAllEventListeners();
+
+ this._queryInputEvents.addEventListener(input, 'keydown', this._onSearchKeydown.bind(this), false);
+
+ if (this._wanakanaEnabled !== enabled) {
+ this._wanakanaEnabled = enabled;
+ if (enabled) {
+ wanakana.bind(input);
+ } else {
+ wanakana.unbind(input);
+ }
+ }
+
+ this._queryInputEvents.addEventListener(input, 'input', this._onSearchInput.bind(this), false);
}
_setIntroVisible(visible, animate) {
@@ -314,10 +319,6 @@ class DisplaySearch extends Display {
this._introElement.style.height = '0';
}
- _updateSearchButton() {
- this._searchButton.disabled = this._introVisible && (this._queryInput === null || this._queryInput.value.length === 0);
- }
-
async _prepareNestedPopups() {
let complete = false;
@@ -388,4 +389,18 @@ class DisplaySearch extends Display {
);
});
}
+
+ _search() {
+ const query = this._queryInput.value;
+ this._onSearchQueryUpdated(query, true);
+ }
+
+ _updateSearchHeight() {
+ const node = this._queryInput;
+ const {scrollHeight} = node;
+ const currentHeight = node.getBoundingClientRect().height;
+ if (scrollHeight >= currentHeight - 1) {
+ node.style.height = `${scrollHeight}px`;
+ }
+ }
}
diff --git a/ext/bg/search.html b/ext/bg/search.html
index 8df20581..6988a235 100644
--- a/ext/bg/search.html
+++ b/ext/bg/search.html
@@ -1,105 +1,95 @@
<!DOCTYPE html>
<html lang="en" data-yomichan-page="search">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width,initial-scale=1" />
- <title>Yomichan Search</title>
- <link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">
- <link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">
- <link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32">
- <link rel="icon" type="image/png" href="/mixed/img/icon38.png" sizes="38x38">
- <link rel="icon" type="image/png" href="/mixed/img/icon48.png" sizes="48x48">
- <link rel="icon" type="image/png" href="/mixed/img/icon64.png" sizes="64x64">
- <link rel="icon" type="image/png" href="/mixed/img/icon128.png" sizes="128x128">
- <link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
- <link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
- <link rel="stylesheet" type="text/css" href="/mixed/css/display.css">
- </head>
- <body>
- <div class="container">
- <div id="intro" style="overflow: hidden;">
- <div class="page-header">
- <h1>Yomichan Search</h1>
- </div>
- <p style="margin-bottom: 0;">Search your installed dictionaries by entering a Japanese expression into the field below.</p>
- </div>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <title>Yomichan Search</title>
+ <link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">
+ <link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">
+ <link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32">
+ <link rel="icon" type="image/png" href="/mixed/img/icon38.png" sizes="38x38">
+ <link rel="icon" type="image/png" href="/mixed/img/icon48.png" sizes="48x48">
+ <link rel="icon" type="image/png" href="/mixed/img/icon64.png" sizes="64x64">
+ <link rel="icon" type="image/png" href="/mixed/img/icon128.png" sizes="128x128">
+ <link rel="stylesheet" type="text/css" href="/mixed/css/search.css">
+ <link rel="stylesheet" type="text/css" href="/mixed/css/display.css">
+</head>
+<body>
- <div class="search-input">
- <div class="input-group" style="padding-top: 20px;">
- <span title="Enable kana input method" class="input-group-text">
- <input type="checkbox" id="wanakana-enable" class="icon-checkbox" />
- <label for="wanakana-enable" class="scan-disable">あ</label>
- </span>
- <span title="Enable clipboard monitor" class="input-group-text">
- <input type="checkbox" id="clipboard-monitor-enable" class="icon-checkbox" />
- <label for="clipboard-monitor-enable"><span class="glyphicon glyphicon-paste"></span></label>
- </span>
- </div>
+<div class="content"><div class="content-center">
- <form class="input-group">
- <input type="text" class="form-control" placeholder="Search for..." id="query" autofocus>
- <span class="input-group-btn">
- <input type="submit" class="btn btn-default form-control" id="search" value="Search">
- </span>
- </form>
- </div>
+ <div id="intro">
+ <h1>Yomichan Search</h1>
+ </div>
- <div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
-
- <div class="scan-disable" id="query-parser-container">
- <div id="query-parser-select-container" class="input-group"></div>
- <div id="query-parser-content"></div>
- </div>
+ <div class="scan-disable">
+ <div class="search-options">
+ <label class="search-option">
+ <label class="toggle"><input type="checkbox" id="wanakana-enable"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+ <span class="search-option-label">Automatic kana conversion</span>
+ </label>
+ <label class="search-option">
+ <label class="toggle"><input type="checkbox" id="clipboard-monitor-enable"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
+ <span class="search-option-label">Clipboard monitor</span>
+ </label>
+ </div>
+ <div class="search-textbox-container">
+ <textarea id="search-textbox" placeholder="Input a term, expression, sentence, or block of text" autocomplete="false" autofocus></textarea>
+ <button id="search-button"><span class="icon-button-icon" data-icon="magnifying-glass"></span></button>
+ </div>
+ </div>
- <hr>
+ <div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
- <div id="navigation-header" class="navigation-header" hidden><div class="navigation-header-actions">
- <button class="action-button action-previous" data-icon="source-term" title="Source term (Alt + B)"></button>
- <button class="action-button action-next" data-icon="source-term" title="Next term (Alt + F)"></button>
- </div></div><div class="navigation-header-spacer"></div>
+ <div class="scan-disable" id="query-parser-container">
+ <div id="query-parser-select-container"></div>
+ <div id="query-parser-content"></div>
+ </div>
- <div id="content"></div>
+ <div id="definitions"></div>
- <div id="no-results" hidden>
- <div class="entry">
- <p>No results found.</p>
- </div>
- </div>
+ <div id="no-results" hidden>
+ <div class="entry">
+ <p>No results found.</p>
</div>
+ </div>
+
+</div></div>
+
+<script src="/mixed/lib/wanakana.min.js"></script>
- <script src="/mixed/lib/wanakana.min.js"></script>
+<script src="/mixed/js/core.js"></script>
+<script src="/mixed/js/yomichan.js"></script>
+<script src="/mixed/js/comm.js"></script>
+<script src="/mixed/js/api.js"></script>
+<script src="/mixed/js/japanese.js"></script>
- <script src="/mixed/js/core.js"></script>
- <script src="/mixed/js/yomichan.js"></script>
- <script src="/mixed/js/comm.js"></script>
- <script src="/mixed/js/api.js"></script>
- <script src="/mixed/js/japanese.js"></script>
+<script src="/mixed/js/cache-map.js"></script>
+<script src="/mixed/js/document-util.js"></script>
+<script src="/fg/js/dom-text-scanner.js"></script>
+<script src="/fg/js/text-source-range.js"></script>
+<script src="/fg/js/text-source-element.js"></script>
+<script src="/mixed/js/audio-system.js"></script>
+<script src="/mixed/js/dictionary-data-util.js"></script>
+<script src="/mixed/js/display.js"></script>
+<script src="/mixed/js/display-generator.js"></script>
+<script src="/mixed/js/display-history.js"></script>
+<script src="/mixed/js/dynamic-loader.js"></script>
+<script src="/mixed/js/media-loader.js"></script>
+<script src="/mixed/js/scroll.js"></script>
+<script src="/mixed/js/text-scanner.js"></script>
+<script src="/mixed/js/html-template-collection.js"></script>
+<script src="/mixed/js/text-to-speech-audio.js"></script>
- <script src="/mixed/js/cache-map.js"></script>
- <script src="/mixed/js/document-util.js"></script>
- <script src="/fg/js/dom-text-scanner.js"></script>
- <script src="/fg/js/text-source-range.js"></script>
- <script src="/fg/js/text-source-element.js"></script>
- <script src="/mixed/js/audio-system.js"></script>
- <script src="/mixed/js/dictionary-data-util.js"></script>
- <script src="/mixed/js/display.js"></script>
- <script src="/mixed/js/display-generator.js"></script>
- <script src="/mixed/js/display-history.js"></script>
- <script src="/mixed/js/dynamic-loader.js"></script>
- <script src="/mixed/js/media-loader.js"></script>
- <script src="/mixed/js/scroll.js"></script>
- <script src="/mixed/js/text-scanner.js"></script>
- <script src="/mixed/js/html-template-collection.js"></script>
- <script src="/mixed/js/text-to-speech-audio.js"></script>
+<script src="/bg/js/anki-note-builder.js"></script>
+<script src="/bg/js/template-renderer-proxy.js"></script>
- <script src="/bg/js/anki-note-builder.js"></script>
- <script src="/bg/js/template-renderer-proxy.js"></script>
+<script src="/bg/js/query-parser-generator.js"></script>
+<script src="/bg/js/query-parser.js"></script>
+<script src="/bg/js/clipboard-monitor.js"></script>
+<script src="/bg/js/search.js"></script>
- <script src="/bg/js/query-parser-generator.js"></script>
- <script src="/bg/js/query-parser.js"></script>
- <script src="/bg/js/clipboard-monitor.js"></script>
- <script src="/bg/js/search.js"></script>
+<script src="/bg/js/search-main.js"></script>
- <script src="/bg/js/search-main.js"></script>
- </body>
+</body>
</html>
diff --git a/ext/fg/float.html b/ext/fg/float.html
index 4a7cb616..d5172145 100644
--- a/ext/fg/float.html
+++ b/ext/fg/float.html
@@ -1,80 +1,81 @@
<!DOCTYPE html>
<html lang="en" data-yomichan-page="float">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width,initial-scale=1" />
- <title>Yomichan Search</title>
- <link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">
- <link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">
- <link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32">
- <link rel="icon" type="image/png" href="/mixed/img/icon38.png" sizes="38x38">
- <link rel="icon" type="image/png" href="/mixed/img/icon48.png" sizes="48x48">
- <link rel="icon" type="image/png" href="/mixed/img/icon64.png" sizes="64x64">
- <link rel="icon" type="image/png" href="/mixed/img/icon128.png" sizes="128x128">
- <link rel="stylesheet" href="/mixed/css/display.css">
- </head>
- <body>
- <div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <title>Yomichan Search</title>
+ <link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">
+ <link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">
+ <link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32">
+ <link rel="icon" type="image/png" href="/mixed/img/icon38.png" sizes="38x38">
+ <link rel="icon" type="image/png" href="/mixed/img/icon48.png" sizes="48x48">
+ <link rel="icon" type="image/png" href="/mixed/img/icon64.png" sizes="64x64">
+ <link rel="icon" type="image/png" href="/mixed/img/icon128.png" sizes="128x128">
+ <link rel="stylesheet" type="text/css" href="/mixed/css/display.css">
+</head>
+<body>
- <div id="navigation-header" class="navigation-header" hidden><div class="navigation-header-actions">
- <button class="action-button action-previous" data-icon="source-term" title="Source term (Alt + B)"></button>
- <button class="action-button action-next" data-icon="source-term" title="Next term (Alt + F)"></button>
- </div></div><div class="navigation-header-spacer"></div>
+ <div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
- <div class="scan-disable" id="query-parser-container" hidden>
- <div id="query-parser-select-container" class="input-group"></div>
- <div id="query-parser-content"></div>
- </div>
+ <div id="navigation-header" class="navigation-header" hidden><div class="navigation-header-actions">
+ <button class="action-button action-previous" data-icon="source-term" title="Source term (Alt + B)"></button>
+ <button class="action-button action-next" data-icon="source-term" title="Next term (Alt + F)"></button>
+ </div></div><div class="navigation-header-spacer"></div>
+
+ <div class="scan-disable" id="query-parser-container" hidden>
+ <div id="query-parser-select-container" class="input-group"></div>
+ <div id="query-parser-content"></div>
+ </div>
- <div id="definitions"></div>
+ <div id="definitions"></div>
- <div id="no-results" hidden>
- <div class="entry">
- <p>No results found.</p>
- </div>
+ <div id="no-results" hidden>
+ <div class="entry">
+ <p>No results found.</p>
</div>
+ </div>
- <div id="error-extension-unloaded" hidden>
- <div class="entry">
- <h1>Yomichan Updated!</h1>
- <p>
- The Yomichan extension has been updated to a new version! In order to continue
- viewing definitions on this page, you must reload this tab or restart your browser.
- </p>
- </div>
+ <div id="error-extension-unloaded" hidden>
+ <div class="entry">
+ <h1>Yomichan Updated!</h1>
+ <p>
+ The Yomichan extension has been updated to a new version! In order to continue
+ viewing definitions on this page, you must reload this tab or restart your browser.
+ </p>
</div>
+ </div>
- <script src="/mixed/js/core.js"></script>
- <script src="/mixed/js/yomichan.js"></script>
- <script src="/mixed/js/comm.js"></script>
- <script src="/mixed/js/api.js"></script>
- <script src="/mixed/js/japanese.js"></script>
+<script src="/mixed/js/core.js"></script>
+<script src="/mixed/js/yomichan.js"></script>
+<script src="/mixed/js/comm.js"></script>
+<script src="/mixed/js/api.js"></script>
+<script src="/mixed/js/japanese.js"></script>
- <script src="/mixed/js/cache-map.js"></script>
- <script src="/mixed/js/document-util.js"></script>
- <script src="/fg/js/dom-text-scanner.js"></script>
- <script src="/fg/js/text-source-range.js"></script>
- <script src="/fg/js/text-source-element.js"></script>
- <script src="/mixed/js/audio-system.js"></script>
- <script src="/mixed/js/dictionary-data-util.js"></script>
- <script src="/mixed/js/display.js"></script>
- <script src="/mixed/js/display-generator.js"></script>
- <script src="/mixed/js/display-history.js"></script>
- <script src="/mixed/js/dynamic-loader.js"></script>
- <script src="/mixed/js/frame-endpoint.js"></script>
- <script src="/mixed/js/media-loader.js"></script>
- <script src="/mixed/js/scroll.js"></script>
- <script src="/mixed/js/text-scanner.js"></script>
- <script src="/mixed/js/html-template-collection.js"></script>
- <script src="/mixed/js/text-to-speech-audio.js"></script>
+<script src="/mixed/js/cache-map.js"></script>
+<script src="/mixed/js/document-util.js"></script>
+<script src="/fg/js/dom-text-scanner.js"></script>
+<script src="/fg/js/text-source-range.js"></script>
+<script src="/fg/js/text-source-element.js"></script>
+<script src="/mixed/js/audio-system.js"></script>
+<script src="/mixed/js/dictionary-data-util.js"></script>
+<script src="/mixed/js/display.js"></script>
+<script src="/mixed/js/display-generator.js"></script>
+<script src="/mixed/js/display-history.js"></script>
+<script src="/mixed/js/dynamic-loader.js"></script>
+<script src="/mixed/js/frame-endpoint.js"></script>
+<script src="/mixed/js/media-loader.js"></script>
+<script src="/mixed/js/scroll.js"></script>
+<script src="/mixed/js/text-scanner.js"></script>
+<script src="/mixed/js/html-template-collection.js"></script>
+<script src="/mixed/js/text-to-speech-audio.js"></script>
- <script src="/bg/js/anki-note-builder.js"></script>
- <script src="/bg/js/template-renderer-proxy.js"></script>
+<script src="/bg/js/anki-note-builder.js"></script>
+<script src="/bg/js/template-renderer-proxy.js"></script>
- <script src="/bg/js/query-parser-generator.js"></script>
- <script src="/bg/js/query-parser.js"></script>
- <script src="/fg/js/float.js"></script>
+<script src="/bg/js/query-parser-generator.js"></script>
+<script src="/bg/js/query-parser.js"></script>
+<script src="/fg/js/float.js"></script>
- <script src="/fg/js/float-main.js"></script>
- </body>
+<script src="/fg/js/float-main.js"></script>
+</body>
</html>
diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js
index bfa2195e..7a6cae64 100644
--- a/ext/fg/js/float.js
+++ b/ext/fg/js/float.js
@@ -23,7 +23,7 @@
class DisplayFloat extends Display {
constructor() {
- super(document.querySelector('#spinner'), document.querySelector('#definitions'));
+ super();
this._nestedPopupsPrepared = false;
this._frameEndpoint = new FrameEndpoint();
this._windowMessageHandlers = new Map([
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index 5af211bb..3b6e3b3e 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -187,23 +187,6 @@ h2 {
* Search page
*/
-.icon-checkbox {
- display: none;
-}
-
-.icon-checkbox+label {
- cursor: pointer;
- font-size: 1.6em;
- padding: 0.1em;
- user-select: none;
-}
-
-.icon-checkbox:checked+label {
- /* Invert colors */
- background-color: var(--default-text-color);
- color: var(--background-color);
-}
-
#query-parser-content {
margin-top: 0.5em;
font-size: 2em;
diff --git a/ext/mixed/css/search.css b/ext/mixed/css/search.css
new file mode 100644
index 00000000..cd986233
--- /dev/null
+++ b/ext/mixed/css/search.css
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2020 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Variables */
+:root {
+ --main-content-size: 700px;
+ --main-content-padding: 10px;
+ --shadow-color: rgba(0, 0, 0, 0.185);
+ --shadow-vertical: 0 1px 4px 0 var(--shadow-color), 0 2px 2px 0 var(--shadow-color);
+
+ --font-size-default: 14px;
+ --line-height-default: calc(20 / 14);
+ --thin-border-size: 1px;
+ --toggle-size: 16px;
+
+ --textarea-line-height: 1.25em;
+ --textarea-padding: 0.5em;
+
+ --animation-duration: 0s;
+ --animation-duration2: calc(var(--animation-duration) * 2);
+
+ --text-color-default: #222222;
+ --background-color: #ffffff;
+ --background-color-light: #ffffff;
+ --input-background-color: #f2f2f2;
+ --input-background-color-dark: #dddddd;
+ --input-background-color-darker: #cccccc;
+ --separator-color1: #cccccc;
+ --button-icon-color: #333333;
+ --toggle-track-color: #cccccc;
+ --toggle-knob-color: #ffffff;
+ --selectable-indicator-color: rgba(160, 160, 160, 0.25);
+ --disabled-color: #aaaaaa;
+ --disabled-color-light: #dddddd;
+ --disabled-color-lighter: #eeeeee;
+ --accent-color: #1a73e8;
+ --accent-color-lighter: #8db9f4;
+ --accent-color-transparent25: rgba(28, 116, 233, 0.25);
+}
+:root[data-loaded=true] {
+ --animation-duration: 0.125s;
+}
+:root[data-yomichan-theme=dark] {
+ --text-color-default: #d8d8d8;
+ --background-color: #1e1e1e;
+ --shadow-color: rgba(255, 255, 255, 0.185);
+ --separator-color1: #cccccc;
+ --input-background-color: #2f2f2f;
+ --input-background-color-dark: #383838;
+ --input-background-color-darker: #484848;
+ --selectable-indicator-color: rgba(100, 100, 100, 0.25);
+ --button-icon-color: #cccccc;
+ --disabled-color: #444444;
+ --disabled-color-light: #585858;
+ --disabled-color-lighter: #777777;
+}
+
+/* Common styles */
+:root {
+ height: 100%;
+}
+body {
+ background-color: var(--background-color);
+ margin: 0;
+ padding: 0;
+ margin: 0;
+ font-size: var(--font-size-default);
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: var(--text-color-default);
+ line-height: var(--line-height-default);
+ height: 100%;
+ overflow: hidden;
+}
+h1 {
+ font-size: 2em;
+ line-height: 1.5em;
+ margin: 0;
+ padding: 0.25em 0 0;
+ font-weight: normal;
+ box-sizing: border-box;
+ border-bottom: var(--thin-border-size) solid var(--separator-color1);
+}
+
+/* Material design toggle switch */
+label.toggle {
+ cursor: default;
+}
+.toggle {
+ font-size: var(--toggle-size);
+ display: inline-block;
+}
+.toggle>input[type=checkbox] {
+ opacity: 0;
+ width: 0;
+ height: 0;
+ display: block;
+ margin: 0;
+ padding: 0;
+ border: none;
+ appearance: none;
+ -moz-appearance: none;
+}
+.toggle-body {
+ display: block;
+ cursor: pointer;
+ width: 2em;
+ height: 1em;
+ position: relative;
+}
+.toggle-track {
+ display: block;
+ position: absolute;
+ left: 0.125em;
+ right: 0.125em;
+ top: 0.125em;
+ bottom: 0.125em;
+ background-color: var(--toggle-track-color);
+ border-radius: 0.4375em;
+ transition: background-color var(--animation-duration) ease-in-out;
+}
+.toggle-knob {
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 1em;
+ height: 1em;
+ background-color: var(--toggle-knob-color);
+ border-radius: 0.5em;
+ box-shadow: var(--shadow-vertical);
+ transition: transform var(--animation-duration) ease-in-out,
+ background-color var(--animation-duration) ease-in-out;
+}
+.toggle-body>.toggle-knob::after {
+ position: absolute;
+ display: block;
+ content: "";
+ left: -0.75em;
+ top: -0.75em;
+ right: -0.75em;
+ bottom: -0.75em;
+ border-radius: 2.5em;
+ background-color: var(--selectable-indicator-color);
+ pointer-events: none;
+ transform: scale(0);
+ opacity: 0;
+ visibility: hidden;
+ transition: transform 0s ease-in-out var(--animation-duration2),
+ background-color var(--animation-duration2) ease-in-out,
+ opacity var(--animation-duration2) ease-in-out,
+ visibility 0s ease-in-out var(--animation-duration2);
+}
+.toggle>input[type=checkbox]:focus:not(:disabled)+.toggle-body>.toggle-knob::after,
+.toggle:active>input[type=checkbox]:not(:disabled)+.toggle-body>.toggle-knob::after {
+ transform: scale(1);
+ opacity: 1;
+ visibility: visible;
+ transition: transform var(--animation-duration2) ease-in-out,
+ background-color var(--animation-duration2) ease-in-out,
+ opacity var(--animation-duration2) ease-in-out,
+ visibility var(--animation-duration2) ease-in-out;
+}
+.toggle>input[type=checkbox]:focus+.toggle-body>.toggle-knob::after {
+ opacity: 0.5;
+}
+.toggle:active>input[type=checkbox]:focus+.toggle-body>.toggle-knob::after {
+ opacity: 1;
+}
+.toggle>input[type=checkbox]:checked+.toggle-body>.toggle-knob {
+ transform: translateX(1em);
+}
+.toggle>input[type=checkbox]:checked:not(:disabled)+.toggle-body>.toggle-track {
+ background-color: var(--accent-color-lighter);
+}
+.toggle>input[type=checkbox]:checked:not(:disabled)+.toggle-body>.toggle-knob {
+ background-color: var(--accent-color);
+}
+.toggle>input[type=checkbox]:focus:checked:not(:disabled)+.toggle-body>.toggle-knob::after,
+.toggle:active>input[type=checkbox]:checked:not(:disabled)+.toggle-body>.toggle-knob::after {
+ background-color: var(--accent-color-transparent25);
+}
+.toggle>input[type=checkbox]:disabled+.toggle-body {
+ cursor: default;
+}
+.toggle>input[type=checkbox]:disabled+.toggle-body>.toggle-track {
+ background-color: var(--disabled-color-light);
+}
+.toggle>input[type=checkbox]:disabled+.toggle-body>.toggle-knob {
+ background-color: var(--disabled-color-lighter);
+}
+
+/* Content layout */
+.content {
+ flex: 1 0 auto;
+ flex-flow: row nowrap;
+ width: 100%;
+ display: flex;
+ position: relative;
+ align-items: stretch;
+ justify-content: center;
+}
+.content-center {
+ flex: 1 1 auto;
+ width: var(--main-content-size);
+ padding: 0 var(--main-content-padding);
+ max-width: var(--main-content-size);
+ box-sizing: border-box;
+}
+
+/* Search bar */
+.search-textbox-container {
+ display: flex;
+ flex-flow: row nowrap;
+ width: 100%;
+ align-items: stretch;
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+#search-textbox {
+ color: var(--text-color-default);
+ flex: 1 1 auto;
+ box-sizing: border-box;
+ padding: var(--textarea-padding);
+ font-family: 'Courier New', Courier, monospace;
+ background-color: var(--input-background-color);
+ border-radius: 0;
+ line-height: var(--textarea-line-height);
+ border: 0;
+ outline: none;
+ width: 100%;
+ height: calc(var(--textarea-line-height) + var(--textarea-padding) * 2);
+ min-height: calc(var(--textarea-line-height) + var(--textarea-padding) * 2);
+ max-height: calc(var(--textarea-line-height) * 10 + var(--textarea-padding) * 2);
+ resize: vertical;
+ font-size: var(--font-size-default);
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: var(--text-color-default);
+ white-space: pre-wrap;
+}
+#search-button {
+ flex: 0 0 auto;
+ position: relative;
+ width: 2.5em;
+ background-color: var(--input-background-color);
+ border: 0;
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+ outline: none;
+ transition: background-color var(--animation-duration) ease-in-out;
+}
+#search-button:hover,
+#search-button:focus {
+ background-color: var(--input-background-color-dark);
+}
+#search-button:active {
+ background-color: var(--input-background-color-darker);
+}
+
+.icon-button-icon {
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ background-color: var(--button-icon-color);
+ mask-repeat: no-repeat;
+ mask-position: center center;
+ mask-mode: alpha;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center center;
+ -webkit-mask-mode: alpha;
+}
+.icon-button-icon[data-icon=magnifying-glass] {
+ mask-image: url(/mixed/img/magnifying-glass.svg);
+ -webkit-mask-image: url(/mixed/img/magnifying-glass.svg);
+ mask-size: 16px 16px;
+ -webkit-mask-size: 16px 16px;
+}
+
+/* Search options */
+.search-options {
+ display: flex;
+ flex-flow: row wrap;
+ margin: 0.5em -1em;
+ align-items: center;
+}
+.search-option {
+ display: flex;
+ flex: 0 1 auto;
+ margin: 0.5em 1em;
+ align-items: center;
+ cursor: pointer;
+}
+.search-option-label {
+ padding-left: 0.5em;
+}
+
+/* Search styles */
+#intro {
+ overflow: hidden;
+}
+#intro>p {
+ margin: 0;
+}
+
+#query-parser-content:not(:empty) {
+ border-bottom: var(--thin-border-size) solid var(--separator-color1);
+ padding-bottom: 0.5em;
+}
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 0d724aa0..38a441bd 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -32,10 +32,10 @@
*/
class Display extends EventDispatcher {
- constructor(spinner, container) {
+ constructor() {
super();
- this._spinner = spinner;
- this._container = container;
+ this._spinner = document.querySelector('#spinner');
+ this._container = document.querySelector('#definitions');
this._definitions = [];
this._optionsContext = {depth: 0, url: window.location.href};
this._options = null;
@@ -804,7 +804,10 @@ class Display extends EventDispatcher {
async _setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs) {
let source = urlSearchParams.get('query');
- if (!source) { return false; }
+ if (!source) {
+ this._setQueryParserText('');
+ return false;
+ }
let {state, content} = this._history;
let changeHistory = false;