8000 Add `{glossary-plain}` and `{glossary-plain-no-dictionary}` handlebars by Kuuuube · Pull Request #2047 · yomidevs/yomitan · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add {glossary-plain} and {glossary-plain-no-dictionary} handlebars #2047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions ext/data/templates/anki-field-templates-upgrade-v66.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{~#*inline "glossary-plain"~}}
{{~#scope~}}
{{~#each definition.definitions~}}
{{~#unless (op "&&" ../selectedDictionary (op "!=" ../selectedDictionary dictionary))~}}
{{~#unless ../noDictionaryTag~}}
({{dictionaryAlias}})<br>
{{~/unless~}}
{{#each glossary}}{{{formatGlossaryPlain ../dictionary .}}}{{#unless @last}}<br>{{/unless}}{{/each}}{{#unless @last}}<br>{{/unless}}
{{~/unless~}}
{{~/each~}}
{{~/scope~}}
{{~/inline~}}

{{#*inline "glossary-plain-no-dictionary"~}}
{{~> glossary-plain noDictionaryTag=true ~}}
{{/inline}}
17 changes: 17 additions & 0 deletions ext/data/templates/default-anki-field-templates.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@
{{~> glossary brief=true ~}}
{{/inline}}

{{~#*inline "glossary-plain"~}}
{{~#scope~}}
{{~#each definition.definitions~}}
{{~#unless (op "&&" ../selectedDictionary (op "!=" ../selectedDictionary dictionary))~}}
{{~#unless ../noDictionaryTag~}}
({{dictionaryAlias}})<br>
{{~/unless~}}
{{#each glossary}}{{{formatGlossaryPlain ../dictionary .}}}{{#unless @last}}<br>{{/unless}}{{/each}}{{#unless @last}}<br>{{/unless}}
{{~/unless~}}
{{~/each~}}
{{~/scope~}}
{{~/inline~}}

{{#*inline "glossary-plain-no-dictionary"~}}
{{~> glossary-plain noDictionaryTag=true ~}}
{{/inline}}

{{~#*inline "glossary-first"~}}
<div style="text-align: left;" class="yomitan-glossary">
{{~#scope~}}
Expand Down
10 changes: 10 additions & 0 deletions ext/js/data/anki-template-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export function getStandardFieldMarkers(type, language = 'ja') {
'glossary',
'glossary-brief',
'glossary-no-dictionary',
'glossary-plain',
'glossary-plain-no-dictionary',
'glossary-first',
'glossary-first-brief',
'glossary-first-no-dictionary',
Expand Down Expand Up @@ -133,6 +135,14 @@ export function getDynamicTemplates(options, dictionaryInfo) {
{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-brief"}}
{{~> glossary selectedDictionary='${escapeDictName(dictionary.name)}' brief=true}}
{{/inline}}

{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-plain"}}
{{~> glossary-plain selectedDictionary='${escapeDictName(dictionary.name)}'}}
{{/inline}}

{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-plain-no-dictionary"}}
{{~> glossary-plain-no-dictionary selectedDictionary='${escapeDictName(dictionary.name)}' noDictionaryTag=true}}
{{/inline}}
`;
}
return dynamicTemplates;
Expand Down
9 changes: 9 additions & 0 deletions ext/js/data/options-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ export class OptionsUtil {
this._updateVersion63,
this._updateVersion64,
this._updateVersion65,
this._updateVersion66,
];
/* eslint-enable @typescript-eslint/unbound-method */
if (typeof targetVersion === 'number' && targetVersion < result.length) {
Expand Down Expand Up @@ -1736,6 +1737,14 @@ export class OptionsUtil {
}
}

/**
* - Added glossary-plain handlebars
* @type {import('options-util').UpdateFunction}
*/
async _updateVersion66(options) {
await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v66.handlebars');
}

/**
* @param {string} url
* @returns {Promise<chrome.tabs.Tab>}
Expand Down
102 changes: 102 additions & 0 deletions ext/js/templates/anki-template-renderer.js
6D40
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
['concat', this._concat.bind(this)],
['pitchCategories', this._pitchCategories.bind(this)],
['formatGlossary', this._formatGlossary.bind(this)],
['formatGlossaryPlain', this._formatGlossaryPlain.bind(this)],
['hasMedia', this._hasMedia.bind(this)],
['getMedia', this._getMedia.bind(this)],
['pronunciation', this._pronunciation.bind(this)],
Expand Down Expand Up @@ -580,6 +581,14 @@
return this._getHtml(node, this._structuredContentStyleApplier, this._structuredContentDatasetKeyIgnorePattern);
}

/**
* @param {Element} node
* @returns {string}
*/
_getStructuredContentText(node) {
return this._getText(node, this._structuredContentStyleApplier, this._structuredContentDatasetKeyIgnorePattern);
}

/**
* @param {Element} node
* @returns {string}
Expand All @@ -603,6 +612,31 @@
return this._safeString(result);
}

/**
* @param {Element} node
* @param {CssStyleApplier} styleApplier
* @param {?RegExp} datasetKeyIgnorePattern
* @returns {string}
*/
_getText(node, styleApplier, datasetKeyIgnorePattern) {
const container = this._getTemporaryElement();
container.appendChild(node);
this._normalizeHtml(container, styleApplier, datasetKeyIgnorePattern);
const result = container.innerHTML
.replaceAll(/<(div|li|ol|ul|br|details|summary|hr)(\s.*?>|>)/g, '\n') // tags that usually cause line breaks
.replaceAll(/<(span|a|ruby)(\s.*?>|>)/g, ' ') // tags that usually signify some change in content
.replaceAll(/<rt(\s.*?>|>)/g, '[') // ruby start
.replaceAll('</rt>', ']') // ruby end
.replaceAll(/<.*?>/gs, '') // remove all remaining tags
.replaceAll('<', '&lt;') // escape remaining <
.replaceAll('>', '&rt;') // and >
.replaceAll(/\n+/g, '<br>') // convert newlines into linebreaks and condense newlines
.replaceAll(/^(\s*<br>\s*|\s)*/g, '') // remove leading linebreaks and whitespace
.replaceAll('<br>', '<br>\n');
container.textContent = '';
return this._safeString(result);
}

/**
* @param {Element} root
* @param {CssStyleApplier} styleApplier
Expand Down Expand Up @@ -716,6 +750,74 @@
return node !== null ? this._getStructuredContentHtml(node) : '';
}

/**
* @type {import('template-renderer').HelperFunction<string>}
*/
_formatGlossaryPlain(args, _context, options) {
const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossaryContent]} */ (args);
const data = this._getNoteDataFromOptions(options);
if (typeof content === 'string') { return this._safeString(content); }
if (!(typeof content === 'object' && content !== null)) { return ''; }
const structuredContentGenerator = this._createStructuredContentGenerator(data);
switch (content.type) {
case 'image': return '';
case 'structured-content': {
const glossaryStrings = this._extractGlossaryData(content, structuredContentGenerator);
if (glossaryStrings.length > 0) {
return glossaryStrings.join('<br>\n');
} else {
const node = structuredContentGenerator.createStructuredContent(content.content, dictionary);
return node !== null ? this._getStructuredContentText(node) : '';
}
}
case 'text': return this._safeString(content.text);
}
return '';
}

/**
* @param {import('dictionary-data').TermGlossaryStructuredContent} content
* @param {StructuredContentGenerator} structuredContentGenerator
* @returns {string[]}
*/
_extractGlossaryData(content, structuredContentGenerator) {
/** @type {import('structured-content.js').Content[]} */
const glossaryContentQueue = [];
const structuredContentQueue = [content.content];
while (structuredContentQueue.length > 0) {
const structuredContent = structuredContentQueue.pop();
if (Array.isArray(structuredContent)) {
structuredContentQueue.push(...structuredContent);
} else if (typeof structuredContent === 'object' && structuredContent.content) {
// @ts-expect-error - Checking if `data` exists
if (structuredContent.data?.content === 'glossary') {
glossaryContentQueue.push(structuredContent);
continue;
}
structuredContentQueue.push(structuredContent.content);
}
}

/** @type {string[]} */
const rawGlossaryContent = [];
while (glossaryContentQueue.length > 0) {
const structuredGloss = glossaryContentQueue.pop();
if (typeof structuredGloss === 'string') {
rawGlossaryContent.push(structuredGloss);
} else if (Array.isArray(structuredGloss)) {
glossaryContentQueue.push(...structuredGloss);
} else if (typeof structuredGloss === 'object' && structuredGloss.content) {
if (structuredGloss.tag === 'ruby') {
const node = structuredContentGenerator.createStructuredContent(structuredGloss.content, '');
rawGlossaryContent.push(node !== null ? this._getStructuredContentText(node) : '');
continue;
}
glossaryContentQueue.push(structuredGloss.content);
}
}
return rawGlossaryContent;
}

/**
* @type {import('template-renderer').HelperFunction<boolean>}
*/
Expand Down
20 changes: 20 additions & 0 deletions ext/templates-modals.html
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,14 @@ <h1 class="modal-title">Pronunciation Dictionaries</h1>
<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-plain}</code></td>
<td>List of definitions for the term with html only used for line breaks. This may break the formatting of some dictionaries.</td>
</tr>
<tr>
<td><code class="anki-field-marker">{glossary-plain-no-dictionary}</code></td>
<td><code class="anki-field-marker">{glossary-plain}</code> except the dictionary tag is omitted.</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>
Expand Down Expand Up @@ -1045,6 +1053,18 @@ <h1 class="modal-title">Pronunciation Dictionaries</h1>
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">{single-glossary-DICT-NAME-plain}</code></td>
<td>
See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-plain}</code>.
</td>
</tr>
<tr>
<td><code class="anki-field-marker">{single-glossary-DICT-NAME-plain-no-dictionary}</code></td>
<td>
See <code class="anki-field-marker">{single-glossary-DICT-NAME}</code> and <code class="anki-field-marker">{glossary-plain-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>
Expand Down
Loading
Loading
0