diff --git a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownTextConverter.java b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownTextConverter.java index 4d09b0ad8..0e36944c5 100644 --- a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownTextConverter.java +++ b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownTextConverter.java @@ -47,6 +47,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import other.com.vladsch.flexmark.ext.katex.FlexmarkKatexExtension; import other.de.stanetz.jpencconverter.JavaPasswordbasedCryption; @SuppressWarnings("WeakerAccess") @@ -91,13 +92,9 @@ public class MarkdownTextConverter extends TextConverter { public static final String HTML_KATEX_INCLUDE = "" + "" + - "" + - ""; - public static final String JS_KATEX = "" + - "renderMathInElement(document.body, {" + - " 'delimiters': [ " + - " {left: '$$', right: '$$', display: true}, { left: '$', right: '$', display: false }," + - "]});\n"; + "" + + ""; + public static final String CSS_KATEX = CSS_S + ".katex { font-size: inherit; }" + CSS_E; public static final String HTML_MERMAID_INCLUDE = ""; @@ -109,6 +106,7 @@ public class MarkdownTextConverter extends TextConverter { StrikethroughExtension.create(), AutolinkExtension.create(), InsExtension.create(), + FlexmarkKatexExtension.KatexExtension.create(), JekyllTagExtension.create(), JekyllFrontMatterExtension.create(), SuperscriptExtension.create(), // https://github.com/vsch/flexmark-java/wiki/Extensions#superscript @@ -198,8 +196,7 @@ public String convertMarkup(String markup, Context context, boolean isExportInLi // Enable Math / KaTex if (appSettings.isMarkdownMathEnabled() && markup.contains("$")) { head += HTML_KATEX_INCLUDE; - onLoadJs += JS_KATEX; - markup = markup.replaceAll("(?ms)^([$]{2}.*?[$]{2})$", "
\n$1\n
"); + head += CSS_KATEX; } // Enable Mermaid diff --git a/app/src/main/java/net/gsantner/markor/ui/NewFileDialog.java b/app/src/main/java/net/gsantner/markor/ui/NewFileDialog.java index 1a04dec99..dc3de224b 100644 --- a/app/src/main/java/net/gsantner/markor/ui/NewFileDialog.java +++ b/app/src/main/java/net/gsantner/markor/ui/NewFileDialog.java @@ -233,7 +233,7 @@ private byte[] getTemplateContent(final Spinner templateSpinner, final File base byte[] bytes = null; switch (templateSpinner.getSelectedItemPosition()) { case 1: { - t = "# Markdown Reference\nAutomatically generate _table of contents_ by checking the option here: `Settings > Format > Markdown`.\n\n## H2 Header\n### H3 header\n#### H4 Header\n##### H5 Header\n###### H6 Header\n\n\n\n## Format Text\n\n*Italic emphasis* , _Alternative italic emphasis_\n\n**Bold emphasis** , __Alternative bold emphasis__\n\n~~Strikethrough~~\n\nBreak line (two spaces at end of line) \n\n> Block quote\n\n`Inline code`\n\n```\nCode blocks\nare\nawesome\n```\n\n\n \n## Lists\n### Ordered & unordered\n\n* Unordered list\n* ...with asterisk/star\n* Test\n\n- Another unordered list\n- ...with hyphen/minus\n- Test\n\n1. Ordered list\n2. Test\n3. Test\n4. Test\n\n- Nested lists\n * Unordered nested list\n * Test\n * Test\n * Test\n- Ordered nested list\n 1. Test\n 2. Test\n 3. Test\n 4. Test\n- Double-nested unordered list\n - Test\n - Unordered\n - Test a\n - Test b\n - Ordered\n 1. Test 1\n 2. Test 2\n\n### Checklist\n* [ ] Salad\n* [x] Potatoes\n\n1. [x] Clean\n2. [ ] Cook\n\n\n\n## Links\n[Link](https://duckduckgo.com/)\n\n[File in same folder as the document.](markor-markdown-reference.md) Use %20 for spaces!\n\n\n\n## Tables\n\n| Left aligned | Middle aligned | Right aligned |\n| :--------------- | :------------------: | -----------------: |\n| Test | Test | Test |\n| Test | Test | Test |\n\n÷÷÷÷\n\nShorter | Table | Syntax\n:---: | ---: | :---\nTest | Test | Test\nTest | Test | Test\n\n\n\n\n\n## Math (KaTeX)\nSee [reference](https://katex.org/docs/supported.html) & [examples](https://github.com/waylonflinn/markdown-it-katex/blob/master/README.md). Enable by checking Math at `Settings > Markdown`.\n\n### Math inline\n\n$ I = \\frac V R $\n\n### Math block\n\n
\n$$\\begin{array}{c} \\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\ \\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\ \\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \\end{array}$$\n
\n\n\n$$\\frac{k_t}{k_e} = \\sqrt{2}$$\n\n\n\n## Format Text (continued)\n\n### Text color\n\nText with background color / highlight\n\nText foreground color\n\nText with colored outline / Text with colored outline\n\n\n### Text sub & superscript\n\nUnderline\n\nThe Subway sandwich was super\n\nSuper special characters: ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ⁿ ™ ® ℠\n\n### Text positioning\n
\n\ntext on the **right**\n\n
\n\n
\n\ntext in the **center** \n(one empy line above and below \nrequired for Markdown support OR markdown='1')\n\n
\n\n### Block Text\n\n
\nlorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. \n
\n\n### Dropdown\n\n
Click to Expand/Collapse\n\nExpanded content. Shows up and keeps visible when clicking expand. Hide again by clicking the dropdown button again.\n\n
\n\n\n\n\n## Multimedia\n\n### Images\n![Image](https://gsantner.net/assets/blog/img/markor/markor-v1-7-showcase-3.jpg)\n\n### Videos\n**Youtube** [Welcome to Upper Austria](https://www.youtube.com/watch?v=RJREFH7Lmm8)\n\n\n**Peertube** [Road in the wood](https://open.tube/videos/watch/8116312a-dbbd-43a3-9260-9ea6367c72fc)\n
\n\n\n\n### Audio & Music\n**Web audio** [Guifrog - Xia Yu](https://www.freemusicarchive.org/music/Guifrog/Xia_Yu)\n\n\n**Local audio** Yellowcard - Lights up in the sky\n\n\n------------------\n\nThis Markdown reference file was created for the [Markor](https://gsantner.net/project/markor?source=markdownref) project by [Gregor Santner](https://gsantner.net) and is licensed [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode) (public domain). File revision 2.\n\n------------------\n\n\n"; + t = "# Markdown Reference\nAutomatically generate _table of contents_ by checking the option here: `Settings > Format > Markdown`.\n\n## H2 Header\n### H3 header\n#### H4 Header\n##### H5 Header\n###### H6 Header\n\n\n\n## Format Text\n\n*Italic emphasis* , _Alternative italic emphasis_\n\n**Bold emphasis** , __Alternative bold emphasis__\n\n~~Strikethrough~~\n\nBreak line (two spaces at end of line) \n\n> Block quote\n\n`Inline code`\n\n```\nCode blocks\nare\nawesome\n```\n\n\n \n## Lists\n### Ordered & unordered\n\n* Unordered list\n* ...with asterisk/star\n* Test\n\n- Another unordered list\n- ...with hyphen/minus\n- Test\n\n1. Ordered list\n2. Test\n3. Test\n4. Test\n\n- Nested lists\n * Unordered nested list\n * Test\n * Test\n * Test\n- Ordered nested list\n 1. Test\n 2. Test\n 3. Test\n 4. Test\n- Double-nested unordered list\n - Test\n - Unordered\n - Test a\n - Test b\n - Ordered\n 1. Test 1\n 2. Test 2\n\n### Checklist\n* [ ] Salad\n* [x] Potatoes\n\n1. [x] Clean\n2. [ ] Cook\n\n\n\n## Links\n[Link](https://duckduckgo.com/)\n\n[File in same folder as the document.](markor-markdown-reference.md) Use %20 for spaces!\n\n\n\n## Tables\n\n| Left aligned | Middle aligned | Right aligned |\n| :--------------- | :------------------: | -----------------: |\n| Test | Test | Test |\n| Test | Test | Test |\n\n÷÷÷÷\n\nShorter | Table | Syntax\n:---: | ---: | :---\nTest | Test | Test\nTest | Test | Test\n\n\n\n\n\n## Math (KaTeX)\nSee [reference](https://katex.org/docs/supported.html) & [examples](https://github.com/waylonflinn/markdown-it-katex/blob/master/README.md). Enable by checking Math at `Settings > Markdown`.\n\n### Math inline\n\n$ I = \\frac V R $\n\n### Math block\n\n$$\\begin{array}{c} \\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\ \\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\ \\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \\end{array}$$\n\n\n$$\\frac{k_t}{k_e} = \\sqrt{2}$$\n\n\n\n## Format Text (continued)\n\n### Text color\n\nText with background color / highlight\n\nText foreground color\n\nText with colored outline / Text with colored outline\n\n\n### Text sub & superscript\n\nUnderline\n\nThe Subway sandwich was super\n\nSuper special characters: ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ⁿ ™ ® ℠\n\n### Text positioning\n
\n\ntext on the **right**\n\n
\n\n
\n\ntext in the **center** \n(one empy line above and below \nrequired for Markdown support OR markdown='1')\n\n
\n\n### Block Text\n\n
\nlorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. \n
\n\n### Dropdown\n\n
Click to Expand/Collapse\n\nExpanded content. Shows up and keeps visible when clicking expand. Hide again by clicking the dropdown button again.\n\n
\n\n\n\n\n## Multimedia\n\n### Images\n![Image](https://gsantner.net/assets/blog/img/markor/markor-v1-7-showcase-3.jpg)\n\n### Videos\n**Youtube** [Welcome to Upper Austria](https://www.youtube.com/watch?v=RJREFH7Lmm8)\n\n\n**Peertube** [Road in the wood](https://open.tube/videos/watch/8116312a-dbbd-43a3-9260-9ea6367c72fc)\n
\n\n\n\n### Audio & Music\n**Web audio** [Guifrog - Xia Yu](https://www.freemusicarchive.org/music/Guifrog/Xia_Yu)\n\n\n**Local audio** Yellowcard - Lights up in the sky\n\n\n------------------\n\nThis Markdown reference file was created for the [Markor](https://gsantner.net/project/markor?source=markdownref) project by [Gregor Santner](https://gsantner.net) and is licensed [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode) (public domain). File revision 2.\n\n------------------\n\n\n"; break; } case 2: { diff --git a/app/thirdparty/assets/katex/katex-render.js b/app/thirdparty/assets/katex/katex-render.js new file mode 100644 index 000000000..72727e4d6 --- /dev/null +++ b/app/thirdparty/assets/katex/katex-render.js @@ -0,0 +1,13 @@ +(function () { + document.addEventListener("DOMContentLoaded", function () { + var mathElems = document.getElementsByClassName("katex"); + var elems = []; + for (const i in mathElems) { + if (mathElems.hasOwnProperty(i)) elems.push(mathElems[i]); + } + + elems.forEach(elem => { + katex.render(elem.textContent, elem, { throwOnError: false, displayMode: elem.nodeName !== 'SPAN', }); + }); + }); +})(); diff --git a/app/thirdparty/java/other/com/vladsch/flexmark/ext/katex/FlexmarkKatexExtension.java b/app/thirdparty/java/other/com/vladsch/flexmark/ext/katex/FlexmarkKatexExtension.java new file mode 100644 index 000000000..9fbf5be84 --- /dev/null +++ b/app/thirdparty/java/other/com/vladsch/flexmark/ext/katex/FlexmarkKatexExtension.java @@ -0,0 +1,457 @@ +/*############################################################################## + * + * Copyright (c) 2015-2016, Atlassian Pty Ltd + * All rights reserved. + * + * Copyright (c) 2016-2018, Vladimir Schneider, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *#############################################################################*/ + +package other.com.vladsch.flexmark.ext.katex; + +import com.vladsch.flexmark.ast.DelimitedNode; +import com.vladsch.flexmark.ast.util.ReferenceRepository; +import com.vladsch.flexmark.html.CustomNodeRenderer; +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.html.HtmlWriter; +import com.vladsch.flexmark.html.renderer.NodeRenderer; +import com.vladsch.flexmark.html.renderer.NodeRendererContext; +import com.vladsch.flexmark.html.renderer.NodeRendererFactory; +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; +import com.vladsch.flexmark.parser.InlineParser; +import com.vladsch.flexmark.parser.InlineParserExtension; +import com.vladsch.flexmark.parser.InlineParserExtensionFactory; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.VisitHandler; +import com.vladsch.flexmark.util.ast.Visitor; +import com.vladsch.flexmark.util.builder.Extension; +import com.vladsch.flexmark.util.html.Attribute; +import com.vladsch.flexmark.util.options.DataHolder; +import com.vladsch.flexmark.util.options.DataKey; +import com.vladsch.flexmark.util.options.MutableDataHolder; +import com.vladsch.flexmark.util.options.MutableDataSetter; +import com.vladsch.flexmark.util.sequence.BasedSequence; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/* This extension is based on Flexmark's GitLab Flavoured Markdown Extension[1]. + * The sources [2] were stripped of everything not relevant to math parsing. + * + * The extension implements parsers for inline math mode, `$...$`, and for display + * math mode, `$$...$$`. Both are based on `internal/GitLabInlineMathParser.java`, + * i.e. both are InlineParsers, not BlockParsers. + * + * [1]: https://github.com/vsch/flexmark-java/wiki/Extensions#gitlab-flavoured-markdown + * [2]: https://github.com/vsch/flexmark-java/tree/0.42.14/flexmark-ext-gitlab/src/main/java/com/vladsch/flexmark/ext/gitlab + */ +public class FlexmarkKatexExtension { + + /** + * Extension for KaTeX + */ + public static class KatexExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension { + public static final DataKey INLINE_MATH_PARSER = new DataKey<>("INLINE_MATH_PARSER", true); + public static final DataKey DISPLAY_MATH_PARSER = new DataKey<>("DISPLAY_MATH_PARSER", true); + + public static final DataKey INLINE_MATH_CLASS = new DataKey<>("INLINE_MATH_CLASS", "katex"); + public static final DataKey DISPLAY_MATH_CLASS = new DataKey<>("DISPLAY_MATH_CLASS", "katex"); + + private KatexExtension() { + } + + public static Extension create() { + return new KatexExtension(); + } + + @Override + public void rendererOptions(final MutableDataHolder options) { + } + + @Override + public void parserOptions(final MutableDataHolder options) { + } + + @Override + public void extend(Parser.Builder parserBuilder) { + FlexmarkKatexExtensionInternal.KatexOptions options = new FlexmarkKatexExtensionInternal.KatexOptions(parserBuilder); + if (options.inlineMathParser) { + parserBuilder.customInlineParserExtensionFactory(new FlexmarkKatexExtensionInternal.KatexInlineMathParser.Factory()); + } + if (options.displayMathParser) { + parserBuilder.customInlineParserExtensionFactory(new FlexmarkKatexExtensionInternal.KatexDisplayMathParser.Factory()); + } + } + + @Override + public void extend(HtmlRenderer.Builder rendererBuilder, String rendererType) { + if (rendererBuilder.isRendererType("HTML")) { + rendererBuilder.nodeRendererFactory(new FlexmarkKatexExtensionInternal.KatexNodeRenderer.Factory()); + } else if (rendererBuilder.isRendererType("JIRA")) { + //rendererBuilder.nodeRendererFactory(new KatexJiraRenderer.Factory()); + } + } + } + + public static interface KatexVisitor { + void visit(final KatexInlineMath node); + + void visit(final KatexDisplayMath node); + } + + public static class KatexDisplayMath extends Node implements DelimitedNode { + protected BasedSequence openingMarker = BasedSequence.NULL; + protected BasedSequence text = BasedSequence.NULL; + protected BasedSequence closingMarker = BasedSequence.NULL; + + public KatexDisplayMath() { + } + + public KatexDisplayMath(BasedSequence chars) { + super(chars); + } + + public KatexDisplayMath(BasedSequence openingMarker, BasedSequence text, BasedSequence closingMarker) { + super(openingMarker.baseSubSequence(openingMarker.getStartOffset(), closingMarker.getEndOffset())); + this.openingMarker = openingMarker; + this.text = text; + this.closingMarker = closingMarker; + } + + @Override + public BasedSequence[] getSegments() { + //return EMPTY_SEGMENTS; + return new BasedSequence[]{openingMarker, text, closingMarker}; + } + + @Override + public void getAstExtra(StringBuilder out) { + delimitedSegmentSpanChars(out, openingMarker, text, closingMarker, "text"); + } + + public BasedSequence getOpeningMarker() { + return openingMarker; + } + + public void setOpeningMarker(BasedSequence openingMarker) { + this.openingMarker = openingMarker; + } + + public BasedSequence getText() { + return text; + } + + public void setText(BasedSequence text) { + this.text = text; + } + + public BasedSequence getClosingMarker() { + return closingMarker; + } + + public void setClosingMarker(BasedSequence closingMarker) { + this.closingMarker = closingMarker; + } + } + + public static class KatexInlineMath extends Node implements DelimitedNode { + protected BasedSequence openingMarker = BasedSequence.NULL; + protected BasedSequence text = BasedSequence.NULL; + protected BasedSequence closingMarker = BasedSequence.NULL; + + public KatexInlineMath() { + } + + public KatexInlineMath(BasedSequence chars) { + super(chars); + } + + public KatexInlineMath(BasedSequence openingMarker, BasedSequence text, BasedSequence closingMarker) { + super(openingMarker.baseSubSequence(openingMarker.getStartOffset(), closingMarker.getEndOffset())); + this.openingMarker = openingMarker; + this.text = text; + this.closingMarker = closingMarker; + } + + @Override + public BasedSequence[] getSegments() { + //return EMPTY_SEGMENTS; + return new BasedSequence[]{openingMarker, text, closingMarker}; + } + + @Override + public void getAstExtra(StringBuilder out) { + delimitedSegmentSpanChars(out, openingMarker, text, closingMarker, "text"); + } + + public BasedSequence getOpeningMarker() { + return openingMarker; + } + + public void setOpeningMarker(BasedSequence openingMarker) { + this.openingMarker = openingMarker; + } + + public BasedSequence getText() { + return text; + } + + public void setText(BasedSequence text) { + this.text = text; + } + + public BasedSequence getClosingMarker() { + return closingMarker; + } + + public void setClosingMarker(BasedSequence closingMarker) { + this.closingMarker = closingMarker; + } + } + + public static class KatexVisitorExt { + public static VisitHandler[] VISIT_HANDLERS(final V visitor) { + return new VisitHandler[]{ + // @formatter:off + new VisitHandler(KatexInlineMath.class, new Visitor() { + @Override + public void visit(KatexInlineMath node) { + visitor.visit(node); + } + }), + new VisitHandler(KatexDisplayMath.class, new Visitor() { + @Override + public void visit(KatexDisplayMath node) { + visitor.visit(node); + } + }), + // @formatter:on + }; + } + } + + public static class FlexmarkKatexExtensionInternal { + public static class KatexDisplayMathParser implements InlineParserExtension { + private final KatexOptions options; + Pattern MATH_PATTERN = Pattern.compile("\\$\\$((?:.|\n)+?)\\$\\$"); + + public KatexDisplayMathParser(final InlineParser inlineParser) { + options = new KatexOptions(inlineParser.getDocument()); + } + + @Override + public void finalizeDocument(final InlineParser inlineParser) { + } + + @Override + public void finalizeBlock(final InlineParser inlineParser) { + } + + @Override + public boolean parse(final InlineParser inlineParser) { + if (inlineParser.peek(1) == '$') { + BasedSequence input = inlineParser.getInput(); + Matcher matcher = inlineParser.matcher(MATH_PATTERN); + if (matcher != null) { + inlineParser.flushTextNode(); + + BasedSequence mathOpen = input.subSequence(matcher.start(), matcher.start(1)); + BasedSequence mathClosed = input.subSequence(matcher.end(1), matcher.end()); + KatexDisplayMath displayMath = new KatexDisplayMath(mathOpen, mathOpen.baseSubSequence(mathOpen.getEndOffset(), mathClosed.getStartOffset()), mathClosed); + inlineParser.getBlock().appendChild(displayMath); + return true; + } + } + return false; + } + + public static class Factory implements InlineParserExtensionFactory { + @Override + public Set> getAfterDependents() { + return null; + } + + @Override + public CharSequence getCharacters() { + return "$"; + } + + @Override + public Set> getBeforeDependents() { + return null; + } + + @Override + public InlineParserExtension create(final InlineParser inlineParser) { + return new KatexDisplayMathParser(inlineParser); + } + + @Override + public boolean affectsGlobalScope() { + return false; + } + } + } + + public static class KatexInlineMathParser implements InlineParserExtension { + private final KatexOptions options; + Pattern MATH_PATTERN = Pattern.compile("(?> getAfterDependents() { + return null; + } + + @Override + public CharSequence getCharacters() { + return "$"; + } + + @Override + public Set> getBeforeDependents() { + return null; + } + + @Override + public InlineParserExtension create(final InlineParser inlineParser) { + return new KatexInlineMathParser(inlineParser); + } + + @Override + public boolean affectsGlobalScope() { + return false; + } + } + } + + public static class KatexNodeRenderer implements NodeRenderer { + final KatexOptions options; + private final ReferenceRepository referenceRepository; + private final boolean recheckUndefinedReferences; + + public KatexNodeRenderer(DataHolder options) { + this.options = new KatexOptions(options); + this.referenceRepository = options.get(Parser.REFERENCES); + this.recheckUndefinedReferences = HtmlRenderer.RECHECK_UNDEFINED_REFERENCES.getFrom(options); + } + + @Override + public Set> getNodeRenderingHandlers() { + Set> set = new HashSet>(); + // @formatter:off + set.add(new NodeRenderingHandler(KatexInlineMath.class, new CustomNodeRenderer() { + @Override + public void render(KatexInlineMath node, NodeRendererContext context, HtmlWriter html) { + KatexNodeRenderer.this.render(node, context, html); + } + })); + set.add(new NodeRenderingHandler(KatexDisplayMath.class, new CustomNodeRenderer() { + @Override + public void render(KatexDisplayMath node, NodeRendererContext context, HtmlWriter html) { + KatexNodeRenderer.this.render(node, context, html); + } + })); + // @formatter:on + return set; + } + + private void render(final KatexInlineMath node, final NodeRendererContext context, final HtmlWriter html) { + html.withAttr().attr(Attribute.CLASS_ATTR, options.inlineMathClass).withAttr().tag("span"); + html.text(node.getText()); + html.tag("/span"); + } + + private void render(final KatexDisplayMath node, final NodeRendererContext context, final HtmlWriter html) { + html.withAttr().attr(Attribute.CLASS_ATTR, options.inlineMathClass).withAttr().tag("div"); + html.text(node.getText()); + html.tag("/div"); + } + + public static class Factory implements NodeRendererFactory { + @Override + public NodeRenderer create(final DataHolder options) { + return new KatexNodeRenderer(options); + } + } + } + + public static class KatexOptions implements MutableDataSetter { + public final boolean inlineMathParser; + public final boolean displayMathParser; + public final String inlineMathClass; + public final String displayMathClass; + + public KatexOptions(DataHolder options) { + inlineMathParser = KatexExtension.INLINE_MATH_PARSER.getFrom(options); + inlineMathClass = KatexExtension.INLINE_MATH_CLASS.getFrom(options); + displayMathParser = KatexExtension.DISPLAY_MATH_PARSER.getFrom(options); + displayMathClass = KatexExtension.DISPLAY_MATH_CLASS.getFrom(options); + } + + @Override + public MutableDataHolder setIn(final MutableDataHolder dataHolder) { + dataHolder.set(KatexExtension.INLINE_MATH_PARSER, inlineMathParser); + dataHolder.set(KatexExtension.INLINE_MATH_CLASS, inlineMathClass); + dataHolder.set(KatexExtension.DISPLAY_MATH_PARSER, displayMathParser); + dataHolder.set(KatexExtension.DISPLAY_MATH_CLASS, displayMathClass); + return dataHolder; + } + } + } +} diff --git a/samples/markor-markdown-reference.md b/samples/markor-markdown-reference.md index d19829e36..b0396c014 100644 --- a/samples/markor-markdown-reference.md +++ b/samples/markor-markdown-reference.md @@ -109,9 +109,7 @@ $ I = \frac V R $ ### Math block -
$$\begin{array}{c} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{array}$$ -
$$\frac{k_t}{k_e} = \sqrt{2}$$