plugin.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. tinymce.PluginManager.add('mathjax', function(editor, url) {
  2. // plugin configuration options
  3. let settings = editor.getParam('mathjax');
  4. let mathjaxClassName = settings.className || "math-tex";
  5. let mathjaxTempClassName = mathjaxClassName + '-original';
  6. let mathjaxSymbols = settings.symbols || {start: '\\(', end: '\\)'};
  7. let mathjaxUrl = settings.lib || null;
  8. let mathjaxConfigUrl = (settings.configUrl || url + '/config.js');
  9. if (settings.className) {
  10. mathjaxConfigUrl += '?class=' + settings.className
  11. }
  12. let mathjaxScripts = [mathjaxConfigUrl];
  13. if (mathjaxUrl) {
  14. mathjaxScripts.push(mathjaxUrl);
  15. }
  16. // load mathjax and its config on editor init
  17. editor.on('init', function () {
  18. let scripts = editor.getDoc().getElementsByTagName('script');
  19. for (let i = 0; i < mathjaxScripts.length; i++) {
  20. // check if script have already loaded
  21. let id = editor.dom.uniqueId();
  22. let script = editor.dom.create('script', {id: id, type: 'text/javascript', src: mathjaxScripts[i]});
  23. let found = false;
  24. for (let j = 0; j < scripts.length; j++) {
  25. if (scripts[j].src == script.src || scripts[j].src == mathjaxScripts[i]) {
  26. found = true;
  27. break;
  28. }
  29. }
  30. // load script
  31. if (!found) {
  32. editor.getDoc().getElementsByTagName('head')[0].appendChild(script);
  33. }
  34. }
  35. });
  36. // remove extra tags on get content
  37. editor.on('GetContent', function (e) {
  38. let div = editor.dom.create('div');
  39. div.innerHTML = e.content;
  40. let elements = div.querySelectorAll('.' + mathjaxClassName);
  41. for (let i = 0; i < elements.length; i++) {
  42. let children = elements[i].querySelectorAll('span');
  43. for (let j = 0; j < children.length; j++) {
  44. children[j].remove();
  45. }
  46. let latex = elements[i].getAttribute('data-latex');
  47. elements[i].removeAttribute('contenteditable');
  48. elements[i].removeAttribute('style');
  49. elements[i].removeAttribute('data-latex');
  50. elements[i].innerHTML = latex;
  51. }
  52. e.content = div.innerHTML;
  53. });
  54. let checkElement = function(element) {
  55. if (element.childNodes.length != 2) {
  56. element.setAttribute('contenteditable', false);
  57. element.style.cursor = 'pointer';
  58. let latex = element.getAttribute('data-latex') || element.innerHTML;
  59. element.setAttribute('data-latex', latex);
  60. element.innerHTML = '';
  61. let math = editor.dom.create('span');
  62. math.innerHTML = latex;
  63. math.classList.add(mathjaxTempClassName);
  64. element.appendChild(math);
  65. let dummy = editor.dom.create('span');
  66. dummy.classList.add('dummy');
  67. dummy.innerHTML = 'dummy';
  68. dummy.setAttribute('hidden', 'hidden');
  69. element.appendChild(dummy);
  70. }
  71. };
  72. // add dummy tag on set content
  73. editor.on('BeforeSetContent', function (e) {
  74. let div = editor.dom.create('div');
  75. div.innerHTML = e.content;
  76. let elements = div.querySelectorAll('.' + mathjaxClassName);
  77. for (let i = 0 ; i < elements.length; i++) {
  78. checkElement(elements[i]);
  79. }
  80. e.content = div.innerHTML;
  81. });
  82. // refresh mathjax on set content
  83. editor.on('SetContent', function(e) {
  84. if (editor.getDoc().defaultView.MathJax) {
  85. editor.getDoc().defaultView.MathJax.typesetPromise();
  86. }
  87. });
  88. // refresh mathjax on any content change
  89. editor.on('Change', function(data) {
  90. elements = editor.dom.getRoot().querySelectorAll('.' + mathjaxClassName);
  91. if (elements.length) {
  92. for (let i = 0 ; i < elements.length; i++) {
  93. checkElement(elements[i]);
  94. }
  95. if (editor.getDoc().defaultView.MathJax) {
  96. editor.getDoc().defaultView.MathJax.typesetPromise();
  97. }
  98. }
  99. });
  100. // add button to tinimce
  101. editor.ui.registry.addToggleButton('mathjax', {
  102. text: 'Σ',
  103. tooltip: 'Mathjax',
  104. onAction: function() {
  105. let selected = editor.selection.getNode();
  106. let target = undefined;
  107. if (selected.classList.contains(mathjaxClassName)) {
  108. target = selected;
  109. }
  110. openMathjaxEditor(target);
  111. },
  112. onSetup: function (buttonApi) {
  113. return editor.selection.selectorChangedWithUnbind('.' + mathjaxClassName, buttonApi.setActive).unbind;
  114. }
  115. });
  116. // handle click on existing
  117. editor.on("click", function (e) {
  118. let closest = e.target.closest('.' + mathjaxClassName);
  119. if (closest) {
  120. openMathjaxEditor(closest);
  121. }
  122. });
  123. // open window with editor
  124. let openMathjaxEditor = function(target) {
  125. let mathjaxId = editor.id + '_' + editor.dom.uniqueId();
  126. let latex = '';
  127. if (target) {
  128. latex_attribute = target.getAttribute('data-latex');
  129. if (latex_attribute.length >= (mathjaxSymbols.start + mathjaxSymbols.end).length) {
  130. latex = latex_attribute.substr(mathjaxSymbols.start.length, latex_attribute.length - (mathjaxSymbols.start + mathjaxSymbols.end).length);
  131. }
  132. }
  133. // show new window
  134. editor.windowManager.open({
  135. title: 'Mathjax',
  136. width: 600,
  137. height: 300,
  138. body: {
  139. type: 'panel',
  140. items: [{
  141. type: 'textarea',
  142. name: 'title',
  143. label: 'LaTex'
  144. }, {
  145. type: 'htmlpanel',
  146. html: '<div style="text-align:right"><a href="https://wikibooks.org/wiki/LaTeX/Mathematics" target="_blank" style="font-size:small">LaTex</a></div>'
  147. }, {
  148. type: 'htmlpanel',
  149. html: '<iframe id="' + mathjaxId + '" style="width: 100%; min-height: 50px;"></iframe>'
  150. }]
  151. },
  152. buttons: [{type: 'submit', text: 'OK'}],
  153. onSubmit: function onsubmit(api) {
  154. let value = api.getData().title.trim();
  155. if (target) {
  156. target.innerHTML = '';
  157. target.setAttribute('data-latex', getMathText(value));
  158. checkElement(target);
  159. } else {
  160. let newElement = editor.getDoc().createElement('span');
  161. newElement.innerHTML = getMathText(value);
  162. newElement.classList.add(mathjaxClassName);
  163. checkElement(newElement);
  164. editor.insertContent(newElement.outerHTML);
  165. }
  166. editor.getDoc().defaultView.MathJax.typesetPromise();
  167. api.close();
  168. },
  169. onChange: function(api) {
  170. var value = api.getData().title.trim();
  171. if (value != latex) {
  172. refreshDialogMathjax(value, document.getElementById(mathjaxId));
  173. latex = value;
  174. }
  175. },
  176. initialData: {title: latex}
  177. });
  178. // add scripts to iframe
  179. let iframe = document.getElementById(mathjaxId);
  180. let iframeWindow = iframe.contentWindow || iframe.contentDocument.document || iframe.contentDocument;
  181. let iframeDocument = iframeWindow.document;
  182. let iframeHead = iframeDocument.getElementsByTagName('head')[0];
  183. let iframeBody = iframeDocument.getElementsByTagName('body')[0];
  184. // get latex for mathjax from simple text
  185. let getMathText = function (value, symbols) {
  186. if (!symbols) {
  187. symbols = mathjaxSymbols;
  188. }
  189. return symbols.start + ' ' + value + ' ' + symbols.end;
  190. };
  191. // refresh latex in mathjax iframe
  192. let refreshDialogMathjax = function(latex) {
  193. let MathJax = iframeWindow.MathJax;
  194. let div = iframeBody.querySelector('div');
  195. if (!div) {
  196. div = iframeDocument.createElement('div');
  197. div.classList.add(mathjaxTempClassName);
  198. iframeBody.appendChild(div);
  199. }
  200. div.innerHTML = getMathText(latex, {start: '$$', end: '$$'});
  201. if (MathJax) {
  202. MathJax.typesetPromise();
  203. }
  204. };
  205. refreshDialogMathjax(latex);
  206. // add scripts for dialog iframe
  207. for (let i = 0; i < mathjaxScripts.length; i++) {
  208. let node = iframeWindow.document.createElement('script');
  209. node.src = mathjaxScripts[i];
  210. node.type = 'text/javascript';
  211. node.async = false;
  212. node.charset = 'utf-8';
  213. iframeHead.appendChild(node);
  214. }
  215. };
  216. });