aboutsummaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/background/backend.js20
-rw-r--r--ext/js/data/options-util.js35
-rw-r--r--ext/js/pages/welcome-main.js7
-rw-r--r--ext/js/templates/sandbox/anki-template-renderer.js54
4 files changed, 67 insertions, 49 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 308ae4d5..8e8e6945 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -2127,20 +2127,12 @@ class Backend {
}
async _openWelcomeGuidePageOnce() {
- if (isObject(chrome.storage) && isObject(chrome.storage.session)) {
- // Chrome
- chrome.storage.session.get(['openedWelcomePage']).then((result) => {
- if (!result.openedWelcomePage) {
- this._openWelcomeGuidePage();
- chrome.storage.session.set({'openedWelcomePage': true});
- }
- });
- } else {
- // Firefox (storage.session is not supported yet)
- // NOTE: This means that the welcome page will repeatedly open in Firefox
- // until they support storage.session.
- this._openWelcomeGuidePage();
- }
+ chrome.storage.session.get(['openedWelcomePage']).then((result) => {
+ if (!result.openedWelcomePage) {
+ this._openWelcomeGuidePage();
+ chrome.storage.session.set({'openedWelcomePage': true});
+ }
+ });
}
async _openWelcomeGuidePage() {
diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js
index 2674701f..1f2ffb05 100644
--- a/ext/js/data/options-util.js
+++ b/ext/js/data/options-util.js
@@ -470,7 +470,8 @@ class OptionsUtil {
{async: false, update: this._updateVersion17.bind(this)},
{async: false, update: this._updateVersion18.bind(this)},
{async: false, update: this._updateVersion19.bind(this)},
- {async: false, update: this._updateVersion20.bind(this)}
+ {async: false, update: this._updateVersion20.bind(this)},
+ {async: true, update: this._updateVersion21.bind(this)}
];
if (typeof targetVersion === 'number' && targetVersion < result.length) {
result.splice(targetVersion);
@@ -997,4 +998,36 @@ class OptionsUtil {
}
return options;
}
+
+ async _updateVersion21(options) {
+ await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v21.handlebars');
+
+ let customTemplates = false;
+ for (const {options: profileOptions} of options.profiles) {
+ if (profileOptions.anki.fieldTemplates !== null) {
+ customTemplates = true;
+ }
+ }
+
+ if (customTemplates && isObject(chrome.storage)) {
+ chrome.storage.session.set({'needsCustomTemplatesWarning': true});
+ await this._createTab(chrome.runtime.getURL('/welcome.html'));
+ chrome.storage.session.set({'openedWelcomePage': true});
+ }
+
+ return options;
+ }
+
+ _createTab(url) {
+ return new Promise((resolve, reject) => {
+ chrome.tabs.create({url}, (tab) => {
+ const e = chrome.runtime.lastError;
+ if (e) {
+ reject(new Error(e.message));
+ } else {
+ resolve(tab);
+ }
+ });
+ });
+ }
}
diff --git a/ext/js/pages/welcome-main.js b/ext/js/pages/welcome-main.js
index 521ce2c2..8039dae5 100644
--- a/ext/js/pages/welcome-main.js
+++ b/ext/js/pages/welcome-main.js
@@ -58,6 +58,13 @@ async function setupGenericSettingsController(genericSettingController) {
setupEnvironmentInfo();
+ chrome.storage.session.get({'needsCustomTemplatesWarning': false}).then((result) => {
+ if (result.needsCustomTemplatesWarning) {
+ document.documentElement.dataset.warnCustomTemplates = 'true';
+ chrome.storage.session.remove(['needsCustomTemplatesWarning']);
+ }
+ });
+
const preparePromises = [];
const modalController = new ModalController();
diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js
index 789f0942..766c7798 100644
--- a/ext/js/templates/sandbox/anki-template-renderer.js
+++ b/ext/js/templates/sandbox/anki-template-renderer.js
@@ -68,9 +68,7 @@ class AnkiTemplateRenderer {
['dumpObject', this._dumpObject.bind(this)],
['furigana', this._furigana.bind(this)],
['furiganaPlain', this._furiganaPlain.bind(this)],
- ['kanjiLinks', this._kanjiLinks.bind(this)],
['multiLine', this._multiLine.bind(this)],
- ['sanitizeCssClass', this._sanitizeCssClass.bind(this)],
['regexReplace', this._regexReplace.bind(this)],
['regexMatch', this._regexMatch.bind(this)],
['mergeTags', this._mergeTags.bind(this)],
@@ -132,10 +130,14 @@ class AnkiTemplateRenderer {
return Handlebars.Utils.escapeExpression(text);
}
+ _safeString(text) {
+ return new Handlebars.SafeString(text);
+ }
+
// Template helpers
- _dumpObject(context, options) {
- const dump = JSON.stringify(options.fn(context), null, 4);
+ _dumpObject(context, object) {
+ const dump = JSON.stringify(object, null, 4);
return this._escape(dump);
}
@@ -145,14 +147,16 @@ class AnkiTemplateRenderer {
let result = '';
for (const {text, reading: reading2} of segs) {
- if (reading2.length > 0) {
- result += `<ruby>${text}<rt>${reading2}</rt></ruby>`;
+ const safeText = this._escape(text);
+ const safeReading = this._escape(reading2);
+ if (safeReading.length > 0) {
+ result += `<ruby>${safeText}<rt>${safeReading}</rt></ruby>`;
} else {
- result += text;
+ result += safeText;
}
}
- return result;
+ return this._safeString(result);
}
_furiganaPlain(context, ...args) {
@@ -173,29 +177,16 @@ class AnkiTemplateRenderer {
}
_getFuriganaExpressionAndReading(context, ...args) {
- const options = args[args.length - 1];
if (args.length >= 3) {
return {expression: args[0], reading: args[1]};
- } else {
- const {expression, reading} = options.fn(context);
+ } else if (args.length === 2) {
+ const {expression, reading} = args[0];
return {expression, reading};
+ } else {
+ return void 0;
}
}
- _kanjiLinks(context, options) {
- const jp = this._japaneseUtil;
- let result = '';
- for (const c of options.fn(context)) {
- if (jp.isCodePointKanji(c.codePointAt(0))) {
- result += `<a href="#" class="kanji-link">${c}</a>`;
- } else {
- result += c;
- }
- }
-
- return result;
- }
-
_stringToMultiLineHtml(string) {
return string.split('\n').join('<br>');
}
@@ -204,10 +195,6 @@ class AnkiTemplateRenderer {
return this._stringToMultiLineHtml(options.fn(context));
}
- _sanitizeCssClass(context, options) {
- return options.fn(context).replace(/[^_a-z0-9\u00a0-\uffff]/ig, '_');
- }
-
_regexReplace(context, ...args) {
// Usage:
// {{#regexReplace regex string [flags] [content]...}}content{{/regexReplace}}
@@ -219,7 +206,7 @@ class AnkiTemplateRenderer {
const options = args[argCount];
let value = typeof options.fn === 'function' ? options.fn(context) : '';
if (argCount > 3) {
- value = `${args.slice(3).join('')}${value}`;
+ value = `${args.slice(3, -1).join('')}${value}`;
}
if (argCount > 1) {
try {
@@ -243,7 +230,7 @@ class AnkiTemplateRenderer {
const options = args[argCount];
let value = typeof options.fn === 'function' ? options.fn(context) : '';
if (argCount > 2) {
- value = `${args.slice(2).join('')}${value}`;
+ value = `${args.slice(2, -1).join('')}${value}`;
}
if (argCount > 0) {
try {
@@ -490,7 +477,7 @@ class AnkiTemplateRenderer {
this._normalizeHtml(container, styleApplier, datasetKeyIgnorePattern);
const result = container.innerHTML;
container.textContent = '';
- return result;
+ return this._safeString(result);
}
_normalizeHtml(root, styleApplier, datasetKeyIgnorePattern) {
@@ -543,9 +530,8 @@ class AnkiTemplateRenderer {
return instance;
}
- _formatGlossary(context, dictionary, options) {
+ _formatGlossary(context, dictionary, content, options) {
const data = options.data.root;
- const content = options.fn(context);
if (typeof content === 'string') { return this._stringToMultiLineHtml(this._escape(content)); }
if (!(typeof content === 'object' && content !== null)) { return ''; }
switch (content.type) {