UNPKG

12.2 kBJavaScriptView Raw
1/*! markdown-it-footnote 3.0.0 https://github.com//markdown-it/markdown-it-footnote @license MIT */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitFootnote = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2// Process footnotes
3//
4'use strict';
5
6////////////////////////////////////////////////////////////////////////////////
7// Renderer partials
8
9function render_footnote_anchor_name(tokens, idx, options, env/*, slf*/) {
10 var n = Number(tokens[idx].meta.id + 1).toString();
11
12 if (tokens[idx].meta.subId > 0) {
13 n += ':' + tokens[idx].meta.subId;
14 }
15
16 var prefix = '';
17
18 if (typeof env.docId === 'string') {
19 prefix = '-' + env.docId + '-';
20 }
21
22 return prefix + n;
23}
24
25function render_footnote_caption(tokens, idx/*, options, env, slf*/) {
26 var n = Number(tokens[idx].meta.id + 1).toString();
27
28 if (tokens[idx].meta.subId > 0) {
29 n += ':' + tokens[idx].meta.subId;
30 }
31
32 return '[' + n + ']';
33}
34
35function render_footnote_ref(tokens, idx, options, env, slf) {
36 var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf);
37 var caption = slf.rules.footnote_caption(tokens, idx, options, env, slf);
38
39 return '<sup class="footnote-ref"><a href="#fn' + id + '" id="fnref' + id + '">' + caption + '</a></sup>';
40}
41
42function render_footnote_block_open(tokens, idx, options) {
43 return (options.xhtmlOut ? '<hr class="footnotes-sep" />\n' : '<hr class="footnotes-sep">\n') +
44 '<section class="footnotes">\n' +
45 '<ol class="footnotes-list">\n';
46}
47
48function render_footnote_block_close() {
49 return '</ol>\n</section>\n';
50}
51
52function render_footnote_open(tokens, idx, options, env, slf) {
53 var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf);
54
55 return '<li id="fn' + id + '" class="footnote-item">';
56}
57
58function render_footnote_close() {
59 return '</li>\n';
60}
61
62function render_footnote_anchor(tokens, idx, options, env, slf) {
63 var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf);
64
65 /* ↩ with escape code to prevent display as Apple Emoji on iOS */
66 return ' <a href="#fnref' + id + '" class="footnote-backref">\u21a9\uFE0E</a>';
67}
68
69
70module.exports = function footnote_plugin(md) {
71 var parseLinkLabel = md.helpers.parseLinkLabel,
72 isSpace = md.utils.isSpace;
73
74 md.renderer.rules.footnote_ref = render_footnote_ref;
75 md.renderer.rules.footnote_block_open = render_footnote_block_open;
76 md.renderer.rules.footnote_block_close = render_footnote_block_close;
77 md.renderer.rules.footnote_open = render_footnote_open;
78 md.renderer.rules.footnote_close = render_footnote_close;
79 md.renderer.rules.footnote_anchor = render_footnote_anchor;
80
81 // helpers (only used in other rules, no tokens are attached to those)
82 md.renderer.rules.footnote_caption = render_footnote_caption;
83 md.renderer.rules.footnote_anchor_name = render_footnote_anchor_name;
84
85 // Process footnote block definition
86 function footnote_def(state, startLine, endLine, silent) {
87 var oldBMark, oldTShift, oldSCount, oldParentType, pos, label, token,
88 initial, offset, ch, posAfterColon,
89 start = state.bMarks[startLine] + state.tShift[startLine],
90 max = state.eMarks[startLine];
91
92 // line should be at least 5 chars - "[^x]:"
93 if (start + 4 > max) { return false; }
94
95 if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; }
96 if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; }
97
98 for (pos = start + 2; pos < max; pos++) {
99 if (state.src.charCodeAt(pos) === 0x20) { return false; }
100 if (state.src.charCodeAt(pos) === 0x5D /* ] */) {
101 break;
102 }
103 }
104
105 if (pos === start + 2) { return false; } // no empty footnote labels
106 if (pos + 1 >= max || state.src.charCodeAt(++pos) !== 0x3A /* : */) { return false; }
107 if (silent) { return true; }
108 pos++;
109
110 if (!state.env.footnotes) { state.env.footnotes = {}; }
111 if (!state.env.footnotes.refs) { state.env.footnotes.refs = {}; }
112 label = state.src.slice(start + 2, pos - 2);
113 state.env.footnotes.refs[':' + label] = -1;
114
115 token = new state.Token('footnote_reference_open', '', 1);
116 token.meta = { label: label };
117 token.level = state.level++;
118 state.tokens.push(token);
119
120 oldBMark = state.bMarks[startLine];
121 oldTShift = state.tShift[startLine];
122 oldSCount = state.sCount[startLine];
123 oldParentType = state.parentType;
124
125 posAfterColon = pos;
126 initial = offset = state.sCount[startLine] + pos - (state.bMarks[startLine] + state.tShift[startLine]);
127
128 while (pos < max) {
129 ch = state.src.charCodeAt(pos);
130
131 if (isSpace(ch)) {
132 if (ch === 0x09) {
133 offset += 4 - offset % 4;
134 } else {
135 offset++;
136 }
137 } else {
138 break;
139 }
140
141 pos++;
142 }
143
144 state.tShift[startLine] = pos - posAfterColon;
145 state.sCount[startLine] = offset - initial;
146
147 state.bMarks[startLine] = posAfterColon;
148 state.blkIndent += 4;
149 state.parentType = 'footnote';
150
151 if (state.sCount[startLine] < state.blkIndent) {
152 state.sCount[startLine] += state.blkIndent;
153 }
154
155 state.md.block.tokenize(state, startLine, endLine, true);
156
157 state.parentType = oldParentType;
158 state.blkIndent -= 4;
159 state.tShift[startLine] = oldTShift;
160 state.sCount[startLine] = oldSCount;
161 state.bMarks[startLine] = oldBMark;
162
163 token = new state.Token('footnote_reference_close', '', -1);
164 token.level = --state.level;
165 state.tokens.push(token);
166
167 return true;
168 }
169
170 // Process inline footnotes (^[...])
171 function footnote_inline(state, silent) {
172 var labelStart,
173 labelEnd,
174 footnoteId,
175 token,
176 tokens,
177 max = state.posMax,
178 start = state.pos;
179
180 if (start + 2 >= max) { return false; }
181 if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; }
182 if (state.src.charCodeAt(start + 1) !== 0x5B/* [ */) { return false; }
183
184 labelStart = start + 2;
185 labelEnd = parseLinkLabel(state, start + 1);
186
187 // parser failed to find ']', so it's not a valid note
188 if (labelEnd < 0) { return false; }
189
190 // We found the end of the link, and know for a fact it's a valid link;
191 // so all that's left to do is to call tokenizer.
192 //
193 if (!silent) {
194 if (!state.env.footnotes) { state.env.footnotes = {}; }
195 if (!state.env.footnotes.list) { state.env.footnotes.list = []; }
196 footnoteId = state.env.footnotes.list.length;
197
198 state.md.inline.parse(
199 state.src.slice(labelStart, labelEnd),
200 state.md,
201 state.env,
202 tokens = []
203 );
204
205 token = state.push('footnote_ref', '', 0);
206 token.meta = { id: footnoteId };
207
208 state.env.footnotes.list[footnoteId] = { tokens: tokens };
209 }
210
211 state.pos = labelEnd + 1;
212 state.posMax = max;
213 return true;
214 }
215
216 // Process footnote references ([^...])
217 function footnote_ref(state, silent) {
218 var label,
219 pos,
220 footnoteId,
221 footnoteSubId,
222 token,
223 max = state.posMax,
224 start = state.pos;
225
226 // should be at least 4 chars - "[^x]"
227 if (start + 3 > max) { return false; }
228
229 if (!state.env.footnotes || !state.env.footnotes.refs) { return false; }
230 if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; }
231 if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; }
232
233 for (pos = start + 2; pos < max; pos++) {
234 if (state.src.charCodeAt(pos) === 0x20) { return false; }
235 if (state.src.charCodeAt(pos) === 0x0A) { return false; }
236 if (state.src.charCodeAt(pos) === 0x5D /* ] */) {
237 break;
238 }
239 }
240
241 if (pos === start + 2) { return false; } // no empty footnote labels
242 if (pos >= max) { return false; }
243 pos++;
244
245 label = state.src.slice(start + 2, pos - 1);
246 if (typeof state.env.footnotes.refs[':' + label] === 'undefined') { return false; }
247
248 if (!silent) {
249 if (!state.env.footnotes.list) { state.env.footnotes.list = []; }
250
251 if (state.env.footnotes.refs[':' + label] < 0) {
252 footnoteId = state.env.footnotes.list.length;
253 state.env.footnotes.list[footnoteId] = { label: label, count: 0 };
254 state.env.footnotes.refs[':' + label] = footnoteId;
255 } else {
256 footnoteId = state.env.footnotes.refs[':' + label];
257 }
258
259 footnoteSubId = state.env.footnotes.list[footnoteId].count;
260 state.env.footnotes.list[footnoteId].count++;
261
262 token = state.push('footnote_ref', '', 0);
263 token.meta = { id: footnoteId, subId: footnoteSubId, label: label };
264 }
265
266 state.pos = pos;
267 state.posMax = max;
268 return true;
269 }
270
271 // Glue footnote tokens to end of token stream
272 function footnote_tail(state) {
273 var i, l, j, t, lastParagraph, list, token, tokens, current, currentLabel,
274 insideRef = false,
275 refTokens = {};
276
277 if (!state.env.footnotes) { return; }
278
279 state.tokens = state.tokens.filter(function (tok) {
280 if (tok.type === 'footnote_reference_open') {
281 insideRef = true;
282 current = [];
283 currentLabel = tok.meta.label;
284 return false;
285 }
286 if (tok.type === 'footnote_reference_close') {
287 insideRef = false;
288 // prepend ':' to avoid conflict with Object.prototype members
289 refTokens[':' + currentLabel] = current;
290 return false;
291 }
292 if (insideRef) { current.push(tok); }
293 return !insideRef;
294 });
295
296 if (!state.env.footnotes.list) { return; }
297 list = state.env.footnotes.list;
298
299 token = new state.Token('footnote_block_open', '', 1);
300 state.tokens.push(token);
301
302 for (i = 0, l = list.length; i < l; i++) {
303 token = new state.Token('footnote_open', '', 1);
304 token.meta = { id: i, label: list[i].label };
305 state.tokens.push(token);
306
307 if (list[i].tokens) {
308 tokens = [];
309
310 token = new state.Token('paragraph_open', 'p', 1);
311 token.block = true;
312 tokens.push(token);
313
314 token = new state.Token('inline', '', 0);
315 token.children = list[i].tokens;
316 token.content = '';
317 tokens.push(token);
318
319 token = new state.Token('paragraph_close', 'p', -1);
320 token.block = true;
321 tokens.push(token);
322
323 } else if (list[i].label) {
324 tokens = refTokens[':' + list[i].label];
325 }
326
327 state.tokens = state.tokens.concat(tokens);
328 if (state.tokens[state.tokens.length - 1].type === 'paragraph_close') {
329 lastParagraph = state.tokens.pop();
330 } else {
331 lastParagraph = null;
332 }
333
334 t = list[i].count > 0 ? list[i].count : 1;
335 for (j = 0; j < t; j++) {
336 token = new state.Token('footnote_anchor', '', 0);
337 token.meta = { id: i, subId: j, label: list[i].label };
338 state.tokens.push(token);
339 }
340
341 if (lastParagraph) {
342 state.tokens.push(lastParagraph);
343 }
344
345 token = new state.Token('footnote_close', '', -1);
346 state.tokens.push(token);
347 }
348
349 token = new state.Token('footnote_block_close', '', -1);
350 state.tokens.push(token);
351 }
352
353 md.block.ruler.before('reference', 'footnote_def', footnote_def, { alt: [ 'paragraph', 'reference' ] });
354 md.inline.ruler.after('image', 'footnote_inline', footnote_inline);
355 md.inline.ruler.after('footnote_inline', 'footnote_ref', footnote_ref);
356 md.core.ruler.after('inline', 'footnote_tail', footnote_tail);
357};
358
359},{}]},{},[1])(1)
360});
\No newline at end of file