summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rwxr-xr-xbuild_tmpl.sh2
-rwxr-xr-xbuild_tmpl_auto.sh16
-rw-r--r--ext/bg/background.html1
-rw-r--r--ext/bg/js/api.js4
-rw-r--r--ext/bg/js/backend.js6
-rw-r--r--ext/bg/js/search-query-parser-generator.js77
-rw-r--r--ext/bg/js/search-query-parser.js64
-rw-r--r--ext/bg/js/search.js2
-rw-r--r--ext/bg/js/templates.js55
-rw-r--r--ext/bg/query-parser-templates.html11
-rw-r--r--ext/bg/search.html7
-rw-r--r--ext/bg/settings.html1
-rw-r--r--ext/mixed/css/display.css4
-rw-r--r--ext/mixed/js/api.js4
-rw-r--r--ext/mixed/js/template-handler.js47
-rw-r--r--tmpl/query-parser.html27
17 files changed, 178 insertions, 160 deletions
diff --git a/README.md b/README.md
index 7b25b852..631f5a8b 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,6 @@ Yomichan provides advanced features not available in other browser-based diction
* [Flashcard Creation](https://foosoft.net/projects/yomichan/#flashcard-creation)
* [Keyboard Shortcuts](https://foosoft.net/projects/yomichan/#keyboard-shortcuts)
* [Development](https://foosoft.net/projects/yomichan/#development)
- * [Templates](https://foosoft.net/projects/yomichan/#templates)
* [Dependencies](https://foosoft.net/projects/yomichan/#dependencies)
* [Frequently Asked Questions](https://foosoft.net/projects/yomichan/#frequently-asked-questions)
* [Screenshots](https://foosoft.net/projects/yomichan/#screenshots)
@@ -241,15 +240,6 @@ following basic guidelines when creating pull requests:
* Large pull requests without a clear scope will not be merged.
* Incomplete or non-standalone features will not be merged.
-### Templates ###
-
-Yomichan uses [Handlebars](https://handlebarsjs.com/) templates for user interface generation. The source templates are
-found in the `tmpl` directory and the compiled version is stored in the `ext/bg/js/templates.js` file. If you modify the
-source templates, you will need to also recompile them. If you are developing on Linux or Mac OS X, you can use the
-included `build_tmpl.sh` and `build_tmpl_auto.sh` shell scripts to do this for you
-([inotify-tools](https://github.com/rvoicilas/inotify-tools/wiki) required). Otherwise, simply execute `handlebars
-tmpl/*.html -f ext/bg/js/templates.js` from the project's base directory to compile all the templates.
-
### Dependencies ###
Yomichan uses several third-party libraries to function. Below are links to homepages, snapshots, and licenses of the exact
diff --git a/build_tmpl.sh b/build_tmpl.sh
deleted file mode 100755
index e91f8de8..00000000
--- a/build_tmpl.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-handlebars tmpl/*.html -f ext/bg/js/templates.js
diff --git a/build_tmpl_auto.sh b/build_tmpl_auto.sh
deleted file mode 100755
index 98065cb7..00000000
--- a/build_tmpl_auto.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-DIRECTORY_TO_OBSERVE="tmpl"
-BUILD_SCRIPT="build_tmpl.sh"
-
-function block_for_change {
- inotifywait -e modify,move,create,delete $DIRECTORY_TO_OBSERVE
-}
-
-function build {
- bash $BUILD_SCRIPT
-}
-
-build
-while block_for_change; do
- build
-done
diff --git a/ext/bg/background.html b/ext/bg/background.html
index 11023221..7fd1c477 100644
--- a/ext/bg/background.html
+++ b/ext/bg/background.html
@@ -37,7 +37,6 @@
<script src="/bg/js/options.js"></script>
<script src="/bg/js/profile-conditions.js"></script>
<script src="/bg/js/request.js"></script>
- <script src="/bg/js/templates.js"></script>
<script src="/bg/js/translator.js"></script>
<script src="/bg/js/util.js"></script>
<script src="/mixed/js/audio.js"></script>
diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js
index f4be7a0c..cd6a9d18 100644
--- a/ext/bg/js/api.js
+++ b/ext/bg/js/api.js
@@ -33,6 +33,10 @@ function apiClipboardGet() {
return _apiInvoke('clipboardGet');
}
+function apiGetQueryParserTemplatesHtml() {
+ return _apiInvoke('getQueryParserTemplatesHtml');
+}
+
function _apiInvoke(action, params={}) {
const data = {action, params};
return new Promise((resolve, reject) => {
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 668d1fb7..529055d2 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -567,6 +567,11 @@ class Backend {
return await requestText(url, 'GET');
}
+ async _onApiGetQueryParserTemplatesHtml() {
+ const url = chrome.runtime.getURL('/bg/query-parser-templates.html');
+ return await requestText(url, 'GET');
+ }
+
_onApiGetZoom(params, sender) {
if (!sender || !sender.tab) {
return Promise.reject(new Error('Invalid tab'));
@@ -854,6 +859,7 @@ Backend._messageHandlers = new Map([
['getEnvironmentInfo', (self, ...args) => self._onApiGetEnvironmentInfo(...args)],
['clipboardGet', (self, ...args) => self._onApiClipboardGet(...args)],
['getDisplayTemplatesHtml', (self, ...args) => self._onApiGetDisplayTemplatesHtml(...args)],
+ ['getQueryParserTemplatesHtml', (self, ...args) => self._onApiGetQueryParserTemplatesHtml(...args)],
['getZoom', (self, ...args) => self._onApiGetZoom(...args)]
]);
diff --git a/ext/bg/js/search-query-parser-generator.js b/ext/bg/js/search-query-parser-generator.js
new file mode 100644
index 00000000..8d71890b
--- /dev/null
+++ b/ext/bg/js/search-query-parser-generator.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 Alex Yatskov <alex@foosoft.net>
+ * Author: Alex Yatskov <alex@foosoft.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+class QueryParserGenerator {
+ constructor() {
+ this._templateHandler = null;
+ }
+
+ async prepare() {
+ const html = await apiGetQueryParserTemplatesHtml();
+ this._templateHandler = new TemplateHandler(html);
+ }
+
+ createParseResult(terms, preview=false) {
+ const fragment = document.createDocumentFragment();
+ for (const term of terms) {
+ const termContainer = this._templateHandler.instantiate(preview ? 'term-preview' : 'term');
+ for (const segment of term) {
+ if (!segment.text.trim()) { continue; }
+ if (!segment.reading || !segment.reading.trim()) {
+ termContainer.appendChild(this.createSegmentText(segment.text));
+ } else {
+ termContainer.appendChild(this.createSegment(segment));
+ }
+ }
+ fragment.appendChild(termContainer);
+ }
+ return fragment;
+ }
+
+ createSegment(segment) {
+ const segmentContainer = this._templateHandler.instantiate('segment');
+ const segmentTextContainer = segmentContainer.querySelector('.query-parser-segment-text');
+ const segmentReadingContainer = segmentContainer.querySelector('.query-parser-segment-reading');
+ segmentTextContainer.appendChild(this.createSegmentText(segment.text));
+ segmentReadingContainer.innerText = segment.reading;
+ return segmentContainer;
+ }
+
+ createSegmentText(text) {
+ const fragment = document.createDocumentFragment();
+ for (const chr of text) {
+ const charContainer = this._templateHandler.instantiate('char');
+ charContainer.innerText = chr;
+ fragment.appendChild(charContainer);
+ }
+ return fragment;
+ }
+
+ createParserSelect(parseResults, selectedParser) {
+ const selectContainer = this._templateHandler.instantiate('select');
+ for (const parseResult of parseResults) {
+ const optionContainer = this._templateHandler.instantiate('select-option');
+ optionContainer.value = parseResult.id;
+ optionContainer.innerText = parseResult.name;
+ optionContainer.defaultSelected = selectedParser === parseResult.id;
+ selectContainer.appendChild(optionContainer);
+ }
+ return selectContainer;
+ }
+}
diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js
index e8e6d11f..f648fdd4 100644
--- a/ext/bg/js/search-query-parser.js
+++ b/ext/bg/js/search-query-parser.js
@@ -19,14 +19,20 @@
class QueryParser extends TextScanner {
constructor(search) {
- super(document.querySelector('#query-parser'), [], [], []);
+ super(document.querySelector('#query-parser-content'), [], [], []);
this.search = search;
this.parseResults = [];
this.selectedParser = null;
- this.queryParser = document.querySelector('#query-parser');
- this.queryParserSelect = document.querySelector('#query-parser-select');
+ this.queryParser = document.querySelector('#query-parser-content');
+ this.queryParserSelect = document.querySelector('#query-parser-select-container');
+
+ this.queryParserGenerator = new QueryParserGenerator();
+ }
+
+ async prepare() {
+ await this.queryParserGenerator.prepare();
}
onError(error) {
@@ -64,7 +70,7 @@ class QueryParser extends TextScanner {
const selectedParser = e.target.value;
this.selectedParser = selectedParser;
apiOptionsSet({parsing: {selectedParser}}, this.search.getOptionsContext());
- this.renderParseResult(this.getParseResult());
+ this.renderParseResult();
}
getMouseEventListeners() {
@@ -113,13 +119,13 @@ class QueryParser extends TextScanner {
async setText(text) {
this.search.setSpinnerVisible(true);
- await this.setPreview(text);
+ this.setPreview(text);
this.parseResults = await this.parseText(text);
this.refreshSelectedParser();
this.renderParserSelect();
- await this.renderParseResult();
+ this.renderParseResult();
this.search.setSpinnerVisible(false);
}
@@ -146,57 +152,29 @@ class QueryParser extends TextScanner {
return results;
}
- async setPreview(text) {
+ setPreview(text) {
const previewTerms = [];
for (let i = 0, ii = text.length; i < ii; i += 2) {
const tempText = text.substring(i, i + 2);
- previewTerms.push([{text: tempText.split('')}]);
+ previewTerms.push([{text: tempText}]);
}
- this.queryParser.innerHTML = await apiTemplateRender('query-parser.html', {
- terms: previewTerms,
- preview: true
- });
+ this.queryParser.textContent = '';
+ this.queryParser.appendChild(this.queryParserGenerator.createParseResult(previewTerms, true));
}
renderParserSelect() {
this.queryParserSelect.innerHTML = '';
if (this.parseResults.length > 1) {
- const select = document.createElement('select');
- select.classList.add('form-control');
- for (const parseResult of this.parseResults) {
- const option = document.createElement('option');
- option.value = parseResult.id;
- option.innerText = parseResult.name;
- option.defaultSelected = this.selectedParser === parseResult.id;
- select.appendChild(option);
- }
+ const select = this.queryParserGenerator.createParserSelect(this.parseResults, this.selectedParser);
select.addEventListener('change', this.onParserChange.bind(this));
this.queryParserSelect.appendChild(select);
}
}
- async renderParseResult() {
+ renderParseResult() {
const parseResult = this.getParseResult();
- if (!parseResult) {
- this.queryParser.innerHTML = '';
- return;
- }
-
- this.queryParser.innerHTML = await apiTemplateRender(
- 'query-parser.html',
- {terms: QueryParser.processParseResultForDisplay(parseResult.parsedText)}
- );
- }
-
- static processParseResultForDisplay(result) {
- return result.map((term) => {
- return term.filter((part) => part.text.trim()).map((part) => {
- return {
- text: part.text.split(''),
- reading: part.reading,
- raw: !part.reading || !part.reading.trim()
- };
- });
- });
+ this.queryParser.textContent = '';
+ if (!parseResult) { return; }
+ this.queryParser.appendChild(this.queryParserGenerator.createParseResult(parseResult.parsedText));
}
}
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index 4da27513..6641255f 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -49,6 +49,8 @@ class DisplaySearch extends Display {
try {
await this.initialize();
+ await this.queryParser.prepare();
+
const {queryParams: {query='', mode=''}} = parseUrl(window.location.href);
if (this.search !== null) {
diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js
deleted file mode 100644
index 2f65be31..00000000
--- a/ext/bg/js/templates.js
+++ /dev/null
@@ -1,55 +0,0 @@
-(function() {
- var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
-templates['query-parser.html'] = template({"1":function(container,depth0,helpers,partials,data) {
- var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
-
- return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.preview : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(4, data, 0),"data":data})) != null ? stack1 : "")
- + ((stack1 = helpers.each.call(alias1,depth0,{"name":"each","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + "</span>";
-},"2":function(container,depth0,helpers,partials,data) {
- return "<span class=\"query-parser-term-preview\">";
-},"4":function(container,depth0,helpers,partials,data) {
- return "<span class=\"query-parser-term\">";
-},"6":function(container,depth0,helpers,partials,data) {
- var stack1;
-
- return ((stack1 = container.invokePartial(partials.part,depth0,{"name":"part","data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
-},"8":function(container,depth0,helpers,partials,data) {
- var stack1;
-
- return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.raw : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.program(12, data, 0),"data":data})) != null ? stack1 : "");
-},"9":function(container,depth0,helpers,partials,data) {
- var stack1;
-
- return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.text : depth0),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
-},"10":function(container,depth0,helpers,partials,data) {
- return "<span class=\"query-parser-char\">"
- + container.escapeExpression(container.lambda(depth0, depth0))
- + "</span>";
-},"12":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {});
-
- return "<ruby>"
- + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.text : depth0),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + "<rt>"
- + container.escapeExpression(((helper = (helper = helpers.reading || (depth0 != null ? depth0.reading : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"reading","hash":{},"data":data}) : helper)))
- + "</rt></ruby>";
-},"14":function(container,depth0,helpers,partials,data,blockParams,depths) {
- var stack1;
-
- return ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"preview":(depths[1] != null ? depths[1].preview : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
-},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
- var stack1;
-
- return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.terms : depth0),{"name":"each","hash":{},"fn":container.program(14, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
-},"main_d": function(fn, props, container, depth0, data, blockParams, depths) {
-
- var decorators = container.decorators;
-
- fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"args":["term"],"data":data}) || fn;
- fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(8, data, 0, blockParams, depths),"inverse":container.noop,"args":["part"],"data":data}) || fn;
- return fn;
- }
-
-,"useDecorators":true,"usePartial":true,"useData":true,"useDepths":true});
-})(); \ No newline at end of file
diff --git a/ext/bg/query-parser-templates.html b/ext/bg/query-parser-templates.html
new file mode 100644
index 00000000..7cab16a9
--- /dev/null
+++ b/ext/bg/query-parser-templates.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html><html><head></head><body>
+
+<template id="term-template"><span class="query-parser-term" data-type="normal"></span></template>
+<template id="term-preview-template"><span class="query-parser-term" data-type="preview"></span></template>
+<template id="segment-template"><ruby class="query-parser-segment"><span class="query-parser-segment-text"></span><rt class="query-parser-segment-reading"></rt></ruby></template>
+<template id="char-template"><span class="query-parser-char"></span></template>
+
+<template id="select-template"><select class="query-parser-select form-control"></select></template>
+<template id="select-option-template"><option class="query-parser-select-option"></option></template>
+
+</body></html>
diff --git a/ext/bg/search.html b/ext/bg/search.html
index 10e5aa8e..d6336826 100644
--- a/ext/bg/search.html
+++ b/ext/bg/search.html
@@ -48,8 +48,8 @@
<div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div>
<div class="scan-disable">
- <div id="query-parser-select" class="input-group"></div>
- <div id="query-parser"></div>
+ <div id="query-parser-select-container" class="input-group"></div>
+ <div id="query-parser-content"></div>
</div>
<hr>
@@ -78,7 +78,6 @@
<script src="/bg/js/dictionary.js"></script>
<script src="/bg/js/handlebars.js"></script>
<script src="/bg/js/japanese.js"></script>
- <script src="/bg/js/templates.js"></script>
<script src="/fg/js/document.js"></script>
<script src="/fg/js/source.js"></script>
<script src="/mixed/js/audio.js"></script>
@@ -87,7 +86,9 @@
<script src="/mixed/js/display-generator.js"></script>
<script src="/mixed/js/scroll.js"></script>
<script src="/mixed/js/text-scanner.js"></script>
+ <script src="/mixed/js/template-handler.js"></script>
+ <script src="/bg/js/search-query-parser-generator.js"></script>
<script src="/bg/js/search-query-parser.js"></script>
<script src="/bg/js/clipboard-monitor.js"></script>
<script src="/bg/js/search.js"></script>
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 57616873..b048a36c 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -1097,7 +1097,6 @@
<script src="/bg/js/options.js"></script>
<script src="/bg/js/page-exit-prevention.js"></script>
<script src="/bg/js/profile-conditions.js"></script>
- <script src="/bg/js/templates.js"></script>
<script src="/bg/js/util.js"></script>
<script src="/mixed/js/audio.js"></script>
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index f131bda3..6a5383bc 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -127,12 +127,12 @@ html:root[data-yomichan-page=float] .navigation-header:not([hidden])~.navigation
user-select: none;
}
-#query-parser {
+#query-parser-content {
margin-top: 0.5em;
font-size: 2em;
}
-#query-parser[data-term-spacing=true] .query-parser-term {
+#query-parser-content[data-term-spacing=true] .query-parser-term {
margin-right: 0.2em;
}
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js
index 5ec93b01..0b1e7e4f 100644
--- a/ext/mixed/js/api.js
+++ b/ext/mixed/js/api.js
@@ -105,6 +105,10 @@ function apiGetDisplayTemplatesHtml() {
return _apiInvoke('getDisplayTemplatesHtml');
}
+function apiGetQueryParserTemplatesHtml() {
+ return _apiInvoke('getQueryParserTemplatesHtml');
+}
+
function apiGetZoom() {
return _apiInvoke('getZoom');
}
diff --git a/ext/mixed/js/template-handler.js b/ext/mixed/js/template-handler.js
new file mode 100644
index 00000000..a5a62937
--- /dev/null
+++ b/ext/mixed/js/template-handler.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 Alex Yatskov <alex@foosoft.net>
+ * Author: Alex Yatskov <alex@foosoft.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+class TemplateHandler {
+ constructor(html) {
+ this._templates = new Map();
+
+ const doc = new DOMParser().parseFromString(html, 'text/html');
+ for (const template of doc.querySelectorAll('template')) {
+ this._setTemplate(template);
+ }
+ }
+
+ _setTemplate(template) {
+ const idMatch = template.id.match(/^([a-z-]+)-template$/);
+ if (!idMatch) {
+ throw new Error(`Invalid template ID: ${template.id}`);
+ }
+ this._templates.set(idMatch[1], template);
+ }
+
+ instantiate(name) {
+ const template = this._templates.get(name);
+ return document.importNode(template.content.firstChild, true);
+ }
+
+ instantiateFragment(name) {
+ const template = this._templates.get(name);
+ return document.importNode(template.content, true);
+ }
+}
diff --git a/tmpl/query-parser.html b/tmpl/query-parser.html
deleted file mode 100644
index db98b5ff..00000000
--- a/tmpl/query-parser.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{{~#*inline "term"~}}
-{{~#if preview~}}
-<span class="query-parser-term-preview">
-{{~else~}}
-<span class="query-parser-term">
-{{~/if~}}
-{{~#each this~}}
-{{> part }}
-{{~/each~}}
-</span>
-{{~/inline~}}
-
-{{~#*inline "part"~}}
-{{~#if raw~}}
-{{~#each text~}}
-<span class="query-parser-char">{{this}}</span>
-{{~/each~}}
-{{~else~}}
-<ruby>{{~#each text~}}
-<span class="query-parser-char">{{this}}</span>
-{{~/each~}}<rt>{{reading}}</rt></ruby>
-{{~/if~}}
-{{~/inline~}}
-
-{{~#each terms~}}
-{{> term preview=../preview }}
-{{~/each~}}