mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-10-30 18:22:25 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			495 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			495 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- 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-2015 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.
 | |
|  */
 | |
| 
 | |
| MathJax.Extension["TeX/mhchem"] = {
 | |
|   version: "2.6.0"
 | |
| };
 | |
| 
 | |
| 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");
 |