anki-md-template/模板-支持数学公式/正面内容模版.html

250 lines
9.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="markdown-body">
{{Front}}
</div>
<script>
// 日志级别: debug、warn、error
var config_logLevel = "error";
// javascript 资源加载
initScriptResource([
"https://gcore.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js",
"https://gcore.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js",
"https://gcore.jsdelivr.net/npm/markdown-it-texmath@1.0.0/texmath.min.js",
"https://gcore.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"
]).then(() => {
// Anki 原来的逻辑执行完成后再转换内容
setTimeout(() => {
renderMarkDownFn();
});
})
/**
* 转换卡片内容为 markdown 网页格式
*/
function renderMarkDownFn() {
try {
document.querySelectorAll('.markdown-body').forEach((div) => {
div.innerHTML = renderMarkDown(div.innerHTML);
div.style.opacity = '1';
});
} catch (e) {
DivLog.error("Error: " + e);
}
}
function getAnkiMarkDownIt() {
if (typeof AnkiMarkDownIt !== 'undefined') {
DivLog.info("AnkiMarkDownIt is loaded!")
return AnkiMarkDownIt;
}
DivLog.info("AnkiMarkDownIt did not load!")
window.AnkiMarkDownIt = markdownit({
// 配置参考https://markdown-it.docschina.org/api/MarkdownIt.html#markdownit-new
html: true,
linkify: true,
typographer: false, // 为 true 时,将 (C)、(R)、(TM) 转化为 ©、®、™
breaks: true, // 为 true 时,将 \n 转化为 <br>
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, {language: lang}).value
} catch (__) {
}
}
return '';
}
}).use(window.texmath, {
engine: katex,
// 数学公式标识符参考https://www.npmjs.com/package/markdown-it-texmath
// 'dollars'$...$ or $$...$$
// 'brackets' \(...\) or \[...\],这个标识符号是 Anki 默认的,不用解析
delimiters: ['dollars'],
// katex 的配置选项参考https://katex.org/docs/options
katexOptions: {
output: 'html',
throwOnError: true, // true-页面展示解析异常false-解析异常的公式展示原字符并标记为红色
strict: (errorCode, errorMsg, token) => {
DivLog.warn('Warn: ' + errorCode + ' ' + errorMsg + ' ' + token);
return "ignore";
}
}
});
return window.AnkiMarkDownIt;
}
/**
* 绘制内容为 markdown 网页格式
* @param {string} text
* @returns {string}
*/
function renderMarkDown(text) {
DivLog.debug("==========================================");
DivLog.debug("Original content", text);
// <code></code> 包裹的内容不做处理
let code_tag_matches
[text, code_tag_matches] = Censor.censorCodeTag(text);
if (code_tag_matches.length > 0) {
DivLog.debug("After hide html tag <code></code>", text);
}
// <mjx-container></mjx-container> 包裹的内容不做处理
let mjx_tag_matches
[text, mjx_tag_matches] = Censor.censorMJXTag(text);
if (mjx_tag_matches.length > 0) {
DivLog.debug("After hide html tag <mjx-container></mjx-container>", text);
}
text = text.trim()
.replace(/&(amp|lt|gt|nbsp);/g, (_, type) => {
const entities = {amp: '&', lt: '<', gt: '>', nbsp: ' '};
return entities[type] || '';
})
.replace(/<br>/g, '\n');
DivLog.debug("After reverse some HTML tags", text);
// 转换成 markdown 网页格式
text = getAnkiMarkDownIt().render(text);
DivLog.debug("After markdown-it conversion", text);
// 还原隐藏的内容
text = Censor.decensorMJXTag(text, mjx_tag_matches)
text = Censor.decensorCodeTag(text, code_tag_matches)
return text;
}
/**
* 加载 javascript 资源
* @param scriptUrls 多个资源地址数组
* @returns {Promise<Awaited<unknown>[]>|Promise<void>}
*/
function initScriptResource(scriptUrls) {
initMyClass();
// 添加判断,避免重复加载
if (typeof markdownit !== 'undefined') {
DivLog.info("markdown-it is loaded!")
return Promise.resolve();
}
DivLog.info("markdown-it did not load, re-adding triggers loading!")
return Promise.all(scriptUrls.map(url => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.onload = resolve;
script.onerror = () => reject(new Error(`Failed to load script from ${url}`));
script.src = url;
document.head.appendChild(script);
});
})).catch(error => {
DivLog.error(error);
throw error;
});
}
function initMyClass() {
// 日志工具
if (typeof DivLog === 'undefined') {
window.DivLog = {
LOG_LEVEL: "info",
levelMap: {'debug': 5, 'info': 4, 'warn': 3, 'error': 2, 'off': 1},
debug(...messages) {
if (this.levelMap[this.LOG_LEVEL] >= 5) {
this._log("", ...messages);
}
},
info(...messages) {
if (this.levelMap[this.LOG_LEVEL] >= 4) {
this._log("", ...messages);
}
},
warn(...messages) {
if (this.levelMap[this.LOG_LEVEL] >= 3) {
this._log("orange", ...messages);
}
},
error(...messages) {
if (this.levelMap[this.LOG_LEVEL] >= 2) {
this._log("red", ...messages);
}
},
_log(fontColor, ...messages) {
const messageDiv = document.createElement("div");
messageDiv.style.color = fontColor;
messages.forEach(message => {
messageDiv.appendChild(document.createTextNode(message));
messageDiv.appendChild(document.createElement("br"));
});
messageDiv.appendChild(document.createElement("hr"));
this._getMsgContainer().appendChild(messageDiv)
},
_getMsgContainer() {
let msgContainer = document.getElementById("msgContainer");
if (!msgContainer) {
msgContainer = document.createElement("div");
msgContainer.id = "msgContainer";
msgContainer.style.textAlign = "left";
// 将日志信息放在最后一个 class="markdown-body" 的 div 的后面
const divs = document.querySelectorAll("div.markdown-body");
const lastDiv = divs.length > 0 ? divs[divs.length - 1] : null;
if (lastDiv) {
lastDiv.parentNode.appendChild(msgContainer);
}
}
return msgContainer
},
clearLogDiv() {
const msgContainer = document.getElementById("msgContainer");
if (msgContainer) {
msgContainer.innerHTML = "";
}
}
};
DivLog.LOG_LEVEL = config_logLevel;
}
DivLog.clearLogDiv();
// 隐藏特殊片段
if (typeof Censor === 'undefined') {
const ANKI_CODE_REGEXP = /<code[^>]*>([\s\S]*?)<\/code>/g;
const ANKI_CODE_REPLACE = "ANKI_CODE_REPLACE";
const ANKI_MJX_REGEXP = /<mjx-container[^>]*>([\s\S]*?)<\/mjx-container>/g;
const ANKI_MJX_REPLACE = "ANKI_MJX_CONTAINER_REPLACE";
window.Censor = {
censorCodeTag: function (note_text) {
return this._censor(note_text, ANKI_CODE_REGEXP, ANKI_CODE_REPLACE)
},
censorMJXTag: function (note_text) {
return this._censor(note_text, ANKI_MJX_REGEXP, ANKI_MJX_REPLACE)
},
decensorCodeTag: function (note_text, replacements) {
return this._decensor(note_text, ANKI_CODE_REPLACE, replacements)
},
decensorMJXTag: function (note_text, replacements) {
return this._decensor(note_text, ANKI_MJX_REPLACE, replacements)
},
_censor: function (note_text, regexp, mask) {
/*Take note_text and replace every match of regexp with mask, simultaneously adding it to a string array*/
let matches = []
for (let match of note_text.matchAll(regexp)) {
matches.push(match[0])
}
return [note_text.replace(regexp, mask), matches]
},
_decensor: function (note_text, mask, replacements) {
for (let replacement of replacements) {
note_text = note_text.replace(mask, replacement)
}
return note_text
}
}
}
}
</script>