PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/talk2/mathjax/unpacked/extensions/TeX/mhchem.js

https://github.com/williamstein/mazur-explicit-formula
JavaScript | 454 lines | 324 code | 32 blank | 98 comment | 32 complexity | 81ce602cec9833855d61046a8c7c4abb MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, MIT
  1. /*************************************************************
  2. *
  3. * MathJax/extensions/TeX/mhchem.js
  4. *
  5. * Implements the \ce command for handling chemical formulas
  6. * from the mhchem LaTeX package.
  7. *
  8. * ---------------------------------------------------------------------
  9. *
  10. * Copyright (c) 2011-2012 Design Science, Inc.
  11. *
  12. * Licensed under the Apache License, Version 2.0 (the "License");
  13. * you may not use this file except in compliance with the License.
  14. * You may obtain a copy of the License at
  15. *
  16. * http://www.apache.org/licenses/LICENSE-2.0
  17. *
  18. * Unless required by applicable law or agreed to in writing, software
  19. * distributed under the License is distributed on an "AS IS" BASIS,
  20. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. * See the License for the specific language governing permissions and
  22. * limitations under the License.
  23. */
  24. MathJax.Extension["TeX/mhchem"] = {
  25. version: "2.1"
  26. };
  27. MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
  28. var TEX = MathJax.InputJax.TeX;
  29. /*
  30. * This is the main class for handing the \ce and related commands.
  31. * Its main method is Parse() which takes the argument to \ce and
  32. * returns the corresponding TeX string.
  33. */
  34. var CE = MathJax.Object.Subclass({
  35. string: "", // the \ce string being parsed
  36. i: 0, // the current position in the string
  37. tex: "", // the processed TeX result
  38. atom: false, // last processed token is an atom
  39. sup: "", // pending superscript
  40. sub: "", // pending subscript
  41. //
  42. // Store the string when a CE object is created
  43. //
  44. Init: function (string) {this.string = string},
  45. //
  46. // These are the special characters and the methods that
  47. // handle them. All others are passed through verbatim.
  48. //
  49. ParseTable: {
  50. '-': "Minus",
  51. '+': "Plus",
  52. '(': "Open",
  53. ')': "Close",
  54. '[': "Open",
  55. ']': "Close",
  56. '<': "Less",
  57. '^': "Superscript",
  58. '_': "Subscript",
  59. '*': "Dot",
  60. '.': "Dot",
  61. '=': "Equal",
  62. '#': "Pound",
  63. '$': "Math",
  64. '\\': "Macro",
  65. ' ': "Space"
  66. },
  67. //
  68. // Basic arrow names for reactions
  69. //
  70. Arrows: {
  71. '->': "rightarrow",
  72. '<-': "leftarrow",
  73. '<->': "leftrightarrow",
  74. '<=>': "rightleftharpoons",
  75. '<=>>': "Rightleftharpoons",
  76. '<<=>': "Leftrightharpoons",
  77. '^': "uparrow",
  78. 'v': "downarrow"
  79. },
  80. //
  81. // Implementations for the various bonds
  82. // (the ~ ones are hacks that don't work well in NativeMML)
  83. //
  84. Bonds: {
  85. '-': "-",
  86. '=': "=",
  87. '#': "\\equiv",
  88. '~': "\\tripledash",
  89. '~-': "\\begin{CEstack}{}\\tripledash\\\\-\\end{CEstack}",
  90. '~=': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
  91. '~--': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
  92. '-~-': "\\raise2mu{\\begin{CEstack}{}-\\\\\\tripledash\\\\-\\end{CEstack}}",
  93. '...': "{\\cdot}{\\cdot}{\\cdot}",
  94. '....': "{\\cdot}{\\cdot}{\\cdot}{\\cdot}",
  95. '->': "\\rightarrow",
  96. '<-': "\\leftarrow",
  97. '??': "\\text{??}" // unknown bond
  98. },
  99. //
  100. // This converts the CE string to a TeX string.
  101. // It loops through the string and calls the proper
  102. // method depending on the ccurrent character.
  103. //
  104. Parse: function () {
  105. this.tex = ""; this.atom = false;
  106. while (this.i < this.string.length) {
  107. var c = this.string.charAt(this.i);
  108. if (c.match(/[a-z]/i)) {this.ParseLetter()}
  109. else if (c.match(/[0-9]/)) {this.ParseNumber()}
  110. else {this["Parse"+(this.ParseTable[c]||"Other")](c)}
  111. }
  112. this.FinishAtom();
  113. return this.tex;
  114. },
  115. //
  116. // Make an atom name or a down arrow
  117. //
  118. ParseLetter: function () {
  119. this.FinishAtom();
  120. if (this.Match(/^v( |$)/)) {
  121. this.tex += "{\\"+this.Arrows["v"]+"}";
  122. } else {
  123. this.tex += "\\text{"+this.Match(/^[a-z]+/i)+"}";
  124. this.atom = true;
  125. }
  126. },
  127. //
  128. // Make a number or fraction preceeding an atom,
  129. // or a subscript for an atom.
  130. //
  131. ParseNumber: function () {
  132. var n = this.Match(/^\d+/);
  133. if (this.atom && !this.sub) {
  134. this.sub = n;
  135. } else {
  136. this.FinishAtom();
  137. var match = this.Match(/^\/\d+/);
  138. if (match) {
  139. var frac = "\\frac{"+n+"}{"+match.substr(1)+"}";
  140. this.tex += "\\mathchoice{\\textstyle"+frac+"}{"+frac+"}{"+frac+"}{"+frac+"}";
  141. } else {
  142. this.tex += n;
  143. if (this.i < this.string.length) {this.tex += "\\,"}
  144. }
  145. }
  146. },
  147. //
  148. // Make a superscript minus, or an arrow, or a single bond.
  149. //
  150. ParseMinus: function (c) {
  151. if (this.atom && (this.i === this.string.length-1 || this.string.charAt(this.i+1) === " ")) {
  152. this.sup += c;
  153. } else {
  154. this.FinishAtom();
  155. if (this.string.substr(this.i,2) === "->") {this.i += 2; this.AddArrow("->"); return}
  156. else {this.tex += "{-}"}
  157. }
  158. this.i++;
  159. },
  160. //
  161. // Make a superscript plus, or pass it through
  162. //
  163. ParsePlus: function (c) {
  164. if (this.atom) {this.sup += c} else {this.FinishAtom(); this.tex += c}
  165. this.i++;
  166. },
  167. //
  168. // Handle dots and double or triple bonds
  169. //
  170. ParseDot: function (c) {this.FinishAtom(); this.tex += "\\cdot "; this.i++},
  171. ParseEqual: function (c) {this.FinishAtom(); this.tex += "{=}"; this.i++},
  172. ParsePound: function (c) {this.FinishAtom(); this.tex += "{\\equiv}"; this.i++},
  173. //
  174. // Look for (v) or (^), or pass it through
  175. //
  176. ParseOpen: function (c) {
  177. this.FinishAtom();
  178. var match = this.Match(/^\([v^]\)/);
  179. if (match) {this.tex += "{\\"+this.Arrows[match.charAt(1)]+"}"}
  180. else {this.tex += "{"+c; this.i++}
  181. },
  182. //
  183. // Allow ) and ] to get super- and subscripts
  184. //
  185. ParseClose: function (c) {this.FinishAtom(); this.atom = true; this.tex += c+"}"; this.i++},
  186. //
  187. // Make the proper arrow
  188. //
  189. ParseLess: function (c) {
  190. this.FinishAtom();
  191. var arrow = this.Match(/^(<->?|<=>>?|<<=>)/);
  192. if (!arrow) {this.tex += c; this.i++} else {this.AddArrow(arrow)}
  193. },
  194. //
  195. // Look for a superscript, or an up arrow
  196. //
  197. ParseSuperscript: function (c) {
  198. c = this.string.charAt(++this.i);
  199. if (c === "{") {
  200. this.i++; var m = this.Find("}");
  201. if (m === "-.") {this.sup += "{-}{\\cdot}"}
  202. else if (m) {this.sup += CE(m).Parse().replace(/^\{-\}/,"-")}
  203. } else if (c === " " || c === "") {
  204. this.tex += "{\\"+this.Arrows["^"]+"}"; this.i++;
  205. } else {
  206. var n = this.Match(/^(\d+|-\.)/);
  207. if (n) {this.sup += n}
  208. }
  209. },
  210. //
  211. // Look for subscripts
  212. //
  213. ParseSubscript: function (c) {
  214. if (this.string.charAt(++this.i) == "{") {
  215. this.i++; this.sub += CE(this.Find("}")).Parse().replace(/^\{-\}/,"-");
  216. } else {
  217. var n = this.Match(/^\d+/);
  218. if (n) {this.sub += n}
  219. }
  220. },
  221. //
  222. // Look for raw TeX code to include
  223. //
  224. ParseMath: function (c) {
  225. this.FinishAtom();
  226. this.i++; this.tex += this.Find(c);
  227. },
  228. //
  229. // Look for specific macros for bonds
  230. // and allow \} to have subscripts
  231. //
  232. ParseMacro: function (c) {
  233. this.FinishAtom();
  234. this.i++; var match = this.Match(/^([a-z]+|.)/i)||" ";
  235. if (match === "sbond") {this.tex += "{-}"}
  236. else if (match === "dbond") {this.tex += "{=}"}
  237. else if (match === "tbond") {this.tex += "{\\equiv}"}
  238. else if (match === "bond") {
  239. var bond = (this.Match(/^\{.*?\}/)||"");
  240. bond = bond.substr(1,bond.length-2);
  241. this.tex += "{"+(this.Bonds[bond]||"\\text{??}")+"}";
  242. }
  243. else if (match === "{") {this.tex += "{\\{"}
  244. else if (match === "}") {this.tex += "\\}}"; this.atom = true}
  245. else {this.tex += c+match}
  246. },
  247. //
  248. // Ignore spaces
  249. //
  250. ParseSpace: function (c) {this.FinishAtom(); this.i++},
  251. //
  252. // Pass anything else on verbatim
  253. //
  254. ParseOther: function (c) {this.FinishAtom(); this.tex += c; this.i++},
  255. //
  256. // Process an arrow (looking for brackets for above and below)
  257. //
  258. AddArrow: function (arrow) {
  259. var c = this.Match(/^[CT]\[/);
  260. if (c) {this.i--; c = c.charAt(0)}
  261. var above = this.GetBracket(c), below = this.GetBracket(c);
  262. arrow = this.Arrows[arrow];
  263. if (above || below) {
  264. if (below) {arrow += "["+below+"]"}
  265. arrow += "{"+above+"}";
  266. arrow = "\\mathrel{\\x"+arrow+"}";
  267. } else {
  268. arrow = "\\long"+arrow+" ";
  269. }
  270. this.tex += arrow;
  271. },
  272. //
  273. // Handle the super and subscripts for an atom
  274. //
  275. FinishAtom: function () {
  276. if (this.sup || this.sub) {
  277. if (this.sup && this.sub && !this.atom) {
  278. //
  279. // right-justify super- and subscripts when they are before the atom
  280. //
  281. var sup = this.sup, sub = this.sub;
  282. if (!sup.match(/\d/)) {sup += "\\vphantom{0}"} // force common heights
  283. if (!sub.match(/\d/)) {sub += "\\vphantom{0}"}
  284. this.tex += "\\raise 1pt{\\scriptstyle\\begin{CEscriptstack}"+sup+"\\\\"+
  285. sub+"\\end{CEscriptstack}}\\kern-.125em ";
  286. } else {
  287. if (!this.sup) {this.sup = "\\Space{0pt}{0pt}{.2em}"} // forces subscripts to align properly
  288. this.tex += "^{"+this.sup+"}_{"+this.sub+"}";
  289. }
  290. this.sup = this.sub = "";
  291. }
  292. this.atom = false;
  293. },
  294. //
  295. // Find a bracket group and handle C and T prefixes
  296. //
  297. GetBracket: function (c) {
  298. if (this.string.charAt(this.i) !== "[") {return ""}
  299. this.i++; var bracket = this.Find("]");
  300. if (c === "C") {bracket = "\\ce{"+bracket+"}"} else
  301. if (c === "T") {
  302. if (!bracket.match(/^\{.*\}$/)) {bracket = "{"+bracket+"}"}
  303. bracket = "\\text"+bracket;
  304. };
  305. return bracket;
  306. },
  307. //
  308. // Check if the string matches a regular expression
  309. // and move past it if so, returning the match
  310. //
  311. Match: function (regex) {
  312. var match = regex.exec(this.string.substr(this.i));
  313. if (match) {match = match[0]; this.i += match.length}
  314. return match;
  315. },
  316. //
  317. // Find a particular character, skipping over braced groups
  318. //
  319. Find: function (c) {
  320. var m = this.string.length, i = this.i, braces = 0;
  321. while (this.i < m) {
  322. var C = this.string.charAt(this.i++);
  323. if (C === c && braces === 0) {return this.string.substr(i,this.i-i-1)}
  324. if (C === "{") {braces++} else
  325. if (C === "}") {
  326. if (braces) {braces--}
  327. else {TEX.Error("Extra close brace or missing open brace")}
  328. }
  329. }
  330. if (braces) {TEX.Error("Missing close brace")};
  331. TEX.Error("Can't find closing "+c);
  332. }
  333. });
  334. MathJax.Extension["TeX/mhchem"].CE = CE;
  335. /***************************************************************************/
  336. TEX.Definitions.Add({
  337. macros: {
  338. //
  339. // Set up the macros for chemistry
  340. //
  341. ce: 'CE',
  342. cf: 'CE',
  343. cee: 'CE',
  344. //
  345. // Make these load AMSmath package (redefined below when loaded)
  346. //
  347. xleftrightarrow: ['Extension','AMSmath'],
  348. xrightleftharpoons: ['Extension','AMSmath'],
  349. xRightleftharpoons: ['Extension','AMSmath'],
  350. xLeftrightharpoons: ['Extension','AMSmath'],
  351. // FIXME: These don't work well in FF NativeMML mode
  352. longrightleftharpoons: ["Macro","\\stackrel{\\textstyle{{-}\\!\\!{\\rightharpoonup}}}{\\smash{{\\leftharpoondown}\\!\\!{-}}}"],
  353. longRightleftharpoons: ["Macro","\\stackrel{\\textstyle{-}\\!\\!{\\rightharpoonup}}{\\small\\smash\\leftharpoondown}"],
  354. longLeftrightharpoons: ["Macro","\\stackrel{\\rightharpoonup}{{{\\leftharpoondown}\\!\\!\\textstyle{-}}}"],
  355. //
  356. // Add \hyphen used in some mhchem examples
  357. //
  358. hyphen: ["Macro","\\text{-}"],
  359. //
  360. // Needed for \bond for the ~ forms
  361. //
  362. tripledash: ["Macro","\\raise3mu{\\tiny\\text{-}\\kern2mu\\text{-}\\kern2mu\\text{-}}"]
  363. },
  364. //
  365. // Needed for \bond for the ~ forms
  366. //
  367. environment: {
  368. CEstack: ['Array',null,null,null,'r',null,"0.001em",'T',1],
  369. CEscriptstack: ['Array',null,null,null,'r',null,"0.2em",'S',1]
  370. }
  371. },null,true);
  372. if (!MathJax.Extension["TeX/AMSmath"]) {
  373. TEX.Definitions.Add({
  374. macros: {
  375. xrightarrow: ['Extension','AMSmath'],
  376. xleftarrow: ['Extension','AMSmath']
  377. }
  378. },null,true);
  379. }
  380. //
  381. // These arrows need to wait until AMSmath is loaded
  382. //
  383. MathJax.Hub.Register.StartupHook("TeX AMSmath Ready",function () {
  384. TEX.Definitions.Add({
  385. macros: {
  386. //
  387. // Some of these are hacks for now
  388. //
  389. xleftrightarrow: ['xArrow',0x2194,6,6],
  390. xrightleftharpoons: ['xArrow',0x21CC,5,7], // FIXME: doesn't stretch in HTML-CSS output
  391. xRightleftharpoons: ['xArrow',0x21CC,5,7], // FIXME: how should this be handled?
  392. xLeftrightharpoons: ['xArrow',0x21CC,5,7]
  393. }
  394. },null,true);
  395. });
  396. TEX.Parse.Augment({
  397. //
  398. // Implements \ce and friends
  399. //
  400. CE: function (name) {
  401. var arg = this.GetArgument(name);
  402. var tex = CE(arg).Parse();
  403. this.string = tex + this.string.substr(this.i); this.i = 0;
  404. }
  405. });
  406. //
  407. // Indicate that the extension is ready
  408. //
  409. MathJax.Hub.Startup.signal.Post("TeX mhchem Ready");
  410. });
  411. MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/mhchem.js");