www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

mhchem.js (15398B)


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