added mathjax (#250)

added markdown-it-mathjax

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
Co-authored-by: Philip Molares <philip@mauricedoepke.de>
Co-authored-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
Philip Molares 2020-06-23 00:38:06 +02:00 committed by GitHub
parent 9e6edb0aeb
commit 8133d565cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 107 additions and 2 deletions

View file

@ -15,6 +15,21 @@ const Editor: React.FC = () => {
const [markdownContent, setMarkdownContent] = useState(`# Embedding demo
[TOC]
## MathJax
You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):
The *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral
$$
x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.
$$
$$
\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.
$$
> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).
## Blockquote
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

View file

@ -13,7 +13,9 @@ import subscript from 'markdown-it-sub'
import superscript from 'markdown-it-sup'
import toc from 'markdown-it-table-of-contents'
import taskList from 'markdown-it-task-lists'
import mathJax from 'markdown-it-mathjax'
import React, { ReactElement, useMemo } from 'react'
import MathJaxReact from 'react-mathjax'
import ReactHtmlParser, { convertNodeToElement, Transform } from 'react-html-parser'
import { createRenderContainer, validAlertLevels } from './container-plugins/alert'
import { highlightedCode } from './markdown-it-plugins/highlighted-code'
@ -32,6 +34,7 @@ import { replaceQuoteExtraTime } from './regex-plugins/replace-quote-extra-time'
import { replaceVimeoLink } from './regex-plugins/replace-vimeo-link'
import { replaceYouTubeLink } from './regex-plugins/replace-youtube-link'
import { getGistReplacement } from './replace-components/gist/gist-frame'
import { getMathJaxReplacement } from './replace-components/mathjax/mathjax-replacer'
import { getHighlightedCodeBlock } from './replace-components/highlighted-code/highlighted-code'
import { getPDFReplacement } from './replace-components/pdf/pdf-frame'
import { getTOCReplacement } from './replace-components/toc/toc-replacer'
@ -47,7 +50,7 @@ export type SubNodeConverter = (node: DomElement, index: number) => ReactElement
export type ComponentReplacer = (node: DomElement, index: number, counterMap: Map<string, number>, nodeConverter: SubNodeConverter) => (ReactElement | undefined);
type ComponentReplacer2Identifier2CounterMap = Map<ComponentReplacer, Map<string, number>>
const allComponentReplacers: ComponentReplacer[] = [getYouTubeReplacement, getVimeoReplacement, getGistReplacement, getPDFReplacement, getTOCReplacement, getHighlightedCodeBlock, getQuoteOptionsReplacement]
const allComponentReplacers: ComponentReplacer[] = [getYouTubeReplacement, getVimeoReplacement, getGistReplacement, getPDFReplacement, getTOCReplacement, getHighlightedCodeBlock, getQuoteOptionsReplacement, getMathJaxReplacement]
const tryToReplaceNode = (node: DomElement, index:number, componentReplacer2Identifier2CounterMap: ComponentReplacer2Identifier2CounterMap, nodeConverter: SubNodeConverter) => {
return allComponentReplacers
@ -75,6 +78,7 @@ const MarkdownRenderer: React.FC<MarkdownPreviewProps> = ({ content }) => {
md.use(inserted)
md.use(marked)
md.use(footnote)
// noinspection CheckTagEmptyBody
md.use(anchor, {
permalink: true,
permalinkBefore: true,
@ -84,6 +88,14 @@ const MarkdownRenderer: React.FC<MarkdownPreviewProps> = ({ content }) => {
md.use(toc, {
markerPattern: /^\[TOC]$/i
})
md.use(mathJax({
beforeMath: '<codimd-mathjax>',
afterMath: '</codimd-mathjax>',
beforeInlineMath: '<codimd-mathjax inline>',
afterInlineMath: '</codimd-mathjax>',
beforeDisplayMath: '<codimd-mathjax>',
afterDisplayMath: '</codimd-mathjax>'
}))
md.use(markdownItRegex, replaceLegacyYoutubeShortCode)
md.use(markdownItRegex, replaceLegacyVimeoShortCode)
md.use(markdownItRegex, replaceLegacyGistShortCode)
@ -119,7 +131,11 @@ const MarkdownRenderer: React.FC<MarkdownPreviewProps> = ({ content }) => {
return (
<div className={'bg-light container-fluid flex-fill h-100 overflow-y-scroll pb-5'}>
<div className={'markdown-body container-fluid'}>{result}</div>
<div className={'markdown-body container-fluid'}>
<MathJaxReact.Provider>
{result}
</MathJaxReact.Provider>
</div>
</div>
)
}

View file

@ -0,0 +1,27 @@
import React from 'react'
import { DomElement } from 'domhandler'
import { ComponentReplacer } from '../../markdown-renderer'
import MathJax from 'react-mathjax'
const getNodeIfMathJaxBlock = (node: DomElement): (DomElement|undefined) => {
if (node.name !== 'p' || !node.children || node.children.length !== 1) {
return
}
const mathJaxNode = node.children[0]
return (mathJaxNode.name === 'codimd-mathjax' && mathJaxNode.attribs?.inline === undefined) ? mathJaxNode : undefined
}
const getNodeIfInlineMathJax = (node: DomElement): (DomElement|undefined) => {
return (node.name === 'codimd-mathjax' && node.attribs?.inline !== undefined) ? node : undefined
}
const getElementReplacement: ComponentReplacer = (node, index: number, counterMap) => {
const mathJax = getNodeIfMathJaxBlock(node) || getNodeIfInlineMathJax(node)
if (mathJax?.children && mathJax.children[0]) {
const mathJaxContent = mathJax.children[0]?.data as string
const isInline = (mathJax.attribs?.inline) !== undefined
return <MathJax.Node key={index} inline={isInline} formula={mathJaxContent}/>
}
}
export { getElementReplacement as getMathJaxReplacement }

View file

@ -0,0 +1,6 @@
declare module 'markdown-it-mathjax' {
import MarkdownIt from 'markdown-it/lib'
import { MathJaxOptions } from './interface'
const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple
export = markdownItMathJax
}

View file

@ -0,0 +1,8 @@
export interface MathJaxOptions {
beforeMath: string,
afterMath: string,
beforeInlineMath: string,
afterInlineMath: string,
beforeDisplayMath: string,
afterDisplayMath: string
}