load-mathjax.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import getMeta from '../../utils/meta'
  2. let mathJaxPromise: Promise<typeof window.MathJax>
  3. export const loadMathJax = async (options?: {
  4. enableMenu?: boolean
  5. numbering?: string
  6. singleDollar?: boolean
  7. useLabelIds?: boolean
  8. }) => {
  9. if (!mathJaxPromise) {
  10. mathJaxPromise = new Promise((resolve, reject) => {
  11. options = {
  12. enableMenu: false,
  13. singleDollar: true,
  14. useLabelIds: false,
  15. ...options,
  16. }
  17. const inlineMath = [['\\(', '\\)']]
  18. if (options.singleDollar) {
  19. inlineMath.push(['$', '$'])
  20. }
  21. // https://docs.mathjax.org/en/stable/options/index.html
  22. window.MathJax = {
  23. // https://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block
  24. tex: {
  25. macros: {
  26. // Implements support for the \bm command from the bm package. It bolds the argument in math mode.
  27. // https://github.com/mathjax/MathJax/issues/1219#issuecomment-341059843
  28. bm: ['\\boldsymbol{#1}', 1],
  29. },
  30. inlineMath,
  31. displayMath: [
  32. ['\\[', '\\]'],
  33. ['$$', '$$'],
  34. ],
  35. packages: {
  36. '[-]': [
  37. 'html', // avoid creating HTML elements/attributes
  38. 'require', // prevent loading disabled packages
  39. 'textmacros', // text macros are loaded by default in v4, disable them
  40. ],
  41. },
  42. processEscapes: true,
  43. processEnvironments: true,
  44. useLabelIds: options.useLabelIds,
  45. },
  46. output: {
  47. displayOverflow: 'linebreak',
  48. },
  49. loader: {
  50. load: [
  51. 'ui/safe', // https://docs.mathjax.org/en/latest/options/safe.html
  52. ],
  53. },
  54. options: {
  55. enableMenu: options.enableMenu, // https://docs.mathjax.org/en/latest/options/menu.html
  56. },
  57. startup: {
  58. typeset: false,
  59. pageReady() {
  60. // disable the "Math Renderer" option in the context menu, as only SVG is available
  61. window.MathJax.startup.document.menu.menu
  62. .findID('Settings', 'Renderer')
  63. .disable()
  64. },
  65. async ready() {
  66. await window.MathJax.startup.defaultReady()
  67. // remove anything from the "font-family" attribute after a semicolon
  68. // so it's not added to the style attribute
  69. // https://github.com/mathjax/MathJax/issues/3129#issuecomment-1807225345
  70. const { safe } = window.MathJax.startup.document
  71. safe.filterAttributes.set('fontfamily', 'filterFontFamily')
  72. safe.filterMethods.filterFontFamily = (
  73. _safe: any,
  74. family: string
  75. ) => {
  76. return family.split(/;/)[0]
  77. }
  78. },
  79. },
  80. }
  81. // "tags" set separately as tags: undefined throws an error
  82. if (options.numbering) {
  83. window.MathJax.tex.tags = options.numbering
  84. }
  85. // if the menu is disabled, disable all the accessibility features, for performance
  86. // https://docs.mathjax.org/en/stable/options/accessibility.html#accessibility-extensions-options
  87. if (!options.enableMenu) {
  88. window.MathJax.options.menuOptions = {
  89. settings: {
  90. enrich: false,
  91. speech: false,
  92. braille: false,
  93. assistiveMml: false,
  94. },
  95. }
  96. }
  97. const script = document.createElement('script')
  98. const path = getMeta('ol-mathJaxPath')
  99. if (!path) {
  100. reject(new Error('No MathJax path found'))
  101. return
  102. }
  103. script.src = path
  104. script.addEventListener('load', async () => {
  105. await window.MathJax.startup.promise
  106. document.head.appendChild(window.MathJax.svgStylesheet())
  107. resolve(window.MathJax)
  108. })
  109. script.addEventListener('error', reject)
  110. document.head.append(script)
  111. })
  112. }
  113. return mathJaxPromise
  114. }