texmath

TeXmath support for cc98 markdown posts

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/425661/926061/texmath.js

  1. // ==UserScript==
  2. // @name texmath
  3. // @namespace https://www.cc98.org/
  4. // @version 0.1
  5. // @description TeXmath support for cc98 markdown posts
  6. // @author Secant
  7. // @match https://www.cc98.org/topic/*
  8. // @grant none
  9. // jshint esversion: 6
  10. (function () {
  11. "use strict";
  12. function texmath(md, options) {
  13. const delimiters = "dollars";
  14. const imgtexOptions = (options && options.imgtexOptions) || {
  15. apiBase: "https://math.now.sh",
  16. color: "black",
  17. };
  18.  
  19. if (!texmath.imgtex) {
  20. // else ... depricated `use` method was used ...
  21. if (options && typeof options.engine === "object") {
  22. texmath.imgtex = options.engine;
  23. }
  24. // artifical error object.
  25. else
  26. texmath.imgtex = {
  27. renderToString() {
  28. return "No math renderer found.";
  29. },
  30. };
  31. }
  32.  
  33. if (delimiters in texmath.rules) {
  34. for (const rule of texmath.rules[delimiters].inline) {
  35. md.inline.ruler.before("escape", rule.name, texmath.inline(rule)); // ! important
  36. md.renderer.rules[rule.name] = (tokens, idx) =>
  37. rule.tmpl.replace(
  38. /\$1/,
  39. texmath.render(
  40. tokens[idx].content,
  41. !!rule.displayMode,
  42. imgtexOptions
  43. )
  44. );
  45. }
  46.  
  47. for (const rule of texmath.rules[delimiters].block) {
  48. md.block.ruler.before("fence", rule.name, texmath.block(rule)); // ! important for ```math delimiters
  49. md.renderer.rules[rule.name] = (tokens, idx) =>
  50. rule.tmpl.replace(
  51. /\$1/,
  52. texmath.render(tokens[idx].content, true, imgtexOptions)
  53. );
  54. }
  55. }
  56. }
  57.  
  58. texmath.inline = (rule) =>
  59. function (state, silent) {
  60. const pos = state.pos;
  61. const str = state.src;
  62. const pre =
  63. str.startsWith(rule.tag, (rule.rex.lastIndex = pos)) &&
  64. (!rule.pre || rule.pre(str, pos)); // valid pre-condition ...
  65. const match = pre && rule.rex.exec(str);
  66. const res =
  67. !!match &&
  68. pos < rule.rex.lastIndex &&
  69. (!rule.post || rule.post(str, rule.rex.lastIndex - 1));
  70.  
  71. if (res) {
  72. if (!silent) {
  73. const token = state.push(rule.name, "math", 0);
  74. token.content = match[1];
  75. token.markup = rule.tag;
  76. }
  77. state.pos = rule.rex.lastIndex;
  78. }
  79. return res;
  80. };
  81.  
  82. texmath.block = (rule) =>
  83. function block(state, begLine, endLine, silent) {
  84. const pos = state.bMarks[begLine] + state.tShift[begLine];
  85. const str = state.src;
  86. const pre =
  87. str.startsWith(rule.tag, (rule.rex.lastIndex = pos)) &&
  88. (!rule.pre || rule.pre(str, pos)); // valid pre-condition ....
  89. const match = pre && rule.rex.exec(str);
  90. const res =
  91. !!match &&
  92. pos < rule.rex.lastIndex &&
  93. (!rule.post || rule.post(str, rule.rex.lastIndex - 1));
  94.  
  95. if (res && !silent) {
  96. // match and valid post-condition ...
  97. const endpos = rule.rex.lastIndex - 1;
  98. let curline;
  99.  
  100. for (curline = begLine; curline < endLine; curline++)
  101. if (
  102. endpos >= state.bMarks[curline] + state.tShift[curline] &&
  103. endpos <= state.eMarks[curline]
  104. )
  105. // line for end of block math found ...
  106. break;
  107.  
  108. // "this will prevent lazy continuations from ever going past our end marker"
  109. // s. https://github.com/markdown-it/markdown-it-container/blob/master/index.js
  110. const lineMax = state.lineMax;
  111. state.lineMax = curline;
  112.  
  113. // begin token
  114. let token = state.push(rule.name, "math", 1); // 'math_block'
  115. token.block = true;
  116. token.markup = rule.tag;
  117. token.content = match[1];
  118. token.map = [begLine, curline];
  119. // end token
  120. token = state.push(rule.name + "_end", "math", -1);
  121. token.block = true;
  122. token.markup = rule.tag;
  123.  
  124. state.lineMax = lineMax;
  125. state.line = curline + 1;
  126. }
  127. return res;
  128. };
  129.  
  130. texmath.render = function (tex, displayMode, options) {
  131. options.displayMode = displayMode;
  132. let res;
  133. try {
  134. res = texmath.imgtex.renderToString(tex, options);
  135. } catch (err) {
  136. res = tex + ": " + err.message;
  137. }
  138. return res;
  139. };
  140.  
  141. // used for enable/disable math rendering by `markdown-it`
  142. texmath.inlineRuleNames = ["math_inline"];
  143. texmath.blockRuleNames = ["math_block"];
  144.  
  145. texmath.$_pre = (str, beg) => {
  146. const prv = beg > 0 ? str[beg - 1].charCodeAt(0) : false;
  147. return (
  148. !prv ||
  149. (prv !== 0x5c && // no backslash,
  150. (prv < 0x30 || prv > 0x39))
  151. ); // no decimal digit .. before opening '$'
  152. };
  153. texmath.$_post = (str, end) => {
  154. const nxt = str[end + 1] && str[end + 1].charCodeAt(0);
  155. return !nxt || nxt < 0x30 || nxt > 0x39; // no decimal digit .. after closing '$'
  156. };
  157.  
  158. texmath.rules = {
  159. dollars: {
  160. inline: [
  161. {
  162. name: "math_inline",
  163. rex: /\$((?:\S)|(?:\S.*?\S))\$/gy,
  164. tmpl: "$1",
  165. tag: "$",
  166. pre: texmath.$_pre,
  167. post: texmath.$_post,
  168. },
  169. ],
  170. block: [
  171. {
  172. name: "math_block",
  173. rex: /\${2}([^$]+?)\${2}/gmy,
  174. tmpl: "<center>$1</center>",
  175. tag: "$$",
  176. },
  177. ],
  178. },
  179. };
  180.  
  181. window.texmath = texmath;
  182. })();