Files
SubMiner/vendor/yomitan/js/data/anki-template-util.js
2026-02-09 19:04:19 -08:00

207 lines
7.1 KiB
JavaScript

/*
* Copyright (C) 2024-2025 Yomitan 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/>.
*/
/**
* Gets a list of field markers from the standard Handlebars template.
* @param {import('dictionary').DictionaryEntryType} type What type of dictionary entry to get the fields for.
* @param {string} language
* @returns {string[]} The list of field markers.
* @throws {Error}
*/
export function getStandardFieldMarkers(type, language = 'ja') {
switch (type) {
case 'term': {
const markers = [
'audio',
'clipboard-image',
'clipboard-text',
'cloze-body',
'cloze-prefix',
'cloze-suffix',
'conjugation',
'dictionary',
'dictionary-alias',
'document-title',
'expression',
'frequencies',
'frequency-harmonic-rank',
'frequency-harmonic-occurrence',
'frequency-average-rank',
'frequency-average-occurrence',
'furigana',
'furigana-plain',
'glossary',
'glossary-brief',
'glossary-no-dictionary',
'glossary-plain',
'glossary-plain-no-dictionary',
'glossary-first',
'glossary-first-brief',
'glossary-first-no-dictionary',
'part-of-speech',
'phonetic-transcriptions',
'reading',
'screenshot',
'search-query',
'popup-selection-text',
'sentence',
'sentence-furigana',
'sentence-furigana-plain',
'tags',
'url',
];
if (language === 'ja') {
markers.push(
'cloze-body-kana',
'pitch-accents',
'pitch-accent-graphs',
'pitch-accent-graphs-jj',
'pitch-accent-positions',
'pitch-accent-categories',
);
}
return markers;
}
case 'kanji':
return [
'character',
'clipboard-image',
'clipboard-text',
'cloze-body',
'cloze-prefix',
'cloze-suffix',
'dictionary',
'dictionary-alias',
'document-title',
'frequencies',
'frequency-harmonic-rank',
'frequency-harmonic-occurrence',
'frequency-average-rank',
'frequency-average-occurrence',
'glossary',
'kunyomi',
'onyomi',
'onyomi-hiragana',
'screenshot',
'search-query',
'popup-selection-text',
'sentence',
'sentence-furigana',
'sentence-furigana-plain',
'stroke-count',
'tags',
'url',
];
default:
throw new Error(`Unsupported type: ${type}`);
}
}
/**
* @param {import('settings').ProfileOptions} options
* @param {import('dictionary-importer').Summary[]} dictionaryInfo
* @returns {string}
*/
export function getDynamicTemplates(options, dictionaryInfo) {
let dynamicTemplates = '\n';
for (const dictionary of options.dictionaries) {
const currentDictionaryInfo = dictionaryInfo.find(({title}) => title === dictionary.name);
if (!dictionary.enabled) { continue; }
const totalTerms = currentDictionaryInfo?.counts?.terms?.total;
if (totalTerms && totalTerms > 0) {
dynamicTemplates += `
{{#*inline "single-glossary-${getKebabCase(dictionary.name)}"}}
{{~> glossary selectedDictionary='${escapeDictName(dictionary.name)}'}}
{{/inline}}
{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-no-dictionary"}}
{{~> glossary selectedDictionary='${escapeDictName(dictionary.name)}' noDictionaryTag=true}}
{{/inline}}
{{#*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}}
`;
}
const totalMeta = currentDictionaryInfo?.counts?.termMeta;
if (totalMeta && totalMeta.freq && totalMeta.freq > 0) {
dynamicTemplates += `
{{#*inline "single-frequency-number-${getKebabCase(dictionary.name)}"}}
{{~> single-frequency-number selectedDictionary='${escapeDictName(dictionary.name)}'}}
{{/inline}}
{{#*inline "single-frequency-${getKebabCase(dictionary.name)}"}}
{{~> frequencies selectedDictionary='${escapeDictName(dictionary.name)}'}}
{{/inline}}
`;
}
}
return dynamicTemplates;
}
/**
* @param {import('settings').DictionariesOptions} dictionaries
* @param {import('dictionary-importer').Summary[]} dictionaryInfo
* @returns {string[]} The list of field markers.
*/
export function getDynamicFieldMarkers(dictionaries, dictionaryInfo) {
const markers = [];
for (const dictionary of dictionaries) {
const currentDictionaryInfo = dictionaryInfo.find(({title}) => title === dictionary.name);
if (!dictionary.enabled) { continue; }
const totalTerms = currentDictionaryInfo?.counts?.terms?.total;
if (totalTerms && totalTerms > 0) {
markers.push(`single-glossary-${getKebabCase(dictionary.name)}`);
}
const totalMeta = currentDictionaryInfo?.counts?.termMeta;
if (totalMeta && totalMeta.freq && totalMeta.freq > 0) {
markers.push(`single-frequency-number-${getKebabCase(dictionary.name)}`);
}
}
return markers;
}
/**
* @param {string} str
* @returns {string}
*/
export function getKebabCase(str) {
return str
.replace(/[\s_\u3000]/g, '-')
.replace(/[^\p{L}\p{N}-]/gu, '')
.replace(/--+/g, '-')
.replace(/^-|-$/g, '')
.toLowerCase();
}
/**
* @param {string} name
* @returns {string}
*/
function escapeDictName(name) {
return name
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'');
}