markdown-it-footnote.js 12 KB

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