inline.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. var util = require('./util')
  2. module.exports = mathInline
  3. const tab = 9 // '\t'
  4. const space = 32 // ' '
  5. const dollarSign = 36 // '$'
  6. const digit0 = 48 // '0'
  7. const digit9 = 57 // '9'
  8. const backslash = 92 // '\\'
  9. const classList = ['math', 'math-inline']
  10. const mathDisplay = 'math-display'
  11. function mathInline(options) {
  12. const parser = this.Parser
  13. const compiler = this.Compiler
  14. if (util.isRemarkParser(parser)) {
  15. attachParser(parser, options)
  16. }
  17. if (util.isRemarkCompiler(compiler)) {
  18. attachCompiler(compiler, options)
  19. }
  20. }
  21. function attachParser(parser, options) {
  22. const proto = parser.prototype
  23. const inlineMethods = proto.inlineMethods
  24. mathInlineTokenizer.locator = locator
  25. proto.inlineTokenizers.math = mathInlineTokenizer
  26. inlineMethods.splice(inlineMethods.indexOf('text'), 0, 'math')
  27. function locator(value, fromIndex) {
  28. return value.indexOf('$', fromIndex)
  29. }
  30. function mathInlineTokenizer(eat, value, silent) {
  31. const length = value.length
  32. let double = false
  33. let escaped = false
  34. let index = 0
  35. let previous
  36. let code
  37. let next
  38. let contentStart
  39. let contentEnd
  40. let valueEnd
  41. let content
  42. if (value.charCodeAt(index) === backslash) {
  43. escaped = true
  44. index++
  45. }
  46. if (value.charCodeAt(index) !== dollarSign) {
  47. return
  48. }
  49. index++
  50. // Support escaped dollars.
  51. if (escaped) {
  52. /* istanbul ignore if - never used (yet) */
  53. if (silent) {
  54. return true
  55. }
  56. return eat(value.slice(0, index))({type: 'text', value: '$'})
  57. }
  58. if (value.charCodeAt(index) === dollarSign) {
  59. double = true
  60. index++
  61. }
  62. next = value.charCodeAt(index)
  63. // Opening fence cannot be followed by a space or a tab.
  64. if (next === space || next === tab) {
  65. return
  66. }
  67. contentStart = index
  68. while (index < length) {
  69. code = next
  70. next = value.charCodeAt(index + 1)
  71. if (code === dollarSign) {
  72. previous = value.charCodeAt(index - 1)
  73. // Closing fence cannot be preceded by a space or a tab, or followed by
  74. // a digit.
  75. // If a double marker was used to open, the closing fence must consist
  76. // of two dollars as well.
  77. if (
  78. previous !== space &&
  79. previous !== tab &&
  80. // eslint-disable-next-line no-self-compare
  81. (next !== next || next < digit0 || next > digit9) &&
  82. (!double || next === dollarSign)
  83. ) {
  84. contentEnd = index - 1
  85. index++
  86. if (double) {
  87. index++
  88. }
  89. valueEnd = index
  90. break
  91. }
  92. } else if (code === backslash) {
  93. index++
  94. next = value.charCodeAt(index + 1)
  95. }
  96. index++
  97. }
  98. if (valueEnd === undefined) {
  99. return
  100. }
  101. /* istanbul ignore if - never used (yet) */
  102. if (silent) {
  103. return true
  104. }
  105. content = value.slice(contentStart, contentEnd + 1)
  106. return eat(value.slice(0, valueEnd))({
  107. type: 'inlineMath',
  108. value: content,
  109. data: {
  110. hName: 'span',
  111. hProperties: {
  112. className: classList.concat(
  113. double && options.inlineMathDouble ? [mathDisplay] : []
  114. )
  115. },
  116. hChildren: [{type: 'text', value: content}]
  117. }
  118. })
  119. }
  120. }
  121. function attachCompiler(compiler) {
  122. const proto = compiler.prototype
  123. proto.visitors.inlineMath = compileInlineMath
  124. function compileInlineMath(node) {
  125. let fence = '$'
  126. const classes =
  127. (node.data && node.data.hProperties && node.data.hProperties.className) ||
  128. []
  129. if (classes.includes(mathDisplay)) {
  130. fence = '$$'
  131. }
  132. return fence + node.value + fence
  133. }
  134. }