├── .travis.yml
├── .idea
├── encodings.xml
├── misc.xml
├── vcs.xml
├── modules.xml
└── markdown-it-katex.iml
├── .github
└── workflows
│ └── npm.yml
├── test
├── all.js
└── fixtures
│ └── default.txt
├── package.json
├── index.html
├── LICENSE
├── browser.js
├── README.md
├── .gitignore
└── index.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "9"
4 | - "8"
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/markdown-it-katex.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.github/workflows/npm.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | node-version: [10, 12, 14]
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Use Node.js ${{ matrix.node-version }}
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: ${{ matrix.node-version }}
19 | - run: npm install
20 | - run: npm run build --if-present
21 | - run: npm test
22 | env:
23 | CI: true
24 |
--------------------------------------------------------------------------------
/test/all.js:
--------------------------------------------------------------------------------
1 | var path = require('path'),
2 | tape = require('tape'),
3 | testLoad = require('markdown-it-testgen').load,
4 | mdk = require('../index');
5 |
6 | var md = require('markdown-it')()
7 | .use(mdk);
8 |
9 | /* this uses the markdown-it-testgen module to automatically generate tests
10 | based on an easy to read text file
11 | */
12 | testLoad(path.join(__dirname, 'fixtures/default.txt'), function(data){
13 | data.fixtures.forEach(function (fixture){
14 |
15 | /* generic test definition code using tape */
16 | tape(fixture.header, function(t){
17 | t.plan(1);
18 |
19 | var expected = fixture.second.text,
20 | actual = md.render(fixture.first.text);
21 |
22 | t.equals(actual, expected);
23 |
24 | });
25 |
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@iktakahiro/markdown-it-katex",
3 | "version": "4.0.1",
4 | "description": "Fast math support for markdown-it with KaTeX",
5 | "main": "index.js",
6 | "scripts": {
7 | "watch": "watchify browser.js -o bundle.js -v",
8 | "test": "node test/all.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+ssh://git@github.com:iktakahiro/markdown-it-katex.git"
13 | },
14 | "keywords": [
15 | "markdown",
16 | "KaTeX",
17 | "math",
18 | "LaTeX",
19 | "markdown-it-plugin",
20 | "markdown-it"
21 | ],
22 | "author": "Takahiro Ethan Ikeuchi @iktakahiro",
23 | "license": "MIT",
24 | "dependencies": {
25 | "katex": "^0.12.0"
26 | },
27 | "devDependencies": {
28 | "markdown-it": "11.0.0",
29 | "markdown-it-testgen": "0.1.6",
30 | "tape": "5.0.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
24 |
25 |
36 | Mathdown!
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Waylon Flinn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ---
24 |
25 | The MIT License (MIT)
26 |
27 | Copyright (c) 2018 Takahiro Ethan Ikeuchi @iktakahiro
28 |
29 | Permission is hereby granted, free of charge, to any person obtaining a copy
30 | of this software and associated documentation files (the "Software"), to deal
31 | in the Software without restriction, including without limitation the rights
32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 | copies of the Software, and to permit persons to whom the Software is
34 | furnished to do so, subject to the following conditions:
35 |
36 | The above copyright notice and this permission notice shall be included in all
37 | copies or substantial portions of the Software.
38 |
39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
45 | SOFTWARE.
46 |
--------------------------------------------------------------------------------
/browser.js:
--------------------------------------------------------------------------------
1 | var md = require('markdown-it')(),
2 | mk = require('./index');
3 |
4 | md.use(mk);
5 |
6 | var input = document.getElementById('input'),
7 | output = document.getElementById('output'),
8 | button = document.getElementById('button');
9 |
10 | button.addEventListener('click', function(ev){
11 |
12 | var result = md.render(input.value);
13 |
14 | output.innerHTML = result;
15 |
16 | });
17 |
18 | /*
19 |
20 | # Some Math
21 |
22 | $\sqrt{3x-1}+(1+x)^2$
23 |
24 | # Maxwells Equations
25 |
26 | $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t}
27 | = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$
28 |
29 | $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ (curl of $\vec{\mathbf{E}}$ is proportional to the time derivative of $\vec{\mathbf{B}}$)
30 |
31 | $\nabla \cdot \vec{\mathbf{B}} = 0$
32 |
33 |
34 |
35 | \sqrt{3x-1}+(1+x)^2
36 |
37 | c = \pm\sqrt{a^2 + b^2}
38 |
39 | Maxwell's Equations
40 |
41 | \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t}
42 | = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho
43 |
44 | \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}
45 |
46 | \nabla \cdot \vec{\mathbf{B}} = 0
47 |
48 | Same thing in a LaTeX array
49 | \begin{array}{c}
50 |
51 | \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
52 | = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
53 |
54 | \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
55 |
56 | \nabla \cdot \vec{\mathbf{B}} & = 0
57 |
58 | \end{array}
59 |
60 |
61 | \begin{array}{c}
62 | y_1 \\
63 | y_2 \mathtt{t}_i \\
64 | z_{3,4}
65 | \end{array}
66 |
67 | \begin{array}{c}
68 | x' &=& &x \sin\phi &+& z \cos\phi \\
69 | z' &=& - &x \cos\phi &+& z \sin\phi \\
70 | \end{array}
71 |
72 |
73 |
74 | # Maxwell's Equations
75 |
76 |
77 | equation | description
78 | ----------|------------
79 | $\nabla \cdot \vec{\mathbf{B}} = 0$ | divergence of $\vec{\mathbf{B}}$ is zero
80 | $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$
81 | $\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$ | wha?
82 |
83 | 
84 | */
85 |
--------------------------------------------------------------------------------
/test/fixtures/default.txt:
--------------------------------------------------------------------------------
1 |
2 | Simple inline math
3 | .
4 | $1+1 = 2$
5 | .
6 | 1 + 1 = 2 1+1 = 2 1 + 1 = 2
7 | .
8 |
9 | Simple block math
10 | .
11 | $$1+1 = 2$$
12 | .
13 | 1 + 1 = 2 1+1 = 2
14 | 1 + 1 = 2
15 | .
16 |
17 | Ignore single dollar sign
18 | .
19 | It costs $5.
20 | .
21 | It costs $5.
22 | .
23 |
24 | Ignore multiple dollar signs in separate paragraphs
25 | .
26 | This costs $5.
27 |
28 | That costs $10.
29 | .
30 | This costs $5.
31 | That costs $10.
32 | .
33 |
34 | Inline math inside heading
35 | .
36 | # The $N$-eigenvalue problem and two applications
37 | .
38 | The N N N -eigenvalue problem and two applications
39 | .
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # markdown-it-katex
2 |
3 | Add Math to your Markdown
4 |
5 | [](https://travis-ci.org/iktakahiro/markdown-it-katex)
6 |
7 | [KaTeX](https://github.com/Khan/KaTeX) is a faster alternative to MathJax. This plugin makes it easy to support in your markdown.
8 |
9 | Need convincing?
10 |
11 | * Check out the comparative benchmark: [KaTeX vs MathJax](https://jsperf.com/katex-vs-mathjax/42)
12 |
13 | ## Usage
14 |
15 | Install markdown-it
16 |
17 | ```bash
18 | npm install markdown-it
19 | ```
20 |
21 | Install the plugin
22 |
23 | ```bash
24 | npm install @iktakahiro/markdown-it-katex
25 | ```
26 |
27 | Use it in your javascript
28 |
29 | ```javascript
30 | var md = require('markdown-it')(),
31 | mk = require('@iktakahiro/markdown-it-katex');
32 |
33 | md.use(mk);
34 |
35 | // double backslash is required for javascript strings, but not html input
36 | var result = md.render('# Math Rulez! \n $\\sqrt{3x-1}+(1+x)^2$');
37 | ```
38 |
39 | Include the KaTeX stylesheet in your html:
40 |
41 | ```html
42 |
43 | ```
44 |
45 | If you're using the default markdown-it parser, I also recommend the [github stylesheet](https://github.com/sindresorhus/github-markdown-css):
46 |
47 | ```html
48 |
49 | ```
50 |
51 | `KaTeX` options can be supplied with the second argument to use.
52 |
53 | ```javascript
54 | md.use(mk, {"throwOnError" : false, "errorColor" : " #cc0000"});
55 | ```
56 |
57 | ## Examples
58 |
59 | ### Inline
60 |
61 | Surround your LaTeX with a single `$` on each side for inline rendering.
62 |
63 | ```latex
64 | $\sqrt{3x-1}+(1+x)^2$
65 | ```
66 |
67 | ### Block
68 |
69 | Use two (`$$`) for block rendering. This mode uses bigger symbols and centers
70 | the result.
71 |
72 | ```latex
73 | $$\begin{array}{c}
74 |
75 | \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
76 | = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
77 |
78 | \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
79 |
80 | \nabla \cdot \vec{\mathbf{B}} & = 0
81 |
82 | \end{array}$$
83 | ```
84 |
85 | ## Syntax
86 |
87 | Math parsing in markdown is designed to agree with the conventions set by pandoc:
88 |
89 | Anything between two $ characters will be treated as TeX math. The opening $ must
90 | have a non-space character immediately to its right, while the closing $ must
91 | have a non-space character immediately to its left, and must not be followed
92 | immediately by a digit. Thus, $20,000 and $30,000 won’t parse as math. If for some
93 | reason you need to enclose text in literal $ characters, backslash-escape them and
94 | they won’t be treated as math delimiters.
95 |
96 | ## Math Syntax Support
97 |
98 | KaTeX is based on TeX and LaTeX. Support for both is growing. Here's a list of
99 | currently supported functions:
100 |
101 | [Things that KaTeX does not (yet) support](https://github.com/KaTeX/KaTeX/wiki/Things-that-KaTeX-does-not-%28yet%29-support)
102 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### macOS template
3 | # General
4 | .DS_Store
5 | .AppleDouble
6 | .LSOverride
7 |
8 | # Icon must end with two \r
9 | Icon
10 |
11 | # Thumbnails
12 | ._*
13 |
14 | # Files that might appear in the root of a volume
15 | .DocumentRevisions-V100
16 | .fseventsd
17 | .Spotlight-V100
18 | .TemporaryItems
19 | .Trashes
20 | .VolumeIcon.icns
21 | .com.apple.timemachine.donotpresent
22 |
23 | # Directories potentially created on remote AFP share
24 | .AppleDB
25 | .AppleDesktop
26 | Network Trash Folder
27 | Temporary Items
28 | .apdisk
29 | ### JetBrains template
30 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
31 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
32 |
33 | # User-specific stuff
34 | .idea/**/workspace.xml
35 | .idea/**/tasks.xml
36 | .idea/**/dictionaries
37 | .idea/**/shelf
38 |
39 | # Sensitive or high-churn files
40 | .idea/**/dataSources/
41 | .idea/**/dataSources.ids
42 | .idea/**/dataSources.local.xml
43 | .idea/**/sqlDataSources.xml
44 | .idea/**/dynamic.xml
45 | .idea/**/uiDesigner.xml
46 | .idea/**/dbnavigator.xml
47 |
48 | # Gradle
49 | .idea/**/gradle.xml
50 | .idea/**/libraries
51 |
52 | # CMake
53 | cmake-build-debug/
54 | cmake-build-release/
55 |
56 | # Mongo Explorer plugin
57 | .idea/**/mongoSettings.xml
58 |
59 | # File-based project format
60 | *.iws
61 |
62 | # IntelliJ
63 | out/
64 |
65 | # mpeltonen/sbt-idea plugin
66 | .idea_modules/
67 |
68 | # JIRA plugin
69 | atlassian-ide-plugin.xml
70 |
71 | # Cursive Clojure plugin
72 | .idea/replstate.xml
73 |
74 | # Crashlytics plugin (for Android Studio and IntelliJ)
75 | com_crashlytics_export_strings.xml
76 | crashlytics.properties
77 | crashlytics-build.properties
78 | fabric.properties
79 |
80 | # Editor-based Rest Client
81 | .idea/httpRequests
82 | ### Windows template
83 | # Windows thumbnail cache files
84 | Thumbs.db
85 | ehthumbs.db
86 | ehthumbs_vista.db
87 |
88 | # Dump file
89 | *.stackdump
90 |
91 | # Folder config file
92 | [Dd]esktop.ini
93 |
94 | # Recycle Bin used on file shares
95 | $RECYCLE.BIN/
96 |
97 | # Windows Installer files
98 | *.cab
99 | *.msi
100 | *.msix
101 | *.msm
102 | *.msp
103 |
104 | # Windows shortcuts
105 | *.lnk
106 | ### Node template
107 | # Logs
108 | logs
109 | *.log
110 | npm-debug.log*
111 | yarn-debug.log*
112 | yarn-error.log*
113 |
114 | # Runtime data
115 | pids
116 | *.pid
117 | *.seed
118 | *.pid.lock
119 |
120 | # Directory for instrumented libs generated by jscoverage/JSCover
121 | lib-cov
122 |
123 | # Coverage directory used by tools like istanbul
124 | coverage
125 |
126 | # nyc test coverage
127 | .nyc_output
128 |
129 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
130 | .grunt
131 |
132 | # Bower dependency directory (https://bower.io/)
133 | bower_components
134 |
135 | # node-waf configuration
136 | .lock-wscript
137 |
138 | # Compiled binary addons (https://nodejs.org/api/addons.html)
139 | build/Release
140 |
141 | # Dependency directories
142 | node_modules/
143 | jspm_packages/
144 |
145 | # TypeScript v1 declaration files
146 | typings/
147 |
148 | # Optional npm cache directory
149 | .npm
150 |
151 | # Optional eslint cache
152 | .eslintcache
153 |
154 | # Optional REPL history
155 | .node_repl_history
156 |
157 | # Output of 'npm pack'
158 | *.tgz
159 |
160 | # Yarn Integrity file
161 | .yarn-integrity
162 |
163 | # dotenv environment variables file
164 | .env
165 |
166 | # next.js build output
167 | .next
168 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* Process inline math */
2 | /*
3 | Like markdown-it-simplemath, this is a stripped down, simplified version of:
4 | https://github.com/runarberg/markdown-it-math
5 |
6 | It differs in that it takes (a subset of) LaTeX as input and relies on KaTeX
7 | for rendering output.
8 | */
9 |
10 | /*jslint node: true */
11 | 'use strict';
12 |
13 | var katex = require('katex');
14 |
15 | // Test if potential opening or closing delimieter
16 | // Assumes that there is a "$" at state.src[pos]
17 | function isValidDelim(state, pos) {
18 | var prevChar, nextChar,
19 | max = state.posMax,
20 | can_open = true,
21 | can_close = true;
22 |
23 | prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
24 | nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
25 |
26 | // Check non-whitespace conditions for opening and closing, and
27 | // check that closing delimeter isn't followed by a number
28 | if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \t */ ||
29 | (nextChar >= 0x30/* "0" */ && nextChar <= 0x39/* "9" */)) {
30 | can_close = false;
31 | }
32 | if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \t */) {
33 | can_open = false;
34 | }
35 |
36 | return {
37 | can_open: can_open,
38 | can_close: can_close
39 | };
40 | }
41 |
42 | function math_inline(state, silent) {
43 | var start, match, token, res, pos, esc_count;
44 |
45 | if (state.src[state.pos] !== "$") { return false; }
46 |
47 | res = isValidDelim(state, state.pos);
48 | if (!res.can_open) {
49 | if (!silent) { state.pending += "$"; }
50 | state.pos += 1;
51 | return true;
52 | }
53 |
54 | // First check for and bypass all properly escaped delimieters
55 | // This loop will assume that the first leading backtick can not
56 | // be the first character in state.src, which is known since
57 | // we have found an opening delimieter already.
58 | start = state.pos + 1;
59 | match = start;
60 | while ( (match = state.src.indexOf("$", match)) !== -1) {
61 | // Found potential $, look for escapes, pos will point to
62 | // first non escape when complete
63 | pos = match - 1;
64 | while (state.src[pos] === "\\") { pos -= 1; }
65 |
66 | // Even number of escapes, potential closing delimiter found
67 | if ( ((match - pos) % 2) == 1 ) { break; }
68 | match += 1;
69 | }
70 |
71 | // No closing delimter found. Consume $ and continue.
72 | if (match === -1) {
73 | if (!silent) { state.pending += "$"; }
74 | state.pos = start;
75 | return true;
76 | }
77 |
78 | // Check if we have empty content, ie: $$. Do not parse.
79 | if (match - start === 0) {
80 | if (!silent) { state.pending += "$$"; }
81 | state.pos = start + 1;
82 | return true;
83 | }
84 |
85 | // Check for valid closing delimiter
86 | res = isValidDelim(state, match);
87 | if (!res.can_close) {
88 | if (!silent) { state.pending += "$"; }
89 | state.pos = start;
90 | return true;
91 | }
92 |
93 | if (!silent) {
94 | token = state.push('math_inline', 'math', 0);
95 | token.markup = "$";
96 | token.content = state.src.slice(start, match);
97 | }
98 |
99 | state.pos = match + 1;
100 | return true;
101 | }
102 |
103 | function math_block(state, start, end, silent){
104 | var firstLine, lastLine, next, lastPos, found = false, token,
105 | pos = state.bMarks[start] + state.tShift[start],
106 | max = state.eMarks[start]
107 |
108 | if(pos + 2 > max){ return false; }
109 | if(state.src.slice(pos,pos+2)!=='$$'){ return false; }
110 |
111 | pos += 2;
112 | firstLine = state.src.slice(pos,max);
113 |
114 | if(silent){ return true; }
115 | if(firstLine.trim().slice(-2)==='$$'){
116 | // Single line expression
117 | firstLine = firstLine.trim().slice(0, -2);
118 | found = true;
119 | }
120 |
121 | for(next = start; !found; ){
122 |
123 | next++;
124 |
125 | if(next >= end){ break; }
126 |
127 | pos = state.bMarks[next]+state.tShift[next];
128 | max = state.eMarks[next];
129 |
130 | if(pos < max && state.tShift[next] < state.blkIndent){
131 | // non-empty line with negative indent should stop the list:
132 | break;
133 | }
134 |
135 | if(state.src.slice(pos,max).trim().slice(-2)==='$$'){
136 | lastPos = state.src.slice(0,max).lastIndexOf('$$');
137 | lastLine = state.src.slice(pos,lastPos);
138 | found = true;
139 | }
140 |
141 | }
142 |
143 | state.line = next + 1;
144 |
145 | token = state.push('math_block', 'math', 0);
146 | token.block = true;
147 | token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '')
148 | + state.getLines(start + 1, next, state.tShift[start], true)
149 | + (lastLine && lastLine.trim() ? lastLine : '');
150 | token.map = [ start, state.line ];
151 | token.markup = '$$';
152 | return true;
153 | }
154 |
155 | function escapeHtml(unsafe) {
156 | return unsafe
157 | .replace(/&/g, "&")
158 | .replace(//g, ">")
160 | .replace(/"/g, """)
161 | .replace(/'/g, "'");
162 | }
163 |
164 | module.exports = function math_plugin(md, options) {
165 | // Default options
166 |
167 | options = options || {};
168 |
169 | // set KaTeX as the renderer for markdown-it-simplemath
170 | var katexInline = function(latex){
171 | options.displayMode = false;
172 | try{
173 | return katex.renderToString(latex, options);
174 | }
175 | catch(error){
176 | if(options.throwOnError){ console.log(error); }
177 | return `${escapeHtml(latex)} `;
178 | }
179 | };
180 |
181 | var inlineRenderer = function(tokens, idx){
182 | return katexInline(tokens[idx].content);
183 | };
184 |
185 | var katexBlock = function(latex){
186 | options.displayMode = true;
187 | try{
188 | return "" + katex.renderToString(latex, options) + "
";
189 | }
190 | catch(error){
191 | if(options.throwOnError){ console.log(error); }
192 | return `${escapeHtml(latex)}
`;
193 | }
194 | }
195 |
196 | var blockRenderer = function(tokens, idx){
197 | return katexBlock(tokens[idx].content) + '\n';
198 | }
199 |
200 | md.inline.ruler.after('escape', 'math_inline', math_inline);
201 | md.block.ruler.after('blockquote', 'math_block', math_block, {
202 | alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
203 | });
204 | md.renderer.rules.math_inline = inlineRenderer;
205 | md.renderer.rules.math_block = blockRenderer;
206 | };
207 |
--------------------------------------------------------------------------------