aboutsummaryrefslogtreecommitdiff
path: root/ext/js/data
diff options
context:
space:
mode:
authorStefanVukovic99 <stefanvukovic44@gmail.com>2024-06-20 19:27:02 +0200
committerGitHub <noreply@github.com>2024-06-20 17:27:02 +0000
commitd2c930a94d6e445053bcb5e5bb629851165425fc (patch)
tree94ff7034e7d3ab36ed663f353aeb5486bd294d1c /ext/js/data
parent1a866b3997310a04fc146b91eb47a59a3f049589 (diff)
support css file in dictionaries (#1080)
* get styles in db * get styles in settings * use styles * fix test * scope * fix comma separated * escape dict name in css selector * g regex * get styles in anki * fix tests * more specificity * whitespace * test importing * test handlebars * add styles to glossary-first
Diffstat (limited to 'ext/js/data')
-rw-r--r--ext/js/data/anki-note-builder.js10
-rw-r--r--ext/js/data/anki-note-data-creator.js83
-rw-r--r--ext/js/data/options-util.js9
3 files changed, 93 insertions, 9 deletions
diff --git a/ext/js/data/anki-note-builder.js b/ext/js/data/anki-note-builder.js
index f7d4a12a..17bc1a5c 100644
--- a/ext/js/data/anki-note-builder.js
+++ b/ext/js/data/anki-note-builder.js
@@ -60,6 +60,7 @@ export class AnkiNoteBuilder {
glossaryLayoutMode = 'default',
compactTags = false,
mediaOptions = null,
+ dictionaryStylesMap,
}) {
let duplicateScopeDeckName = null;
let duplicateScopeCheckChildren = false;
@@ -80,7 +81,7 @@ export class AnkiNoteBuilder {
}
}
- const commonData = this._createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, media);
+ const commonData = this._createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, media, dictionaryStylesMap);
const formattedFieldValuePromises = [];
for (const [, fieldValue] of fields) {
const formattedFieldValuePromise = this._formatField(fieldValue, commonData, template);
@@ -135,8 +136,9 @@ export class AnkiNoteBuilder {
glossaryLayoutMode = 'default',
compactTags = false,
marker,
+ dictionaryStylesMap,
}) {
- const commonData = this._createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, void 0);
+ const commonData = this._createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, void 0, dictionaryStylesMap);
return await this._templateRenderer.getModifiedData({marker, commonData}, 'ankiNote');
}
@@ -181,9 +183,10 @@ export class AnkiNoteBuilder {
* @param {import('settings').GlossaryLayoutMode} glossaryLayoutMode
* @param {boolean} compactTags
* @param {import('anki-templates').Media|undefined} media
+ * @param {Map<string, string>} dictionaryStylesMap
* @returns {import('anki-note-builder').CommonData}
*/
- _createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, media) {
+ _createData(dictionaryEntry, mode, context, resultOutputMode, glossaryLayoutMode, compactTags, media, dictionaryStylesMap) {
return {
dictionaryEntry,
mode,
@@ -192,6 +195,7 @@ export class AnkiNoteBuilder {
glossaryLayoutMode,
compactTags,
media,
+ dictionaryStylesMap,
};
}
diff --git a/ext/js/data/anki-note-data-creator.js b/ext/js/data/anki-note-data-creator.js
index 11618524..0bfd76cb 100644
--- a/ext/js/data/anki-note-data-creator.js
+++ b/ext/js/data/anki-note-data-creator.js
@@ -33,8 +33,9 @@ export function createAnkiNoteData(marker, {
compactTags,
context,
media,
+ dictionaryStylesMap,
}) {
- const definition = createCachedValue(getDefinition.bind(null, dictionaryEntry, context, resultOutputMode));
+ const definition = createCachedValue(getDefinition.bind(null, dictionaryEntry, context, resultOutputMode, dictionaryStylesMap));
const uniqueExpressions = createCachedValue(getUniqueExpressions.bind(null, dictionaryEntry));
const uniqueReadings = createCachedValue(getUniqueReadings.bind(null, dictionaryEntry));
const context2 = createCachedValue(getPublicContext.bind(null, context));
@@ -306,12 +307,13 @@ function getPitchCount(cachedPitches) {
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @param {import('settings').ResultOutputMode} resultOutputMode
+ * @param {Map<string, string>} dictionaryStylesMap
* @returns {import('anki-templates').DictionaryEntry}
*/
-function getDefinition(dictionaryEntry, context, resultOutputMode) {
+function getDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap) {
switch (dictionaryEntry.type) {
case 'term':
- return getTermDefinition(dictionaryEntry, context, resultOutputMode);
+ return getTermDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap);
case 'kanji':
return getKanjiDefinition(dictionaryEntry, context);
default:
@@ -409,9 +411,10 @@ function getKanjiFrequencies(dictionaryEntry) {
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @param {import('settings').ResultOutputMode} resultOutputMode
+ * @param {Map<string, string>} dictionaryStylesMap
* @returns {import('anki-templates').TermDictionaryEntry}
*/
-function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
+function getTermDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap) {
/** @type {import('anki-templates').TermDictionaryEntryType} */
let type = 'term';
switch (resultOutputMode) {
@@ -427,7 +430,7 @@ function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
const primarySource = getPrimarySource(dictionaryEntry);
const dictionaryNames = createCachedValue(getTermDictionaryNames.bind(null, dictionaryEntry));
- const commonInfo = createCachedValue(getTermDictionaryEntryCommonInfo.bind(null, dictionaryEntry, type));
+ const commonInfo = createCachedValue(getTermDictionaryEntryCommonInfo.bind(null, dictionaryEntry, type, dictionaryStylesMap));
const termTags = createCachedValue(getTermTags.bind(null, dictionaryEntry, type));
const expressions = createCachedValue(getTermExpressions.bind(null, dictionaryEntry));
const frequencies = createCachedValue(getTermFrequencies.bind(null, dictionaryEntry));
@@ -436,6 +439,7 @@ function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
const pitches = createCachedValue(getTermPitches.bind(null, dictionaryEntry));
const phoneticTranscriptions = createCachedValue(getTermPhoneticTranscriptions.bind(null, dictionaryEntry));
const glossary = createCachedValue(getTermGlossaryArray.bind(null, dictionaryEntry, type));
+ const styleInfo = createCachedValue(getTermStyles.bind(null, dictionaryEntry, type, dictionaryStylesMap));
const cloze = createCachedValue(getCloze.bind(null, dictionaryEntry, context));
const furiganaSegments = createCachedValue(getTermFuriganaSegments.bind(null, dictionaryEntry, type));
const sequence = createCachedValue(getTermDictionaryEntrySequence.bind(null, dictionaryEntry));
@@ -466,6 +470,8 @@ function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
},
get expressions() { return getCachedValue(expressions); },
get glossary() { return getCachedValue(glossary); },
+ get glossaryScopedStyles() { return getCachedValue(styleInfo)?.glossaryScopedStyles; },
+ get dictScopedStyles() { return getCachedValue(styleInfo)?.dictScopedStyles; },
get definitionTags() { return type === 'term' ? getCachedValue(commonInfo).definitionTags : void 0; },
get termTags() { return getCachedValue(termTags); },
get definitions() { return getCachedValue(commonInfo).definitions; },
@@ -496,9 +502,10 @@ function getTermDictionaryNames(dictionaryEntry) {
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
+ * @param {Map<string, string>} dictionaryStylesMap
* @returns {import('anki-templates').TermDictionaryEntryCommonInfo}
*/
-function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
+function getTermDictionaryEntryCommonInfo(dictionaryEntry, type, dictionaryStylesMap) {
const merged = (type === 'termMerged');
const hasDefinitions = (type !== 'term');
@@ -518,6 +525,13 @@ function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
/** @type {import('anki-templates').Tag[]} */
const definitionTags = [];
for (const {tags, headwordIndices, entries, dictionary, sequences} of dictionaryEntry.definitions) {
+ const dictionaryStyles = dictionaryStylesMap.get(dictionary);
+ let glossaryScopedStyles = '';
+ let dictScopedStyles = '';
+ if (dictionaryStyles) {
+ glossaryScopedStyles = addGlossaryScopeToCss(dictionaryStyles);
+ dictScopedStyles = addGlossaryScopeToCss(addDictionaryScopeToCss(dictionaryStyles, dictionary));
+ }
const definitionTags2 = [];
for (const tag of tags) {
definitionTags.push(convertTag(tag));
@@ -528,6 +542,8 @@ function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
definitions.push({
sequence: sequences[0],
dictionary,
+ glossaryScopedStyles,
+ dictScopedStyles,
glossary: entries,
definitionTags: definitionTags2,
only,
@@ -543,6 +559,39 @@ function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
}
/**
+ * @param {string} css
+ * @returns {string}
+ */
+function addGlossaryScopeToCss(css) {
+ return addScopeToCss(css, '.yomitan-glossary');
+}
+
+/**
+ * @param {string} css
+ * @param {string} dictionaryTitle
+ * @returns {string}
+ */
+function addDictionaryScopeToCss(css, dictionaryTitle) {
+ const escapedTitle = dictionaryTitle
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"');
+
+ return addScopeToCss(css, `[data-dictionary="${escapedTitle}"]`);
+}
+
+/**
+ * @param {string} css
+ * @param {string} scopeSelector
+ * @returns {string}
+ */
+function addScopeToCss(css, scopeSelector) {
+ const regex = /([^\r\n,{}]+)(\s*[,{])/g;
+ const replacement = `${scopeSelector} $1$2`;
+ return css.replace(regex, replacement);
+}
+
+
+/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermFrequency[]}
*/
@@ -770,6 +819,28 @@ function getTermGlossaryArray(dictionaryEntry, type) {
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
+ * @param {Map<string, string>} dictionaryStylesMap
+ * @returns {{glossaryScopedStyles: string, dictScopedStyles: string}|undefined}
+ */
+function getTermStyles(dictionaryEntry, type, dictionaryStylesMap) {
+ if (type !== 'term') {
+ return void 0;
+ }
+ let glossaryScopedStyles = '';
+ let dictScopedStyles = '';
+ for (const {dictionary} of dictionaryEntry.definitions) {
+ const dictionaryStyles = dictionaryStylesMap.get(dictionary);
+ if (dictionaryStyles) {
+ glossaryScopedStyles += addGlossaryScopeToCss(dictionaryStyles);
+ dictScopedStyles += addGlossaryScopeToCss(addDictionaryScopeToCss(dictionaryStyles, dictionary));
+ }
+ }
+ return {glossaryScopedStyles, dictScopedStyles};
+}
+
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates').TermDictionaryEntryType} type
* @returns {import('anki-templates').Tag[]|undefined}
*/
function getTermTags(dictionaryEntry, type) {
diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js
index 64b2e3bc..8af299d8 100644
--- a/ext/js/data/options-util.js
+++ b/ext/js/data/options-util.js
@@ -548,6 +548,7 @@ export class OptionsUtil {
this._updateVersion38,
this._updateVersion39,
this._updateVersion40,
+ this._updateVersion41,
];
/* eslint-enable @typescript-eslint/unbound-method */
if (typeof targetVersion === 'number' && targetVersion < result.length) {
@@ -1340,6 +1341,14 @@ export class OptionsUtil {
}
/**
+ * - Updated `glossary` handlebars to support dictionary css.
+ * @type {import('options-util').UpdateFunction}
+ */
+ async _updateVersion41(options) {
+ await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v41.handlebars');
+ }
+
+ /**
* @param {string} url
* @returns {Promise<chrome.tabs.Tab>}
*/