mhchem.js 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693
  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/TeX/mhchem.js
  6. *
  7. * Implements the \ce command for handling chemical formulas
  8. * from the mhchem LaTeX package.
  9. *
  10. * ---------------------------------------------------------------------
  11. *
  12. * Copyright (c) 2011-2015 The MathJax Consortium
  13. * Copyright (c) 2015-2017 Martin Hensel
  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. //
  28. // Coding Style
  29. // - use '' for identifiers that can by minified/uglified
  30. // - use "" for strings that need to stay untouched
  31. MathJax.Extension["TeX/mhchem"] = {
  32. version: "3.2.0"
  33. };
  34. MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
  35. var TEX = MathJax.InputJax.TeX;
  36. //
  37. // This is the main class for handing the \ce and related commands.
  38. // Its main method is Parse() which takes the argument to \ce and
  39. // returns the corresponding TeX string.
  40. //
  41. var CE = MathJax.Object.Subclass({
  42. string: "", // the \ce string being parsed
  43. //
  44. // Store the string when a CE object is created
  45. //
  46. Init: function (string) { this.string = string; },
  47. //
  48. // This converts the CE string to a TeX string.
  49. //
  50. Parse: function (stateMachine) {
  51. try {
  52. return texify.go(mhchemParser.go(this.string, stateMachine));
  53. } catch (ex) {
  54. TEX.Error(ex);
  55. }
  56. }
  57. });
  58. //
  59. // Core parser for mhchem syntax (recursive)
  60. //
  61. var mhchemParser = {};
  62. //
  63. // Parses mchem \ce syntax
  64. //
  65. // Call like
  66. // go("H2O");
  67. //
  68. // Looks through mhchemParser.transitions, to execute a matching action
  69. // (recursive)
  70. //
  71. mhchemParser.go = function(input, stateMachine) {
  72. if (!input) { return input; }
  73. if (stateMachine === undefined) { stateMachine = 'ce'; }
  74. var state = '0';
  75. //
  76. // String buffers for parsing:
  77. //
  78. // buffer.a == amount
  79. // buffer.o == element
  80. // buffer.b == left-side superscript
  81. // buffer.p == left-side subscript
  82. // buffer.q == right-side subscript
  83. // buffer.d == right-side superscript
  84. //
  85. // buffer.r == arrow
  86. // buffer.rdt == arrow, script above, type
  87. // buffer.rd == arrow, script above, content
  88. // buffer.rqt == arrow, script below, type
  89. // buffer.rq == arrow, script below, content
  90. //
  91. // buffer.text
  92. // buffer.rm
  93. // etc.
  94. //
  95. // buffer.parenthesisLevel == int, starting at 0
  96. // buffer.sb == bool, space before
  97. // buffer.beginsWithBond == bool
  98. //
  99. // These letters are also used as state names.
  100. //
  101. // Other states:
  102. // 0 == begin of main part (arrow/operator unlikely)
  103. // 1 == next entity
  104. // 2 == next entity (arrow/operator unlikely)
  105. // 3 == next atom
  106. // c == macro
  107. //
  108. var buffer = {};
  109. buffer['parenthesisLevel'] = 0;
  110. input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-");
  111. input = input.replace(/[\u2026]/g, "...");
  112. var lastInput, watchdog;
  113. var output = [];
  114. while (true) {
  115. if (lastInput !== input) {
  116. watchdog = 10;
  117. lastInput = input;
  118. } else {
  119. watchdog--;
  120. }
  121. //
  122. // Look for matching string in transition table
  123. //
  124. var machine = mhchemParser.stateMachines[stateMachine];
  125. var iTmax = machine.transitions.length;
  126. iterateTransitions:
  127. for (var iT=0; iT<iTmax; iT++) { // Surprisingly, looping is not slower than another data structure with direct lookups. 635d910e-0a6d-45b4-8d38-2f98ac9d9a94
  128. var t = machine.transitions[iT];
  129. var tasks = t.actions[state] || t.actions['*'] || null;
  130. if (tasks) { // testing tasks (actions) before matches is slightly faster
  131. var matches = mhchemParser.matchh(t.matchh, input);
  132. if (matches) {
  133. //
  134. // Execute action
  135. //
  136. var actions = mhchemParser.concatNotUndefined([], tasks.action);
  137. var iAmax = actions.length;
  138. for (var iA=0; iA<iAmax; iA++) {
  139. var a = actions[iA];
  140. var o;
  141. var option = undefined;
  142. if (a.type) {
  143. option = a.option;
  144. a = a.type;
  145. }
  146. if (typeof a === "string") {
  147. if (machine.actions[a]) {
  148. o = machine.actions[a](buffer, matches.matchh, option);
  149. } else if (mhchemParser.actions[a]) {
  150. o = mhchemParser.actions[a](buffer, matches.matchh, option);
  151. } else {
  152. throw ["MhchemBugA", "mhchem bug A. Please report. (" + a + ")"]; // Trying to use non-existing action
  153. }
  154. } else if (typeof a === "function") {
  155. o = a(buffer, matches.matchh);
  156. }
  157. output = mhchemParser.concatNotUndefined(output, o);
  158. }
  159. //
  160. // Set next state,
  161. // Shorten input,
  162. // Continue with next character
  163. // (= apply only one transition per position)
  164. //
  165. state = tasks.nextState || state;
  166. if (input.length > 0) {
  167. if (!tasks.revisit) {
  168. input = matches.remainder;
  169. }
  170. if (!tasks.toContinue) {
  171. break iterateTransitions;
  172. }
  173. } else {
  174. return output;
  175. }
  176. }
  177. }
  178. }
  179. //
  180. // Prevent infinite loop
  181. //
  182. if (watchdog <= 0) {
  183. throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character
  184. }
  185. }
  186. };
  187. mhchemParser.concatNotUndefined = function(a, b) {
  188. if (!b) { return a; }
  189. if (!a) { return [].concat(b); }
  190. return a.concat(b);
  191. };
  192. //
  193. // Matching patterns
  194. // either regexps or function that return null or {match:"a", remainder:"bc"}
  195. //
  196. mhchemParser.patterns = {
  197. // property names must not look like integers ("2") for correct property traversal order, later on
  198. 'empty': /^$/,
  199. 'else': /^./,
  200. 'else2': /^./,
  201. 'space': /^\s/,
  202. 'space A': /^\s(?=[A-Z\\$])/,
  203. 'a-z': /^[a-z]/,
  204. 'x': /^x/,
  205. 'x$': /^x$/,
  206. 'i$': /^i$/,
  207. 'letters': /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/,
  208. '\\greek': /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/,
  209. 'one lowercase latin letter $': /^(?:([a-z])(?:$|[^a-zA-Z]))$/,
  210. '$one lowercase latin letter$ $': /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/,
  211. 'one lowercase greek letter $': /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/,
  212. 'digits': /^[0-9]+/,
  213. '-9.,9': /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/,
  214. '-9.,9 no missing 0': /^[+\-]?[0-9]+(?:[.,][0-9]+)?/,
  215. '(-)(9.,9)(e)(99)': function (input) {
  216. var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/);
  217. if (m && m[0]) {
  218. return { matchh: m.splice(1), remainder: input.substr(m[0].length) };
  219. }
  220. return null;
  221. },
  222. '(-)(9)^(-9)': function (input) {
  223. var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/);
  224. if (m && m[0]) {
  225. return { matchh: m.splice(1), remainder: input.substr(m[0].length) };
  226. }
  227. return null;
  228. },
  229. 'state of aggregation $': function (input) { // or crystal system
  230. var a = this['_findObserveGroups'](input, "", /^\([a-z]{1,3}(?=[\),])/, ")", ""); // (aq), (aq,$\infty$), (aq, sat)
  231. if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { return a; } // AND end of 'phrase'
  232. var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$)
  233. if (m) {
  234. return { matchh: m[0], remainder: input.substr(m[0].length) };
  235. }
  236. return null;
  237. },
  238. '_{(state of aggregation)}$': /^_\{(\([a-z]{1,3}\))\}/,
  239. '\{[(': /^(?:\\\{|\[|\()/,
  240. ')]\}': /^(?:\)|\]|\\\})/,
  241. ', ': /^[,;]\s*/,
  242. ',': /^[,;]/,
  243. '.': /^[.]/,
  244. '. ': /^([.\u22C5\u00B7\u2022])\s*/,
  245. '...': /^\.\.\.(?=$|[^.])/,
  246. '* ': /^([*])\s*/,
  247. '^{(...)}': function (input) { return this['_findObserveGroups'](input, "^{", "", "", "}"); },
  248. '^($...$)': function (input) { return this['_findObserveGroups'](input, "^", "$", "$", ""); },
  249. '^a': /^\^([0-9]+|[^\\_])/,
  250. '^\\x{}{}': function (input) { return this['_findObserveGroups'](input, "^", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
  251. '^\\x{}': function (input) { return this['_findObserveGroups'](input, "^", /^\\[a-zA-Z]+\{/, "}", ""); },
  252. '^\\x': /^\^(\\[a-zA-Z]+)\s*/,
  253. '^(-1)': /^\^(-?\d+)/,
  254. '\'': /^'/,
  255. '_{(...)}': function (input) { return this['_findObserveGroups'](input, "_{", "", "", "}"); },
  256. '_($...$)': function (input) { return this['_findObserveGroups'](input, "_", "$", "$", ""); },
  257. '_9': /^_([+\-]?[0-9]+|[^\\])/,
  258. '_\\x{}{}': function (input) { return this['_findObserveGroups'](input, "_", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
  259. '_\\x{}': function (input) { return this['_findObserveGroups'](input, "_", /^\\[a-zA-Z]+\{/, "}", ""); },
  260. '_\\x': /^_(\\[a-zA-Z]+)\s*/,
  261. '^_': /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/,
  262. '{}': /^\{\}/,
  263. '{...}': function (input) { return this['_findObserveGroups'](input, "", "{", "}", ""); },
  264. '{(...)}': function (input) { return this['_findObserveGroups'](input, "{", "", "", "}"); },
  265. '$...$': function (input) { return this['_findObserveGroups'](input, "", "$", "$", ""); },
  266. '${(...)}$': function (input) { return this['_findObserveGroups'](input, "${", "", "", "}$"); },
  267. '$(...)$': function (input) { return this['_findObserveGroups'](input, "$", "", "", "$"); },
  268. '=<>': /^[=<>]/,
  269. '#': /^[#\u2261]/,
  270. '+': /^\+/,
  271. '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation
  272. '-9': /^-(?=[0-9])/,
  273. '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,
  274. '-': /^-/,
  275. 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,
  276. 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,
  277. 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,
  278. '\\bond{(...)}': function (input) { return this['_findObserveGroups'](input, "\\bond{", "", "", "}"); },
  279. '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,
  280. 'CMT': /^[CMT](?=\[)/,
  281. '[(...)]': function (input) { return this['_findObserveGroups'](input, "[", "", "", "]"); },
  282. '1st-level escape': /^(&|\\\\|\\hline)\s*/,
  283. '\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before
  284. '\\x{}{}': function (input) { return this['_findObserveGroups'](input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
  285. '\\x{}': function (input) { return this['_findObserveGroups'](input, "", /^\\[a-zA-Z]+\{/, "}", ""); },
  286. '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/,
  287. '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,
  288. 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway
  289. 'others': /^[\/~|]/,
  290. '\\frac{(...)}': function (input) { return this['_findObserveGroups'](input, "\\frac{", "", "", "}", "{", "", "", "}"); },
  291. '\\overset{(...)}': function (input) { return this['_findObserveGroups'](input, "\\overset{", "", "", "}", "{", "", "", "}"); },
  292. '\\underset{(...)}': function (input) { return this['_findObserveGroups'](input, "\\underset{", "", "", "}", "{", "", "", "}"); },
  293. '\\underbrace{(...)}': function (input) { return this['_findObserveGroups'](input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); },
  294. '\\color{(...)}0': function (input) { return this['_findObserveGroups'](input, "\\color{", "", "", "}"); },
  295. '\\color{(...)}{(...)}1': function (input) { return this['_findObserveGroups'](input, "\\color{", "", "", "}", "{", "", "", "}"); },
  296. '\\color(...){(...)}2': function (input) { return this['_findObserveGroups'](input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); },
  297. '\\ce{(...)}': function (input) { return this['_findObserveGroups'](input, "\\ce{", "", "", "}"); },
  298. 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
  299. 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge
  300. 'roman numeral': /^[IVX]+/,
  301. '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,
  302. 'amount': function (input) {
  303. var matchh;
  304. // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing
  305. matchh = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/);
  306. if (matchh) {
  307. return { matchh: matchh[0], remainder: input.substr(matchh[0].length) };
  308. }
  309. var a = this['_findObserveGroups'](input, "", "$", "$", "");
  310. if (a) { // e.g. $2n-1$, $-$
  311. matchh = a.matchh.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/);
  312. if (matchh) {
  313. return { matchh: matchh[0], remainder: input.substr(matchh[0].length) };
  314. }
  315. }
  316. return null;
  317. },
  318. 'amount2': function (input) { return this['amount'](input); },
  319. '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/,
  320. 'formula$': function (input) {
  321. if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula
  322. var matchh = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);
  323. if (matchh) {
  324. return { matchh: matchh[0], remainder: input.substr(matchh[0].length) };
  325. }
  326. return null;
  327. },
  328. 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,
  329. '/': /^\s*(\/)\s*/,
  330. '//': /^\s*(\/\/)\s*/,
  331. '*': /^\s*\*\s*/,
  332. '_findObserveGroups': function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) {
  333. var matchh = this['__match'](input, begExcl);
  334. if (matchh === null) { return null; }
  335. input = input.substr(matchh.length);
  336. matchh = this['__match'](input, begIncl);
  337. if (matchh === null) { return null; }
  338. var e = this['__findObserveGroups'](input, matchh.length, endIncl || endExcl);
  339. if (e === null) { return null; }
  340. var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin));
  341. if (!(beg2Excl || beg2Incl)) {
  342. return {
  343. matchh: match1,
  344. remainder: input.substr(e.endMatchEnd)
  345. };
  346. } else {
  347. var group2 = this['_findObserveGroups'](input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl);
  348. if (group2 === null) { return null; }
  349. var matchRet = [match1, group2.matchh];
  350. if (combine) { matchRet = matchRet.join(""); }
  351. return {
  352. matchh: matchRet,
  353. remainder: group2.remainder
  354. };
  355. }
  356. },
  357. '__match': function (input, pattern) {
  358. if (typeof pattern === "string") {
  359. if (input.indexOf(pattern) !== 0) { return null; }
  360. return pattern;
  361. } else {
  362. var matchh = input.match(pattern);
  363. if (!matchh) { return null; }
  364. return matchh[0];
  365. }
  366. },
  367. '__findObserveGroups': function (input, i, endChars) {
  368. var braces = 0;
  369. while (i < input.length) {
  370. var a = input.charAt(i);
  371. var matchh = this['__match'](input.substr(i), endChars);
  372. if (matchh !== null && braces === 0) {
  373. return { endMatchBegin: i, endMatchEnd: i + matchh.length };
  374. } else if (a === "{") {
  375. braces++;
  376. } else if (a === "}") {
  377. if (braces === 0) {
  378. throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"];
  379. } else {
  380. braces--;
  381. }
  382. }
  383. i++;
  384. }
  385. if (braces > 0) {
  386. return null;
  387. }
  388. return null;
  389. }
  390. };
  391. //
  392. // Matching function
  393. // e.g. matchh("a", input) will look for the regexp called "a" and see if it matches
  394. // returns null or {matchh:"a", remainder:"bc"}
  395. //
  396. mhchemParser.matchh = function (m, input) {
  397. var pattern = mhchemParser.patterns[m];
  398. if (pattern === undefined) {
  399. throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern
  400. } else if (typeof pattern === "function") {
  401. return mhchemParser.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser
  402. } else { // RegExp
  403. var matchh = input.match(pattern);
  404. if (matchh) {
  405. var mm;
  406. if (matchh[2]) {
  407. mm = [ matchh[1], matchh[2] ];
  408. } else if (matchh[1]) {
  409. mm = matchh[1];
  410. } else {
  411. mm = matchh[0];
  412. }
  413. return { matchh: mm, remainder: input.substr(matchh[0].length) };
  414. }
  415. return null;
  416. }
  417. };
  418. //
  419. // Generic state machine actions
  420. //
  421. mhchemParser.actions = {
  422. 'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; },
  423. 'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; },
  424. 'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; },
  425. 'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; },
  426. 'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; },
  427. 'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; },
  428. 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; },
  429. 'text=': function (buffer, m) { buffer.text = (buffer.text || "") + m; },
  430. 'insert': function (buffer, m, a) { return { type: a }; },
  431. 'insert+p1': function (buffer, m, a) { return { type: a, p1: m }; },
  432. 'insert+p1+p2': function (buffer, m, a) { return { type: a, p1: m[0], p2: m[1] }; },
  433. 'copy': function (buffer, m) { return m; },
  434. 'rm': function (buffer, m) { return { type: 'rm', p1: m }; },
  435. 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); },
  436. '{text}': function (buffer, m) {
  437. var ret = [ "{" ];
  438. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.go(m, 'text'));
  439. ret = mhchemParser.concatNotUndefined(ret, "}");
  440. return ret;
  441. },
  442. 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); },
  443. 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); },
  444. 'bond': function (buffer, m, k) { return { type: 'bond', kind: k || m }; },
  445. 'color0-output': function (buffer, m) { return { type: 'color0', color: m[0] }; },
  446. 'ce': function (buffer, m) { return mhchemParser.go(m); },
  447. '1/2': function (buffer, m) {
  448. var ret;
  449. if (m.match(/^[+\-]/)) {
  450. ret = [ m.substr(0, 1) ];
  451. m = m.substr(1);
  452. }
  453. var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);
  454. n[1] = n[1].replace(/\$/g, "");
  455. ret = mhchemParser.concatNotUndefined(ret, { type: 'frac', p1: n[1], p2: n[2] } );
  456. if (n[3]) {
  457. n[3] = n[3].replace(/\$/g, "");
  458. ret = mhchemParser.concatNotUndefined(ret, { type: 'tex-math', p1: n[3] } );
  459. }
  460. return ret;
  461. },
  462. '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); }
  463. };
  464. //
  465. // State machine definitions
  466. //
  467. mhchemParser.stateMachines = {};
  468. //
  469. // convert { 'a': { '*': { action: 'output' } } } to [ { matchh: 'a', actions: { '*': { action: 'output' } } } ]
  470. // with expansion of 'a|b' to 'a' and 'b' (at 2 places)
  471. //
  472. mhchemParser.createTransitions = function (o) {
  473. var a, b, s, i;
  474. //
  475. // 1. o ==> oo, expanding 'a|b'
  476. //
  477. var oo = {};
  478. for (a in o) {
  479. if (a.indexOf("|") !== -1) {
  480. s = a.split("|");
  481. for (i=0; i<s.length; i++) {
  482. oo[s[i]] = o[a];
  483. }
  484. } else {
  485. oo[a] = o[a];
  486. }
  487. }
  488. //
  489. // 2. oo ==> transition array
  490. //
  491. var transitions = [];
  492. for (a in oo) {
  493. var actions = {};
  494. var ooa = oo[a];
  495. for (b in ooa) {
  496. //
  497. // expanding action-state:'a|b' if needed
  498. //
  499. if (b.indexOf("|") !== -1) {
  500. s = b.split("|");
  501. for (i=0; i<s.length; i++) {
  502. actions[s[i]] = ooa[b];
  503. }
  504. } else {
  505. actions[b] = ooa[b];
  506. }
  507. }
  508. transitions.push( { matchh: a, actions: actions } );
  509. }
  510. return transitions;
  511. };
  512. //
  513. //
  514. // \ce state machines
  515. //
  516. //
  517. // Transitions and actions of main parser
  518. //
  519. mhchemParser.stateMachines['ce'] = {
  520. transitions: mhchemParser.createTransitions({
  521. 'empty': {
  522. '*': { action: 'output' } },
  523. 'else': {
  524. '0|1|2': { action: 'beginsWithBond=false', revisit: true, toContinue: true } },
  525. 'oxidation$': {
  526. '0': { action: 'oxidation-output' } },
  527. 'CMT': {
  528. 'r': { action: 'rdt=', nextState: 'rt' },
  529. 'rd': { action: 'rqt=', nextState: 'rdt' } },
  530. 'arrowUpDown': {
  531. '0|1|2|as': { action: [ 'sb=false', 'output', 'operator' ], nextState: '1' } },
  532. 'uprightEntities': {
  533. '0|1|2': { action: [ 'o=', 'output' ], nextState: '1' } },
  534. 'orbital': {
  535. '0|1|2|3': { action: 'o=', nextState: 'o' } },
  536. '->': {
  537. '0|1|2|3': { action: 'r=', nextState: 'r' },
  538. 'a|as': { action: [ 'output', 'r=' ], nextState: 'r' },
  539. '*': { action: [ 'output', 'r=' ], nextState: 'r' } },
  540. '+': {
  541. 'o': { action: 'd= kv', nextState: 'd' },
  542. 'd|D': { action: 'd=', nextState: 'd' },
  543. 'q': { action: 'd=', nextState: 'qd' },
  544. 'qd|qD': { action: 'd=', nextState: 'qd' },
  545. 'dq': { action: [ 'output', 'd=' ], nextState: 'd' },
  546. '3': { action: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
  547. 'amount': {
  548. '0|2': { action: 'a=', nextState: 'a' } },
  549. 'pm-operator': {
  550. '0|1|2|a|as': { action: [ 'sb=false', 'output', { type: 'operator', option: '\\pm' } ], nextState: '0' } },
  551. 'operator': {
  552. '0|1|2|a|as': { action: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
  553. '-$': {
  554. 'o|q': { action: [ 'charge or bond', 'output' ], nextState: 'qd' },
  555. 'd': { action: 'd=', nextState: 'd' },
  556. 'D': { action: [ 'output', { type: 'bond', option: "-" } ], nextState: '3' },
  557. 'q': { action: 'd=', nextState: 'qd' },
  558. 'qd': { action: 'd=', nextState: 'qd' },
  559. 'qD|dq': { action: [ 'output', { type: 'bond', option: "-" } ], nextState: '3' } },
  560. '-9': {
  561. '3|o': { action: [ 'output', { type: 'insert', option: 'hyphen' } ], nextState: '3' } },
  562. '- orbital overlap': {
  563. 'o': { action: { type: '- after o', option: true }, nextState: '2' },
  564. 'd': { action: { type: '- after d', option: true }, nextState: '2' } },
  565. '-': {
  566. '0|1|2': { action: [ { type: 'output', option: 1 }, 'beginsWithBond=true', { type: 'bond', option: "-" } ], nextState: '3' },
  567. '3': { action: { type: 'bond', option: "-" } },
  568. 'a': { action: [ 'output', { type: 'insert', option: 'hyphen' } ], nextState: '2' },
  569. 'as': { action: [ { type: 'output', option: 2 }, { type: 'bond', option: "-" } ], nextState: '3' },
  570. 'b': { action: 'b=' },
  571. 'o': { action: '- after o', nextState: '2' },
  572. 'q': { action: '- after o', nextState: '2' },
  573. 'd|qd|dq': { action: '- after d', nextState: '2' },
  574. 'D|qD|p': { action: [ 'output', { type: 'bond', option: "-" } ], nextState: '3' } },
  575. 'amount2': {
  576. '1|3': { action: 'a=', nextState: 'a' } },
  577. 'letters': {
  578. '0|1|2|3|a|as|b|p|bp|o': { action: 'o=', nextState: 'o' },
  579. 'q|dq': { action: ['output', 'o='], nextState: 'o' },
  580. 'd|D|qd|qD': { action: 'o after d', nextState: 'o' } },
  581. 'digits': {
  582. 'o': { action: 'q=', nextState: 'q' },
  583. 'd|D': { action: 'q=', nextState: 'dq' },
  584. 'q': { action: [ 'output', 'o=' ], nextState: 'o' },
  585. 'a': { action: 'o=', nextState: 'o' } },
  586. 'space A': {
  587. 'b|p|bp': {} },
  588. 'space': {
  589. 'a': { nextState: 'as' },
  590. '0': { action: 'sb=false' },
  591. '1|2': { action: 'sb=true' },
  592. 'r|rt|rd|rdt|rdq': { action: 'output', nextState: '0' },
  593. '*': { action: [ 'output', 'sb=true' ], nextState: '1'} },
  594. '1st-level escape': {
  595. '1|2': { action: [ 'output', { type: 'insert+p1', option: '1st-level escape' } ] },
  596. '*': { action: [ 'output', { type: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } },
  597. '[(...)]': {
  598. 'r|rt': { action: 'rd=', nextState: 'rd' },
  599. 'rd|rdt': { action: 'rq=', nextState: 'rdq' } },
  600. '...': {
  601. 'o|d|D|dq|qd|qD': { action: [ 'output', { type: 'bond', option: "..." } ], nextState: '3' },
  602. '*': { action: [ { type: 'output', option: 1 }, { type: 'insert', option: 'ellipsis' } ], nextState: '1' } },
  603. '. |* ': {
  604. '*': { action: [ 'output', { type: 'insert', option: 'addition compound' } ], nextState: '1' } },
  605. 'state of aggregation $': {
  606. '*': { action: [ 'output', 'state of aggregation' ], nextState: '1' } },
  607. '\{[(': {
  608. 'a|as|o': { action: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
  609. '0|1|2|3': { action: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
  610. '*': { action: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } },
  611. ')]\}': {
  612. '0|1|2|3|b|p|bp|o': { action: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' },
  613. 'a|as|d|D|q|qd|qD|dq': { action: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } },
  614. ', ': {
  615. '*': { action: [ 'output', 'comma' ], nextState: '0' } },
  616. '^_': { // ^ and _ without a sensible argument
  617. '*': { } },
  618. '^{(...)}|^($...$)': {
  619. '0|1|2|as': { action: 'b=', nextState: 'b' },
  620. 'p': { action: 'b=', nextState: 'bp' },
  621. '3|o': { action: 'd= kv', nextState: 'D' },
  622. 'q': { action: 'd=', nextState: 'qD' },
  623. 'd|D|qd|qD|dq': { action: [ 'output', 'd=' ], nextState: 'D' } },
  624. '^a|^\\x{}{}|^\\x{}|^\\x|\'': {
  625. '0|1|2|as': { action: 'b=', nextState: 'b' },
  626. 'p': { action: 'b=', nextState: 'bp' },
  627. '3|o': { action: 'd= kv', nextState: 'd' },
  628. 'q': { action: 'd=', nextState: 'qd' },
  629. 'd|qd|D|qD': { action: 'd=' },
  630. 'dq': { action: [ 'output', 'd=' ], nextState: 'd' } },
  631. '_{(state of aggregation)}$': {
  632. 'd|D|q|qd|qD|dq': { action: [ 'output', 'q=' ], nextState: 'q' } },
  633. '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': {
  634. '0|1|2|as': { action: 'p=', nextState: 'p' },
  635. 'b': { action: 'p=', nextState: 'bp' },
  636. '3|o': { action: 'q=', nextState: 'q' },
  637. 'd|D': { action: 'q=', nextState: 'dq' },
  638. 'q|qd|qD|dq': { action: [ 'output', 'q=' ], nextState: 'q' } },
  639. '=<>': {
  640. '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action: [ { type: 'output', option: 2 }, 'bond' ], nextState: '3' } },
  641. '#': {
  642. '0|1|2|3|a|as|o': { action: [ { type: 'output', option: 2 }, { type: 'bond', option: "#" } ], nextState: '3' } },
  643. '{}': {
  644. '*': { action: { type: 'output', option: 1 }, nextState: '1' } },
  645. '{...}': {
  646. '0|1|2|3|a|as|b|p|bp': { action: 'o=', nextState: 'o' },
  647. 'o|d|D|q|qd|qD|dq': { action: [ 'output', 'o=' ], nextState: 'o' } },
  648. '$...$': {
  649. 'a': { action: 'a=' }, // 2$n$
  650. '0|1|2|3|as|b|p|bp|o': { action: 'o=', nextState: 'o' }, // not 'amount'
  651. 'as|o': { action: 'o=' },
  652. 'q|d|D|qd|qD|dq': { action: [ 'output', 'o=' ], nextState: 'o' } },
  653. '\\bond{(...)}': {
  654. '*': { action: [ { type: 'output', option: 2 }, 'bond' ], nextState: "3" } },
  655. '\\frac{(...)}': {
  656. '*': { action: [ { type: 'output', option: 1 }, 'frac-output' ], nextState: '3' } },
  657. '\\overset{(...)}': {
  658. '*': { action: [ { type: 'output', option: 2 }, 'overset-output' ], nextState: '3' } },
  659. '\\underset{(...)}': {
  660. '*': { action: [ { type: 'output', option: 2 }, 'underset-output' ], nextState: '3' } },
  661. '\\underbrace{(...)}': {
  662. '*': { action: [ { type: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } },
  663. '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
  664. '*': { action: [ { type: 'output', option: 2 }, 'color-output' ], nextState: '3' } },
  665. '\\color{(...)}0': {
  666. '*': { action: [ { type: 'output', option: 2 }, 'color0-output' ] } },
  667. '\\ce{(...)}': {
  668. '*': { action: [ { type: 'output', option: 2 }, 'ce' ], nextState: '3' } },
  669. '\\,': {
  670. '*': { action: [ { type: 'output', option: 1 }, 'copy' ], nextState: '1' } },
  671. '\\x{}{}|\\x{}|\\x': {
  672. '0|1|2|3|a|as|b|p|bp|o|c0': { action: [ 'o=', 'output' ], nextState: '3' },
  673. '*': { action: ['output', 'o=', 'output' ], nextState: '3' } },
  674. 'others': {
  675. '*': { action: [ { type: 'output', option: 1 }, 'copy' ], nextState: '3' } },
  676. 'else2': {
  677. 'a': { action: 'a to o', nextState: 'o', revisit: true },
  678. 'as': { action: [ { type: 'output' }, 'sb=true' ], nextState: '1', revisit: true },
  679. 'r|rt|rd|rdt|rdq': { action: [ 'output' ], nextState: '0', revisit: true },
  680. '*': { action: [ 'output', 'copy' ], nextState: '3' } }
  681. }),
  682. actions: {
  683. 'o after d': function (buffer, m) {
  684. var ret;
  685. if (buffer.d.match(/^[0-9]+$/)) {
  686. var tmp = buffer.d;
  687. buffer.d = undefined;
  688. ret = this['output'](buffer);
  689. buffer.b = tmp;
  690. } else {
  691. ret = this['output'](buffer);
  692. }
  693. mhchemParser.actions['o='](buffer, m);
  694. return ret;
  695. },
  696. 'd= kv': function (buffer, m) {
  697. buffer.d = m;
  698. buffer['d-type'] = 'kv';
  699. },
  700. 'charge or bond': function (buffer, m) {
  701. if (buffer['beginsWithBond']) {
  702. var ret = mhchemParser.concatNotUndefined(ret, this['output'](buffer));
  703. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.actions['bond'](buffer, m, "-"));
  704. return ret;
  705. } else {
  706. buffer.d = m;
  707. }
  708. },
  709. '- after o': function (buffer, m, isOrbitalOverlap) {
  710. var hyphenFollows = isOrbitalOverlap || this['_hyphenFollows'](buffer, m);
  711. var ret = mhchemParser.concatNotUndefined(null, this['output'](buffer, m));
  712. if (hyphenFollows) {
  713. ret = mhchemParser.concatNotUndefined(ret, { type: 'hyphen' });
  714. } else {
  715. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.actions['bond'](buffer, m, "-"));
  716. }
  717. return ret;
  718. },
  719. '- after d': function (buffer, m, isOrbitalOverlap) {
  720. var hyphenFollows = isOrbitalOverlap || this['_hyphenFollows'](buffer, m);
  721. var ret;
  722. if (hyphenFollows) {
  723. ret = mhchemParser.concatNotUndefined(ret, this['output'](buffer, m));
  724. ret = mhchemParser.concatNotUndefined(ret, { type: 'hyphen' });
  725. } else {
  726. var c1 = mhchemParser.matchh('digits', buffer.d || "");
  727. if (c1 && c1.remainder==='') {
  728. ret = mhchemParser.concatNotUndefined(null, mhchemParser.actions['d='](buffer, m));
  729. ret = mhchemParser.concatNotUndefined(ret, this['output'](buffer));
  730. } else {
  731. ret = mhchemParser.concatNotUndefined(ret, this['output'](buffer, m));
  732. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.actions['bond'](buffer, m, "-"));
  733. }
  734. }
  735. return ret;
  736. },
  737. '_hyphenFollows': function (buffer, m) {
  738. var c1 = mhchemParser.matchh('orbital', buffer.o || "");
  739. var c2 = mhchemParser.matchh('one lowercase greek letter $', buffer.o || "");
  740. var c3 = mhchemParser.matchh('one lowercase latin letter $', buffer.o || "");
  741. var c4 = mhchemParser.matchh('$one lowercase latin letter$ $', buffer.o || "");
  742. var hyphenFollows = m==="-" && ( c1 && c1.remainder==='' || c2 || c3 || c4 );
  743. if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) {
  744. buffer.o = '$' + buffer.o + '$';
  745. }
  746. return hyphenFollows;
  747. },
  748. 'a to o': function (buffer, m) {
  749. buffer.o = buffer.a;
  750. buffer.a = undefined;
  751. },
  752. 'sb=true': function (buffer, m) { buffer.sb = true; },
  753. 'sb=false': function (buffer, m) { buffer.sb = false; },
  754. 'beginsWithBond=true': function (buffer, m) { buffer.beginsWithBond = true; },
  755. 'beginsWithBond=false': function (buffer, m) { buffer.beginsWithBond = false; },
  756. 'parenthesisLevel++': function (buffer, m) { buffer.parenthesisLevel++; },
  757. 'parenthesisLevel--': function (buffer, m) { buffer.parenthesisLevel--; },
  758. 'state of aggregation': function (buffer, m) {
  759. m = mhchemParser.go(m, 'o');
  760. return { type: 'state of aggregation', p1: m };
  761. },
  762. 'comma': function (buffer, m) {
  763. var a = m.replace(/\s*$/, '');
  764. var withSpace = (a !== m);
  765. if (withSpace && buffer['parenthesisLevel'] === 0) {
  766. return { type: 'comma enumeration L', p1: a };
  767. } else {
  768. return { type: 'comma enumeration M', p1: a };
  769. }
  770. },
  771. 'output': function (buffer, m, entityFollows) {
  772. // entityFollows:
  773. // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb)
  774. // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1)
  775. // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as)
  776. var ret;
  777. if (!buffer.r) {
  778. ret = [];
  779. if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) {
  780. ret = null;
  781. } else {
  782. if (buffer.sb) {
  783. ret.push({ type: 'entitySkip' });
  784. }
  785. if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) {
  786. buffer.o = buffer.a;
  787. buffer.a = undefined;
  788. } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) {
  789. buffer.o = buffer.a;
  790. buffer.d = buffer.b;
  791. buffer.q = buffer.p;
  792. buffer.a = buffer.b = buffer.p = undefined;
  793. } else {
  794. if (buffer.o && buffer['d-type']==='kv' && mhchemParser.matchh('d-oxidation$', buffer.d || "")) {
  795. buffer['d-type'] = 'oxidation';
  796. } else if (buffer.o && buffer['d-type']==='kv' && !buffer.q) {
  797. buffer['d-type'] = undefined;
  798. }
  799. }
  800. buffer.a = mhchemParser.go(buffer.a, 'a');
  801. buffer.b = mhchemParser.go(buffer.b, 'bd');
  802. buffer.p = mhchemParser.go(buffer.p, 'pq');
  803. buffer.o = mhchemParser.go(buffer.o, 'o');
  804. if (buffer['d-type'] === 'oxidation') {
  805. buffer.d = mhchemParser.go(buffer.d, 'oxidation');
  806. } else {
  807. buffer.d = mhchemParser.go(buffer.d, 'bd');
  808. }
  809. buffer.q = mhchemParser.go(buffer.q, 'pq');
  810. ret.push({
  811. type: 'chemfive',
  812. a: buffer.a,
  813. b: buffer.b,
  814. p: buffer.p,
  815. o: buffer.o,
  816. q: buffer.q,
  817. d: buffer.d,
  818. 'd-type': buffer['d-type']
  819. });
  820. }
  821. } else { // r
  822. if (buffer.rdt === 'M') {
  823. buffer.rd = mhchemParser.go(buffer.rd, 'tex-math');
  824. } else if (buffer.rdt === 'T') {
  825. buffer.rd = [ { type: 'text', p1: buffer.rd } ];
  826. } else {
  827. buffer.rd = mhchemParser.go(buffer.rd);
  828. }
  829. if (buffer.rqt === 'M') {
  830. buffer.rq = mhchemParser.go(buffer.rq, 'tex-math');
  831. } else if (buffer.rqt === 'T') {
  832. buffer.rq = [ { type: 'text', p1: buffer.rq } ];
  833. } else {
  834. buffer.rq = mhchemParser.go(buffer.rq);
  835. }
  836. ret = {
  837. type: 'arrow',
  838. r: buffer.r,
  839. rd: buffer.rd,
  840. rq: buffer.rq
  841. };
  842. }
  843. for (var p in buffer) {
  844. if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') {
  845. delete buffer[p];
  846. }
  847. }
  848. return ret;
  849. },
  850. 'oxidation-output': function (buffer, m) {
  851. var ret = [ "{" ];
  852. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.go(m, 'oxidation'));
  853. ret = ret.concat([ "}" ]);
  854. return ret;
  855. },
  856. 'frac-output': function (buffer, m) {
  857. return { type: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
  858. },
  859. 'overset-output': function (buffer, m) {
  860. return { type: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
  861. },
  862. 'underset-output': function (buffer, m) {
  863. return { type: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
  864. },
  865. 'underbrace-output': function (buffer, m) {
  866. return { type: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
  867. },
  868. 'color-output': function (buffer, m) {
  869. return { type: 'color', color1: m[0], color2: mhchemParser.go(m[1]) };
  870. },
  871. 'r=': function (buffer, m) { buffer.r = (buffer.r || "") + m; },
  872. 'rdt=': function (buffer, m) { buffer.rdt = (buffer.rdt || "") + m; },
  873. 'rd=': function (buffer, m) { buffer.rd = (buffer.rd || "") + m; },
  874. 'rqt=': function (buffer, m) { buffer.rqt = (buffer.rqt || "") + m; },
  875. 'rq=': function (buffer, m) { buffer.rq = (buffer.rq || "") + m; },
  876. 'operator': function (buffer, m, p1) { return { type: 'operator', kind: (p1 || m) }; }
  877. }
  878. };
  879. //
  880. // Transitions and actions of a parser
  881. //
  882. mhchemParser.stateMachines['a'] = {
  883. transitions: mhchemParser.createTransitions({
  884. 'empty': {
  885. '*': {} },
  886. '1/2$': {
  887. '0': { action: '1/2' } },
  888. 'else': {
  889. '0': { nextState: '1', revisit: true } },
  890. '$(...)$': {
  891. '*': { action: 'tex-math tight', nextState: '1' } },
  892. ',': {
  893. '*': { action: { type: 'insert', option: 'commaDecimal' } } },
  894. 'else2': {
  895. '*': { action: 'copy' } }
  896. }),
  897. actions: {}
  898. };
  899. //
  900. // Transitions and actions of o parser
  901. //
  902. mhchemParser.stateMachines['o'] = {
  903. transitions: mhchemParser.createTransitions({
  904. 'empty': {
  905. '*': {} },
  906. '1/2$': {
  907. '0': { action: '1/2' } },
  908. 'else': {
  909. '0': { nextState: '1', revisit: true } },
  910. 'letters': {
  911. '*': { action: 'rm' } },
  912. '\\ca': {
  913. '*': { action: { type: 'insert', option: 'circa' } } },
  914. '\\x{}{}|\\x{}|\\x': {
  915. '*': { action: 'copy' } },
  916. '${(...)}$|$(...)$': {
  917. '*': { action: 'tex-math' } },
  918. '{(...)}': {
  919. '*': { action: '{text}' } },
  920. 'else2': {
  921. '*': { action: 'copy' } }
  922. }),
  923. actions: {}
  924. };
  925. //
  926. // Transitions and actions of text parser
  927. //
  928. mhchemParser.stateMachines['text'] = {
  929. transitions: mhchemParser.createTransitions({
  930. 'empty': {
  931. '*': { action: 'output' } },
  932. '{...}': {
  933. '*': { action: 'text=' } },
  934. '${(...)}$|$(...)$': {
  935. '*': { action: 'tex-math' } },
  936. '\\greek': {
  937. '*': { action: [ 'output', 'rm' ] } },
  938. '\\,|\\x{}{}|\\x{}|\\x': {
  939. '*': { action: [ 'output', 'copy' ] } },
  940. 'else': {
  941. '*': { action: 'text=' } }
  942. }),
  943. actions: {
  944. 'output': function (buffer, m) {
  945. if (buffer.text) {
  946. var ret = { type: 'text', p1: buffer.text };
  947. for (var p in buffer) { delete buffer[p]; }
  948. return ret;
  949. }
  950. return null;
  951. }
  952. }
  953. };
  954. //
  955. // Transitions and actions of pq parser
  956. //
  957. mhchemParser.stateMachines['pq'] = {
  958. transitions: mhchemParser.createTransitions({
  959. 'empty': {
  960. '*': {} },
  961. 'state of aggregation $': {
  962. '*': { action: 'state of aggregation' } },
  963. 'i$': {
  964. '0': { nextState: '!f', revisit: true } },
  965. '(KV letters),': {
  966. '0': { action: 'rm', nextState: '0' } },
  967. 'formula$': {
  968. '0': { nextState: 'f', revisit: true } },
  969. '1/2$': {
  970. '0': { action: '1/2' } },
  971. 'else': {
  972. '0': { nextState: '!f', revisit: true } },
  973. '${(...)}$|$(...)$': {
  974. '*': { action: 'tex-math' } },
  975. '{(...)}': {
  976. '*': { action: 'text' } },
  977. 'a-z': {
  978. 'f': { action: 'tex-math' } },
  979. 'letters': {
  980. '*': { action: 'rm' } },
  981. '-9.,9': {
  982. '*': { action: '9,9' } },
  983. ',': {
  984. '*': { action: { type: 'insert+p1', option: 'comma enumeration S' } } },
  985. '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
  986. '*': { action: 'color-output' } },
  987. '\\color{(...)}0': {
  988. '*': { action: 'color0-output' } },
  989. '\\ce{(...)}': {
  990. '*': { action: 'ce' } },
  991. '\\,|\\x{}{}|\\x{}|\\x': {
  992. '*': { action: 'copy' } },
  993. 'else2': {
  994. '*': { action: 'copy' } }
  995. }),
  996. actions: {
  997. 'state of aggregation': function (buffer, m) {
  998. m = mhchemParser.go(m, 'o');
  999. return { type: 'state of aggregation subscript', p1: m };
  1000. },
  1001. 'color-output': function (buffer, m) {
  1002. return { type: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') };
  1003. }
  1004. }
  1005. };
  1006. //
  1007. // Transitions and actions of bd parser
  1008. //
  1009. mhchemParser.stateMachines['bd'] = {
  1010. transitions: mhchemParser.createTransitions({
  1011. 'empty': {
  1012. '*': {} },
  1013. 'x$': {
  1014. '0': { nextState: '!f', revisit: true } },
  1015. 'formula$': {
  1016. '0': { nextState: 'f', revisit: true } },
  1017. 'else': {
  1018. '0': { nextState: '!f', revisit: true } },
  1019. '-9.,9 no missing 0': {
  1020. '*': { action: '9,9' } },
  1021. '.': {
  1022. '*': { action: { type: 'insert', option: 'electron dot' } } },
  1023. 'a-z': {
  1024. 'f': { action: 'tex-math' } },
  1025. 'x': {
  1026. '*': { action: { type: 'insert', option: 'KV x' } } },
  1027. 'letters': {
  1028. '*': { action: 'rm' } },
  1029. '\'': {
  1030. '*': { action: { type: 'insert', option: 'prime' } } },
  1031. '${(...)}$|$(...)$': {
  1032. '*': { action: 'tex-math' } },
  1033. '{(...)}': {
  1034. '*': { action: 'text' } },
  1035. '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
  1036. '*': { action: 'color-output' } },
  1037. '\\color{(...)}0': {
  1038. '*': { action: 'color0-output' } },
  1039. '\\ce{(...)}': {
  1040. '*': { action: 'ce' } },
  1041. '\\,|\\x{}{}|\\x{}|\\x': {
  1042. '*': { action: 'copy' } },
  1043. 'else2': {
  1044. '*': { action: 'copy' } }
  1045. }),
  1046. actions: {
  1047. 'color-output': function (buffer, m) {
  1048. return { type: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') };
  1049. }
  1050. }
  1051. };
  1052. //
  1053. // Transitions and actions of oxidation parser
  1054. //
  1055. mhchemParser.stateMachines['oxidation'] = {
  1056. transitions: mhchemParser.createTransitions({
  1057. 'empty': {
  1058. '*': {} },
  1059. 'roman numeral': {
  1060. '*': { action: 'roman-numeral' } },
  1061. '${(...)}$|$(...)$': {
  1062. '*': { action: 'tex-math' } },
  1063. 'else': {
  1064. '*': { action: 'copy' } }
  1065. }),
  1066. actions: {
  1067. 'roman-numeral': function (buffer, m) { return { type: 'roman numeral', p1: m }; }
  1068. }
  1069. };
  1070. //
  1071. // Transitions and actions of tex-math parser
  1072. //
  1073. mhchemParser.stateMachines['tex-math'] = {
  1074. transitions: mhchemParser.createTransitions({
  1075. 'empty': {
  1076. '*': { action: 'output' } },
  1077. '\\ce{(...)}': {
  1078. '*': { action: [ 'output', 'ce' ] } },
  1079. '{...}|\\,|\\x{}{}|\\x{}|\\x': {
  1080. '*': { action: 'o=' } },
  1081. 'else': {
  1082. '*': { action: 'o=' } }
  1083. }),
  1084. actions: {
  1085. 'output': function (buffer, m) {
  1086. if (buffer.o) {
  1087. var ret = { type: 'tex-math', p1: buffer.o };
  1088. for (var p in buffer) { delete buffer[p]; }
  1089. return ret;
  1090. }
  1091. return null;
  1092. }
  1093. }
  1094. };
  1095. //
  1096. // Transitions and actions of tex-math-tight parser
  1097. //
  1098. mhchemParser.stateMachines['tex-math tight'] = {
  1099. transitions: mhchemParser.createTransitions({
  1100. 'empty': {
  1101. '*': { action: 'output' } },
  1102. '\\ce{(...)}': {
  1103. '*': { action: [ 'output', 'ce' ] } },
  1104. '{...}|\\,|\\x{}{}|\\x{}|\\x': {
  1105. '*': { action: 'o=' } },
  1106. '-|+': {
  1107. '*': { action: 'tight operator' } },
  1108. 'else': {
  1109. '*': { action: 'o=' } }
  1110. }),
  1111. actions: {
  1112. 'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; },
  1113. 'output': function (buffer, m) {
  1114. if (buffer.o) {
  1115. var ret = { type: 'tex-math', p1: buffer.o };
  1116. for (var p in buffer) { delete buffer[p]; }
  1117. return ret;
  1118. }
  1119. return null;
  1120. }
  1121. }
  1122. };
  1123. //
  1124. // Transitions and actions of 9,9 parser
  1125. //
  1126. mhchemParser.stateMachines['9,9'] = {
  1127. transitions: mhchemParser.createTransitions({
  1128. 'empty': {
  1129. '*': {} },
  1130. ',': {
  1131. '*': { action: 'comma' } },
  1132. 'else': {
  1133. '*': { action: 'copy' } }
  1134. }),
  1135. actions: {
  1136. 'comma': function (buffer, m) { return { type: 'commaDecimal' }; }
  1137. }
  1138. };
  1139. //
  1140. //
  1141. // \pu state machines
  1142. //
  1143. //
  1144. // Transitions and actions of pu main parser
  1145. //
  1146. mhchemParser.stateMachines['pu'] = {
  1147. transitions: mhchemParser.createTransitions({
  1148. 'empty': {
  1149. '*': { action: 'output' } },
  1150. '\{[(|)]\}': {
  1151. '0|a': { action: 'copy' } },
  1152. '(-)(9)^(-9)': {
  1153. '0': { action: 'number^', nextState: 'a' } },
  1154. '(-)(9.,9)(e)(99)': {
  1155. '0': { action: 'enumber', nextState: 'a' } },
  1156. 'space': {
  1157. '0|a': {} },
  1158. 'pm-operator': {
  1159. '0|a': { action: { type: 'operator', option: '\\pm' }, nextState: '0' } },
  1160. 'operator': {
  1161. '0|a': { action: 'copy', nextState: '0' } },
  1162. '//': {
  1163. 'd': { action: 'o=', nextState: '/' } },
  1164. '/': {
  1165. 'd': { action: 'o=', nextState: '/' } },
  1166. '{...}|else': {
  1167. '0|d': { action: 'd=', nextState: 'd' },
  1168. 'a': { action: [ 'space', 'd=' ], nextState: 'd' },
  1169. '/|q': { action: 'q=', nextState: 'q' } }
  1170. }),
  1171. actions: {
  1172. 'enumber': function (buffer, m) {
  1173. var ret = [];
  1174. if (m[0] === "+-" || m[0] === "+/-") {
  1175. ret.push("\\pm ");
  1176. } else if (m[0]) {
  1177. ret.push(m[0]);
  1178. }
  1179. if (m[1]) {
  1180. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.go(m[1], 'pu-9,9'));
  1181. if (m[2]) {
  1182. if (m[2].match(/[,.]/)) {
  1183. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.go(m[2], 'pu-9,9'));
  1184. } else {
  1185. ret.push(m[2]);
  1186. }
  1187. }
  1188. m[3] = m[4] || m[3];
  1189. if (m[3]) {
  1190. m[3] = m[3].trim();
  1191. if (m[3] === "e" || m[3].substr(0, 1) === "*") {
  1192. ret.push({ type: 'cdot' });
  1193. } else {
  1194. ret.push({ type: 'times' });
  1195. }
  1196. }
  1197. }
  1198. if (m[3]) {
  1199. ret.push("10^{"+m[5]+"}");
  1200. }
  1201. return ret;
  1202. },
  1203. 'number^': function (buffer, m) {
  1204. var ret = [];
  1205. if (m[0] === "+-" || m[0] === "+/-") {
  1206. ret.push("\\pm ");
  1207. } else if (m[0]) {
  1208. ret.push(m[0]);
  1209. }
  1210. ret = mhchemParser.concatNotUndefined(ret, mhchemParser.go(m[1], 'pu-9,9'));
  1211. ret.push("^{"+m[2]+"}");
  1212. return ret;
  1213. },
  1214. 'operator': function (buffer, m, p1) { return { type: 'operator', kind: (p1 || m) }; },
  1215. 'space': function (buffer, m) { return { type: 'pu-space-1' }; },
  1216. 'output': function (buffer, m) {
  1217. var ret;
  1218. var md = mhchemParser.matchh('{(...)}', buffer.d || "");
  1219. if (md && md.remainder === '') { buffer.d = md.matchh; }
  1220. var mq = mhchemParser.matchh('{(...)}', buffer.q || "");
  1221. if (mq && mq.remainder === '') { buffer.q = mq.matchh; }
  1222. if (buffer.d) {
  1223. buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
  1224. buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
  1225. }
  1226. if (buffer.q) { // fraction
  1227. buffer.d = mhchemParser.go(buffer.d, 'pu');
  1228. buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
  1229. buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
  1230. buffer.q = mhchemParser.go(buffer.q, 'pu');
  1231. if (buffer.o === '//') {
  1232. ret = { type: 'pu-frac', p1: buffer.d, p2: buffer.q };
  1233. } else {
  1234. ret = buffer.d;
  1235. if (buffer.d.length > 1 || buffer.q.length > 1) {
  1236. ret = mhchemParser.concatNotUndefined(ret, { type: ' / ' });
  1237. } else {
  1238. ret = mhchemParser.concatNotUndefined(ret, { type: '/' });
  1239. }
  1240. ret = mhchemParser.concatNotUndefined(ret, buffer.q);
  1241. }
  1242. } else { // no fraction
  1243. ret = mhchemParser.go(buffer.d, 'pu-2');
  1244. }
  1245. for (var p in buffer) { delete buffer[p]; }
  1246. return ret;
  1247. }
  1248. }
  1249. };
  1250. //
  1251. // Transitions and actions of pu-2 parser
  1252. //
  1253. mhchemParser.stateMachines['pu-2'] = {
  1254. transitions: mhchemParser.createTransitions({
  1255. 'empty': {
  1256. '*': { action: 'output' } },
  1257. '*': {
  1258. '*': { action: [ 'output', 'cdot' ], nextState: '0' } },
  1259. '\\x': {
  1260. '*': { action: 'rm=' }, nextState: '1' },
  1261. 'space': {
  1262. '*': { action: [ 'output', 'space' ], nextState: '0' } },
  1263. '^{(...)}|^(-1)': {
  1264. '1': { action: '^(-1)' } },
  1265. '-9.,9': {
  1266. '0': { action: 'rm=', nextState: '0' },
  1267. '1': { action: '^(-1)', nextState: '0' } },
  1268. '{...}|else': {
  1269. '*': { action: 'rm=', nextState: '1' } }
  1270. }),
  1271. actions: {
  1272. 'cdot': function (buffer, m) { return { type: 'tight cdot' }; },
  1273. '^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; },
  1274. 'space': function (buffer, m) { return { type: 'pu-space-2' }; },
  1275. 'output': function (buffer, m) {
  1276. var ret;
  1277. if (buffer.rm) {
  1278. var mrm = mhchemParser.matchh('{(...)}', buffer.rm || "");
  1279. if (mrm && mrm.remainder === '') {
  1280. ret = mhchemParser.go(mrm.matchh, 'pu');
  1281. } else {
  1282. ret = { type: 'rm', p1: buffer.rm };
  1283. }
  1284. }
  1285. for (var p in buffer) { delete buffer[p]; }
  1286. return ret;
  1287. }
  1288. }
  1289. };
  1290. //
  1291. // Transitions and actions of 9,9 parser
  1292. //
  1293. mhchemParser.stateMachines['pu-9,9'] = {
  1294. transitions: mhchemParser.createTransitions({
  1295. 'empty': {
  1296. '0': { action: 'output-0' },
  1297. 'o': { action: 'output-o' } },
  1298. ',': {
  1299. '0': { action: [ 'output-0', 'comma' ], nextState: 'o' } },
  1300. '.': {
  1301. '0': { action: [ 'output-0', 'copy' ], nextState: 'o' } },
  1302. 'else': {
  1303. '*': { action: 'text=' } }
  1304. }),
  1305. actions: {
  1306. 'comma': function (buffer, m) { return { type: 'commaDecimal' }; },
  1307. 'output-0': function (buffer, m) {
  1308. var ret = [];
  1309. if (buffer.text.length > 4) {
  1310. var a = buffer.text.length % 3;
  1311. if (a === 0) { a = 3; }
  1312. for (var i=buffer.text.length-3; i>0; i-=3) {
  1313. ret.push(buffer.text.substr(i, 3));
  1314. ret.push({ type: '1000 separator' });
  1315. }
  1316. ret.push(buffer.text.substr(0, a));
  1317. ret.reverse();
  1318. } else {
  1319. ret.push(buffer.text);
  1320. }
  1321. for (var p in buffer) { delete buffer[p]; }
  1322. return ret;
  1323. },
  1324. 'output-o': function (buffer, m) {
  1325. var ret = [];
  1326. if (buffer.text.length > 4) {
  1327. var a = buffer.text.length - 3;
  1328. for (var i=0; i<a; i+=3) {
  1329. ret.push(buffer.text.substr(i, 3));
  1330. ret.push({ type: '1000 separator' });
  1331. }
  1332. ret.push(buffer.text.substr(i));
  1333. } else {
  1334. ret.push(buffer.text);
  1335. }
  1336. for (var p in buffer) { delete buffer[p]; }
  1337. return ret;
  1338. }
  1339. }
  1340. };
  1341. //
  1342. //
  1343. // Take MhchemParser output and convert it to TeX
  1344. // (recursive)
  1345. //
  1346. //
  1347. var texify = {
  1348. types: {
  1349. 'chemfive': function (buf) {
  1350. var res = "";
  1351. buf.a = texify.go2(buf.a);
  1352. buf.b = texify.go2(buf.b);
  1353. buf.p = texify.go2(buf.p);
  1354. buf.o = texify.go2(buf.o);
  1355. buf.q = texify.go2(buf.q);
  1356. buf.d = texify.go2(buf.d);
  1357. //
  1358. // a
  1359. //
  1360. if (buf.a) {
  1361. if (buf.a.match(/^[+\-]/)) { buf.a = "{"+buf.a+"}"; }
  1362. res += buf.a + "\\,";
  1363. }
  1364. //
  1365. // b and p
  1366. //
  1367. if (buf.b || buf.p) {
  1368. res += "{\\vphantom{X}}";
  1369. res += "^{\\hphantom{"+(buf.b||"")+"}}_{\\hphantom{"+(buf.p||"")+"}}";
  1370. res += "{\\vphantom{X}}";
  1371. res += "^{\\smash[t]{\\vphantom{2}}\\llap{"+(buf.b||"")+"}}";
  1372. res += "_{\\vphantom{2}\\llap{\\smash[t]{"+(buf.p||"")+"}}}";
  1373. }
  1374. //
  1375. // o
  1376. //
  1377. if (buf.o) {
  1378. if (buf.o.match(/^[+\-]/)) { buf.o = "{"+buf.o+"}"; }
  1379. res += buf.o;
  1380. }
  1381. //
  1382. // q and d
  1383. //
  1384. if (buf['d-type'] === 'kv') {
  1385. if (buf.d || buf.q) {
  1386. res += "{\\vphantom{X}}";
  1387. }
  1388. if (buf.d) {
  1389. res += "^{"+buf.d+"}";
  1390. }
  1391. if (buf.q) {
  1392. res += "_{\\smash[t]{"+buf.q+"}}";
  1393. }
  1394. } else if (buf['d-type'] === 'oxidation') {
  1395. if (buf.d) {
  1396. res += "{\\vphantom{X}}";
  1397. res += "^{"+buf.d+"}";
  1398. }
  1399. if (buf.q) {
  1400. res += "{\\vphantom{X}}";
  1401. res += "_{\\smash[t]{"+buf.q+"}}";
  1402. }
  1403. } else {
  1404. if (buf.q) {
  1405. res += "{\\vphantom{X}}";
  1406. res += "_{\\smash[t]{"+buf.q+"}}";
  1407. }
  1408. if (buf.d) {
  1409. res += "{\\vphantom{X}}";
  1410. res += "^{"+buf.d+"}";
  1411. }
  1412. }
  1413. return res;
  1414. },
  1415. 'rm': function (buf) { return "\\mathrm{"+buf.p1+"}"; },
  1416. 'text': function (buf) {
  1417. if (buf.p1.match(/[\^_]/)) {
  1418. buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}");
  1419. return "\\mathrm{"+buf.p1+"}";
  1420. } else {
  1421. return "\\text{"+buf.p1+"}";
  1422. }
  1423. },
  1424. 'roman numeral': function (buf) { return "\\mathrm{"+buf.p1+"}"; },
  1425. 'state of aggregation': function (buf) { return "\\mskip2mu "+texify.go2(buf.p1); },
  1426. 'state of aggregation subscript': function (buf) { return "\\mskip1mu "+texify.go2(buf.p1); },
  1427. 'bond': function (buf) {
  1428. var ret = texify.bonds[buf.kind];
  1429. if (!ret) {
  1430. throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind + ")"];
  1431. }
  1432. return ret;
  1433. },
  1434. 'frac': function (buf) {
  1435. var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}";
  1436. return "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}";
  1437. },
  1438. 'pu-frac': function (buf) {
  1439. var c = "\\frac{" + texify.go2(buf.p1) + "}{" + texify.go2(buf.p2) + "}";
  1440. return "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}";
  1441. },
  1442. 'tex-math': function (buf) { return buf.p1 + " "; },
  1443. 'frac-ce': function (buf) {
  1444. return "\\frac{" + texify.go2(buf.p1) + "}{" + texify.go2(buf.p2) + "}";
  1445. },
  1446. 'overset': function (buf) {
  1447. return "\\overset{" + texify.go2(buf.p1) + "}{" + texify.go2(buf.p2) + "}";
  1448. },
  1449. 'underset': function (buf) {
  1450. return "\\underset{" + texify.go2(buf.p1) + "}{" + texify.go2(buf.p2) + "}";
  1451. },
  1452. 'underbrace': function (buf) {
  1453. return "\\underbrace{" + texify.go2(buf.p1) + "}_{" + texify.go2(buf.p2) + "}";
  1454. },
  1455. 'color': function (buf) {
  1456. return "{\\color{" + buf.color1 + "}{" + texify.go2(buf.color2) + "}}";
  1457. },
  1458. 'color0': function (buf) {
  1459. return "\\color{" + buf.color + "}";
  1460. },
  1461. 'arrow': function (buf) {
  1462. buf.rd = texify.go2(buf.rd);
  1463. buf.rq = texify.go2(buf.rq);
  1464. var arrow = texify.arrows[buf.r];
  1465. if (buf.rd || buf.rq) {
  1466. if (buf.r === "<=>" || buf.r === "<=>>" || buf.r === "<<=>" || buf.r === "<-->") {
  1467. // arrows that cannot stretch correctly yet, https://github.com/mathjax/MathJax/issues/1491
  1468. arrow = "\\long"+arrow;
  1469. if (buf.rd) { arrow = "\\overset{"+buf.rd+"}{"+arrow+"}"; }
  1470. if (buf.rq) { arrow = "\\underset{\\lower7mu{"+buf.rq+"}}{"+arrow+"}"; }
  1471. arrow = " {}\\mathrel{"+arrow+"}{} ";
  1472. } else {
  1473. if (buf.rq) { arrow += "[{"+buf.rq+"}]"; }
  1474. arrow += "{"+buf.rd+"}";
  1475. arrow = " {}\\mathrel{\\x"+arrow+"}{} ";
  1476. }
  1477. } else {
  1478. arrow = " {}\\mathrel{\\long"+arrow+"}{} ";
  1479. }
  1480. return arrow;
  1481. },
  1482. 'operator': function (buf) { return texify.operators[buf.kind]; }
  1483. },
  1484. arrows: {
  1485. "->": "rightarrow",
  1486. "\u2192": "rightarrow",
  1487. "\u27F6": "rightarrow",
  1488. "<-": "leftarrow",
  1489. "<->": "leftrightarrow",
  1490. "<-->": "leftrightarrows",
  1491. "<=>": "rightleftharpoons",
  1492. "\u21CC": "rightleftharpoons",
  1493. "<=>>": "Rightleftharpoons",
  1494. "<<=>": "Leftrightharpoons"
  1495. },
  1496. bonds: {
  1497. "-": "{-}",
  1498. "1": "{-}",
  1499. "=": "{=}",
  1500. "2": "{=}",
  1501. "#": "{\\equiv}",
  1502. "3": "{\\equiv}",
  1503. "~": "{\\tripledash}",
  1504. "~-": "{\\rlap{\\lower.1em{-}}\\raise.1em{\\tripledash}}",
  1505. "~=": "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}",
  1506. "~--": "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}",
  1507. "-~-": "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{-}}\\tripledash}",
  1508. "...": "{{\\cdot}{\\cdot}{\\cdot}}",
  1509. "....": "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}",
  1510. "->": "{\\rightarrow}",
  1511. "<-": "{\\leftarrow}",
  1512. "<": "{<}",
  1513. ">": "{>}"
  1514. },
  1515. entities: {
  1516. 'space': " ",
  1517. 'entitySkip': "~",
  1518. 'pu-space-1': "~",
  1519. 'pu-space-2': "\\mkern3mu ",
  1520. '1000 separator': "\\mkern2mu ",
  1521. 'commaDecimal': "{,}",
  1522. 'comma enumeration L': "{{0}}\\mkern6mu ",
  1523. 'comma enumeration M': "{{0}}\\mkern3mu ",
  1524. 'comma enumeration S': "{{0}}\\mkern1mu ",
  1525. 'hyphen': "\\text{-}",
  1526. 'addition compound': "\\,{\\cdot}\\,",
  1527. 'electron dot': "\\mkern1mu \\bullet\\mkern1mu ",
  1528. 'KV x': "{\\times}",
  1529. 'prime': "\\prime ",
  1530. 'cdot': "\\cdot ",
  1531. 'tight cdot': "\\mkern1mu{\\cdot}\\mkern1mu ",
  1532. 'times': "\\times ",
  1533. 'circa': "{\\sim}",
  1534. '^': "uparrow",
  1535. 'v': "downarrow",
  1536. 'ellipsis': "\\ldots ",
  1537. '/': "/",
  1538. ' / ': "\\,/\\,",
  1539. '1st-level escape': "{0} " // &, \\\\, \\hline
  1540. },
  1541. operators: {
  1542. "+": " {}+{} ",
  1543. "-": " {}-{} ",
  1544. "=": " {}={} ",
  1545. "<": " {}<{} ",
  1546. ">": " {}>{} ",
  1547. "<<": " {}\\ll{} ",
  1548. ">>": " {}\\gg{} ",
  1549. "\\pm": " {}\\pm{} ",
  1550. "\\approx": " {}\\approx{} ",
  1551. "$\\approx$": " {}\\approx{} ",
  1552. "v": " \\downarrow{} ",
  1553. "(v)": " \\downarrow{} ",
  1554. "^": " \\uparrow{} ",
  1555. "(^)": " \\uparrow{} "
  1556. },
  1557. go: function (input, isInner) {
  1558. if (!input) { return input; }
  1559. var res = "";
  1560. var cee = false;
  1561. for (var i=0; i<input.length; i++) {
  1562. var inputi = input[i];
  1563. if (typeof inputi === "string") {
  1564. res += inputi;
  1565. } else if (this.types[inputi.type]) {
  1566. res += this.types[inputi.type](inputi);
  1567. } else if (this.entities[inputi.type]) {
  1568. var a = this.entities[inputi.type];
  1569. a = a.replace("{0}", inputi.p1 || "");
  1570. a = a.replace("{1}", inputi.p2 || "");
  1571. res += a;
  1572. if (inputi.type === '1st-level escape') { cee = true; }
  1573. } else {
  1574. throw ["MhchemBugT", "mhchem bug T. Please report."]; // Missing texify rule or unknown MhchemParser output
  1575. }
  1576. }
  1577. if (!isInner && !cee) {
  1578. res = "{" + res + "}";
  1579. }
  1580. return res;
  1581. },
  1582. go2: function(input) {
  1583. return this.go(input, true);
  1584. }
  1585. };
  1586. MathJax.Extension["TeX/mhchem"].CE = CE;
  1587. /***************************************************************************/
  1588. TEX.Definitions.Add({
  1589. macros: {
  1590. //
  1591. // Set up the macros for chemistry
  1592. //
  1593. ce: "CE",
  1594. pu: "PU",
  1595. //
  1596. // Make these load AMSmath package (redefined below when loaded)
  1597. //
  1598. xleftrightarrow: ["Extension","AMSmath"],
  1599. xrightleftharpoons: ["Extension","AMSmath"],
  1600. xRightleftharpoons: ["Extension","AMSmath"],
  1601. xLeftrightharpoons: ["Extension","AMSmath"],
  1602. // FIXME: These don't work well in FF NativeMML mode
  1603. longrightleftharpoons: ["Macro","\\stackrel{\\textstyle{-}\\!\\!{\\rightharpoonup}}{\\smash{{\\leftharpoondown}\\!\\!{-}}}"],
  1604. longRightleftharpoons: ["Macro","\\stackrel{\\textstyle{-}\\!\\!{\\rightharpoonup}}{\\smash{\\leftharpoondown}}"],
  1605. longLeftrightharpoons: ["Macro","\\stackrel{\\textstyle\\vphantom{{-}}{\\rightharpoonup}}{\\smash{{\\leftharpoondown}\\!\\!{-}}}"],
  1606. longleftrightarrows: ["Macro","\\stackrel{\\longrightarrow}{\\smash{\\longleftarrow}\\Rule{0px}{.25em}{0px}}"],
  1607. //
  1608. // Needed for \bond for the ~ forms
  1609. // Not perfectly aligned when zoomed in, but on 100%
  1610. //
  1611. tripledash: ["Macro","\\vphantom{-}\\raise2mu{\\kern2mu\\tiny\\text{-}\\kern1mu\\text{-}\\kern1mu\\text{-}\\kern2mu}"]
  1612. },
  1613. },null,true);
  1614. if (!MathJax.Extension["TeX/AMSmath"]) {
  1615. TEX.Definitions.Add({
  1616. macros: {
  1617. xrightarrow: ["Extension","AMSmath"],
  1618. xleftarrow: ["Extension","AMSmath"]
  1619. }
  1620. },null,true);
  1621. }
  1622. //
  1623. // These arrows need to wait until AMSmath is loaded
  1624. //
  1625. MathJax.Hub.Register.StartupHook("TeX AMSmath Ready",function () {
  1626. TEX.Definitions.Add({
  1627. macros: {
  1628. //
  1629. // Some of these are hacks for now
  1630. //
  1631. xleftrightarrow: ["xArrow",0x2194,6,6],
  1632. xrightleftharpoons: ["xArrow",0x21CC,5,7], // FIXME: doesn't stretch in HTML-CSS output
  1633. xRightleftharpoons: ["xArrow",0x21CC,5,7], // FIXME: how should this be handled?
  1634. xLeftrightharpoons: ["xArrow",0x21CC,5,7]
  1635. }
  1636. },null,true);
  1637. });
  1638. TEX.Parse.Augment({
  1639. //
  1640. // Implements \ce and friends
  1641. //
  1642. CE: function (name) {
  1643. var arg = this.GetArgument(name);
  1644. var tex = CE(arg).Parse();
  1645. this.string = tex + this.string.substr(this.i); this.i = 0;
  1646. },
  1647. PU: function (name) {
  1648. var arg = this.GetArgument(name);
  1649. var tex = CE(arg).Parse('pu');
  1650. this.string = tex + this.string.substr(this.i); this.i = 0;
  1651. }
  1652. });
  1653. //
  1654. // Indicate that the extension is ready
  1655. //
  1656. MathJax.Hub.Startup.signal.Post("TeX mhchem Ready");
  1657. });
  1658. MathJax.Ajax.loadComplete("[mhchem]/mhchem.js");