diff --git a/README.md b/README.md index 406cba3a..a232b926 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ In VNote, almost everything is configurable, such as background color, font, and - [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License) - [markdown-it-imsize](https://github.com/tatsy/markdown-it-imsize) (Unknown) (Thanks @Kinka for help) - [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) (MIT License) +- [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath) (MIT License) - [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License) - [MathJax](https://www.mathjax.org/) (Apache-2.0) - [showdown](https://github.com/showdownjs/showdown) (Unknown) diff --git a/README_zh.md b/README_zh.md index 046d2a1b..7d424b02 100644 --- a/README_zh.md +++ b/README_zh.md @@ -200,6 +200,7 @@ VNote中,几乎一切都是可以定制的,例如背景颜色、字体以及 - [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License) - [markdown-it-imsize](https://github.com/tatsy/markdown-it-imsize) (Unknown) (Thanks @Kinka for help) - [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) (MIT License) +- [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath) (MIT License) - [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License) - [MathJax](https://www.mathjax.org/) (Apache-2.0) - [showdown](https://github.com/showdownjs/showdown) (Unknown) diff --git a/src/resources/markdown-it.js b/src/resources/markdown-it.js index 4948cd13..b0d0f964 100644 --- a/src/resources/markdown-it.js +++ b/src/resources/markdown-it.js @@ -108,6 +108,8 @@ mdit = mdit.use(window.markdownitFootnote); mdit = mdit.use(window["markdown-it-imsize.js"]); +mdit = mdit.use(texmath, { delimiters: 'dollars' }); + var mdHasTocSection = function(markdown) { var n = markdown.search(/(\n|^)\[toc\]/i); return n != -1; @@ -150,8 +152,20 @@ var updateText = function(text) { // If you add new logics after handling MathJax, please pay attention to // finishLoading logic. if (VEnableMathjax) { + var texToRender = document.getElementsByClassName('tex-to-render'); + var nrTex = texToRender.length; + if (nrTex == 0) { + finishOneAsyncJob(); + return; + } + + var eles = []; + for (var i = 0; i < nrTex; ++i) { + eles.push(texToRender[i]); + } + try { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentDiv, postProcessMathJax]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, eles, postProcessMathJax]); } catch (err) { content.setLog("err: " + err); finishOneAsyncJob(); @@ -196,3 +210,49 @@ var handleMetaData = function() { pre.appendChild(code); contentDiv.insertAdjacentElement('afterbegin', pre); }; + +var postProcessMathJaxWhenMathjaxReady = function() { + var all = MathJax.Hub.getAllJax(); + for (var i = 0; i < all.length; ++i) { + var node = all[i].SourceElement().parentNode; + if (VRemoveMathjaxScript) { + // Remove the SourceElement. + try { + node.removeChild(all[i].SourceElement()); + } catch (err) { + content.setLog("err: " + err); + } + } + + if (node.tagName.toLowerCase() == 'code') { + var pre = node.parentNode; + var p = document.createElement('p'); + p.innerHTML = node.innerHTML; + pre.parentNode.replaceChild(p, pre); + } + } +}; + +var handleMathjaxReady = function() { + if (!VEnableMathjax) { + return; + } + + var texToRender = document.getElementsByClassName('tex-to-render'); + var nrTex = texToRender.length; + if (nrTex == 0) { + return; + } + + var eles = []; + for (var i = 0; i < nrTex; ++i) { + eles.push(texToRender[i]); + } + + try { + MathJax.Hub.Queue(["Typeset", MathJax.Hub, eles, postProcessMathJaxWhenMathjaxReady]); + } catch (err) { + content.setLog("err: " + err); + finishOneAsyncJob(); + } +}; diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js index 25c179fe..74f543f8 100644 --- a/src/resources/markdown_template.js +++ b/src/resources/markdown_template.js @@ -92,6 +92,10 @@ if (typeof VOS == 'undefined') { VOS = 'win'; } +if (typeof handleMathjaxReady == 'undefined') { + var handleMathjaxReady = function() {}; +} + // Whether highlight special blocks like puml, flowchart. var highlightSpecialBlocks = false; @@ -1181,6 +1185,7 @@ var addClassToCodeBlock = function() { // Add the class to pre. pare.classList.add("lang-mathjax"); pare.classList.add("language-mathjax"); + pare.classList.add("tex-to-render"); } } } @@ -1268,7 +1273,11 @@ var postProcessMathJax = function() { var node = all[i].SourceElement().parentNode; if (VRemoveMathjaxScript) { // Remove the SourceElement. - node.removeChild(all[i].SourceElement()); + try { + node.removeChild(all[i].SourceElement()); + } catch (err) { + content.setLog("err: " + err); + } } if (node.tagName.toLowerCase() == 'code') { diff --git a/src/resources/mathjax_preview.js b/src/resources/mathjax_preview.js index 4efb2af5..b00faa91 100644 --- a/src/resources/mathjax_preview.js +++ b/src/resources/mathjax_preview.js @@ -43,6 +43,11 @@ var previewMathJax = function(identifier, id, timeStamp, text, isHtml) { p = htmlToElement(text); if (isEmptyMathJax(p.textContent)) { p = null; + } else if (p.tagName.toLowerCase() != 'p') { + // Need to wrap it in a
, or domtoimage won't work.
+ var tp = document.createElement('p');
+ tp.appendChild(p);
+ p = tp;
}
} else {
p = document.createElement('p');
diff --git a/src/utils/markdown-it/README.md b/src/utils/markdown-it/README.md
index ac1c1e0a..c1480bae 100644
--- a/src/utils/markdown-it/README.md
+++ b/src/utils/markdown-it/README.md
@@ -35,3 +35,7 @@ Tatsuya Yatagawa
# [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji)
v1.4.0
Vitaly Puzrin
+
+# [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath)
+v0.0.0
+Stefan Goessner
diff --git a/src/utils/markdown-it/markdown-it-texmath.js b/src/utils/markdown-it/markdown-it-texmath.js
new file mode 100644
index 00000000..bd259589
--- /dev/null
+++ b/src/utils/markdown-it/markdown-it-texmath.js
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Stefan Goessner - 2017-18. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ * Modified by Le Tan for MathJax support in VNote.
+ * We mark all the formulas and enclose them with $ in class 'tex-to-render' for
+ * further processing by MathJax.
+ *--------------------------------------------------------------------------------------------*/
+'use strict';
+
+function texmath(md, options) {
+ let delimiters = options && options.delimiters || 'dollars';
+
+ if (delimiters in texmath.rules) {
+ for (let rule of texmath.rules[delimiters].inline) {
+ md.inline.ruler.before('escape', rule.name, texmath.inline(rule)); // ! important
+ md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$1/,texmath.render(tokens[idx].content,false));
+ }
+
+ for (let rule of texmath.rules[delimiters].block) {
+ md.block.ruler.before('fence', rule.name, texmath.block(rule));
+ md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$1/,texmath.render(tokens[idx].content,true));
+ }
+ }
+}
+
+texmath.applyRule = function(rule, str, beg) {
+ let pre, match, post;
+ rule.rex.lastIndex = beg;
+ pre = str.startsWith(rule.tag,beg) && (!rule.pre || rule.pre(str,beg));
+ match = pre && rule.rex.exec(str);
+ if (match) {
+ match.lastIndex = rule.rex.lastIndex;
+ post = !rule.post || rule.post(str, match.lastIndex-1);
+ }
+ rule.rex.lastIndex = 0;
+ return post && match;
+}
+
+texmath.inline = (rule) =>
+ function(state, silent) {
+ let res = texmath.applyRule(rule, state.src, state.pos);
+ if (res) {
+ if (!silent) {
+ let token = state.push(rule.name, 'math', 0);
+ token.content = res[1]; // group 1 from regex ..
+ token.markup = rule.tag;
+ }
+ state.pos = res.lastIndex;
+ }
+ return !!res;
+ }
+
+texmath.block = (rule) =>
+ function(state, begLine, endLine, silent) {
+ let res = texmath.applyRule(rule, state.src, state.bMarks[begLine] + state.tShift[begLine]);
+ if (res) {
+ if (!silent) {
+ let token = state.push(rule.name, 'math', 0);
+ token.block = true;
+ token.content = res[1];
+ token.markup = rule.tag;
+ }
+ for (let line=begLine, endpos=res.lastIndex-1; line < endLine; line++)
+ if (endpos >= state.bMarks[line] && endpos <= state.eMarks[line]) { // line for end of block math found ...
+ state.line = line+1;
+ break;
+ }
+ state.pos = res.lastIndex;
+ }
+ return !!res;
+ }
+
+texmath.render = function(tex, isblock) {
+ let res;
+ if (isblock) {
+ res = '$$$$' + tex + '$$$$';
+ } else {
+ res = '$$' + tex + '$$';
+ }
+
+ return res;
+}
+
+texmath.$_pre = (str,beg) => {
+ let prv = beg > 0 ? str[beg-1].charCodeAt(0) : false;
+ return !prv || prv !== 0x5c // no backslash,
+ && (prv < 0x30 || prv > 0x39); // no decimal digit .. before opening '$'
+}
+texmath.$_post = (str,end) => {
+ let nxt = str[end+1] && str[end+1].charCodeAt(0);
+ return !nxt || nxt < 0x30 || nxt > 0x39; // no decimal digit .. after closing '$'
+}
+
+texmath.rules = {
+ brackets: {
+ inline: [
+ { name: 'math_inline',
+ rex: /\\\((.+?)\\\)/gy,
+ tmpl: '