mml2jax.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
  2. /* vim: set ts=2 et sw=2 tw=80: */
  3. /*************************************************************
  4. *
  5. * MathJax/extensions/mml2jax.js
  6. *
  7. * Implements the MathML to Jax preprocessor that locates <math> nodes
  8. * within the text of a document and replaces them with SCRIPT tags
  9. * for processing by MathJax.
  10. *
  11. * ---------------------------------------------------------------------
  12. *
  13. * Copyright (c) 2010-2013 The MathJax Consortium
  14. *
  15. * Licensed under the Apache License, Version 2.0 (the "License");
  16. * you may not use this file except in compliance with the License.
  17. * You may obtain a copy of the License at
  18. *
  19. * http://www.apache.org/licenses/LICENSE-2.0
  20. *
  21. * Unless required by applicable law or agreed to in writing, software
  22. * distributed under the License is distributed on an "AS IS" BASIS,
  23. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  24. * See the License for the specific language governing permissions and
  25. * limitations under the License.
  26. */
  27. MathJax.Extension.mml2jax = {
  28. version: "2.2",
  29. config: {
  30. preview: "alttext" // Use the <math> element's alttext as the
  31. // preview. Set to "none" for no preview,
  32. // or set to an array specifying an HTML snippet
  33. // to use a fixed preview for all math
  34. },
  35. MMLnamespace: "http://www.w3.org/1998/Math/MathML",
  36. PreProcess: function (element) {
  37. if (!this.configured) {
  38. this.config = MathJax.Hub.CombineConfig("mml2jax",this.config);
  39. if (this.config.Augment) {MathJax.Hub.Insert(this,this.config.Augment)}
  40. this.InitBrowser();
  41. this.configured = true;
  42. }
  43. if (typeof(element) === "string") {element = document.getElementById(element)}
  44. if (!element) {element = document.body}
  45. //
  46. // Handle all math tags with no namespaces
  47. //
  48. this.ProcessMathArray(element.getElementsByTagName("math"));
  49. //
  50. // Handle math with namespaces in XHTML
  51. //
  52. if (element.getElementsByTagNameNS)
  53. {this.ProcessMathArray(element.getElementsByTagNameNS(this.MMLnamespace,"math"))}
  54. //
  55. // Handle math with namespaces in HTML
  56. //
  57. var i, m;
  58. if (typeof(document.namespaces) !== "undefined") {
  59. //
  60. // IE namespaces are listed in document.namespaces
  61. //
  62. try {
  63. for (i = 0, m = document.namespaces.length; i < m; i++) {
  64. var ns = document.namespaces[i];
  65. if (ns.urn === this.MMLnamespace)
  66. {this.ProcessMathArray(element.getElementsByTagName(ns.name+":math"))}
  67. }
  68. } catch (err) {}
  69. } else {
  70. //
  71. // Everybody else
  72. //
  73. var html = document.getElementsByTagName("html")[0];
  74. if (html) {
  75. for (i = 0, m = html.attributes.length; i < m; i++) {
  76. var attr = html.attributes[i];
  77. if (attr.nodeName.substr(0,6) === "xmlns:" && attr.nodeValue === this.MMLnamespace)
  78. {this.ProcessMathArray(element.getElementsByTagName(attr.nodeName.substr(6)+":math"))}
  79. }
  80. }
  81. }
  82. },
  83. ProcessMathArray: function (math) {
  84. var i;
  85. if (math.length) {
  86. if (this.MathTagBug) {
  87. for (i = math.length-1; i >= 0; i--) {
  88. if (math[i].nodeName === "MATH") {this.ProcessMathFlattened(math[i])}
  89. else {this.ProcessMath(math[i])}
  90. }
  91. } else {
  92. for (i = math.length-1; i >= 0; i--) {this.ProcessMath(math[i])}
  93. }
  94. }
  95. },
  96. ProcessMath: function (math) {
  97. var parent = math.parentNode;
  98. var script = document.createElement("script");
  99. script.type = "math/mml";
  100. parent.insertBefore(script,math);
  101. if (this.AttributeBug) {
  102. var html = this.OuterHTML(math);
  103. if (this.CleanupHTML) {
  104. html = html.replace(/<\?import .*?>/i,"").replace(/<\?xml:namespace .*?\/>/i,"");
  105. html = html.replace(/&nbsp;/g,"&#xA0;");
  106. }
  107. MathJax.HTML.setScript(script,html); parent.removeChild(math);
  108. } else {
  109. var span = MathJax.HTML.Element("span"); span.appendChild(math);
  110. MathJax.HTML.setScript(script,span.innerHTML);
  111. }
  112. if (this.config.preview !== "none") {this.createPreview(math,script)}
  113. },
  114. ProcessMathFlattened: function (math) {
  115. var parent = math.parentNode;
  116. var script = document.createElement("script");
  117. script.type = "math/mml";
  118. parent.insertBefore(script,math);
  119. var mml = "", node, MATH = math;
  120. while (math && math.nodeName !== "/MATH") {
  121. node = math; math = math.nextSibling;
  122. mml += this.NodeHTML(node);
  123. node.parentNode.removeChild(node);
  124. }
  125. if (math && math.nodeName === "/MATH") {math.parentNode.removeChild(math)}
  126. script.text = mml + "</math>";
  127. if (this.config.preview !== "none") {this.createPreview(MATH,script)}
  128. },
  129. NodeHTML: function (node) {
  130. var html, i, m;
  131. if (node.nodeName === "#text") {
  132. html = this.quoteHTML(node.nodeValue);
  133. } else if (node.nodeName === "#comment") {
  134. html = "<!--" + node.nodeValue + "-->"
  135. } else {
  136. // In IE, outerHTML doesn't properly quote attributes, so quote them by hand
  137. // In Opera, HTML special characters aren't quoted in attributes, so quote them
  138. html = "<"+node.nodeName.toLowerCase();
  139. for (i = 0, m = node.attributes.length; i < m; i++) {
  140. var attribute = node.attributes[i];
  141. if (attribute.specified) {
  142. // Opera 11.5 beta turns xmlns into xmlns:xmlns, so put it back (*** check after 11.5 is out ***)
  143. html += " "+attribute.nodeName.toLowerCase().replace(/xmlns:xmlns/,"xmlns")+"=";
  144. var value = attribute.nodeValue; // IE < 8 doesn't properly set style by setAttributes
  145. if (value == null && attribute.nodeName === "style" && node.style) {value = node.style.cssText}
  146. html += '"'+this.quoteHTML(value)+'"';
  147. }
  148. }
  149. html += ">";
  150. // Handle internal HTML (possibly due to <semantics> annotation or missing </math>)
  151. if (node.outerHTML != null && node.outerHTML.match(/(.<\/[A-Z]+>|\/>)$/)) {
  152. for (i = 0, m = node.childNodes.length; i < m; i++)
  153. {html += this.OuterHTML(node.childNodes[i])}
  154. html += "</"+node.nodeName.toLowerCase()+">";
  155. }
  156. }
  157. return html;
  158. },
  159. OuterHTML: function (node) {
  160. if (node.nodeName.charAt(0) === "#") {return this.NodeHTML(node)}
  161. if (!this.AttributeBug) {return node.outerHTML}
  162. var html = this.NodeHTML(node);
  163. for (var i = 0, m = node.childNodes.length; i < m; i++)
  164. {html += this.OuterHTML(node.childNodes[i]);}
  165. html += "</"+node.nodeName.toLowerCase()+">";
  166. return html;
  167. },
  168. quoteHTML: function (string) {
  169. if (string == null) {string = ""}
  170. return string.replace(/&/g,"&#x26;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\"/g,"&quot;");
  171. },
  172. createPreview: function (math,script) {
  173. var preview = this.config.preview;
  174. if (preview === "none") return;
  175. if (preview === "alttext") {
  176. var text = math.getAttribute("alttext");
  177. if (text != null) {preview = [this.filterPreview(text)]} else {preview = null}
  178. }
  179. if (preview) {
  180. preview = MathJax.HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass},preview);
  181. script.parentNode.insertBefore(preview,script);
  182. }
  183. },
  184. filterPreview: function (text) {return text},
  185. InitBrowser: function () {
  186. var test = MathJax.HTML.Element("span",{id:"<", className: "mathjax", innerHTML: "<math><mi>x</mi><mspace /></math>"});
  187. var html = test.outerHTML || "";
  188. this.AttributeBug = html !== "" && !(
  189. html.match(/id="&lt;"/) && // "<" should convert to "&lt;"
  190. html.match(/class="mathjax"/) && // IE leaves out quotes
  191. html.match(/<\/math>/) // Opera 9 drops tags after self-closing tags
  192. );
  193. this.MathTagBug = test.childNodes.length > 1; // IE < 9 flattens unknown tags
  194. this.CleanupHTML = MathJax.Hub.Browser.isMSIE; // remove namespace and other added tags
  195. }
  196. };
  197. MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.mml2jax]);
  198. MathJax.Ajax.loadComplete("[MathJax]/extensions/mml2jax.js");