1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | 'use strict';
|
11 |
|
12 | var katex = require('katex');
|
13 |
|
14 |
|
15 | function scanDelims(state, start, delimLength) {
|
16 | var pos = start, lastChar, nextChar, count, can_open, can_close,
|
17 | isLastWhiteSpace, isLastPunctChar,
|
18 | isNextWhiteSpace, isNextPunctChar,
|
19 | left_flanking = true,
|
20 | right_flanking = true,
|
21 | max = state.posMax,
|
22 | isWhiteSpace = state.md.utils.isWhiteSpace,
|
23 | isPunctChar = state.md.utils.isPunctChar,
|
24 | isMdAsciiPunct = state.md.utils.isMdAsciiPunct;
|
25 |
|
26 |
|
27 | lastChar = start > 0 ? state.src.charCodeAt(start - 1) : 0x20;
|
28 |
|
29 | if (pos >= max) {
|
30 | can_open = false;
|
31 | }
|
32 |
|
33 | pos += delimLength;
|
34 |
|
35 | count = pos - start;
|
36 |
|
37 |
|
38 | nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20;
|
39 |
|
40 | isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
|
41 | isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
|
42 |
|
43 | isLastWhiteSpace = isWhiteSpace(lastChar);
|
44 | isNextWhiteSpace = isWhiteSpace(nextChar);
|
45 |
|
46 | if (isNextWhiteSpace) {
|
47 | left_flanking = false;
|
48 | } else if (isNextPunctChar) {
|
49 | if (!(isLastWhiteSpace || isLastPunctChar)) {
|
50 | left_flanking = false;
|
51 | }
|
52 | }
|
53 |
|
54 | if (isLastWhiteSpace) {
|
55 | right_flanking = false;
|
56 | } else if (isLastPunctChar) {
|
57 | if (!(isNextWhiteSpace || isNextPunctChar)) {
|
58 | right_flanking = false;
|
59 | }
|
60 | }
|
61 |
|
62 | can_open = left_flanking;
|
63 | can_close = right_flanking;
|
64 |
|
65 | return {
|
66 | can_open: can_open,
|
67 | can_close: can_close,
|
68 | delims: count
|
69 | };
|
70 | }
|
71 |
|
72 |
|
73 | function makeMath_inline(open, close) {
|
74 | return function math_inline(state, silent) {
|
75 | var startCount,
|
76 | found,
|
77 | res,
|
78 | token,
|
79 | closeDelim,
|
80 | max = state.posMax,
|
81 | start = state.pos,
|
82 | openDelim = state.src.slice(start, start + open.length);
|
83 |
|
84 | if (openDelim !== open) { return false; }
|
85 | if (silent) { return false; }
|
86 |
|
87 | res = scanDelims(state, start, openDelim.length);
|
88 | startCount = res.delims;
|
89 |
|
90 | if (!res.can_open) {
|
91 | state.pos += startCount;
|
92 |
|
93 | state.pending += state.src.slice(start, state.pos);
|
94 | return true;
|
95 | }
|
96 |
|
97 | state.pos = start + open.length;
|
98 |
|
99 | while (state.pos < max) {
|
100 | closeDelim = state.src.slice(state.pos, state.pos + close.length);
|
101 | if (closeDelim === close) {
|
102 | res = scanDelims(state, state.pos, close.length);
|
103 | if (res.can_close) {
|
104 | found = true;
|
105 | break;
|
106 | }
|
107 | }
|
108 |
|
109 | state.md.inline.skipToken(state);
|
110 | }
|
111 |
|
112 | if (!found) {
|
113 |
|
114 | state.pos = start;
|
115 | return false;
|
116 | }
|
117 |
|
118 |
|
119 | state.posMax = state.pos;
|
120 | state.pos = start + close.length;
|
121 |
|
122 |
|
123 | token = state.push('math_inline', 'math', 0);
|
124 | token.content = state.src.slice(state.pos, state.posMax);
|
125 | token.markup = open;
|
126 |
|
127 | state.pos = state.posMax + close.length;
|
128 | state.posMax = max;
|
129 |
|
130 | return true;
|
131 | };
|
132 | }
|
133 |
|
134 | function makeMath_block(open, close) {
|
135 | return function math_block(state, startLine, endLine, silent) {
|
136 | var openDelim, len, params, nextLine, token, firstLine, lastLine, lastLinePos,
|
137 | haveEndMarker = false,
|
138 | pos = state.bMarks[startLine] + state.tShift[startLine],
|
139 | max = state.eMarks[startLine];
|
140 |
|
141 | if (pos + open.length > max) { return false; }
|
142 |
|
143 | openDelim = state.src.slice(pos, pos + open.length);
|
144 |
|
145 | if (openDelim !== open) { return false; }
|
146 |
|
147 | pos += open.length;
|
148 | firstLine = state.src.slice(pos, max);
|
149 |
|
150 |
|
151 | if (silent) { return true; }
|
152 |
|
153 | if (firstLine.trim().slice(-close.length) === close) {
|
154 |
|
155 | firstLine = firstLine.trim().slice(0, -close.length);
|
156 | haveEndMarker = true;
|
157 | }
|
158 |
|
159 |
|
160 | nextLine = startLine;
|
161 |
|
162 | for (;;) {
|
163 | if (haveEndMarker) { break; }
|
164 |
|
165 | nextLine++;
|
166 |
|
167 | if (nextLine >= endLine) {
|
168 |
|
169 |
|
170 | break;
|
171 | }
|
172 |
|
173 | pos = state.bMarks[nextLine] + state.tShift[nextLine];
|
174 | max = state.eMarks[nextLine];
|
175 |
|
176 | if (pos < max && state.tShift[nextLine] < state.blkIndent) {
|
177 |
|
178 | break;
|
179 | }
|
180 |
|
181 | if (state.src.slice(pos, max).trim().slice(-close.length) !== close) {
|
182 | continue;
|
183 | }
|
184 |
|
185 | if (state.tShift[nextLine] - state.blkIndent >= 4) {
|
186 |
|
187 | continue;
|
188 | }
|
189 |
|
190 | lastLinePos = state.src.slice(0, max).lastIndexOf(close);
|
191 | lastLine = state.src.slice(pos, lastLinePos);
|
192 |
|
193 | pos += lastLine.length + close.length;
|
194 |
|
195 |
|
196 | pos = state.skipSpaces(pos);
|
197 |
|
198 | if (pos < max) { continue; }
|
199 |
|
200 |
|
201 | haveEndMarker = true;
|
202 | }
|
203 |
|
204 |
|
205 | len = state.tShift[startLine];
|
206 |
|
207 | state.line = nextLine + (haveEndMarker ? 1 : 0);
|
208 |
|
209 | token = state.push('math_block', 'math', 0);
|
210 | token.block = true;
|
211 | token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '') +
|
212 | state.getLines(startLine + 1, nextLine, len, true) +
|
213 | (lastLine && lastLine.trim() ? lastLine : '');
|
214 | token.info = params;
|
215 | token.map = [ startLine, state.line ];
|
216 | token.markup = open;
|
217 |
|
218 | return true;
|
219 | };
|
220 | }
|
221 |
|
222 |
|
223 | module.exports = function math_plugin(md) {
|
224 |
|
225 |
|
226 | var inlineOpen = '$',
|
227 | inlineClose = '$',
|
228 | blockOpen = '$$',
|
229 | blockClose = '$$';
|
230 |
|
231 | var katexInline = function(latex){
|
232 | return katex.renderToString(latex, {"displayMode" : false});
|
233 | };
|
234 |
|
235 | var inlineRenderer = function(tokens, idx){
|
236 | return katexInline(tokens[idx].content);
|
237 | };
|
238 |
|
239 | var katexBlock = function(latex){
|
240 | return katex.renderToString(latex, {"displayMode" : true});
|
241 | }
|
242 |
|
243 | var blockRenderer = function(tokens, idx){
|
244 | return katexBlock(tokens[idx].content) + '\n';
|
245 | }
|
246 |
|
247 | var math_inline = makeMath_inline(inlineOpen, inlineClose);
|
248 | var math_block = makeMath_block(blockOpen, blockClose);
|
249 |
|
250 | md.inline.ruler.before('escape', 'math_inline', math_inline);
|
251 | md.block.ruler.after('blockquote', 'math_block', math_block, {
|
252 | alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
|
253 | });
|
254 | md.renderer.rules.math_inline = inlineRenderer;
|
255 | md.renderer.rules.math_block = blockRenderer;
|
256 | };
|