filter/mathjaxloader/amd/src/loader.js

  1. // This file is part of Moodle - http://moodle.org/ //
  2. // Moodle is free software: you can redistribute it and/or modify
  3. // it under the terms of the GNU General Public License as published by
  4. // the Free Software Foundation, either version 3 of the License, or
  5. // (at your option) any later version.
  6. //
  7. // Moodle is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU General Public License
  13. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  14. /**
  15. * Mathjax JS Loader.
  16. *
  17. * @module filter_mathjaxloader/loader
  18. * @copyright 2014 Damyon Wiese <damyon@moodle.com>
  19. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  20. */
  21. import {
  22. eventTypes,
  23. notifyFilterContentRenderingComplete,
  24. } from 'core_filters/events';
  25. /**
  26. * Called by the filter when it is active on any page.
  27. * This does not load MathJAX yet - it addes the configuration to the head incase it gets loaded later.
  28. * It also subscribes to the filter-content-updated event so MathJax can respond to content loaded by Ajax.
  29. *
  30. * @param {Object} params List of configuration params containing mathjaxurl, mathjaxconfig (text) and lang
  31. */
  32. export const configure = (params) => {
  33. loadMathJax(params.mathjaxurl, () => {
  34. if (window.MathJax) {
  35. // Let's still set the locale even if the localization is not yet ported to version 3.2.2
  36. // https://docs.mathjax.org/en/v3.2-latest/upgrading/v2.html#not-yet-ported-to-version-3.
  37. window.MathJax.config.locale = params.lang;
  38. }
  39. // Listen for events triggered when new text is added to a page that needs
  40. // processing by a filter.
  41. document.addEventListener(eventTypes.filterContentUpdated, contentUpdated);
  42. });
  43. };
  44. /**
  45. * Add the node to the typeset queue.
  46. *
  47. * @param {HTMLElement} node The Node to be processed by MathJax
  48. * @private
  49. */
  50. const typesetNode = (node) => {
  51. if (!(node instanceof HTMLElement)) {
  52. // We may have been passed a #text node.
  53. // These cannot be formatted.
  54. return;
  55. }
  56. if (window.MathJax) {
  57. window.MathJax.typesetPromise([node]).then(() => {
  58. notifyFilterContentRenderingComplete([node]);
  59. return;
  60. })
  61. .catch(e => {
  62. window.console.log(e);
  63. });
  64. }
  65. };
  66. /**
  67. * Called by the filter when an equation is found while rendering the page.
  68. */
  69. export const typeset = () => {
  70. const elements = document.getElementsByClassName('filter_mathjaxloader_equation');
  71. for (const element of elements) {
  72. if (typeof window.MathJax !== "undefined") {
  73. typesetNode(element);
  74. }
  75. }
  76. };
  77. /**
  78. * Handle content updated events - typeset the new content.
  79. *
  80. * @param {CustomEvent} event - Custom event with "nodes" indicating the root of the updated nodes.
  81. */
  82. export const contentUpdated = (event) => {
  83. if (typeof window.MathJax === "undefined") {
  84. return;
  85. }
  86. let listOfElementContainMathJax = [];
  87. let hasMathJax = false;
  88. // The list of HTMLElements in an Array.
  89. event.detail.nodes.forEach((node) => {
  90. if (!(node instanceof HTMLElement)) {
  91. // We may have been passed a #text node.
  92. return;
  93. }
  94. const mathjaxElements = node.querySelectorAll('.filter_mathjaxloader_equation');
  95. if (mathjaxElements.length > 0) {
  96. hasMathJax = true;
  97. }
  98. listOfElementContainMathJax.push(mathjaxElements);
  99. });
  100. if (!hasMathJax) {
  101. return;
  102. }
  103. listOfElementContainMathJax.forEach((mathjaxElements) => {
  104. mathjaxElements.forEach((node) => typesetNode(node));
  105. });
  106. };
  107. /**
  108. * Load the MathJax script.
  109. *
  110. * @param {String} url The URL of the MathJax script to load.
  111. * @param {function} callback The function to call when the script has loaded.
  112. */
  113. const loadMathJax = (url, callback) => {
  114. const script = document.createElement('script');
  115. script.type = 'text/javascript';
  116. script.onload = () => {
  117. callback();
  118. };
  119. script.src = url;
  120. document.getElementsByTagName('head')[0].appendChild(script);
  121. };