summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/js/search.js58
-rw-r--r--ext/bg/search.html2
-rw-r--r--ext/fg/float.html4
-rw-r--r--ext/mixed/css/display.css1
-rw-r--r--ext/mixed/js/display-generator.js1
-rw-r--r--ext/mixed/js/display.js172
-rw-r--r--ext/mixed/js/yomichan.js9
7 files changed, 153 insertions, 94 deletions
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index fc857368..d95fd5e4 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -63,11 +63,14 @@ class DisplaySearch extends Display {
this.on('contentUpdating', this._onContentUpdating.bind(this));
+ this.queryParserVisible = true;
this.setHistorySettings({useBrowserHistory: true});
const options = this.getOptions();
- const {queryParams: {query='', mode=''}} = parseUrl(window.location.href);
+ const urlSearchParams = new URLSearchParams(location.search);
+ let mode = urlSearchParams.get('mode');
+ if (mode === null) { mode = ''; }
document.documentElement.dataset.searchMode = mode;
@@ -78,8 +81,6 @@ class DisplaySearch extends Display {
this._wanakanaEnable.checked = false;
}
- this._setQuery(query);
-
if (mode !== 'popup') {
if (options.general.enableClipboardMonitor === true) {
this._clipboardMonitorEnable.checked = true;
@@ -147,14 +148,24 @@ class DisplaySearch extends Display {
if (!this._isPrepared) { return; }
const query = this._query.value;
if (query) {
- this._setQuery(query);
this._onSearchQueryUpdated(query, false);
}
}
+ postProcessQuery(query) {
+ if (this._isWanakanaEnabled()) {
+ try {
+ query = wanakana.toKana(query);
+ } catch (e) {
+ // NOP
+ }
+ }
+ return query;
+ }
+
// Private
- _onContentUpdating({type, source, content, urlSearchParams}) {
+ _onContentUpdating({type, content, source}) {
let animate = false;
let valid = false;
switch (type) {
@@ -173,13 +184,8 @@ class DisplaySearch extends Display {
if (typeof source !== 'string') { source = ''; }
- let full = urlSearchParams.get('full');
- if (full === null) { full = source; }
-
- this._closePopups();
- this._setQuery(full);
+ this._query.value = source;
this._setIntroVisible(!valid, animate);
- this._setTitleText(source);
this._updateSearchButton();
}
@@ -289,19 +295,6 @@ class DisplaySearch extends Display {
return this._wanakanaEnable !== null && this._wanakanaEnable.checked;
}
- _setQuery(query) {
- let interpretedQuery = query;
- if (this._isWanakanaEnabled()) {
- try {
- interpretedQuery = wanakana.toKana(query);
- } catch (e) {
- // NOP
- }
- }
- this._query.value = interpretedQuery;
- this.setQueryParserText(interpretedQuery);
- }
-
_setIntroVisible(visible, animate) {
if (this._introVisible === visible) {
return;
@@ -362,19 +355,6 @@ class DisplaySearch extends Display {
this._search.disabled = this._introVisible && (this._query === null || this._query.value.length === 0);
}
- _setTitleText(text) {
- // Chrome limits title to 1024 characters
- if (text.length > 1000) {
- text = text.substring(0, 1000) + '...';
- }
-
- if (text.length === 0) {
- document.title = 'Yomichan Search';
- } else {
- document.title = `${text} - Yomichan Search`;
- }
- }
-
async _prepareNestedPopups() {
let complete = false;
@@ -401,8 +381,4 @@ class DisplaySearch extends Display {
await onOptionsUpdated();
}
-
- _closePopups() {
- yomichan.trigger('closePopups');
- }
}
diff --git a/ext/bg/search.html b/ext/bg/search.html
index 653a708f..eb85e368 100644
--- a/ext/bg/search.html
+++ b/ext/bg/search.html
@@ -46,7 +46,7 @@
<div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
- <div class="scan-disable">
+ <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>
diff --git a/ext/fg/float.html b/ext/fg/float.html
index 427a7e57..17378713 100644
--- a/ext/fg/float.html
+++ b/ext/fg/float.html
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1" />
- <title></title>
+ <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">
@@ -21,7 +21,7 @@
<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" hidden>
+ <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>
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index 703cef1c..f5d9403a 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -305,6 +305,7 @@ button.action-button {
border-bottom: 0.03571428em dashed var(--dark-border-color); /* 28px => 1px */
color: var(--default-text-color);
text-decoration: none;
+ cursor: pointer;
}
.term-expression[data-frequency=popular]>.term-expression-text,
diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js
index 276b37de..2b0b5b1c 100644
--- a/ext/mixed/js/display-generator.js
+++ b/ext/mixed/js/display-generator.js
@@ -281,7 +281,6 @@ class DisplayGenerator {
_createKanjiLink(character) {
const node = document.createElement('a');
- node.href = '#';
node.className = 'kanji-link';
node.textContent = character;
return node;
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index a9f217b4..40febadc 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -69,7 +69,12 @@ class Display extends EventDispatcher {
this._historyChangeIgnore = false;
this._historyHasChanged = false;
this._navigationHeader = document.querySelector('#navigation-header');
+ this._defaultTitle = 'Yomichan Search';
+ this._defaultTitleMaxLength = 1000;
this._fullQuery = '';
+ this._queryParserVisible = false;
+ this._queryParserVisibleOverride = null;
+ this._queryParserContainer = document.querySelector('#query-parser-container');
this._queryParser = new QueryParser({
getOptionsContext: this.getOptionsContext.bind(this),
setSpinnerVisible: this.setSpinnerVisible.bind(this)
@@ -123,6 +128,15 @@ class Display extends EventDispatcher {
this._autoPlayAudioDelay = value;
}
+ get queryParserVisible() {
+ return this._queryParserVisible;
+ }
+
+ set queryParserVisible(value) {
+ this._queryParserVisible = value;
+ this._updateQueryParserVisibility();
+ }
+
async prepare() {
this._setInteractive(true);
await this._displayGenerator.prepare();
@@ -322,10 +336,8 @@ class Display extends EventDispatcher {
return data;
}
- setQueryParserText(text) {
- if (this._fullQuery === text) { return; }
- this._fullQuery = text;
- this._queryParser.setText(text);
+ postProcessQuery(query) {
+ return query;
}
// Message handlers
@@ -371,6 +383,13 @@ class Display extends EventDispatcher {
let type = urlSearchParams.get('type');
if (type === null) { type = 'terms'; }
+ const fullVisible = urlSearchParams.get('full-visible');
+ this._queryParserVisibleOverride = (fullVisible === null ? null : (fullVisible !== 'false'));
+ this._updateQueryParserVisibility();
+
+ this._closePopups();
+ this._setEventListenersActive(false);
+
let asigned = false;
const eventArgs = {type, urlSearchParams, token};
this._historyHasChanged = true;
@@ -379,38 +398,8 @@ class Display extends EventDispatcher {
case 'terms':
case 'kanji':
{
- const source = urlSearchParams.get('query');
- if (!source) { break; }
-
const isTerms = (type === 'terms');
- let {state, content} = this._history;
- let changeHistory = false;
- if (!isObject(content)) {
- content = {};
- changeHistory = true;
- }
- if (!isObject(state)) {
- state = {};
- changeHistory = true;
- }
-
- let {definitions} = content;
- if (!Array.isArray(definitions)) {
- definitions = await this._findDefinitions(isTerms, source, urlSearchParams);
- if (this._setContentToken !== token) { return; }
- content.definitions = definitions;
- changeHistory = true;
- }
-
- if (changeHistory) {
- this._historyStateUpdate(state, content);
- }
-
- asigned = true;
- eventArgs.source = source;
- eventArgs.content = content;
- this.trigger('contentUpdating', eventArgs);
- await this._setContentTermsOrKanji(token, isTerms, definitions, state);
+ asigned = await this._setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs);
}
break;
case 'unloaded':
@@ -419,19 +408,25 @@ class Display extends EventDispatcher {
eventArgs.content = content;
this.trigger('contentUpdating', eventArgs);
this._setContentExtensionUnloaded();
+ asigned = true;
}
break;
}
- if (!asigned) {
- const {content} = this._history;
- eventArgs.type = 'clear';
- eventArgs.content = content;
- this.trigger('contentUpdating', eventArgs);
- this._clearContent();
+ const stale = (this._setContentToken !== token);
+ if (!stale) {
+ if (!asigned) {
+ const {content} = this._history;
+ eventArgs.type = 'clear';
+ eventArgs.content = content;
+ this.trigger('contentUpdating', eventArgs);
+ this._clearContent();
+ }
+
+ this._setEventListenersActive(true);
}
- eventArgs.stale = (this._setContentToken !== token);
+ eventArgs.stale = stale;
this.trigger('contentUpdated', eventArgs);
} catch (e) {
this.onError(e);
@@ -739,12 +734,47 @@ class Display extends EventDispatcher {
}
}
- async _setContentTermsOrKanji(token, isTerms, definitions, {sentence=null, url=null, focusEntry=null, scrollX=null, scrollY=null}) {
+ async _setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs) {
+ let source = urlSearchParams.get('query');
+ if (!source) { return false; }
+
+ let {state, content} = this._history;
+ let changeHistory = false;
+ if (!isObject(content)) {
+ content = {};
+ changeHistory = true;
+ }
+ if (!isObject(state)) {
+ state = {};
+ changeHistory = true;
+ }
+
+ source = this.postProcessQuery(source);
+ let full = urlSearchParams.get('full');
+ full = (full === null ? source : this.postProcessQuery(full));
+ this._setQueryParserText(full);
+ this._setTitleText(source);
+
+ let {definitions} = content;
+ if (!Array.isArray(definitions)) {
+ definitions = await this._findDefinitions(isTerms, source, urlSearchParams);
+ if (this._setContentToken !== token) { return true; }
+ content.definitions = definitions;
+ changeHistory = true;
+ }
+
+ if (changeHistory) {
+ this._historyStateUpdate(state, content);
+ }
+
+ eventArgs.source = source;
+ eventArgs.content = content;
+ this.trigger('contentUpdating', eventArgs);
+
+ let {sentence=null, url=null, focusEntry=null, scrollX=null, scrollY=null} = state;
if (typeof url !== 'string') { url = window.location.href; }
sentence = this._getValidSentenceData(sentence);
- this._setEventListenersActive(false);
-
this._definitions = definitions;
for (const definition of definitions) {
@@ -761,7 +791,7 @@ class Display extends EventDispatcher {
for (let i = 0, ii = definitions.length; i < ii; ++i) {
if (i > 0) {
await promiseTimeout(1);
- if (this._setContentToken !== token) { return; }
+ if (this._setContentToken !== token) { return true; }
}
const entry = (
@@ -791,8 +821,12 @@ class Display extends EventDispatcher {
this.autoPlayAudio();
}
- this._setEventListenersActive(true);
+ this._setContentTermsOrKanjiUpdateAdderButtons(token, isTerms, definitions);
+ return true;
+ }
+
+ async _setContentTermsOrKanjiUpdateAdderButtons(token, isTerms, definitions) {
const modes = isTerms ? ['term-kanji', 'term-kana'] : ['kanji'];
const states = await this._getDefinitionsAddable(definitions, modes);
if (this._setContentToken !== token) { return; }
@@ -813,11 +847,12 @@ class Display extends EventDispatcher {
this._updateNavigation(null, null);
this._setNoContentVisible(false);
+ this._setTitleText('');
}
_clearContent() {
- this._setEventListenersActive(false);
this._container.textContent = '';
+ this._setTitleText('');
}
_setNoContentVisible(visible) {
@@ -828,6 +863,28 @@ class Display extends EventDispatcher {
}
}
+ _setQueryParserText(text) {
+ if (this._fullQuery === text) { return; }
+ this._fullQuery = text;
+ if (!this._isQueryParserVisible()) { return; }
+ this._queryParser.setText(text);
+ }
+
+ _setTitleText(text) {
+ // Chrome limits title to 1024 characters
+ const ellipsis = '...';
+ const maxLength = this._defaultTitleMaxLength - this._defaultTitle.length;
+ if (text.length > maxLength) {
+ text = `${text.substring(0, Math.max(0, maxLength - maxLength))}${ellipsis}`;
+ }
+
+ document.title = (
+ text.length === 0 ?
+ this._defaultTitle :
+ `${text} - ${this._defaultTitle}`
+ );
+ }
+
_updateNavigation(previous, next) {
if (this._navigationHeader === null) { return; }
this._navigationHeader.hidden = !(previous || next);
@@ -1177,6 +1234,25 @@ class Display extends EventDispatcher {
if (!wildcards) {
params.wildcards = 'off';
}
+ if (this._queryParserVisibleOverride !== null) {
+ params['full-visible'] = `${this._queryParserVisibleOverride}`;
+ }
return params;
}
+
+ _isQueryParserVisible() {
+ return (
+ this._queryParserVisibleOverride !== null ?
+ this._queryParserVisibleOverride :
+ this._queryParserVisible
+ );
+ }
+
+ _updateQueryParserVisibility() {
+ this._queryParserContainer.hidden = !this._isQueryParserVisible();
+ }
+
+ _closePopups() {
+ yomichan.trigger('closePopups');
+ }
}
diff --git a/ext/mixed/js/yomichan.js b/ext/mixed/js/yomichan.js
index 6eba68b2..2de82274 100644
--- a/ext/mixed/js/yomichan.js
+++ b/ext/mixed/js/yomichan.js
@@ -48,6 +48,7 @@ const yomichan = (() => {
}
this._isExtensionUnloaded = false;
+ this._isTriggeringExtensionUnloaded = false;
this._isReady = false;
const {promise, resolve} = deferPromise();
@@ -256,7 +257,13 @@ const yomichan = (() => {
triggerExtensionUnloaded() {
this._isExtensionUnloaded = true;
- this.trigger('extensionUnloaded');
+ if (this._isTriggeringExtensionUnloaded) { return; }
+ try {
+ this._isTriggeringExtensionUnloaded = true;
+ this.trigger('extensionUnloaded');
+ } finally {
+ this._isTriggeringExtensionUnloaded = false;
+ }
}
// Private