Merge branch 'pryor'
[lambda.git] / jsMath / plugins / mimeTeX.js
1 /*
2  *  mimeTeX.js
3  *  
4  *  Part of the jsMath package for mathematics on the web.
5  *
6  *  This file makes jsMath more compatible with the mimeTeX program.
7  *  It does not make everything work, but it goes a long way.
8  *
9  *  ---------------------------------------------------------------------
10  *
11  *  Copyright 2004-2006 by Davide P. Cervone
12  * 
13  *  Licensed under the Apache License, Version 2.0 (the "License");
14  *  you may not use this file except in compliance with the License.
15  *  You may obtain a copy of the License at
16  * 
17  *      http://www.apache.org/licenses/LICENSE-2.0
18  * 
19  *  Unless required by applicable law or agreed to in writing, software
20  *  distributed under the License is distributed on an "AS IS" BASIS,
21  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  *  See the License for the specific language governing permissions and
23  *  limitations under the License.
24  */
25
26
27 /*
28  *  Treat ~ as space
29  */
30 jsMath.Parser.prototype.nextIsSpace = function () {
31   return this.string.charAt(this.i) == ' ' ||
32          this.string.charAt(this.i) == '~';
33 }
34 jsMath.Parser.prototype.special['~'] = 'Space';
35
36 /*
37  *  Implement \[ ... \], \( ... \), etc.
38  */
39 jsMath.Macro('[','\\left[');  jsMath.Macro(']','\\right]');
40 jsMath.Macro('(','\\left(');  jsMath.Macro(')','\\right)');
41 jsMath.Macro('<','\\left<');  jsMath.Macro('>','\\right>');
42 // can't do \. in a reasonable way
43 jsMath.Parser.prototype.macros['|'] = ['HandleLR','|','|'];
44 jsMath.Parser.prototype.macros['='] = ['HandleLR','\\|','\\|'];
45
46 /*
47  *  Make non-standard \left{ and \right} work
48  */
49 jsMath.Parser.prototype.delimiter['}'] = [5,2,0x67,3,0x09];
50 jsMath.Parser.prototype.delimiter['{'] = [4,2,0x66,3,0x08];
51
52
53 /*
54  *  Immitate mimeTeX \big... and \Big... ops
55  */
56
57 // make the normal ones in text mode
58 jsMath.Macro('int','\\intop\\nolimits');
59 jsMath.Macro('oint','\\ointop\\nolimits');
60 jsMath.Macro('sum','\\sumop\\nolimits');
61 jsMath.Macro('prod','\\prodop\\nolimits');
62 jsMath.Macro('coprod','\\coprodop\\nolimits');
63
64 jsMath.Macro('bigint','\\bigintop\\nolimits'); 
65 jsMath.Macro('bigoint','\\bigointop\\nolimits');
66 jsMath.Macro('bigsum','\\bigsumop\\nolimits');
67 jsMath.Macro('bigprod','\\bigprodop\\nolimits');
68 jsMath.Macro('bigcoprod','\\bigcoprodop\\nolimits');
69
70 jsMath.Macro('Bigint','\\bigintop\\limits');
71 jsMath.Macro('Bigoint','\\bigointop\\limits');
72 jsMath.Macro('Bigsum','\\bigsumop\\limits');
73 jsMath.Macro('Bigprod','\\bigprodop\\limits');
74 jsMath.Macro('Bigcoprod','\\bigcoprod\\limits');
75
76 /*
77  *  The characters needed for the macros above
78  */
79 jsMath.Parser.prototype.mathchardef['coprodop'] = [1,3,0x60];
80 jsMath.Parser.prototype.mathchardef['prodop']   = [1,3,0x51];
81 jsMath.Parser.prototype.mathchardef['sumop']    = [1,3,0x50];
82
83 jsMath.Parser.prototype.mathchardef['bigintop']    = [1,3,0x5A];
84 jsMath.Parser.prototype.mathchardef['bigointop']   = [1,3,0x49];
85 jsMath.Parser.prototype.mathchardef['bigcoprodop'] = [1,3,0x61];
86 jsMath.Parser.prototype.mathchardef['bigprodop']   = [1,3,0x59];
87 jsMath.Parser.prototype.mathchardef['bigsumop']    = [1,3,0x58];
88
89 /*
90  * Unlink the small versions so they don't enlarge in display mode
91  */
92 jsMath.TeX['cmex10'][0x48].n = null;
93 jsMath.TeX['cmex10'][0x50].n = null;
94 jsMath.TeX['cmex10'][0x51].n = null;
95 jsMath.TeX['cmex10'][0x52].n = null;
96 jsMath.TeX['cmex10'][0x60].n = null;
97
98
99 /*
100  *  Some other missing items
101  */
102 jsMath.Macro('/','{}'); // insert an empty box \/
103 jsMath.Macro('raisebox','\\raise #1px ',1); // convert to \raise
104 jsMath.Macro('hfill','\\quad ',1); // punt
105 jsMath.Macro('fbox','\\oldfbox{$#1$}',1); // do fbox in math mode
106
107 /*
108  *  These get new JavaScript routines
109  */
110 jsMath.Parser.prototype.macros['unitlength'] = 'unitlength';
111 jsMath.Parser.prototype.macros['hspace']     = 'hspace';
112 jsMath.Parser.prototype.macros['fs']         = 'fs';
113 jsMath.Parser.prototype.macros['oldfbox']    = 'FBox';
114
115 /*
116  *  Add some JavaScript functions to the parser
117  */
118 jsMath.Package(jsMath.Parser,{
119   
120   /*
121    *  Implement \left x ... \right x
122    */
123   HandleLR: function (name,data) {
124     var arg = this.GetUpto(name,name); if (this.error) return;
125     this.string = '\\left'+data[0]+arg+'\\right'+data[1];
126     this.i = 0;
127   },
128
129   /*
130    *  Hold the unit length in mlist.data
131    */
132   unitlength: function (name) {
133     var n = this.GetArgument(this.cmd+name); if (this.error) return;
134     if (!n.match(/^-?(\d+(\.\d*)?|\.\d+)$/)) {
135       this.Error("Argument for "+this.cmd+name+" must be a number");
136       return;
137     }
138     this.mlist.data['unitlength'] = n;
139   },
140
141   /*
142    *  Get the length (converted to ems) and multiply by the unit length
143    */
144   hspace: function (name) {
145     var w = this.GetArgument(this.cmd+name); if (this.error) return;
146     if (!w.match(/^-?(\d+(\.\d*)?|\.\d+)$/)) {
147       this.Error("Argument for "+this.cmd+name+" must be a number");
148       return;
149     }
150     w /= jsMath.em
151     if (this.mlist.data['unitlength']) {w *= this.mlist.data['unitlength']}
152     this.mlist.Add(jsMath.mItem.Space(w));
153   },
154   
155   /*
156    *  Implement \fs{...} for font-size changing
157    */
158   fs: function (name) {
159     var n = this.GetArgument(this.cmd+name); if (this.error) return;
160     if (!n.match(/^[-+]?\d+$/)) {
161       this.Error("Argument for "+this.cmd+name+" must be an integer");
162       return;
163     }
164     if (n.match(/[-+]/)) {n = n - 0; n += this.mlist.data.size}
165     this.mlist.data.size = n = Math.max(0,Math.min(9,n));
166     this.mlist.Add(new jsMath.mItem('size',{size: n}));
167   },
168
169   /*
170    *  Repalce the Array function by one that accepts an optional
171    *  parameter for the column types, and that handle's mimeTeX's
172    *  "preamble" format.
173    */
174   Array: function (name,delim) {
175     var columns = delim[2]; var cspacing = delim[3];
176     if (!columns && this.GetNext() == '{') {
177       columns = this.GetArgument(this.cmd+'begin{'+name+'}');
178       if (this.error) return;
179     } else {
180       columns = '';
181     }
182     columns = columns.replace(/[^clr]/g,'');
183     columns = columns.split('');
184     var data = this.mlist.data; var style = delim[5] || 'T';
185     var arg = this.GetEnd(name); if (this.error) return;
186     if (arg.match(/\$/)) {arg = arg.replace(/^([^$]*)\$/,''); columns = RegExp.$1}
187     var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,style);
188     parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = [];
189     parse.Parse(); if (parse.error) {this.Error(parse); return}
190     parse.HandleRow(name,1);  // be sure the last row is recorded
191     var box = jsMath.Box.Layout(data.size,parse.table,columns,cspacing,parse.rspacing,delim[4]||null);
192     // Add parentheses, if needed
193     if (delim[0] && delim[1]) {
194       var left  = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T');
195       var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T');
196       box = jsMath.Box.SetList([left,box,right],data.style,data.size);
197     }
198     this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box));
199   },
200
201   /*
202    *  Similarly for Matrix (used by \matrix and \array)
203    */
204   Matrix: function (name,delim) {
205     var data = this.mlist.data;
206     var arg = this.GetArgument(this.cmd+name); if (this.error) return;
207     if (arg.match(/\$/)) {arg = arg.replace(/^([^$]*)\$/,''); delim[2] = RegExp.$1}
208     var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,delim[5] || 'T');
209     parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = [];
210     parse.Parse(); if (parse.error) {this.Error(parse); return}
211     parse.HandleRow(name,1);  // be sure the last row is recorded
212     var box = jsMath.Box.Layout(data.size,parse.table,delim[2]||null,delim[3]||null,parse.rspacing,delim[4]||null);
213     // Add parentheses, if needed
214     if (delim[0] && delim[1]) {
215       var left  = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T');
216       var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T');
217       box = jsMath.Box.SetList([left,box,right],data.style,data.size);
218     }
219     this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box));
220   }
221 });