Merge branch 'pryor'
[lambda.git] / jsMath / extensions / AMSmath.js
1 /*
2  *  extensions/AMSmath.js
3  *  
4  *  Part of the jsMath package for mathematics on the web.
5  *
6  *  This file defines most of the macros and environments from
7  *  the amsmath LaTeX package.  You can activate it by calling
8  *  
9  *    jsMath.Extension.Require('AMSmath');
10  *  
11  *  once jsMath.js has been loaded, or by adding "extensions/AMSmath.js"
12  *  to the loadFiles array in jsMath/easy/load.js.
13  *  
14  *  You may wish to load AMSsymbols.js as well, but note that it
15  *  requires the extra msam10 and msb10 fonts that you will have 
16  *  to install on your server first.
17  *  
18  *  ---------------------------------------------------------------------
19  *
20  *  Copyright 2007 by Davide P. Cervone
21  * 
22  *  Licensed under the Apache License, Version 2.0 (the "License");
23  *  you may not use this file except in compliance with the License.
24  *  You may obtain a copy of the License at
25  * 
26  *      http://www.apache.org/licenses/LICENSE-2.0
27  * 
28  *  Unless required by applicable law or agreed to in writing, software
29  *  distributed under the License is distributed on an "AS IS" BASIS,
30  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  *  See the License for the specific language governing permissions and
32  *  limitations under the License.
33  */
34
35 /********************************************************************/
36
37 jsMath.Extension.Require("moreArrows");
38
39 jsMath.Package(jsMath.Parser,{
40   macros: {
41     intI:       ['Macro','\\mathchoice{\\!}{}{}{}\\!\\!\\int'],
42     iint:       ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI}'],
43     iiint:      ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI\\intI}'],
44     iiiint:     ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI\\intI\\intI}'],
45     idotsint:   ['Macro','\\!\\!\\mathop{\\,\\,\\int\\cdots\\int}'],
46     
47     dddot:      ['Macro','\\mathop{#1}\\limits^{\\textstyle ...}',1],
48     ddddot:     ['Macro','\\mathop{#1}\\limits^{\\textstyle ....}',1],
49     
50     sideset:    ['Macro','\\mathop{\\rlap{\\phantom{#3}}}#1\\!{#3}#2',3],
51     stackrel:   ['Macro','\\mathrel{\\mathop{#2}\\limits^{#1}}',2],
52     
53     boxed:      ['Macro','\\fbox{$\\displaystyle{#1}$}',1],
54     
55     tag:        'HandleTag',
56     notag:      ['Macro',''],
57     
58     substack:   ['Macro','\\begin{subarray}{c}#1\\end{subarray}',1],
59     
60     varliminf:  ['Macro','\\mathop{\\underline{\\raise1.5pt{\\rule{0pt}{.6em}{0pt}\\smash{\\lower1.5pt{\\rm lim}}}}}'],
61     varlimsup:  ['Macro','\\mathop{\\overline{\\rule{0pt}{.6em}{0pt}\\smash{\\rm lim}}}'],
62     varinjlim:  ['Macro','\\mathop{\\underrightarrow{\\rm lim}}'],
63     varprojlim: ['Macro','\\mathop{\\underleftarrow{\\rm lim}}'],
64     
65     DeclareMathOperator: 'HandleDeclareOp',
66     operatorname: 'HandleOperatorName',
67
68     genfrac:    'Genfrac',
69     frac:       ['Genfrac',"","","",""],
70     tfrac:      ['Genfrac',"","","","1"],
71     dfrac:      ['Genfrac',"","","","0"],
72     binom:      ['Genfrac',"(",")","0pt",""],
73     tbinom:     ['Genfrac',"(",")","0pt","1"],
74     dbinom:     ['Genfrac',"(",")","0pt","0"],
75     
76     cfrac:      'CFrac',
77     
78     shoveleft:  ['HandleShove','left'],
79     shoveright: ['HandleShove','right']
80   },
81   
82   environments: {
83     align:         ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'],
84     'align*':      ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'],
85     aligned:       ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'],
86     multline:     'Multline',
87     'multline*':  'Multline',
88     split:         ['Array',null,null,'rl',[5/18],1,'D'],
89     gather:        ['Array',null,null,'c',null,1,'D'],
90     'gather*':     ['Array',null,null,'c',null,1,'D'],
91     gathered:      ['Array',null,null,'c',null,1,'D'],
92     subarray:      ['Array',null,null,null,[0,0,0,0],1,'S',0,.25],
93     smallmatrix:   ['Array',null,null,'cccccccccc',[1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3],1,'S',0]
94   },
95   
96   delimiter: {
97     '\\lvert':     [4,2,0x6A,3,0x0C],
98     '\\rvert':     [5,2,0x6A,3,0x0C],
99     '\\lVert':     [4,2,0x6B,3,0x0D],
100     '\\rVert':     [5,2,0x6B,3,0x0D]
101   },
102
103   /*
104    *  Ignore the tag for now
105    */
106   HandleTag: function (name) {
107     var arg = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return;
108     if (arg == "*") this.GetArgument(this.cmd+name);
109   },
110   
111   /*
112    *  Handle \DeclareMathOperator
113    */
114   HandleDeclareOp: function (name) {
115     var limits = "";
116     var cs = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return;
117     if (cs == "*") {
118       limits = "\\limits";
119       cs = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return;
120     }
121     if (cs.charAt(0) == "\\") {cs = cs.substr(1)}
122     var op = this.GetArgument(this.cmd+name); if (this.error) return;
123     op = op.replace(/\*/g,'\\char{cmr10}{0x2A}').replace(/-/g,'\\char{cmr10}{0x2D}');
124     jsMath.Parser.prototype.macros[cs] = ['Macro','\\mathop{\\rm '+op+'}'+limits];
125   },
126   
127   HandleOperatorName: function (name) {
128     var limits = "\\nolimits";
129     var op = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return;
130     if (op == "*") {
131       limits = "\\limits";
132       op = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return;
133     }
134     op = op.replace(/\*/g,'\\char{cmr10}{0x2A}').replace(/-/g,'\\char{cmr10}{0x2D}');
135     this.string = '\\mathop{\\rm '+op+'}'+limits+this.string.slice(this.i);
136     this.i = 0;
137   },
138   
139   /*
140    *  Record presence of \shoveleft and \shoveright
141    */
142   HandleShove: function (name,data) {
143     if (this.mlist.data.entry == null) {this.mlist.data.entry = {}}
144     this.mlist.data.entry.shove = data[0];
145   },
146   
147   /*
148    *  Handle \cfrac
149    */
150   CFrac: function (name) {
151     var lr = this.GetBrackets(this.cmd+name); if (this.error) return;
152     var num = this.GetArgument(this.cmd+name); if (this.error) return;
153     var den = this.GetArgument(this.cmd+name); if (this.error) return;
154     
155     num = this.Process('\\strut\\textstyle{'+num+'}'); if (this.error) return;
156     den = this.Process('\\strut\\textstyle{'+den+'}'); if (this.error) return;
157     var data = this.mlist.data;
158     var TeX = jsMath.Typeset.TeX(data.style,data.size);
159     
160     if (lr != "") {
161       if (lr != 'l' && lr != 'r') {this.Error("Illegal alignment specified in "+this.cmd+name); return}
162       num = jsMath.Box.Set(num,data.style,data.size);
163       den = jsMath.Box.Set(den,data.style,data.size);
164       if (num.w > den.w) {
165         if (lr == 'l') {den.html += jsMath.HTML.Spacer(num.w-den.w)}
166                   else {den.html = jsMath.HTML.Spacer(num.w-den.w) + den.html}
167         den.w = num.w;
168       } else if (num.w < den.w) {
169         if (lr == 'l') {num.html += jsMath.HTML.Spacer(den.w-num.w)}
170                   else {num.html = jsMath.HTML.Spacer(den.w-num.w) + num.html}
171         num.w = den.w;
172       }
173     }
174     
175     this.mlist.Add(jsMath.mItem.Fraction(name,num,den,TeX.default_rule_thickness));
176   },
177   
178   /*
179    *  Implement AMS generalized fraction
180    */
181   Genfrac: function (name,data) {
182     var left = data[0]; var right = data[1];
183     var thickness = data[2]; var style = data[3];
184     
185     if (left != null) {left = this.delimiter[left]} else
186       {left = this.GetDelimiterArg(this.cmd+name); if (this.error) return}
187     if (right != null) {right = this.delimiter[right]} else 
188       {right = this.GetDelimiterArg(this.cmd+name); if (this.error) return}
189     if (thickness == null) {thickness = this.GetArgument(this.cmd+name); if (this.error) return}
190     if (style == null) {style = this.GetArgument(this.cmd+name); if (this.error) return}
191
192     var num = this.ProcessArg(this.cmd+name); if (this.error) return;
193     var den = this.ProcessArg(this.cmd+name); if (this.error) return;
194     
195     if (left == "") {left = null}; if (right == "") {right = null}
196     if (thickness == "") {
197       var TeX =jsMath.Typeset.TeX(this.mlist.data.style,this.mlist.data.size);
198       thickness = TeX.default_rule_thickness;
199     } else {
200       thickness = this.ParseDimen(thickness,this.cmd+name,0,0);
201     }
202     
203     var frac = jsMath.mItem.Fraction(name,num,den,thickness,left,right);
204
205     if (style != "") {
206       style = (["D","T","S","SS"])[style];
207       if (style == null) {this.Error("Bad math style for "+this.cmd+name); return}
208       var mlist = new jsMath.mList([new jsMath.mItem('style',{style:style}),frac]);
209       this.mlist.Add(jsMath.mItem.Atom('inner',{type:'mlist',mlist: mlist}));
210     } else {
211       this.mlist.Add(frac);
212     }
213   },
214   
215   /*
216    *  Implements the multline environment
217    */
218   Multline: function (name,delim) {
219     var data = this.mlist.data;
220     var width = this.GetBrackets(this.cmd+'begin{'+name+'}'); if (this.error) return;
221     var arg = this.GetEnd(name); if (this.error) return;
222
223     var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,'D');
224     parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = [];
225     parse.Parse(); if (parse.error) {this.Error(parse); return}
226     parse.HandleRow(name,1);  // be sure the last row is recorded
227     
228     //
229     // check rows for extra columns and maximum width
230     // 
231     var i; var row; var W = 0;
232     for (i = 0; i < parse.table.length; i++) {
233       row = parse.table[i];
234       if (row.length > 1) {
235         this.Error("Rows can contain only one equation in '"+name+"' environment");
236         return;
237       }
238       if (row[0].w > W) {W = row[0].w}
239     }
240
241     //
242     //  Determine width of display
243     //
244     if (width == "") {width = W+2} else {
245       width = this.ParseDimen(width,name,0,0);
246       if (width < W) {width = W}
247     }
248     
249     //
250     //  Shove the top and bottom lines
251     //
252     if (parse.table.length > 1) {
253       parse.table[0][0].entry.shove = 'left';
254       row = parse.table[parse.table.length-1];
255       if (!row[0].entry.shove) {row[0].entry.shove = 'right'}
256     }
257     //
258     //  Adjust widths of shoved lines
259     //
260     for (i = 0; i < parse.table.length; i++) {
261       row = parse.table[i][0];
262       if (row.entry.shove && row.w < width) {
263         switch (row.entry.shove) {
264           case 'left':
265             row.html += jsMath.HTML.Spacer(width-row.w);
266             break;
267             
268           case 'right':
269             row.html = jsMath.HTML.Spacer(width-row.w)+row.html;
270             break;
271         }
272         row.w = width;
273       }
274     }
275     
276     //
277     //  Do the layout
278     //
279     var box = jsMath.Box.Layout(data.size,parse.table);
280     this.mlist.Add(jsMath.mItem.Atom('ord',box));
281   },
282
283   /*
284    *  Get a delimiter or empty argument
285    */
286   GetDelimiterArg: function (name) {
287     var c = this.trimSpaces(this.GetArgument(name)); if (this.error) return null;
288     if (c == "") return null;
289     if (this.delimiter[c]) return this.delimiter[c];
290     this.Error("Missing or unrecognized delimiter for "+name);
291     return null;
292   }
293 });