123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
- /* vim: set ts=2 et sw=2 tw=80: */
- /*************************************************************
- *
- * MathJax/extensions/TeX/mhchem.js
- *
- * Implements the \ce command for handling chemical formulas
- * from the mhchem LaTeX package.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (c) 2011-2017 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- //
- // Don't replace [Contrib]/mhchem if it is already loaded
- //
- if (MathJax.Extension["TeX/mhchem"]) {
- MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/mhchem.js");
- } else {
-
- MathJax.Extension["TeX/mhchem"] = {
- version: "2.7.2",
- config: MathJax.Hub.CombineConfig("TeX.mhchem",{
- legacy: true
- })
- };
- //
- // Load [mhchem]/mhchem.js if not configured for legacy vesion
- //
- if (!MathJax.Extension["TeX/mhchem"].config.legacy) {
- if (!MathJax.Ajax.config.path.mhchem) {
- MathJax.Ajax.config.path.mhchem = MathJax.Hub.config.root + "/extensions/TeX/mhchem3";
- }
- MathJax.Callback.Queue(
- ["Require",MathJax.Ajax,"[mhchem]/mhchem.js"],
- ["loadComplete",MathJax.Ajax,"[MathJax]/extensions/TeX/mhchem.js"]
- );
- } else {
- MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
-
- var TEX = MathJax.InputJax.TeX;
-
- /*
- * This is the main class for handing the \ce and related commands.
- * Its main method is Parse() which takes the argument to \ce and
- * returns the corresponding TeX string.
- */
- var CE = MathJax.Object.Subclass({
- string: "", // the \ce string being parsed
- i: 0, // the current position in the string
- tex: "", // the partially processed TeX result
- TEX: "", // the full TeX result
- atom: false, // last processed token is an atom
- sup: "", // pending superscript
- sub: "", // pending subscript
- presup: "", // pending pre-superscript
- presub: "", // pending pre-subscript
-
- //
- // Store the string when a CE object is created
- //
- Init: function (string) {this.string = string},
-
- //
- // These are the special characters and the methods that
- // handle them. All others are passed through verbatim.
- //
- ParseTable: {
- '-': "Minus",
- '+': "Plus",
- '(': "Open",
- ')': "Close",
- '[': "Open",
- ']': "Close",
- '<': "Less",
- '^': "Superscript",
- '_': "Subscript",
- '*': "Dot",
- '.': "Dot",
- '=': "Equal",
- '#': "Pound",
- '$': "Math",
- '\\': "Macro",
- ' ': "Space"
- },
- //
- // Basic arrow names for reactions
- //
- Arrows: {
- '->': "rightarrow",
- '<-': "leftarrow",
- '<->': "leftrightarrow",
- '<=>': "rightleftharpoons",
- '<=>>': "Rightleftharpoons",
- '<<=>': "Leftrightharpoons",
- '^': "uparrow",
- 'v': "downarrow"
- },
-
- //
- // Implementations for the various bonds
- // (the ~ ones are hacks that don't work well in NativeMML)
- //
- Bonds: {
- '-': "-",
- '=': "=",
- '#': "\\equiv",
- '~': "\\tripledash",
- '~-': "\\begin{CEstack}{}\\tripledash\\\\-\\end{CEstack}",
- '~=': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
- '~--': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
- '-~-': "\\raise2mu{\\begin{CEstack}{}-\\\\\\tripledash\\\\-\\end{CEstack}}",
- '...': "{\\cdot}{\\cdot}{\\cdot}",
- '....': "{\\cdot}{\\cdot}{\\cdot}{\\cdot}",
- '->': "\\rightarrow",
- '<-': "\\leftarrow",
- '??': "\\text{??}" // unknown bond
- },
- //
- // This converts the CE string to a TeX string.
- // It loops through the string and calls the proper
- // method depending on the ccurrent character.
- //
- Parse: function () {
- this.tex = ""; this.atom = false;
- while (this.i < this.string.length) {
- var c = this.string.charAt(this.i);
- if (c.match(/[a-z]/i)) {this.ParseLetter()}
- else if (c.match(/[0-9]/)) {this.ParseNumber()}
- else {this["Parse"+(this.ParseTable[c]||"Other")](c)}
- }
- this.FinishAtom(true);
- return this.TEX;
- },
-
- //
- // Make an atom name or a down arrow
- //
- ParseLetter: function () {
- this.FinishAtom();
- if (this.Match(/^v( |$)/)) {
- this.tex += "{\\"+this.Arrows["v"]+"}";
- } else {
- this.tex += "\\text{"+this.Match(/^[a-z]+/i)+"}";
- this.atom = true;
- }
- },
-
- //
- // Make a number or fraction preceeding an atom,
- // or a subscript for an atom.
- //
- ParseNumber: function () {
- var n = this.Match(/^\d+/);
- if (this.atom && !this.sub) {
- this.sub = n;
- } else {
- this.FinishAtom();
- var match = this.Match(/^\/\d+/);
- if (match) {
- var frac = "\\frac{"+n+"}{"+match.substr(1)+"}";
- this.tex += "\\mathchoice{\\textstyle"+frac+"}{"+frac+"}{"+frac+"}{"+frac+"}";
- } else {
- this.tex += n;
- if (this.i < this.string.length) {this.tex += "\\,"}
- }
- }
- },
-
- //
- // Make a superscript minus, or an arrow, or a single bond.
- //
- ParseMinus: function (c) {
- if (this.atom && (this.i === this.string.length-1 || this.string.charAt(this.i+1) === " ")) {
- this.sup += c;
- } else {
- this.FinishAtom();
- if (this.string.substr(this.i,2) === "->") {this.i += 2; this.AddArrow("->"); return}
- else {this.tex += "{-}"}
- }
- this.i++;
- },
- //
- // Make a superscript plus, or pass it through
- //
- ParsePlus: function (c) {
- if (this.atom) {this.sup += c} else {this.FinishAtom(); this.tex += c}
- this.i++;
- },
-
- //
- // Handle dots and double or triple bonds
- //
- ParseDot: function (c) {this.FinishAtom(); this.tex += "\\cdot "; this.i++},
- ParseEqual: function (c) {this.FinishAtom(); this.tex += "{=}"; this.i++},
- ParsePound: function (c) {this.FinishAtom(); this.tex += "{\\equiv}"; this.i++},
- //
- // Look for (v) or (^), or pass it through
- //
- ParseOpen: function (c) {
- this.FinishAtom();
- var match = this.Match(/^\([v^]\)/);
- if (match) {this.tex += "{\\"+this.Arrows[match.charAt(1)]+"}"}
- else {this.tex += "{"+c; this.i++}
- },
- //
- // Allow ) and ] to get super- and subscripts
- //
- ParseClose: function (c) {this.FinishAtom(); this.atom = true; this.tex += c+"}"; this.i++},
- //
- // Make the proper arrow
- //
- ParseLess: function (c) {
- this.FinishAtom();
- var arrow = this.Match(/^(<->?|<=>>?|<<=>)/);
- if (!arrow) {this.tex += c; this.i++} else {this.AddArrow(arrow)}
- },
- //
- // Look for a superscript, or an up arrow
- //
- ParseSuperscript: function (c) {
- c = this.string.charAt(++this.i);
- if (c === "{") {
- this.i++; var m = this.Find("}");
- if (m === "-.") {this.sup += "{-}{\\cdot}"}
- else if (m) {this.sup += CE(m).Parse().replace(/^\{-\}/,"-")}
- } else if (c === " " || c === "") {
- this.tex += "{\\"+this.Arrows["^"]+"}"; this.i++;
- } else {
- var n = this.Match(/^(\d+|-\.)/);
- if (n) {this.sup += n}
- }
- },
- //
- // Look for subscripts
- //
- ParseSubscript: function (c) {
- if (this.string.charAt(++this.i) == "{") {
- this.i++; this.sub += CE(this.Find("}")).Parse().replace(/^\{-\}/,"-");
- } else {
- var n = this.Match(/^\d+/);
- if (n) {this.sub += n}
- }
- },
- //
- // Look for raw TeX code to include
- //
- ParseMath: function (c) {
- this.FinishAtom();
- this.i++; this.tex += this.Find(c);
- },
-
- //
- // Look for specific macros for bonds
- // and allow \} to have subscripts
- //
- ParseMacro: function (c) {
- this.FinishAtom();
- this.i++; var match = this.Match(/^([a-z]+|.)/i)||" ";
- if (match === "sbond") {this.tex += "{-}"}
- else if (match === "dbond") {this.tex += "{=}"}
- else if (match === "tbond") {this.tex += "{\\equiv}"}
- else if (match === "bond") {
- var bond = (this.Match(/^\{.*?\}/)||"");
- bond = bond.substr(1,bond.length-2);
- this.tex += "{"+(this.Bonds[bond]||"\\text{??}")+"}";
- }
- else if (match === "{") {this.tex += "{\\{"}
- else if (match === "}") {this.tex += "\\}}"; this.atom = true}
- else {this.tex += c+match}
- },
-
- //
- // Ignore spaces
- //
- ParseSpace: function (c) {this.FinishAtom(); this.i++},
-
- //
- // Pass anything else on verbatim
- //
- ParseOther: function (c) {this.FinishAtom(); this.tex += c; this.i++},
- //
- // Process an arrow (looking for brackets for above and below)
- //
- AddArrow: function (arrow) {
- var c = this.Match(/^[CT]\[/);
- if (c) {this.i--; c = c.charAt(0)}
- var above = this.GetBracket(c), below = this.GetBracket(c);
- arrow = this.Arrows[arrow];
- if (above || below) {
- if (below) {arrow += "["+below+"]"}
- arrow += "{"+above+"}";
- arrow = "\\mathrel{\\x"+arrow+"}";
- } else {
- arrow = "\\long"+arrow+" ";
- }
- this.tex += arrow;
- },
- //
- // Handle the super and subscripts for an atom
- //
- FinishAtom: function (force) {
- if (this.sup || this.sub || this.presup || this.presub) {
- if (!force && !this.atom) {
- if (this.tex === "" && !this.sup && !this.sub) return;
- if (!this.presup && !this.presub &&
- (this.tex === "" || this.tex === "{" ||
- (this.tex === "}" && this.TEX.substr(-1) === "{"))) {
- this.presup = this.sup, this.presub = this.sub; // save for later
- this.sub = this.sup = "";
- this.TEX += this.tex; this.tex = "";
- return;
- }
- }
- if (this.sub && !this.sup) {this.sup = "\\Space{0pt}{0pt}{.2em}"} // forces subscripts to align properly
- if ((this.presup || this.presub) && this.tex !== "{") {
- if (!this.presup && !this.sup) {this.presup = "\\Space{0pt}{0pt}{.2em}"}
- this.tex = "\\CEprescripts{"+(this.presub||"\\CEnone")+"}{"+(this.presup||"\\CEnone")+"}"
- + "{"+(this.tex !== "}" ? this.tex : "")+"}"
- + "{"+(this.sub||"\\CEnone")+"}{"+(this.sup||"\\CEnone")+"}"
- + (this.tex === "}" ? "}" : "");
- this.presub = this.presup = "";
- } else {
- if (this.sup) this.tex += "^{"+this.sup+"}";
- if (this.sub) this.tex += "_{"+this.sub+"}";
- }
- this.sup = this.sub = "";
- }
- this.TEX += this.tex; this.tex = "";
- this.atom = false;
- },
-
- //
- // Find a bracket group and handle C and T prefixes
- //
- GetBracket: function (c) {
- if (this.string.charAt(this.i) !== "[") {return ""}
- this.i++; var bracket = this.Find("]");
- if (c === "C") {bracket = "\\ce{"+bracket+"}"} else
- if (c === "T") {
- if (!bracket.match(/^\{.*\}$/)) {bracket = "{"+bracket+"}"}
- bracket = "\\text"+bracket;
- };
- return bracket;
- },
- //
- // Check if the string matches a regular expression
- // and move past it if so, returning the match
- //
- Match: function (regex) {
- var match = regex.exec(this.string.substr(this.i));
- if (match) {match = match[0]; this.i += match.length}
- return match;
- },
-
- //
- // Find a particular character, skipping over braced groups
- //
- Find: function (c) {
- var m = this.string.length, i = this.i, braces = 0;
- while (this.i < m) {
- var C = this.string.charAt(this.i++);
- if (C === c && braces === 0) {return this.string.substr(i,this.i-i-1)}
- if (C === "{") {braces++} else
- if (C === "}") {
- if (braces) {braces--}
- else {
- TEX.Error(["ExtraCloseMissingOpen","Extra close brace or missing open brace"])
- }
- }
- }
- if (braces) {TEX.Error(["MissingCloseBrace","Missing close brace"])}
- TEX.Error(["NoClosingChar","Can't find closing %1",c]);
- }
-
- });
-
- MathJax.Extension["TeX/mhchem"].CE = CE;
-
- /***************************************************************************/
-
- TEX.Definitions.Add({
- macros: {
- //
- // Set up the macros for chemistry
- //
- ce: 'CE',
- cf: 'CE',
- cee: 'CE',
-
- //
- // Make these load AMSmath package (redefined below when loaded)
- //
- xleftrightarrow: ['Extension','AMSmath'],
- xrightleftharpoons: ['Extension','AMSmath'],
- xRightleftharpoons: ['Extension','AMSmath'],
- xLeftrightharpoons: ['Extension','AMSmath'],
- // FIXME: These don't work well in FF NativeMML mode
- longrightleftharpoons: ["Macro","\\stackrel{\\textstyle{{-}\\!\\!{\\rightharpoonup}}}{\\smash{{\\leftharpoondown}\\!\\!{-}}}"],
- longRightleftharpoons: ["Macro","\\stackrel{\\textstyle{-}\\!\\!{\\rightharpoonup}}{\\small\\smash\\leftharpoondown}"],
- longLeftrightharpoons: ["Macro","\\stackrel{\\rightharpoonup}{{{\\leftharpoondown}\\!\\!\\textstyle{-}}}"],
- //
- // Add \hyphen used in some mhchem examples
- //
- hyphen: ["Macro","\\text{-}"],
-
- //
- // Handle prescripts and none
- //
- CEprescripts: "CEprescripts",
- CEnone: "CEnone",
- //
- // Needed for \bond for the ~ forms
- //
- tripledash: ["Macro","\\raise3mu{\\tiny\\text{-}\\kern2mu\\text{-}\\kern2mu\\text{-}}"]
- },
-
- //
- // Needed for \bond for the ~ forms
- //
- environment: {
- CEstack: ['Array',null,null,null,'r',null,"0.001em",'T',1]
- }
- },null,true);
-
- if (!MathJax.Extension["TeX/AMSmath"]) {
- TEX.Definitions.Add({
- macros: {
- xrightarrow: ['Extension','AMSmath'],
- xleftarrow: ['Extension','AMSmath']
- }
- },null,true);
- }
-
- //
- // These arrows need to wait until AMSmath is loaded
- //
- MathJax.Hub.Register.StartupHook("TeX AMSmath Ready",function () {
- TEX.Definitions.Add({
- macros: {
- //
- // Some of these are hacks for now
- //
- xleftrightarrow: ['xArrow',0x2194,6,6],
- xrightleftharpoons: ['xArrow',0x21CC,5,7], // FIXME: doesn't stretch in HTML-CSS output
- xRightleftharpoons: ['xArrow',0x21CC,5,7], // FIXME: how should this be handled?
- xLeftrightharpoons: ['xArrow',0x21CC,5,7]
- }
- },null,true);
- });
- TEX.Parse.Augment({
- //
- // Implements \ce and friends
- //
- CE: function (name) {
- var arg = this.GetArgument(name);
- var tex = CE(arg).Parse();
- this.string = tex + this.string.substr(this.i); this.i = 0;
- },
-
- //
- // Implements \CEprescripts{presub}{presup}{base}{sub}{sup}
- //
- CEprescripts: function (name) {
- var presub = this.ParseArg(name),
- presup = this.ParseArg(name),
- base = this.ParseArg(name),
- sub = this.ParseArg(name),
- sup = this.ParseArg(name);
- var MML = MathJax.ElementJax.mml;
- this.Push(MML.mmultiscripts(base,sub,sup,MML.mprescripts(),presub,presup));
- },
- CEnone: function (name) {
- this.Push(MathJax.ElementJax.mml.none());
- }
-
- });
-
- //
- // Indicate that the extension is ready
- //
- MathJax.Hub.Startup.signal.Post("TeX mhchem Ready");
- });
- MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/mhchem.js");
- }}
|