added LaTeXMathML files
[lambda.git] / jsMath / uncompressed / jsMath.js
1 /*****************************************************************************
2  * 
3  *  jsMath: Mathematics on the Web
4  *  
5  *  This jsMath package makes it possible to display mathematics in HTML pages
6  *  that are viewable by a wide range of browsers on both the Mac and the IBM PC,
7  *  including browsers that don't process MathML.  See
8  *  
9  *            http://www.math.union.edu/locate/jsMath
10  *
11  *  for the latest version, and for documentation on how to use jsMath.
12  *
13  *  Copyright 2004-2010 by Davide P. Cervone
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
29 /*
30  *  Prevent running everything again if this file is loaded twice
31  */
32 if (!window.jsMath || !window.jsMath.loaded) {
33
34 var jsMath_old = window.jsMath;  // save user customizations
35
36 //
37 // debugging routine
38 // 
39 /* 
40  * function ShowObject (obj,spaces) {
41  *   var s = ''; if (!spaces) {spaces = ""}
42  *   for (var i in obj) {
43  *     if (obj[i] != null) {
44  *       if (typeof(obj[i]) == "object") {
45  *         s += spaces + i + ": {\n"
46  *           + ShowObject(obj[i],spaces + '  ')
47  *           + spaces + "}\n";
48  *       } else if (typeof(obj[i]) != "function") {
49  *         s += spaces + i + ': ' + obj[i] + "\n";
50  *       }
51  *     }
52  *   }
53  *   return s;
54  * }
55  */
56
57 /***************************************************************************/
58 //
59 //  Check for DOM support
60 //
61 if (!document.getElementById || !document.childNodes || !document.createElement) {
62   alert('The mathematics on this page requires W3C DOM support in its JavaScript. '
63       + 'Unfortunately, your browser doesn\'t seem to have this.');
64 } else {
65
66 /***************************************************************************/
67
68 window.jsMath = {
69   
70   version: "3.6e",  // change this if you edit the file, but don't edit this file
71   
72   document: document,  // the document loading jsMath
73   window: window,      // the window of the of loading document
74   
75   platform: (navigator.platform.match(/Mac/) ? "mac" :
76              navigator.platform.match(/Win/) ? "pc" : "unix"),
77   
78   // Font sizes for \tiny, \small, etc. (must match styles below)
79   sizes: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249],
80
81   //
82   //  The styles needed for the TeX fonts and other jsMath elements
83   //
84   styles: {
85     '.math': {                  // unprocessed mathematics
86       'font-family': 'serif',
87       'font-style':  'normal',
88       'font-weight': 'normal'
89     },
90
91     '.typeset': {               // final typeset mathematics
92       'font-family': 'serif',
93       'font-style':  'normal',
94       'font-weight': 'normal',
95       'line-height': 'normal',
96       'text-indent': '0px',
97       'white-space': 'normal'
98     },
99     
100     '.typeset .normal': {       // \hbox contents style
101       'font-family': 'serif',
102       'font-style':  'normal',
103       'font-weight': 'normal'
104     },
105     
106     'div.typeset': {            // display mathematics
107       'text-align':  'center',
108        margin:       '1em 0px'
109     },
110     
111     'span.typeset': {           // in-line mathematics
112       'text-align':  'left'
113     },
114     
115     '.typeset span': {          // prevent outside CSS from setting these
116       'text-align':  'left',
117        border:       '0px',
118        margin:       '0px',
119        padding:      '0px'
120     },
121     
122     'a .typeset img, .typeset a img': {   // links in image mode
123        border: '0px',
124       'border-bottom': '1px solid blue;'
125     },
126
127     // Font sizes
128     '.typeset .size0': {'font-size': '50%'},  // tiny (\scriptscriptsize)
129     '.typeset .size1': {'font-size': '60%'},  //       (50% of \large for consistency)
130     '.typeset .size2': {'font-size': '70%'},  // scriptsize
131     '.typeset .size3': {'font-size': '85%'},  // small (70% of \large for consistency)
132     '.typeset .size4': {'font-size': '100%'}, // normalsize
133     '.typeset .size5': {'font-size': '120%'}, // large
134     '.typeset .size6': {'font-size': '144%'}, // Large
135     '.typeset .size7': {'font-size': '173%'}, // LARGE
136     '.typeset .size8': {'font-size': '207%'}, // huge
137     '.typeset .size9': {'font-size': '249%'}, // Huge
138   
139     // TeX fonts
140     '.typeset .cmr10':  {'font-family': 'jsMath-cmr10, serif'},
141     '.typeset .cmbx10': {'font-family': 'jsMath-cmbx10, jsMath-cmr10'},
142     '.typeset .cmti10': {'font-family': 'jsMath-cmti10, jsMath-cmr10'},
143     '.typeset .cmmi10': {'font-family': 'jsMath-cmmi10'},
144     '.typeset .cmsy10': {'font-family': 'jsMath-cmsy10'},
145     '.typeset .cmex10': {'font-family': 'jsMath-cmex10'},
146     
147     '.typeset .textit': {'font-family': 'serif', 'font-style': 'italic'},
148     '.typeset .textbf': {'font-family': 'serif', 'font-weight': 'bold'},
149     
150     '.typeset .link':   {'text-decoration': 'none'},  // links in mathematics
151
152     '.typeset .error':  {      // in-line error messages
153       'font-size':        '90%',
154       'font-style':       'italic',
155       'background-color': '#FFFFCC',
156        padding:           '1px',
157        border:            '1px solid #CC0000'
158     },
159
160     '.typeset .blank': {       // internal use
161       display:  'inline-block',
162       overflow: 'hidden',
163       border:   '0px none',
164       width:    '0px',
165       height:   '0px'
166     },
167     '.typeset .spacer': {      // internal use
168       display: 'inline-block'
169     },
170     
171     '#jsMath_hiddenSpan': {    // used for measuring BBoxes
172        visibility:    'hidden',
173        position:      'absolute',
174        top:           '0px',
175        left:          '0px',
176       'line-height':  'normal',
177       'text-indent':  '0px'
178     },
179
180     '#jsMath_message': {      // percentage complete message
181        position:   'fixed',
182        bottom:     '1px',
183        left:       '2px',
184       'background-color': '#E6E6E6',
185        border:     'solid 1px #959595',
186        margin:     '0px',
187        padding:    '1px 8px',
188       'z-index':   '102',
189        color:      'black',
190       'font-size': 'small',
191        width:      'auto'
192     },
193
194     '#jsMath_panel': {        // control panel
195        position:    'fixed',
196        bottom:      '1.75em',
197        right:       '1.5em',
198        padding:     '.8em 1.6em',
199       'background-color': '#DDDDDD',
200        border:      'outset 2px',
201       'z-index':    '103',
202        width:       'auto',
203        color:       'black',
204       'font-size':  '10pt',
205       'font-style': 'normal'
206     },
207     '#jsMath_panel .disabled': {color: '#888888'},   // disabled items in the panel
208     '#jsMath_panel .infoLink': {'font-size': '85%'}, // links to web pages
209
210     // avoid CSS polution from outside the panel
211     '#jsMath_panel *':  {
212       'font-size':   'inherit',
213       'font-style':  'inherit',
214       'font-family': 'inherit',
215       'line-height': 'normal'
216     },
217     '#jsMath_panel div':   {'background-color': 'inherit', color: 'inherit'},
218     '#jsMath_panel span':  {'background-color': 'inherit', color: 'inherit'},
219     '#jsMath_panel td': {
220        border: '0px', padding: '0px', margin: '0px',
221       'background-color': 'inherit', color: 'inherit'
222     },
223     '#jsMath_panel tr': {
224        border: '0px', padding: '0px', margin: '0px',
225       'background-color': 'inherit', color: 'inherit'
226     },
227     '#jsMath_panel table': {
228        border: '0px', padding: '0px', margin: '0px',
229       'background-color': 'inherit', color: 'inherit',
230        height: 'auto', width: 'auto'
231     },
232     
233     '#jsMath_button': {  // the jsMath floating button (to open control panel)
234        position:   'fixed',
235        bottom:     '1px',
236        right:      '2px',
237       'background-color': 'white',
238        border:     'solid 1px #959595',
239        margin:     '0px',
240        padding:    '0px 3px 1px 3px',
241       'z-index':   '102',
242        color:      'black',
243       'text-decoration':  'none',
244       'font-size': 'x-small',
245        width:      'auto',
246        cursor:     'hand'
247     },
248     '#jsMath_button *': {
249       padding: '0px', border: '0px', margin: '0px', 'line-height': 'normal',
250       'font-size': 'inherit', 'font-style': 'inherit', 'font-family': 'inherit'
251     },
252     
253     '#jsMath_global': {'font-style': 'italic'},  // 'global' in jsMath button
254
255     '#jsMath_noFont .message': {  // missing font message window
256       'text-align': 'center',
257        padding:     '.8em 1.6em',
258        border:      '3px solid #DD0000',
259       'background-color': '#FFF8F8',
260        color:       '#AA0000',
261       'font-size':  'small',
262        width:       'auto'
263     },
264     '#jsMath_noFont .link': {
265        padding:    '0px 5px 2px 5px',
266        border:     '2px outset',
267       'background-color': '#E8E8E8',
268        color:      'black',
269       'font-size': '80%',
270        width:      'auto',
271        cursor:     'hand'
272     },
273
274     '#jsMath_PrintWarning .message': {  // warning on print pages
275       'text-align': 'center',
276        padding: '.8em 1.6em',
277        border: '3px solid #DD0000',
278       'background-color': '#FFF8F8',
279        color: '#AA0000',
280       'font-size': 'x-small',
281        width: 'auto'
282     },
283
284     '@media print': {
285       '#jsMath_button': {display: 'none'},
286       '#jsMath_Warning': {display: 'none'}
287     },
288                          
289     '@media screen': {
290       '#jsMath_PrintWarning': {display:'none'}
291     }
292
293   },
294   
295
296   /***************************************************************************/
297
298   /*
299    *  Get a jsMath DOM element
300    */
301   Element: function (name) {return jsMath.document.getElementById('jsMath_'+name)},
302   
303   /*
304    *  Get the width and height (in pixels) of an HTML string
305    */
306   BBoxFor: function (s) {
307     this.hidden.innerHTML = 
308       '<nobr><span class="typeset"><span class="scale">'+s+'</span></span></nobr>';
309     var math = (jsMath.Browser.msieBBoxBug ? this.hidden.firstChild.firstChild : this.hidden);
310     var bbox = {w: math.offsetWidth, h: this.hidden.offsetHeight};
311     this.hidden.innerHTML = '';
312     return bbox;
313   },
314
315   /*
316    *  Get the width and height (in ems) of an HTML string.
317    *  Check the cache first to see if we've already measured it.
318    */
319   EmBoxFor: function (s) {
320     var cache = jsMath.Global.cache.R;
321     if (!cache[this.em]) {cache[this.em] = {}}
322     if (!cache[this.em][s]) {
323       var bbox = this.BBoxFor(s);
324       cache[this.em][s] = {w: bbox.w/this.em, h: bbox.h/this.em};
325     }
326     return cache[this.em][s];
327   },
328
329   /*
330    *  Initialize jsMath.  This determines the em size, and a variety
331    *  of other parameters used throughout jsMath.
332    */
333   Init: function () {
334     if (jsMath.Setup.inited != 1) {
335       if (!jsMath.Setup.inited) {jsMath.Setup.Body()}
336       if (jsMath.Setup.inited != 1) {
337         if (jsMath.Setup.inited == -100) return;
338         alert("It looks like jsMath failed to set up properly (error code "
339                + jsMath.Setup.inited + ").  "
340                + "I will try to keep going, but it could get ugly.");
341         jsMath.Setup.inited = 1;
342       }
343     }
344     this.em = this.CurrentEm();
345     var cache = jsMath.Global.cache.B;
346     if (!cache[this.em]) {
347       cache[this.em] = {};
348       cache[this.em].bb = this.BBoxFor('x'); var hh = cache[this.em].bb.h;
349       cache[this.em].d = this.BBoxFor('x'+jsMath.HTML.Strut(hh/this.em)).h - hh;
350     }
351     jsMath.Browser.italicCorrection = cache[this.em].ic;
352     var bb = cache[this.em].bb; var h = bb.h; var d = cache[this.em].d
353     this.h = (h-d)/this.em; this.d = d/this.em;
354     this.hd = this.h + this.d;
355
356     this.Setup.TeXfonts();
357     
358     var x_height = this.EmBoxFor('<span class="cmr10">M</span>').w/2;
359     this.TeX.M_height = x_height*(26/14);
360     this.TeX.h = this.h; this.TeX.d = this.d; this.TeX.hd = this.hd;
361     
362     this.Img.Scale();
363     if (!this.initialized) {
364       this.Setup.Sizes();
365       this.Img.UpdateFonts();
366     }
367
368     // factor for \big and its brethren
369     this.p_height = (this.TeX.cmex10[0].h + this.TeX.cmex10[0].d) / .85;
370
371     this.initialized = 1;
372   },
373   
374   /*
375    *  Get the x size and if it has changed, reinitialize the sizes
376    */
377   ReInit: function () {
378     if (this.em != this.CurrentEm()) {this.Init()}
379   },
380   
381   /*
382    *  Find the em size in effect at the current text location
383    */
384   CurrentEm: function () {
385     var em = this.BBoxFor('<span style="'+jsMath.Browser.block+';width:27em;height:1em"></span>').w/27;
386     if (em > 0) {return em}
387     // handle older browsers
388     return this.BBoxFor('<img src="'+jsMath.blank+'" style="width:13em;height:1em" />').w/13;
389   },
390   
391   /*
392    *  Mark jsMath as loaded and copy any user-provided overrides
393    */
394   Loaded: function () {
395     if (jsMath_old) {
396       var override = ['Process', 'ProcessBeforeShowing','ProcessElement',
397         'ConvertTeX','ConvertTeX2','ConvertLaTeX','ConvertCustom',
398         'CustomSearch', 'Synchronize', 'Macro', 'document'];
399       for (var i = 0; i < override.length; i++) {
400         if (jsMath_old[override[i]]) {delete jsMath_old[override[i]]}
401       }
402     }
403     if (jsMath_old) {this.Insert(jsMath,jsMath_old)}
404     jsMath_old = null;
405     jsMath.loaded = 1;
406   },
407   
408   /*
409    *  Manage JavaScript objects:
410    *  
411    *      Add:        add/replace items in an object
412    *      Insert:     add items to an object
413    *      Package:    add items to an object prototype
414    */
415   Add: function (dst,src) {for (var id in src) {dst[id] = src[id]}},
416   Insert: function (dst,src) {
417     for (var id in src) {
418       if (dst[id] && typeof(src[id]) == 'object'
419                   && (typeof(dst[id]) == 'object'
420                   ||  typeof(dst[id]) == 'function')) {
421         this.Insert(dst[id],src[id]);
422       } else {
423         dst[id] = src[id];
424       }
425     }
426   },
427   Package: function (obj,def) {this.Insert(obj.prototype,def)}
428
429 };
430
431
432 /***************************************************************************/
433
434   /*
435    *  Implements items associated with the global cache.
436    *
437    *  This object will be replaced by a global version when 
438    *  (and if) jsMath-global.html is loaded.
439    */
440 jsMath.Global = {
441     isLocal: 1,  // a local copy if jsMath-global.html hasn't been loaded
442     cache: {T: {}, D: {}, R: {}, B: {}},
443
444     /*
445      *  Clear the global (or local) cache
446      */
447     ClearCache: function () {jsMath.Global.cache = {T: {}, D: {}, R: {}, B: {}}},
448
449     /*
450      *  Initiate global mode
451      */
452     GoGlobal: function (cookie) {
453       var url = String(jsMath.window.location);
454       var c = (jsMath.isCHMmode ? '#' : '?');
455       if (cookie) {url = url.replace(/\?.*/,'') + '?' + cookie}
456       jsMath.Controls.Reload(jsMath.root + "jsMath-global.html" + c +escape(url));
457     },
458
459     /*
460      *  Check if we need to go to global mode
461      */
462     Init: function () {
463       if (jsMath.Controls.cookie.global == "always" && !jsMath.noGoGlobal) {
464         if (navigator.accentColorName) return; // OmniWeb crashes on GoGlobal
465         if (!jsMath.window) {jsMath.window = window}
466         jsMath.Controls.loaded = 1;
467         jsMath.Controls.defaults.hiddenGlobal = null;
468         this.GoGlobal(jsMath.Controls.SetCookie(2));
469       }
470     },
471
472     /*
473      *  Try to register with a global.html window that contains us
474      */
475     Register: function () {
476       var parent = jsMath.window.parent;
477       if (!jsMath.isCHMmode)
478         {jsMath.isCHMmode = (jsMath.window.location.protocol == 'mk:')}
479       try {
480         if (!jsMath.isCHMmode) this.Domain();
481         if (parent.jsMath && parent.jsMath.isGlobal)
482           {parent.jsMath.Register(jsMath.window)}
483       } catch (err) {jsMath.noGoGlobal = 1}
484     },
485
486     /*
487      *  If we're not the parent window, try to set the domain to
488      *  match the parent's domain (so we can use the Global data
489      *  if the surrounding frame is a Global frame).
490      */
491     Domain: function () {
492       // MSIE/Mac can't do domain changes, so don't bother trying
493       if (navigator.appName == 'Microsoft Internet Explorer' &&
494           jsMath.platform == 'mac' && navigator.userProfile != null) return;
495       // MSIE/PC can do domain change, but gets mixed up if we don't
496       //  find a domain that works, and then can't look in window.location
497       //  any longer.  So don't try, since we can't afford to leave it confused.
498       if (jsMath.document.all && !jsMath.window.opera) return;
499
500       if (window == parent) return;
501       var oldDomain = jsMath.document.domain;
502       try {
503         while (true) {
504           try {if (parent.document.title != null) return} catch (err) {}
505           if (!document.domain.match(/\..*\./)) break;
506           jsMath.document.domain = jsMath.document.domain.replace(/^[^.]*\./,'');
507         }
508       } catch (err) {}
509       jsMath.document.domain = oldDomain;
510     }
511
512 };
513   
514
515
516 /***************************************************************************/
517
518 /*
519  *
520  *  Implement loading of remote scripts using XMLHttpRequest, if
521  *  possible, otherwise use a hidden IFRAME and fake it.  That
522  *  method runs asynchronously, which causes lots of headaches.
523  *  Solve these using Push command, which queues actions
524  *  until files have loaded.
525  */
526
527 jsMath.Script = {
528
529   request: null, // the XMLHttpRequest object
530
531   /*
532    *  Create the XMLHttpRequest object, if we can.
533    *  Otherwise, use the iframe-based fallback method.
534    */
535   Init: function () {
536     if (!(jsMath.Controls.cookie.asynch && jsMath.Controls.cookie.progress)) {
537       if (window.XMLHttpRequest) {
538         try {this.request = new XMLHttpRequest} catch (err) {}
539         // MSIE and FireFox3 can't use xmlRequest on local files,
540         // but we don't have jsMath.browser yet to tell, so use this check
541         if (this.request && jsMath.root.match(/^file:\/\//)) {
542           try {
543             this.request.open("GET",jsMath.root+"jsMath.js",false);
544             this.request.send(null);
545           } catch (err) {
546             this.request = null;
547             //  Firefox3 has window.postMessage for inter-window communication. 
548             //  It can be used to handle the new file:// security model,
549             //  so set up the listener.
550             if (window.postMessage && window.addEventListener) {
551               this.mustPost = 1;
552               jsMath.window.addEventListener("message",jsMath.Post.Listener,false);
553             }
554           }
555         }
556       }
557       if (!this.request && window.ActiveXObject && !this.mustPost) {
558         var xml = ["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0",
559                    "MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"];
560         for (var i = 0; i < xml.length && !this.request; i++) {
561           try {this.request = new ActiveXObject(xml[i])} catch (err) {}
562         }
563       }
564     }
565     //
566     //  Use the delayed-script fallback for MSIE/Mac and old versions
567     //  of several browsers (Opera 7.5, OmniWeb 4.5).
568     //
569     if (!this.request || jsMath.Setup.domainChanged)
570       {this.Load = this.delayedLoad; this.needsBody = 1}
571   },
572
573   /*
574    *  Load a script and evaluate it in the window's context
575    */
576   Load: function (url,show) {
577     if (show) {
578       jsMath.Message.Set("Loading "+url);
579       jsMath.Script.Delay(1);
580       jsMath.Script.Push(this,'xmlRequest',url);
581       jsMath.Script.Push(jsMath.Message,'Clear');
582     } else {
583       jsMath.Script.Push(this,'xmlRequest',url);
584     }
585   },
586
587   /*
588    *  Load a URL and run the contents of the file
589    */
590   xmlRequest: function (url) {
591     this.blocking = 1;
592 //    this.debug('xmlRequest: '+url);
593     try {
594       this.request.open("GET",url,false);
595       this.request.send(null);
596     } catch (err) {
597       this.blocking = 0;
598       if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""}
599       throw Error("jsMath can't load the file '"+url+"'\n"
600           + "Message: "+err.message);
601     }
602     if (this.request.status != null && (this.request.status >= 400 || this.request.status < 0)) {
603       // Do we need to deal with redirected links?
604       this.blocking = 0;
605       if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""}
606       throw Error("jsMath can't load the file '"+url+"'\n"
607           + "Error status: "+this.request.status);
608     }
609     if (!url.match(/\.js$/)) {return(this.request.responseText)}
610     var tmpQueue = this.queue; this.queue = [];
611 //    this.debug('xml Eval ['+tmpQueue.length+']');
612     jsMath.window.eval(this.request.responseText);
613 //    this.debug('xml Done ['+this.queue.length+' + '+tmpQueue.length+']');
614     this.blocking = 0; this.queue = this.queue.concat(tmpQueue);
615     this.Process();
616     return "";
617   },
618
619   /********************************************************************
620    *
621    *  Implement asynchronous loading and execution of scripts
622    *  (via hidden IFRAME) interleved with other JavaScript commands
623    *  that must be synchronized with the file loading.  (Basically, this
624    *  is for MSIE/Mac and Opera 7.5, which don't have XMLHttpRequest.)
625    */
626
627   cancelTimeout: 30*1000,   // delay for canceling load (30 sec)
628
629   blocking: 0,       // true when an asynchronous action is being performed
630   cancelTimer: null, // timer to cancel load if it takes too long
631   needsBody: 0,      // true if loading files requires BODY to be present
632   
633   queue: [],         // the stack of pending actions
634
635   /*
636    *  Provide mechanism for synchronizing with the asynchronous jsMath
637    *  file-loading mechanism.  'code' can be a string or a function.
638    */
639   Synchronize: function (code,data) {
640     if (typeof(code) != 'string') {jsMath.Script.Push(null,code,data)}
641       else {jsMath.Script.Push(jsMath.window,'eval',code)}
642   },
643   
644   /*
645    *  Queue a function to be processed.
646    *  If nothing is being loaded, do the pending commands.
647    */
648   Push: function (object,method,data) {
649 //  this.debug('Pushing: '+method+' at '+this.queue.length); // debug
650     this.queue[this.queue.length] = [object,method,data];
651     if (!(this.blocking || (this.needsBody && !jsMath.document.body))) this.Process();
652   },
653
654   /*
655    *  Do any pending functions (stopping if a file load is started)
656    */
657   Process: function () {
658     while (this.queue.length && !this.blocking) {
659       var call = this.queue[0]; this.queue = this.queue.slice(1);
660       var savedQueue = this.SaveQueue();
661       var object = call[0]; var method = call[1]; var data = call[2];
662 //    this.debug('Calling: '+method+' ['+savedQueue.length+']'); // debug
663       if (object) {object[method](data)} else if (method) {method(data)}
664 //    this.debug('Done:    '+method+' ['+this.queue.length+' + '+savedQueue.length+'] ('+this.blocking+')'); // debug
665       this.RestoreQueue(savedQueue);
666     }
667   },
668   
669   /*
670    *  Allows pushes to occur at the FRONT of the queue
671    *  (so a command acts as a single unit, including anything
672    *  that it pushes on to the command stack)
673    */
674   SaveQueue: function () {
675     var queue = this.queue;
676     this.queue = [];
677     return queue;
678   },
679   RestoreQueue: function (queue) {
680     this.queue = this.queue.concat(queue);
681   },
682   
683   /*
684    *  Handle loading of scripts that run asynchronously
685    */
686   delayedLoad: function (url) {
687 //    this.debug('Loading: '+url);
688     this.Push(this,'startLoad',url);
689   },
690   startLoad: function (url) {
691     var iframe = jsMath.document.createElement('iframe');
692     iframe.style.visibility = 'hidden';
693     iframe.style.position = 'absolute';
694     iframe.style.width = '0px';
695     iframe.style.height = '0px';
696     if (jsMath.document.body.firstChild) {
697       jsMath.document.body.insertBefore(iframe,jsMath.document.body.firstChild);
698     } else {
699       jsMath.document.body.appendChild(iframe);
700     }
701     this.blocking = 1; this.url = url;
702     if (url.substr(0,jsMath.root.length) == jsMath.root)
703       {url = url.substr(jsMath.root.length)}
704     jsMath.Message.Set("Loading "+url);
705     this.cancelTimer = setTimeout('jsMath.Script.cancelLoad()',this.cancelTimeout);
706     if (this.mustPost)           {iframe.src = jsMath.Post.startLoad(url,iframe)}
707     else if (url.match(/\.js$/)) {iframe.src = jsMath.root+"jsMath-loader.html"}
708     else                         {iframe.src = this.url}
709   },
710   endLoad: function (action) {
711     if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null}
712     jsMath.Post.endLoad();
713     jsMath.Message.Clear();
714     if (action != 'cancel') {this.blocking = 0; this.Process()}
715   },
716   
717   Start: function () {
718 //    this.debug('Starting: ['+this.queue.length+'] '+this.url);
719     this.tmpQueue = this.queue; this.queue = [];
720   },
721   End:   function () {
722 //    this.debug('Ending:   ['+this.queue.length+' + '+this.tmpQueue.length+'] '+this.url);
723     this.queue = this.queue.concat(this.tmpQueue); delete this.tmpQueue;
724   },
725
726   /*
727    *  If the loading takes too long, cancel it and end the load.
728    */
729   cancelLoad: function (message,delay) {
730     if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null}
731     if (message == null) {message = "Can't load file"}
732     if (delay == null) {delay = 2000}
733     jsMath.Message.Set(message);
734     setTimeout('jsMath.Script.endLoad("cancel")',delay);
735   },
736
737   /*
738    *  Perform a delay (to let the browser catch up)
739    */
740   Delay: function (time) {
741     this.blocking = 1;
742     setTimeout('jsMath.Script.endDelay()',time);
743   },
744   endDelay: function () {
745 //    this.debug('endDelay');
746     this.blocking = 0;
747     this.Process();
748   },
749   
750   /*
751    *  Load an image and wait for it
752    *  (so MSIE won't load extra copies of it)
753    */
754   imageCount: 0,
755   WaitForImage: function (file) {
756     this.blocking = 1; this.imageCount++;
757     if (this.img == null) {this.img = []}
758     var img = new Image(); this.img[this.img.length] = img;
759     img.onload = function () {if (--jsMath.Script.imageCount == 0) jsMath.Script.endDelay()}
760     img.onerror = img.onload; img.onabort = img.onload;
761     img.src = file;
762   },
763   
764   /*
765    *  The code uncompressor
766    */
767   Uncompress: function (data) {
768     for (var k = 0; k <  data.length; k++) {
769       var d = data[k]; var n = d.length;
770       for (var i = 0; i < n; i++) {if (typeof(d[i]) == 'number') {d[i] = d[d[i]]}}
771       data[k] = d.join('');
772     }
773     window.eval(data.join(''));
774   }
775   
776   /*
777    *  for debugging the event queue
778    */
779   /* 
780    * ,debug: function (message) {
781    *   if (jsMath.document.body && jsMath.window.debug) {jsMath.window.debug(message)}
782    *     else {alert(message)}
783    * }
784    */
785
786 };
787
788 /***************************************************************************/
789
790 /*
791  *  Handle window.postMessage() events in Firefox3
792  */
793
794 jsMath.Post = {
795   window: null,  // iframe we are listening to
796   
797   Listener: function (event) {
798     if (event.source != jsMath.Post.window) return;
799     var domain = event.origin.replace(/^file:\/\//,'');
800     var ddomain = document.domain.replace(/^file:\/\//,'');
801     if (domain == null || domain == "" || domain == "null") {domain = "localhost"}
802     if (ddomain == null || ddomain == "" || ddomain == "null") {ddomain = "localhost"}
803     if (domain != ddomain || !event.data.substr(0,6).match(/jsM(CP|LD|AL):/)) return;
804     var type = event.data.substr(6,3).replace(/ /g,'');
805     var message = event.data.substr(10);
806     if (jsMath.Post.Commands[type]) (jsMath.Post.Commands[type])(message);
807     // cancel event?
808   },
809   
810   /*
811    *  Commands that can be performed by the listener
812    */
813   Commands: {
814     SCR: function (message) {jsMath.window.eval(message)},
815     ERR: function (message) {jsMath.Script.cancelLoad(message,3000)},
816     BGN: function (message) {jsMath.Script.Start()},
817     END: function (message) {if (message) jsMath.Script.End(); jsMath.Script.endLoad()}
818   },
819   
820   startLoad: function (url,iframe) {
821     this.window = iframe.contentWindow;
822     if (!url.match(/\.js$/)) {return jsMath.root+url}
823     return jsMath.root+"jsMath-loader-post.html?"+url;
824   },
825   endLoad: function () {this.window = null}
826 };
827
828 /***************************************************************************/
829
830 /*
831  *  Message and screen blanking facility
832  */
833
834 jsMath.Message = {
835   
836   blank: null,    // the div to blank out the screen
837   message: null,  // the div for the messages
838   text: null,     // the text node for messages
839   clear: null,    // timer for clearing message
840
841   /*
842    *  Create the elements needed for the message box
843    */
844   Init: function () {
845     if (!jsMath.document.body || !jsMath.Controls.cookie.progress) return;
846     this.message = jsMath.Element('message');
847     if (!this.message) {
848       if (jsMath.Setup.stylesReady) {
849         this.message = jsMath.Setup.DIV('message',{visibility:'hidden'},jsMath.fixedDiv);
850       } else {
851         this.message = jsMath.Setup.DIV('message',{
852           visibility:'hidden', position:'absolute', bottom:'1px', left:'2px',
853           backgroundColor:'#E6E6E6', border:'solid 1px #959595',
854           margin:'0px', padding:'1px 8px', zIndex:102,
855           color:'black', fontSize:'small', width:'auto'
856         },jsMath.fixedDiv);
857       }
858     }
859     this.text = jsMath.document.createTextNode('');
860     this.message.appendChild(this.text);
861     this.message.onmousedown = jsMath.Translate.Cancel;
862   },
863   
864   /*
865    *  Set the contents of the message box, or use the window status line
866    */
867   Set: function (text,canCancel) {
868     if (this.clear) {clearTimeout(this.clear); this.clear = null}
869     if (jsMath.Controls.cookie.progress) {
870       if (!this.text) {this.Init(); if (!this.text) return}
871       if (jsMath.Browser.textNodeBug) {this.message.innerHTML = text}
872         else {this.text.nodeValue = text}
873       this.message.style.visibility = 'visible';
874       if (canCancel) {
875         this.message.style.cursor = 'pointer';
876         if (!this.message.style.cursor) {this.message.style.cursor = 'hand'}
877         this.message.title = ' Cancel Processing of Math ';
878       } else {
879         this.message.style.cursor = '';
880         this.message.title = '';
881       }
882     } else {
883       if (text.substr(0,8) != "Loading ") {jsMath.window.status = text}
884     }
885   },
886   
887   /*
888    *  Clear the message box or status line
889    */
890   Clear: function () {
891     if (this.clear) {clearTimeout(this.clear)}
892     this.clear = setTimeout("jsMath.Message.doClear()",1000);
893   },
894   doClear: function () {
895     if (this.clear) {
896       this.clear = null;
897       jsMath.window.status = '';
898       if (this.text) {this.text.nodeValue = ''}
899       if (this.message) {this.message.style.visibility = 'hidden'}
900     }
901   },
902   
903   
904   /*
905    *  Put up a DIV that covers the window so that the
906    *  "flicker" of processing the mathematics will not be visible
907    */
908   Blank: function () {
909     if (this.blank || !jsMath.document.body) return;
910     this.blank = jsMath.Setup.DIV("blank",{
911       position:(jsMath.Browser.msiePositionFixedBug? 'absolute': 'fixed'),
912       top:'0px', left:'0px', bottom:'0px', right:'0px',
913       zIndex:101, backgroundColor:'white'
914     },jsMath.fixedDiv);
915     if (jsMath.Browser.msieBlankBug) {
916       this.blank.innerHTML = '&nbsp;';
917       this.blank.style.width = "110%";
918       this.blank.style.height = "110%";
919     }
920   },
921   
922   UnBlank: function () {
923     if (this.blank) {jsMath.document.body.removeChild(this.blank)}
924     this.blank = null;
925   }
926 };
927
928
929 /***************************************************************************/
930
931 /*
932  *  Miscellaneous setup and initialization
933  */
934 jsMath.Setup = {
935   
936   loaded: [],  // array of files already loaded
937   
938   /*
939    *  Insert a DIV at the top of the page with given ID,
940    *  attributes, and style settings
941    */
942   DIV: function (id,styles,parent) {
943     if (parent == null) {parent = jsMath.document.body}
944     var div = jsMath.document.createElement('div');
945     div.id = 'jsMath_'+id;
946     for (var i in styles) {div.style[i]= styles[i]}
947     if (!parent.hasChildNodes) {parent.appendChild(div)}
948       else {parent.insertBefore(div,parent.firstChild)}
949     return div;
950   },
951   
952   /*
953    *  Source a jsMath JavaScript file (only load any given file once)
954    */
955   Script: function (file,show) {
956     if (this.loaded[file]) {return} else {this.loaded[file] = 1}
957     if (!file.match('^([a-zA-Z]+:/?)?/')) {file = jsMath.root + file}
958     jsMath.Script.Load(file,show);
959   },
960   
961   /*
962    *  Use a hidden <DIV> for measuring the BBoxes of things
963    */
964   Hidden: function () {
965     jsMath.hidden = this.DIV("Hidden",{
966       visibility: 'hidden', position:"absolute",
967       top:0, left:0, border:0, padding:0, margin:0
968     });
969     jsMath.hiddenTop = jsMath.hidden;
970     return;
971   },
972
973   /*
974    *  Find the root URL for the jsMath files (so we can load
975    *  the other .js and .gif files)
976    */
977   Source: function () {
978     if (jsMath.Autoload && jsMath.Autoload.root) {
979       jsMath.root = jsMath.Autoload.root;
980     } else {
981       jsMath.root = '';
982       var script = jsMath.document.getElementsByTagName('script');
983       if (script) {
984         for (var i = 0; i < script.length; i++) {
985           var src = script[i].src;
986           if (src && src.match('(^|/|\\\\)jsMath.js$')) {
987             jsMath.root = src.replace(/jsMath.js$/,'');
988             break;
989           }
990         }
991       }
992     }
993     if (jsMath.root.charAt(0) == '\\') {jsMath.root = jsMath.root.replace(/\\/g,'/')}
994     if (jsMath.root.charAt(0) == '/') {
995       if (jsMath.root.charAt(1) != '/')
996         {jsMath.root = '//' + jsMath.document.location.host + jsMath.root}
997       jsMath.root = jsMath.document.location.protocol + jsMath.root;
998     } else if (!jsMath.root.match(/^[a-z]+:/i)) {
999       var src = new String(jsMath.document.location);
1000       var pattern = new RegExp('/[^/]*/\\.\\./')
1001       jsMath.root = src.replace(new RegExp('[^/]*$'),'') + jsMath.root;
1002       while (jsMath.root.match(pattern))
1003         {jsMath.root = jsMath.root.replace(pattern,'/')}
1004     }
1005     jsMath.Img.root = jsMath.root + "fonts/";
1006     jsMath.blank = jsMath.root + "blank.gif";
1007     this.Domain();
1008   },
1009   
1010   /*
1011    *  Find the most restricted common domain for the main
1012    *  page and jsMath.  Report an error if jsMath is outside
1013    *  the domain of the calling page.
1014    */
1015   Domain: function () {
1016     try {jsMath.document.domain} catch (err) {return}
1017     var jsDomain = ''; var pageDomain = jsMath.document.domain;
1018     if (jsMath.root.match('://([^/]*)/')) {jsDomain = RegExp.$1}
1019     jsDomain = jsDomain.replace(/:\d+$/,'');
1020     if (jsDomain == "" || jsDomain == pageDomain) return;
1021     //
1022     // MSIE on the Mac can't change jsMath.document.domain and 'try' won't
1023     //   catch the error (Grrr!), so exit for them.
1024     //
1025     if (navigator.appName == 'Microsoft Internet Explorer' &&
1026         jsMath.platform == 'mac' && navigator.onLine &&
1027         navigator.userProfile && jsMath.document.all) return;
1028     jsDomain = jsDomain.split(/\./); pageDomain = pageDomain.split(/\./);
1029     if (jsDomain.length < 2 || pageDomain.length < 2 ||
1030         jsDomain[jsDomain.length-1] != pageDomain[pageDomain.length-1] ||
1031         jsDomain[jsDomain.length-2] != pageDomain[pageDomain.length-2]) {
1032       this.DomainWarning();
1033       return;
1034     }
1035     var domain = jsDomain[jsDomain.length-2] + '.' + jsDomain[jsDomain.length-1];
1036     for (var i = 3; i <= jsDomain.length && i <= pageDomain.length; i++) {
1037       if (jsDomain[jsDomain.length-i] != pageDomain[pageDomain.length-i]) break;
1038       domain = jsDomain[jsDomain.length-i] + '.' + domain;
1039     }
1040     jsMath.document.domain = domain;
1041     this.domainChanged = 1;
1042   },
1043
1044   DomainWarning: function () {
1045     alert("In order for jsMath to be able to load the additional "
1046         + "components that it may need, the jsMath.js file must be "
1047         + "loaded from a server in the same domain as the page that "
1048         + "contains it.  Because that is not the case for this page, "
1049         + "the mathematics displayed here may not appear correctly.");
1050   },
1051   
1052   /*
1053    *  Initialize a font's encoding array
1054    */
1055   EncodeFont: function (name) {
1056     var font = jsMath.TeX[name];
1057     if (font[0].c != null) return;
1058     for (var k = 0; k < 128; k++) {
1059       var data = font[k]; font[k] = data[3];
1060       if (font[k] == null) {font[k] = {}};
1061       font[k].w = data[0]; font[k].h = data[1];
1062       if (data[2] != null) {font[k].d = data[2]}
1063       font[k].c = jsMath.TeX.encoding[k];
1064     }
1065   },
1066   
1067   /*
1068    *  Initialize the encodings for all fonts
1069    */
1070   Fonts: function () {
1071     for (var i = 0; i < jsMath.TeX.fam.length; i++) {
1072       var name = jsMath.TeX.fam[i];
1073       if (name) {this.EncodeFont(name)}
1074     }
1075   },
1076   
1077   /*
1078    *  Look up the default height and depth for a TeX font
1079    *  and set the skewchar
1080    */
1081   TeXfont: function (name) {
1082     var font = jsMath.TeX[name]; if (font == null) return;
1083     font.hd = jsMath.EmBoxFor('<span class="'+name+'">'+font[65].c+'</span>').h;
1084     font.d  = jsMath.EmBoxFor('<span class="'+name+'">'+font[65].c+jsMath.HTML.Strut(font.hd)+'</span>').h - font.hd;
1085     font.h = font.hd - font.d;
1086     if (name == 'cmmi10') {font.skewchar = 0177} 
1087     else if (name == 'cmsy10') {font.skewchar = 060}
1088   },
1089
1090   /*
1091    *  Init all the TeX fonts
1092    */
1093   TeXfonts: function () {
1094     for (var i = 0; i < jsMath.TeX.fam.length; i++) 
1095       {if (jsMath.TeX.fam[i]) {this.TeXfont(jsMath.TeX.fam[i])}}
1096   },
1097
1098   /*
1099    *  Compute font parameters for various sizes
1100    */
1101   Sizes: function () {
1102     jsMath.TeXparams = []; var i; var j;
1103     for (j=0; j < jsMath.sizes.length; j++) {jsMath.TeXparams[j] = {}}
1104     for (i in jsMath.TeX) {
1105       if (typeof(jsMath.TeX[i]) != 'object') {
1106         for (j=0; j < jsMath.sizes.length; j++) {
1107           jsMath.TeXparams[j][i] = jsMath.sizes[j]*jsMath.TeX[i]/100;
1108         }
1109       }
1110     }
1111   },
1112
1113   /*
1114    *  Send the style definitions to the browser (these may be adjusted
1115    *  by the browser-specific code)
1116    */
1117   Styles: function (styles) {
1118     if (!styles) {
1119       styles = jsMath.styles;
1120       styles['.typeset .scale'] = {'font-size': jsMath.Controls.cookie.scale+'%'};
1121       this.stylesReady = 1;
1122     }
1123     jsMath.Script.Push(this,'AddStyleSheet',styles);
1124     if (jsMath.Browser.styleChangeDelay) {jsMath.Script.Push(jsMath.Script,'Delay',1)}
1125   },
1126   
1127   /*
1128    *  Make a style string from a hash of style definitions, which are
1129    *  either strings themselves or hashes of style settings.
1130    */
1131   StyleString: function (styles) {
1132     var styleString = {}, id;
1133     for (id in styles) {
1134       if (typeof styles[id] === 'string') {
1135         styleString[id] = styles[id];
1136       } else if (id.substr(0,1) === '@') {
1137         styleString[id] = this.StyleString(styles[id]);
1138       } else if (styles[id] != null) {
1139         var style = [];
1140         for (var name in styles[id]) {
1141           if (styles[id][name] != null)
1142             {style[style.length] = name + ': ' + styles[id][name]}
1143         }
1144         styleString[id] = style.join('; ');
1145       }
1146     }
1147     var string = '';
1148     for (id in styleString) {string += id + " {"+styleString[id]+"}\n"}
1149     return string;
1150   },
1151   
1152   AddStyleSheet: function (styles) {
1153     var head = jsMath.document.getElementsByTagName('head')[0];
1154     if (head) {
1155       var string = this.StyleString(styles);
1156       if (jsMath.document.createStyleSheet) {// check for MSIE
1157         head.insertAdjacentHTML('beforeEnd',
1158             '<span style="display:none">x</span>'  // MSIE needs this for some reason
1159             + '<style type="text/css">'+string+'</style>');
1160       } else {
1161         var style = jsMath.document.createElement('style'); style.type = "text/css";
1162         style.appendChild(jsMath.document.createTextNode(string));
1163         head.appendChild(style);
1164       }
1165     } else if (!jsMath.noHEAD) {
1166       jsMath.noHEAD = 1;
1167       alert("Document is missing its <HEAD> section.  Style sheet can't be created without it.");
1168     }
1169   },
1170
1171   /*
1172    *  Do the initialization that requires the <body> to be in place.
1173    */
1174   Body: function () {
1175     if (this.inited) return;
1176     
1177     this.inited = -1;
1178
1179     jsMath.Setup.Hidden(); this.inited = -2;
1180     jsMath.Browser.Init(); this.inited = -3;
1181
1182     // blank screen if necessary
1183     if (jsMath.Controls.cookie.blank) {jsMath.Message.Blank()}; this.inited = -4;
1184
1185     jsMath.Setup.Styles(); this.inited = -5;
1186     jsMath.Controls.Init(); this.inited = -6;
1187     
1188     // do user-specific initialization
1189     jsMath.Script.Push(jsMath.Setup,'User','pre-font'); this.inited = -7;
1190
1191     // make sure browser-specific loads are done before this
1192     jsMath.Script.Push(jsMath.Font,'Check');
1193     if (jsMath.Font.register.length)
1194       {jsMath.Script.Push(jsMath.Font,'LoadRegistered')}
1195
1196     this.inited = 1;
1197   },
1198   
1199   /*
1200    *  Web page author can override the entries to the UserEvent hash 
1201    *  functions that will be run at various times during jsMath's setup
1202    *  process.
1203    */
1204   User: function (when) {
1205     if (jsMath.Setup.UserEvent[when]) {(jsMath.Setup.UserEvent[when])()}
1206   },
1207   
1208   UserEvent: {
1209     "pre-font": null,  // after browser is set up but before fonts are tested
1210     "onload": null     // after jsMath.js is loaded and finished running
1211   }
1212   
1213 };
1214
1215 jsMath.Update = {
1216
1217   /*
1218    *  Update specific parameters for a limited number of font entries
1219    */
1220   TeXfonts: function (change) {
1221     for (var font in change) {
1222       for (var code in change[font]) {
1223         for (var id in change[font][code]) {
1224           jsMath.TeX[font][code][id] = change[font][code][id];
1225         }
1226       }
1227     }
1228   },
1229   
1230   /*
1231    *  Update the character code for every character in a list
1232    *  of fonts
1233    */
1234   TeXfontCodes: function (change) {
1235     for (var font in change) {
1236       for (var i = 0; i < change[font].length; i++) {
1237         jsMath.TeX[font][i].c = change[font][i];
1238       }
1239     }
1240   }
1241   
1242 };
1243
1244 /***************************************************************************/
1245
1246 /*
1247  *  Implement browser-specific checks
1248  */
1249
1250 jsMath.Browser = {
1251
1252   allowAbsolute: 1,           // tells if browser can nest absolutely positioned
1253                               //   SPANs inside relative SPANs
1254   allowAbsoluteDelim: 0,      // OK to use absolute placement for building delims?
1255   separateSkips: 0,           // MSIE doesn't do negative left margins, and
1256                               //   Netscape doesn't combine skips well
1257
1258   valignBug: 0,               // Konqueror doesn't nest vertical-align
1259   operaHiddenFix: '',         // for Opera to fix bug with math in tables
1260   msieCenterBugFix: '',       // for MSIE centering bug with image fonts
1261   msieInlineBlockFix: '',     // for MSIE alignment bug in non-quirks mode
1262   msieSpaceFix: '',           // for MSIE to avoid dropping empty spans
1263   imgScale: 1,                // MSI scales images for 120dpi screens, so compensate
1264
1265   renameOK: 1,                // tells if brower will find a tag whose name
1266                               //   has been set via setAttributes
1267   styleChangeDelay: 0,        // true if style changes need a delay in order
1268                               //   for them to be available
1269
1270   delay: 1,                   // delay for asynchronous math processing
1271   processAtOnce: 16,          // number of math elements to process before screen update
1272   
1273   version: 0,                 // browser version number (when needed)
1274
1275   /*
1276    *  Determine if the "top" of a <SPAN> is always at the same height
1277    *  or varies with the height of the rest of the line (MSIE).
1278    */
1279   TestSpanHeight: function () {
1280     jsMath.hidden.innerHTML = '<span><span style="'+this.block+';height:2em;width:1px"></span>x</span>';
1281     var span = jsMath.hidden.firstChild;
1282     var img  = span.firstChild;
1283     this.spanHeightVaries = (span.offsetHeight >= img.offsetHeight && span.offsetHeight > 0);
1284     this.spanHeightTooBig = (span.offsetHeight > img.offsetHeight);
1285     jsMath.hidden.innerHTML = '';
1286   },
1287   
1288   /*
1289    *  Determine if an inline-block with 0 width is OK or not
1290    *  and decide whether to use spans or images for spacing
1291    */
1292   TestInlineBlock: function () {
1293     this.block = "display:-moz-inline-box";
1294     this.hasInlineBlock = jsMath.BBoxFor('<span style="'+this.block+';width:10px;height:5px"></span>').w > 0;
1295     if (this.hasInlineBlock) {
1296       jsMath.styles['.typeset .blank'].display = '-moz-inline-box';
1297       delete jsMath.styles['.typeset .spacer'].display;
1298     } else {
1299       this.block = "display:inline-block";
1300       this.hasInlineBlock = jsMath.BBoxFor('<span style="'+this.block+';width:10px;height:5px"></span>').w > 0;
1301       if (!this.hasInlineBlock) return;
1302     }
1303     this.block += ';overflow:hidden';
1304     var h = jsMath.BBoxFor('x').h;
1305     this.mozInlineBlockBug = jsMath.BBoxFor(
1306        '<span style="'+this.block+';height:'+h+'px;width:1px"></span>x'+
1307        '<span style="'+this.block+';height:'+h+'px;width:1px;vertical-align:-'+h+'px"></span>').h > 2*h;
1308     this.widthAddsBorder = jsMath.BBoxFor('<span style="'+this.block+
1309         ';overflow:hidden;height:1px;width:10px;border-left:10px solid"></span>').w > 10;
1310     var h1 = jsMath.BBoxFor('<span style="'+this.block+';height:'+h+'px;width:1px"></span>x').h,
1311         h2 = jsMath.BBoxFor('<span style="'+this.block+';height:'+h+'px;width:1px;border-left:1px solid"></span>x').h,
1312         h3 = jsMath.BBoxFor('<span style="'+this.block+';height:2em"></span>').h;
1313     this.msieBlockDepthBug = (h1 == h);
1314     this.msieRuleDepthBug = (h2 == h);
1315     this.blankWidthBug = (h3 == 0);
1316   },
1317   
1318   /*
1319    *  Determine if the NAME attribute of a tag can be changed
1320    *  using the setAttribute function, and then be properly
1321    *  returned by getElementByName.
1322    */
1323   TestRenameOK: function () {
1324     jsMath.hidden.innerHTML = '<span></span>';
1325     var test = jsMath.hidden.firstChild;
1326     test.setAttribute('name','jsMath_test');
1327     this.renameOK = (jsMath.document.getElementsByName('jsMath_test').length > 0);
1328     jsMath.hidden.innerHTML = '';
1329   },
1330   
1331   /*
1332    *  See if style changes occur immediately, or if we need to delay
1333    *  in order to let them take effect.
1334    */
1335   TestStyleChange: function () {
1336     jsMath.hidden.innerHTML = '<span ID="jsMath_test">x</span>';
1337     var span = jsMath.hidden.firstChild;
1338     var w = span.offsetWidth;
1339     jsMath.Setup.AddStyleSheet({'#jsMath_test': 'font-size:200%'});
1340     this.styleChangeDelay = (span.offsetWidth == w);
1341     jsMath.hidden.innerHTML = '';
1342   },
1343   
1344   /*
1345    *  Perform a version check on a standard version string
1346    */
1347   VersionAtLeast: function (v) {
1348     var bv = new String(this.version).split('.');
1349     v = new String(v).split('.'); if (v[1] == null) {v[1] = '0'}
1350     return bv[0] > v[0] || (bv[0] == v[0] && bv[1] >= v[1]);
1351   },
1352
1353   /*
1354    *  Test for browser characteristics, and adjust things
1355    *  to overcome specific browser bugs
1356    */
1357   Init: function () {
1358     jsMath.browser = 'unknown';
1359     this.TestInlineBlock();
1360     this.TestSpanHeight();
1361     this.TestRenameOK();
1362     this.TestStyleChange();
1363
1364     this.MSIE();
1365     this.Mozilla();
1366     this.Opera();
1367     this.OmniWeb();
1368     this.Safari();
1369     this.Konqueror();
1370     
1371     //
1372     // Change some routines depending on the browser
1373     // 
1374     if (this.allowAbsoluteDelim) {
1375       jsMath.Box.DelimExtend = jsMath.Box.DelimExtendAbsolute;
1376       jsMath.Box.Layout = jsMath.Box.LayoutAbsolute;
1377     } else {
1378       jsMath.Box.DelimExtend = jsMath.Box.DelimExtendRelative;
1379       jsMath.Box.Layout = jsMath.Box.LayoutRelative;
1380     }
1381     
1382     if (this.separateSkips) {
1383       jsMath.HTML.Place = jsMath.HTML.PlaceSeparateSkips;
1384       jsMath.Typeset.prototype.Place = jsMath.Typeset.prototype.PlaceSeparateSkips;
1385     }
1386   },
1387   
1388   //
1389   //  Handle bug-filled Internet Explorer
1390   //
1391   MSIE: function () {
1392     if (jsMath.BBoxFor("<!--[if IE]>IE<![endif]-->").w) {
1393       jsMath.browser = 'MSIE';
1394       if (jsMath.platform == 'pc') {
1395         this.IE7 = (window.XMLHttpRequest != null);
1396         this.IE8 = (jsMath.BBoxFor("<!--[if gte IE 8]>IE8<![endif]-->").w > 0);
1397         this.isReallyIE8 = (jsMath.document.documentMode != null);
1398         this.quirks = (jsMath.document.compatMode == "BackCompat");
1399         this.msieMode = (jsMath.document.documentMode || (this.quirks ? 5 : 7));
1400         this.msieStandard6 = !this.quirks && !this.IE7;
1401         this.allowAbsoluteDelim = 1; this.separateSkips = 1;
1402         this.buttonCheck = 1; this.msieBlankBug = 1;
1403         this.msieAccentBug = 1; this.msieRelativeClipBug = 1;
1404         this.msieDivWidthBug = 1; this.msiePositionFixedBug = 1;
1405         this.msieIntegralBug = 1; this.waitForImages = 1;
1406         this.msieAlphaBug = !this.IE7; this.alphaPrintBug = !this.IE7;
1407         this.msieCenterBugFix = 'position:relative; ';
1408         this.msieInlineBlockFix = ' display:inline-block;';
1409         this.msie8HeightBug = this.msieBBoxBug = (this.msieMode == 8);
1410         this.blankWidthBug = (this.msieMode != 8);
1411         this.msieSpaceFix = (this.isReallyIE8 ?
1412             '<span style="display:inline-block; margin-right:-1px; width:1px"></span>' :
1413             '<span style="margin-right:-1px; width:1px"></span>');
1414         jsMath.Macro('joinrel','\\mathrel{\\kern-5mu}'),
1415         jsMath.Parser.prototype.mathchardef.mapstocharOrig = jsMath.Parser.prototype.mathchardef.mapstochar;
1416         delete jsMath.Parser.prototype.mathchardef.mapstochar;
1417         jsMath.Macro('mapstochar','\\rlap{\\mapstocharOrig\\,}\\kern1mu'),
1418         jsMath.styles['.typeset .arial'] = {'font-family': "'Arial unicode MS'"};
1419         if (!this.IE7 || this.quirks) {
1420           // MSIE doesn't implement fixed positioning, so use absolute
1421           jsMath.styles['#jsMath_message'].position = 'absolute';
1422           delete jsMath.styles['#jsMath_message'].width;
1423           jsMath.styles['#jsMath_panel'].position = 'absolute';
1424           delete jsMath.styles['#jsMath_panel'].width;
1425           jsMath.styles['#jsMath_button'].width = '1px';
1426           jsMath.styles['#jsMath_button'].position = 'absolute'
1427           delete jsMath.styles['#jsMath_button'].width;
1428           jsMath.fixedDiv = jsMath.Setup.DIV("fixedDiv",{position:'absolute', zIndex: 101});
1429           jsMath.window.attachEvent("onscroll",jsMath.Controls.MoveButton);
1430           jsMath.window.attachEvent("onresize",jsMath.Controls.MoveButton);
1431           jsMath.Controls.MoveButton();
1432         }
1433         // Make MSIE put borders around the whole button
1434         jsMath.styles['#jsMath_noFont .link'].display = "inline-block";
1435         // MSIE needs this NOT to be inline-block
1436         delete jsMath.styles['.typeset .spacer'].display;
1437         // MSIE can't insert DIV's into text nodes, so tex2math must use SPAN's to fake DIV's
1438         jsMath.styles['.tex2math_div'] = {}; jsMath.Add(jsMath.styles['.tex2math_div'],jsMath.styles['div.typeset']);
1439         jsMath.styles['.tex2math_div'].width = '100%';
1440         jsMath.styles['.tex2math_div'].display = 'inline-block';
1441         // Reduce occurrance of zoom bug in IE7
1442         jsMath.styles['.typeset']['letter-spacing'] = '0';
1443         // MSIE will rescale images if the DPIs differ
1444         if (screen.deviceXDPI && screen.logicalXDPI 
1445              && screen.deviceXDPI != screen.logicalXDPI) {
1446           this.imgScale *= screen.logicalXDPI/screen.deviceXDPI;
1447           jsMath.Controls.cookie.alpha = 0;
1448         }
1449         //  IE8 doesn't puts ALL boxes at the bottom rather than on the baseline
1450         if (this.msieRuleDepthBug) {jsMath.HTML.Strut = jsMath.HTML.msieStrut}
1451       } else if (jsMath.platform == 'mac') {
1452         this.msieAbsoluteBug = 1; this.msieButtonBug = 1;
1453         this.msieDivWidthBug = 1; this.msieBlankBug = 1;
1454         this.quirks = 1;
1455         jsMath.Setup.Script('jsMath-msie-mac.js');
1456         jsMath.Parser.prototype.macros.angle = ['Replace','ord','<font face="Symbol">&#x8B;</font>','normal'];
1457         jsMath.styles['#jsMath_panel'].width = '42em';
1458         jsMath.Controls.cookie.printwarn = 0; // MSIE/Mac doesn't handle '@media screen'
1459       }
1460       this.processAtOnce = Math.max(Math.floor((this.processAtOnce+1)/2),1);
1461       jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}');
1462       jsMath.Macro('angle','\\raise1.84pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\kern.5pt\\rule{.4em}{-1.5pt}{1.84pt}\\kern2.5mu}');
1463     }
1464   },
1465
1466   //
1467   //  Handle Netscape/Mozilla (any flavor)
1468   //
1469   Mozilla: function () {
1470     if (jsMath.hidden.ATTRIBUTE_NODE && jsMath.window.directories) {
1471       jsMath.browser = 'Mozilla';
1472       if (jsMath.platform == 'pc') {this.alphaPrintBug = 1}
1473       this.allowAbsoluteDelim = 1;
1474       jsMath.styles['#jsMath_button'].cursor = jsMath.styles['#jsMath_noFont .link'].cursor = 'pointer',
1475       jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}');
1476       jsMath.Macro('angle','\\raise1.34pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\rule{.4em}{-1pt}{1.34pt}\\kern2.5mu}');
1477       if (navigator.vendor == 'Firefox') {
1478         this.version = navigator.vendorSub;
1479       } else if (navigator.userAgent.match(' Firefox/([0-9.]+)([a-z ]|$)')) {
1480         this.version = RegExp.$1;
1481       }
1482       if (this.VersionAtLeast("3.0")) {this.mozImageSizeBug = this.lineBreakBug = 1}
1483     }
1484   },
1485   
1486   //
1487   //  Handle OmniWeb
1488   //
1489   OmniWeb: function () {
1490     if (navigator.accentColorName) {
1491       jsMath.browser = 'OmniWeb';
1492       this.allowAbsolute = this.hasInlineBlock;
1493       this.allowAbsoluteDelim = this.allowAbsolute;
1494       this.valignBug = !this.allowAbsolute;
1495       this.buttonCheck = 1; this.textNodeBug = 1;
1496       jsMath.noChangeGlobal = 1; // OmniWeb craches on GoGlobal
1497       if (!this.hasInlineBlock) {jsMath.Setup.Script('jsMath-old-browsers.js')}
1498     }
1499   },
1500     
1501   //
1502   //  Handle Opera
1503   //
1504   Opera: function () {
1505     if (this.spanHeightTooBig) {
1506       jsMath.browser = 'Opera';
1507       var isOld = navigator.userAgent.match("Opera 7");
1508       this.allowAbsolute = 0;
1509       this.delay = 10;
1510       this.operaHiddenFix = '[Processing]';
1511       if (isOld) {jsMath.Setup.Script('jsMath-old-browsers.js')}
1512       var version = navigator.appVersion.match(/^(\d+\.\d+)/);
1513       if (version) {this.version = version[1]} else {this.vesion = 0}
1514       this.operaAbsoluteWidthBug = this.operaLineHeightBug = (version[1] >= 9.5 && version[1] < 9.6);
1515     }
1516   },
1517
1518   //
1519   //  Handle Safari
1520   //
1521   Safari: function () {
1522     if (navigator.appVersion.match(/Safari\//)) {
1523       jsMath.browser = 'Safari';
1524       if (navigator.vendor.match(/Google/)) {jsMath.browser = 'Chrome'}
1525       var version = navigator.userAgent.match("Safari/([0-9]+)");
1526       version = (version)? version[1] : 400; this.version = version;
1527       jsMath.TeX.axis_height += .05;
1528       this.allowAbsoluteDelim = version >= 125;
1529       this.safariIFRAMEbug = version >= 312 && version < 412;
1530       this.safariButtonBug = version < 412;
1531       this.safariImgBug = 1; this.textNodeBug = 1;
1532       this.lineBreakBug = version >= 526;
1533       this.buttonCheck = version < 500;
1534       this.styleChangeDelay = 1;
1535       jsMath.Macro('not','\\mathrel{\\rlap{\\kern3.25mu/}}');
1536     }
1537   },
1538   
1539   //
1540   //  Handle Konqueror
1541   //
1542   Konqueror: function () {
1543     if (navigator.product && navigator.product.match("Konqueror")) {
1544       jsMath.browser = 'Konqueror';
1545       this.allowAbsolute = 0;
1546       this.allowAbsoluteDelim = 0;
1547       if (navigator.userAgent.match(/Konqueror\/(\d+)\.(\d+)/)) {
1548         if (RegExp.$1 < 3 || (RegExp.$1 == 3 && RegExp.$2 < 3)) {
1549           this.separateSkips = 1;
1550           this.valignBug = 1;
1551           jsMath.Setup.Script('jsMath-old-browsers.js');
1552         }
1553       }
1554       //  Apparently, Konqueror wants the names without the hyphen
1555       jsMath.Add(jsMath.styles,{
1556         '.typeset .cmr10':  'font-family: jsMath-cmr10, jsMath cmr10, serif',
1557         '.typeset .cmbx10': 'font-family: jsMath-cmbx10, jsMath cmbx10, jsMath-cmr10, jsMath cmr10',
1558         '.typeset .cmti10': 'font-family: jsMath-cmti10, jsMath cmti10, jsMath-cmr10, jsMath cmr10',
1559         '.typeset .cmmi10': 'font-family: jsMath-cmmi10, jsMath cmmi10',
1560         '.typeset .cmsy10': 'font-family: jsMath-cmsy10, jsMath cmsy10',
1561         '.typeset .cmex10': 'font-family: jsMath-cmex10, jsMath cmex10'
1562       });
1563       jsMath.Font.testFont = "jsMath-cmex10, jsMath cmex10";
1564     }
1565   }
1566
1567 };
1568
1569 /***************************************************************************/
1570
1571 /*
1572  *  Implement font check and messages
1573  */
1574 jsMath.Font = {
1575   
1576   testFont: "jsMath-cmex10",
1577   fallback: "symbol", // the default fallback method
1578   register: [],       // list of fonts registered before jsMath.Init()
1579
1580   // the HTML for the missing font message
1581   message:    
1582     '<b>No jsMath TeX fonts found</b> -- using image fonts instead.<br/>\n'
1583       + 'These may be slow and might not print well.<br/>\n'
1584       + 'Use the jsMath control panel to get additional information.',
1585       
1586   extra_message:
1587     'Extra TeX fonts not found: <b><span id="jsMath_ExtraFonts"></span></b><br/>'
1588       + 'Using image fonts instead.  This may be slow and might not print well.<br/>\n'
1589       + 'Use the jsMath control panel to get additional information.',
1590
1591   print_message:
1592     'To print higher-resolution math symbols, click the<br/>\n'
1593        + '<b>Hi-Res Fonts for Printing</b> button on the jsMath control panel.<br/>\n',
1594
1595   alpha_message:
1596     'If the math symbols print as black boxes, turn off <b>image alpha channels</b><br/>\n'
1597        + 'using the <B>Options</B> pane of the jsMath control panel.<br/>\n',
1598   
1599   /*
1600    *  Look to see if a font is found.
1601    *  Check the character in a given position, and see if it is
1602    *  wider than the usual one in that position.
1603    */
1604   Test1: function (name,n,factor,prefix) {
1605     if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''}
1606     var wh1 = jsMath.BBoxFor('<span style="font-family: '+prefix+name+', serif">'+jsMath.TeX[name][n].c+'</span>');
1607     var wh2 = jsMath.BBoxFor('<span style="font-family: serif">'+jsMath.TeX[name][n].c+'</span>');
1608     //alert([wh1.w,wh2.w,wh1.h,factor*wh2.w]);
1609     return (wh1.w > factor*wh2.w && wh1.h != 0);
1610   },
1611
1612   Test2: function (name,n,factor,prefix) {
1613     if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''}
1614     var wh1 = jsMath.BBoxFor('<span style="font-family: '+prefix+name+', serif">'+jsMath.TeX[name][n].c+'</span>');
1615     var wh2 = jsMath.BBoxFor('<span style="font-family: serif">'+jsMath.TeX[name][n].c+'</span>');
1616     //alert([wh2.w,wh1.w,wh1.h,factor*wh1.w]);
1617     return (wh2.w > factor*wh1.w && wh1.h != 0);
1618   },
1619   
1620   /*
1621    *  Check for the new jsMath versions of the fonts (blacker with
1622    *  different encoding) and if not found, look for old-style fonts.
1623    *  If they are found, load the BaKoMa encoding information.
1624    */
1625   CheckTeX: function () {
1626     var wh = jsMath.BBoxFor('<span style="font-family: '+jsMath.Font.testFont+', serif">'+jsMath.TeX.cmex10[1].c+'</span>');
1627     jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10',null,null,'jsMath-'));
1628     if (!jsMath.nofonts) return;
1629     /* 
1630      * if (jsMath.browser != 'Mozilla' ||
1631      *      (jsMath.platform == "mac" &&
1632      *        (!jsMath.Browser.VersionAtLeast(1.5) || jsMath.Browser.VersionAtLeast(3.0))) ||
1633      *      (jsMath.platform != "mac" && !jsMath.Browser.VersionAtLeast(3.0))) {
1634      *   wh = jsMath.BBoxFor('<span style="font-family: CMEX10, serif">'+jsMath.TeX.cmex10[1].c+'</span>');
1635      *   jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10'));
1636      *   if (!jsMath.nofonts) {jsMath.Setup.Script("jsMath-BaKoMa-fonts.js")}
1637      * }
1638      */
1639   },
1640   
1641   /*
1642    *  Check for the availability of TeX fonts.  We do this by looking at
1643    *  the width and height of a character in the cmex10 font.  The cmex10
1644    *  font has depth considerably greater than most characters' widths (the
1645    *  whole font has the depth of the character with greatest depth).  This
1646    *  is not the case for most fonts, so if we can access cmex10, the
1647    *  height of a character should be much bigger than the width.
1648    *  Otherwise, if we don't have cmex10, we'll get a character in another
1649    *  font with normal height and width.  In this case, we insert a message
1650    *  pointing the user to the jsMath site, and load one of the fallback
1651    *  definitions.
1652    *  
1653    */
1654   Check: function () {
1655     var cookie = jsMath.Controls.cookie;
1656     this.CheckTeX();
1657     if (jsMath.nofonts) {
1658       if (cookie.autofont || cookie.font == 'tex') {
1659         cookie.font = this.fallback;
1660         if (cookie.warn) {
1661           jsMath.nofontMessage = 1;
1662           cookie.warn = 0; jsMath.Controls.SetCookie(0);
1663           if (jsMath.window.NoFontMessage) {jsMath.window.NoFontMessage()}
1664                                else {this.Message(this.message)}
1665         }
1666       }
1667     } else {
1668       if (cookie.autofont) {cookie.font = 'tex'}
1669       if (cookie.font == 'tex') return;
1670     }
1671     if (jsMath.noImgFonts) {cookie.font = 'unicode'}
1672     if (cookie.font == 'unicode') {
1673       jsMath.Setup.Script('jsMath-fallback-'+jsMath.platform+'.js');
1674       jsMath.Box.TeXnonfallback = jsMath.Box.TeX;
1675       jsMath.Box.TeX = jsMath.Box.TeXfallback;
1676       return;
1677     }
1678     if (!cookie.print && cookie.printwarn) {
1679       this.PrintMessage(
1680         (jsMath.Browser.alphaPrintBug && jsMath.Controls.cookie.alpha) ?
1681           this.print_message + this.alpha_message : this.print_message);
1682     }
1683     if (jsMath.Browser.waitForImages) {
1684       jsMath.Script.Push(jsMath.Script,"WaitForImage",jsMath.blank);
1685     }
1686     if (cookie.font == 'symbol') {
1687       jsMath.Setup.Script('jsMath-fallback-symbols.js');
1688       jsMath.Box.TeXnonfallback = jsMath.Box.TeX;
1689       jsMath.Box.TeX = jsMath.Box.TeXfallback;
1690       return;
1691     }
1692     jsMath.Img.SetFont({
1693       cmr10:  ['all'], cmmi10: ['all'], cmsy10: ['all'],
1694       cmex10: ['all'], cmbx10: ['all'], cmti10: ['all']
1695     });
1696     jsMath.Img.LoadFont('cm-fonts');
1697   },
1698
1699   /*
1700    *  The message for when no TeX fonts.  You can eliminate this message
1701    *  by including
1702    *  
1703    *      <script>jsMath = {Font: {Message: function () {}}}</script>
1704    *
1705    *  in your HTML file, before loading jsMath.js, if you want.  But this
1706    *  means the user may not know that he or she can get a better version
1707    *  of your page.
1708    */
1709   Message: function (message) {
1710     if (jsMath.Element("Warning")) return;
1711     var div = jsMath.Setup.DIV("Warning",{});
1712     div.innerHTML = 
1713       '<center><table><tr><td>'
1714       + '<div id="jsMath_noFont"><div class="message">' + message
1715       + '<div style="text-align:left"><span style="float:left; margin: 8px 0px 0px 20px">'
1716       + '<span onclick="jsMath.Controls.Panel()" title=" Open the jsMath Control Panel " class="link">jsMath Control Panel</span>'
1717       + '</span><span style="margin: 8px 20px 0px 0px; float:right">'
1718       + '<span onclick="jsMath.Font.HideMessage()" title=" Remove this font warning message " class="link">Hide this Message</span>'
1719       + '</span></div><div style="height:6px"></div><br clear="all"/></div></div>'
1720       + '<div style="width:22em; height:1px"></div>'
1721       + '</td></tr></table></center><hr/>';
1722   },
1723   
1724   HideMessage: function () {
1725     var message = jsMath.Element("Warning");
1726     if (message) {message.style.display = "none"}
1727   },
1728   
1729   PrintMessage: function (message) {
1730     if (jsMath.Element("PrintWarning")) return;
1731     var div = jsMath.Setup.DIV("PrintWarning",{});
1732     div.innerHTML = 
1733       '<center><table><tr><td>'
1734       + '<div class="message">' + message + '</div>'
1735       + '<div style="width:22em; height:1px"></div>'
1736       + '</td></tr></table></center><hr/>';
1737   },
1738   
1739   /*
1740    *  Register an extra font so jsMath knows about it
1741    */
1742   Register: function (data,force) {
1743     if (typeof(data) == 'string') {data = {name: data}}
1744     if (!jsMath.Setup.inited && !force) {
1745       this.register[this.register.length] = data;
1746       return;
1747     }
1748     var fontname = data.name; var name = fontname.replace(/10$/,'');
1749     var fontfam = jsMath.TeX.fam.length;
1750     if (data.prefix == null) {data.prefix = ""}
1751     if (!data.style) {data.style = "font-family: "+data.prefix+fontname+", serif"}
1752     if (!data.styles) {data.styles = {}}
1753     if (!data.macros) {data.macros = {}}
1754     /*
1755      *  Register font family
1756      */
1757     jsMath.TeX.fam[fontfam] = fontname;
1758     jsMath.TeX.famName[fontname] = fontfam;
1759     data.macros[name] = ['HandleFont',fontfam];
1760     jsMath.Add(jsMath.Parser.prototype.macros,data.macros);
1761     /*
1762      *  Set up styles
1763      */
1764     data.styles['.typeset .'+fontname] = data.style;
1765     jsMath.Setup.Styles(data.styles);
1766     if (jsMath.initialized) {jsMath.Script.Push(jsMath.Setup,'TeXfont',fontname)}
1767     /*
1768      *  Check for font and give message if missing
1769      */
1770     var cookie = jsMath.Controls.cookie;
1771     var hasTeXfont = !jsMath.nofonts &&
1772                       data.test(fontname,data.testChar,data.testFactor,data.prefix);
1773     if (hasTeXfont && cookie.font == 'tex') {
1774       if (data.tex) {data.tex(fontname,fontfam,data)}
1775       return;
1776     }
1777     if (!hasTeXfont && cookie.warn && cookie.font == 'tex' && !jsMath.nofonts) {
1778       if (!cookie.fonts.match("/"+fontname+"/")) {
1779         cookie.fonts += fontname + "/"; jsMath.Controls.SetCookie(0);
1780         if (!jsMath.Element("Warning")) this.Message(this.extra_message);
1781         var extra = jsMath.Element("ExtraFonts");
1782         if (extra) {
1783           if (extra.innerHTML != "") {extra.innerHTML += ','}
1784           extra.innerHTML += " " + data.prefix+fontname;
1785         }
1786       }
1787     }
1788     if (cookie.font == 'unicode' || jsMath.noImgFonts) {
1789       if (data.fallback) {data.fallback(fontname,fontfam,data)}
1790       return;
1791     }
1792     //  Image fonts
1793     var font = {};
1794     if (cookie.font == 'symbol' && data.symbol != null) {
1795       font[fontname] = data.symbol(fontname,fontfam,data);
1796     } else {
1797       font[fontname] = ['all'];
1798     }
1799     jsMath.Img.SetFont(font);
1800     jsMath.Img.LoadFont(fontname);
1801     if (jsMath.initialized) {
1802       jsMath.Script.Push(jsMath.Img,'Scale');
1803       jsMath.Script.Push(jsMath.Img,'UpdateFonts');
1804     }
1805   },
1806   /*
1807    *  If fonts are registered before jsMath.Init() is called, jsMath.em
1808    *  will not be available, so they need to be delayed.
1809    */
1810   LoadRegistered: function () {
1811     var i = 0;
1812     while (i < this.register.length) {this.Register(this.register[i++],1)}
1813     this.register = [];
1814   },
1815
1816   /*
1817    *  Load a font
1818    */
1819   Load: function (name) {jsMath.Setup.Script(this.URL(name))},
1820   URL: function (name) {return jsMath.Img.root+name+'/def.js'}
1821   
1822 };
1823
1824 /***************************************************************************/
1825
1826 /*
1827  *  Implements the jsMath control panel.
1828  *  Much of the code is in jsMath-controls.html, which is
1829  *  loaded into a hidden IFRAME on demand
1830  */
1831 jsMath.Controls = {
1832
1833   //  Data stored in the jsMath cookie
1834   cookie: {
1835     scale: 100,
1836     font: 'tex', autofont: 1, scaleImg: 0, alpha: 1,
1837     warn: 1, fonts: '/', printwarn: 1, stayhires: 0,
1838     button: 1, progress: 1, asynch: 0, blank: 0,
1839     print: 0, keep: '0D', global: 'auto', hiddenGlobal: 1
1840   },
1841   
1842   cookiePath: '/',  // can also set cookieDomain
1843   noCookiePattern: /^(file|mk):$/,  // pattern for handling cookies locally
1844   
1845   
1846   /*
1847    *  Create the HTML needed for control panel
1848    */
1849   Init: function () {
1850     this.panel = jsMath.Setup.DIV("panel",{display:'none'},jsMath.fixedDiv);
1851     if (!jsMath.Browser.msieButtonBug) {this.Button()}
1852       else {setTimeout("jsMath.Controls.Button()",500)}
1853   },
1854
1855   /*
1856    *  Load the control panel
1857    */
1858   Panel: function () {
1859     jsMath.Translate.Cancel();
1860     if (this.loaded) {this.Main()}
1861       else {jsMath.Script.delayedLoad(jsMath.root+"jsMath-controls.html")}
1862   },
1863   
1864   /*
1865    *  Create the control panel button
1866    */
1867   Button: function () {
1868     var button = jsMath.Setup.DIV("button",{},jsMath.fixedDiv);
1869     button.title = ' Open jsMath Control Panel ';
1870     button.innerHTML = 
1871       '<span onclick="jsMath.Controls.Panel()">jsMath</span>';
1872     if (!jsMath.Global.isLocal && !jsMath.noShowGlobal) {
1873       button.innerHTML +=
1874         '<span id="jsMath_global" title=" Open jsMath Global Panel " '
1875           + 'onclick="jsMath.Global.Show(1)">Global&nbsp;</span>';
1876     }
1877     if (button.offsetWidth < 30) {button.style.width = "auto"}
1878     if (!this.cookie.button) {button.style.display = "none"}
1879   },
1880   
1881   /*
1882    *  Since MSIE doesn't handle position:float, we need to have the
1883    *  window repositioned every time the window scrolls.  We do that
1884    *  putting the floating elements into a window-sized DIV, but
1885    *  absolutely positioned, and then move the DIV.
1886    */
1887   MoveButton: function () {
1888     var body = (jsMath.Browser.quirks ? document.body : document.documentElement);
1889     jsMath.fixedDiv.style.left = body.scrollLeft + 'px';
1890     jsMath.fixedDiv.style.top = body.scrollTop + body.clientHeight + 'px';
1891     jsMath.fixedDiv.style.width = body.clientWidth + 'px';
1892 //    jsMath.fixedDiv.style.top = body.scrollTop + 'px';
1893 //    jsMath.fixedDiv.style.height = body.clientHeight + 'px';
1894   },
1895
1896   /*
1897    *  Get the cookie data from the browser
1898    *  (for file: references, use url '?' syntax)
1899    */
1900   GetCookie: function () {
1901     // save the current cookie settings as the defaults
1902     if (this.defaults == null) {this.defaults = {}}
1903     jsMath.Add(this.defaults,this.cookie); this.userSet = {};
1904     // get the browser's cookie data
1905     var cookies = jsMath.document.cookie;
1906     if (jsMath.window.location.protocol.match(this.noCookiePattern)) {
1907       cookies = this.localGetCookie();
1908       this.isLocalCookie = 1;
1909     }
1910     if (cookies.match(/jsMath=([^;]+)/)) {
1911       var data = unescape(RegExp.$1).split(/,/);
1912       for (var i = 0; i < data.length; i++) {
1913         var x = data[i].match(/(.*):(.*)/);
1914         if (x[2].match(/^\d+$/)) {x[2] = 1*x[2]} // convert from string
1915         this.cookie[x[1]] = x[2];
1916         this.userSet[x[1]] = 1;
1917       }
1918     }
1919   },
1920   localGetCookie: function () {
1921     return jsMath.window.location.search.substr(1);
1922   },
1923   
1924   /*
1925    *  Save the cookie data in the browser
1926    *  (for file: urls, append data like CGI reference)
1927    */
1928   SetCookie: function (warn) {
1929     var cookie = [];
1930     for (var id in this.cookie) {
1931       if (this.defaults[id] == null || this.cookie[id] != this.defaults[id])
1932         {cookie[cookie.length] = id + ':' + this.cookie[id]}
1933     }
1934     cookie = cookie.join(',');
1935     if (this.isLocalCookie) {
1936       if (warn == 2) {return 'jsMath='+escape(cookie)}
1937       this.localSetCookie(cookie,warn);
1938     } else {
1939       cookie = escape(cookie);
1940       if (cookie == '') {warn = 0}
1941       if (this.cookiePath) {cookie += '; path='+this.cookiePath}
1942       if (this.cookieDomain) {cookie += '; domain='+this.cookieDomain}
1943       if (this.cookie.keep != '0D') {
1944         var ms = {
1945           D: 1000*60*60*24,
1946           W: 1000*60*60*24*7,
1947           M: 1000*60*60*24*30,
1948           Y: 1000*60*60*24*365
1949         };
1950         var exp = new Date;
1951         exp.setTime(exp.getTime() +
1952             this.cookie.keep.substr(0,1) * ms[this.cookie.keep.substr(1,1)]);
1953         cookie += '; expires=' + exp.toGMTString();
1954       }
1955       if (cookie != '') {
1956         jsMath.document.cookie = 'jsMath='+cookie;
1957         var cookies = jsMath.document.cookie;
1958         if (warn && !cookies.match(/jsMath=/))
1959           {alert("Cookies must be enabled in order to save jsMath options")}
1960       }
1961     }
1962     return null;
1963   },
1964   localSetCookie: function (cookie,warn) {
1965     if (!warn) return;
1966     var href = String(jsMath.window.location).replace(/\?.*/,"");
1967     if (cookie != '') {href += '?jsMath=' + escape(cookie)}
1968     if (href != jsMath.window.location.href) {this.Reload(href)}
1969   },
1970   
1971   /*
1972    *  Reload the page (with the given URL)
1973    */
1974   Reload: function (url) {
1975     if (!this.loaded) return;
1976     this.loaded = 0; jsMath.Setup.inited = -100;
1977     jsMath.Global.ClearCache();
1978     if (url) {jsMath.window.location.replace(url)}
1979         else {jsMath.window.location.reload()}
1980   }
1981   
1982 };
1983
1984 /***************************************************************************/
1985
1986 /*
1987  *  Implements the actions for clicking and double-clicking
1988  *  on math formulas
1989  */
1990 jsMath.Click = {
1991   
1992   /*
1993    *  Handle clicking on math to get control panel
1994    */
1995   CheckClick: function (event) {
1996     if (!event) {event = jsMath.window.event}
1997     if (event.altKey) jsMath.Controls.Panel();
1998   },
1999   
2000   /*
2001    *  Handle double-click for seeing TeX code
2002    */
2003
2004   CheckDblClick: function (event) {
2005     if (!event) {event = jsMath.window.event}
2006     if (!jsMath.Click.DblClick) {
2007       jsMath.Extension.Require('double-click',1);
2008       // Firefox clears the event, so copy it
2009       var tmpEvent = event; event = {};
2010       for (var id in tmpEvent) {event[id] = tmpEvent[id]}
2011     }
2012     jsMath.Script.Push(jsMath.Click,'DblClick',[event,this.alt]);
2013   }
2014   
2015 };
2016
2017 /***************************************************************************/
2018
2019 /*
2020  *  The TeX font information
2021  */
2022 jsMath.TeX = {
2023
2024   //
2025   //  The TeX font parameters
2026   //
2027   thinmuskip:   3/18,
2028   medmuskip:    4/18,
2029   thickmuskip:  5/18,
2030
2031   x_height:    .430554,
2032   quad:        1,
2033   num1:        .676508,
2034   num2:        .393732,
2035   num3:        .44373,
2036   denom1:      .685951,
2037   denom2:      .344841,
2038   sup1:        .412892,
2039   sup2:        .362892,
2040   sup3:        .288888,
2041   sub1:        .15,
2042   sub2:        .247217,
2043   sup_drop:    .386108,
2044   sub_drop:    .05,
2045   delim1:     2.39,
2046   delim2:     1.0,
2047   axis_height: .25,
2048   default_rule_thickness: .06,
2049   big_op_spacing1:  .111111,
2050   big_op_spacing2:  .166666,
2051   big_op_spacing3:  .2,
2052   big_op_spacing4:  .6,
2053   big_op_spacing5:  .1,
2054
2055   integer:          6553.6,     // conversion of em's to TeX internal integer
2056   scriptspace:         .05,
2057   nulldelimiterspace:  .12,
2058   delimiterfactor:     901,
2059   delimitershortfall:   .5,
2060   scale:                 1,     //  scaling factor for font dimensions
2061  
2062   //  The TeX math atom types (see Appendix G of the TeXbook)
2063   atom: ['ord', 'op', 'bin', 'rel', 'open', 'close', 'punct', 'ord'],
2064
2065   //  The TeX font families
2066   fam: ['cmr10','cmmi10','cmsy10','cmex10','cmti10','','cmbx10',''],
2067   famName: {cmr10:0, cmmi10:1, cmsy10:2, cmex10:3, cmti10:4, cmbx10:6},
2068
2069   //  Encoding used by jsMath fonts
2070   encoding: [
2071     '&#xC0;', '&#xC1;', '&#xC2;', '&#xC3;', '&#xC4;', '&#xC5;', '&#xC6;', '&#xC7;',
2072     '&#xC8;', '&#xC9;', '&#xCA;', '&#xCB;', '&#xCC;', '&#xCD;', '&#xCE;', '&#xCF;',
2073
2074     '&#xB0;', '&#xD1;', '&#xD2;', '&#xD3;', '&#xD4;', '&#xD5;', '&#xD6;', '&#xB7;',
2075     '&#xD8;', '&#xD9;', '&#xDA;', '&#xDB;', '&#xDC;', '&#xB5;', '&#xB6;', '&#xDF;',
2076
2077     '&#xEF;', '!', '&#x22;', '#', '$', '%', '&#x26;', '&#x27;',
2078     '(', ')', '*', '+', ',', '-', '.', '/',
2079
2080     '0', '1', '2', '3', '4', '5', '6', '7',
2081     '8', '9', ':', ';', '&#x3C;', '=', '&#x3E;', '?',
2082
2083     '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
2084     'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
2085
2086     'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
2087     'X', 'Y', 'Z', '[', '&#x5C;', ']', '^', '_',
2088
2089     '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
2090     'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
2091
2092     'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
2093     'x', 'y', 'z', '{', '|', '}', '&#x7E;', '&#xFF;'
2094   ],
2095
2096   /*
2097    *  The following are the TeX font mappings and metrics.  The metric
2098    *  information comes directly from the TeX .tfm files.  Browser-specific
2099    *  adjustments are made to these tables in the Browser.Init() routine
2100    */
2101   cmr10: [
2102     [0.625,0.683], [0.833,0.683], [0.778,0.683], [0.694,0.683],
2103     [0.667,0.683], [0.75,0.683], [0.722,0.683], [0.778,0.683],
2104     [0.722,0.683], [0.778,0.683], [0.722,0.683],
2105     [0.583,0.694,0,{ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}}],
2106     [0.556,0.694], [0.556,0.694], [0.833,0.694], [0.833,0.694],
2107
2108     [0.278,0.431], [0.306,0.431,0.194], [0.5,0.694], [0.5,0.694],
2109     [0.5,0.628], [0.5,0.694], [0.5,0.568], [0.75,0.694],
2110     [0.444,0,0.17], [0.5,0.694], [0.722,0.431], [0.778,0.431],
2111     [0.5,0.528,0.0972], [0.903,0.683], [1.01,0.683], [0.778,0.732,0.0486],
2112
2113     [0.278,0.431,0,{krn: {'108': -0.278, '76': -0.319}}],
2114     [0.278,0.694,0,{lig: {'96': 60}}],
2115     [0.5,0.694], [0.833,0.694,0.194], [0.5,0.75,0.0556],
2116     [0.833,0.75,0.0556], [0.778,0.694],
2117     [0.278,0.694,0,{krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}}],
2118     [0.389,0.75,0.25], [0.389,0.75,0.25], [0.5,0.75],
2119     [0.778,0.583,0.0833], [0.278,0.106,0.194],
2120     [0.333,0.431,0,{lig: {'45': 123}}],
2121     [0.278,0.106], [0.5,0.75,0.25],
2122
2123     [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644],
2124     [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644],
2125     [0.5,0.644], [0.5,0.644], [0.278,0.431], [0.278,0.431,0.194],
2126     [0.278,0.5,0.194], [0.778,0.367,-0.133], [0.472,0.5,0.194],
2127     [0.472,0.694,0,{lig: {'96': 62}}],
2128
2129     [0.778,0.694],
2130     [0.75,0.683,0,{krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}],
2131     [0.708,0.683], [0.722,0.683],
2132     [0.764,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}],
2133     [0.681,0.683],
2134     [0.653,0.683,0,{krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}],
2135     [0.785,0.683], [0.75,0.683], [0.361,0.683,0,{krn: {'73': 0.0278}}],
2136     [0.514,0.683],
2137     [0.778,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}],
2138     [0.625,0.683,0,{krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}],
2139     [0.917,0.683], [0.75,0.683],
2140     [0.778,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}],
2141
2142     [0.681,0.683,0,{krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}],
2143     [0.778,0.683,0.194],
2144     [0.736,0.683,0,{krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}],
2145     [0.556,0.683],
2146     [0.722,0.683,0,{krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}],
2147     [0.75,0.683],
2148     [0.75,0.683,0,{ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}],
2149     [1.03,0.683,0,{ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}],
2150     [0.75,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}],
2151     [0.75,0.683,0,{ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}],
2152     [0.611,0.683], [0.278,0.75,0.25], [0.5,0.694],
2153     [0.278,0.75,0.25], [0.5,0.694], [0.278,0.668],
2154
2155     [0.278,0.694,0,{lig: {'96': 92}}],
2156     [0.5,0.431,0,{krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}],
2157     [0.556,0.694,0,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}],
2158     [0.444,0.431,0,{krn: {'104': -0.0278, '107': -0.0278}}],
2159     [0.556,0.694], [0.444,0.431],
2160     [0.306,0.694,0,{ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}}],
2161     [0.5,0.431,0.194,{ic: 0.0139, krn: {'106': 0.0278}}],
2162     [0.556,0.694,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}],
2163     [0.278,0.668], [0.306,0.668,0.194],
2164     [0.528,0.694,0,{krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}],
2165     [0.278,0.694],
2166     [0.833,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}],
2167     [0.556,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}],
2168     [0.5,0.431,0,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}],
2169
2170     [0.556,0.431,0.194,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}],
2171     [0.528,0.431,0.194], [0.392,0.431], [0.394,0.431],
2172     [0.389,0.615,0,{krn: {'121': -0.0278, '119': -0.0278}}],
2173     [0.556,0.431,0,{krn: {'119': -0.0278}}],
2174     [0.528,0.431,0,{ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}],
2175     [0.722,0.431,0,{ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}],
2176     [0.528,0.431],
2177     [0.528,0.431,0.194,{ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}],
2178     [0.444,0.431], [0.5,0.431,0,{ic: 0.0278, lig: {'45': 124}}],
2179     [1,0.431,0,{ic: 0.0278}], [0.5,0.694], [0.5,0.668], [0.5,0.668]
2180   ],
2181   
2182   cmmi10: [
2183     [0.615,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}],
2184     [0.833,0.683,0,{krn: {'127': 0.167}}],
2185     [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}],
2186     [0.694,0.683,0,{krn: {'127': 0.167}}],
2187     [0.742,0.683,0,{ic: 0.0757, krn: {'127': 0.0833}}],
2188     [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2189     [0.78,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}],
2190     [0.583,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}}],
2191     [0.667,0.683,0,{krn: {'127': 0.0833}}],
2192     [0.612,0.683,0,{ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2193     [0.772,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}],
2194     [0.64,0.431,0,{ic: 0.0037, krn: {'127': 0.0278}}],
2195     [0.566,0.694,0.194,{ic: 0.0528, krn: {'127': 0.0833}}],
2196     [0.518,0.431,0.194,{ic: 0.0556}],
2197     [0.444,0.694,0,{ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2198     [0.406,0.431,0,{krn: {'127': 0.0556}}],
2199
2200     [0.438,0.694,0.194,{ic: 0.0738, krn: {'127': 0.0833}}],
2201     [0.497,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}],
2202     [0.469,0.694,0,{ic: 0.0278, krn: {'127': 0.0833}}],
2203     [0.354,0.431,0,{krn: {'127': 0.0556}}],
2204     [0.576,0.431], [0.583,0.694],
2205     [0.603,0.431,0.194,{krn: {'127': 0.0278}}],
2206     [0.494,0.431,0,{ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}],
2207     [0.438,0.694,0.194,{ic: 0.046, krn: {'127': 0.111}}],
2208     [0.57,0.431,0,{ic: 0.0359}],
2209     [0.517,0.431,0.194,{krn: {'127': 0.0833}}],
2210     [0.571,0.431,0,{ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}}],
2211     [0.437,0.431,0,{ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}],
2212     [0.54,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}],
2213     [0.596,0.694,0.194,{krn: {'127': 0.0833}}],
2214     [0.626,0.431,0.194,{krn: {'127': 0.0556}}],
2215
2216     [0.651,0.694,0.194,{ic: 0.0359, krn: {'127': 0.111}}],
2217     [0.622,0.431,0,{ic: 0.0359}],
2218     [0.466,0.431,0,{krn: {'127': 0.0833}}],
2219     [0.591,0.694,0,{krn: {'127': 0.0833}}],
2220     [0.828,0.431,0,{ic: 0.0278}],
2221     [0.517,0.431,0.194,{krn: {'127': 0.0833}}],
2222     [0.363,0.431,0.0972,{ic: 0.0799, krn: {'127': 0.0833}}],
2223     [0.654,0.431,0.194,{krn: {'127': 0.0833}}],
2224     [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], 
2225     [0.278,0.464,-0.0363], [0.278,0.464,-0.0363], [0.5,0.465,-0.0347], [0.5,0.465,-0.0347],
2226
2227     [0.5,0.431], [0.5,0.431], [0.5,0.431], [0.5,0.431,0.194],
2228     [0.5,0.431,0.194], [0.5,0.431,0.194], [0.5,0.644], [0.5,0.431,0.194],
2229     [0.5,0.644], [0.5,0.431,0.194], [0.278,0.106], [0.278,0.106,0.194],
2230     [0.778,0.539,0.0391],
2231     [0.5,0.75,0.25,{krn: {'1': -0.0556, '65': -0.0556, '77': -0.0556, '78': -0.0556, '89': 0.0556, '90': -0.0556}}],
2232     [0.778,0.539,0.0391], [0.5,0.465,-0.0347],
2233
2234     [0.531,0.694,0,{ic: 0.0556, krn: {'127': 0.0833}}],
2235     [0.75,0.683,0,{krn: {'127': 0.139}}],
2236     [0.759,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}],
2237     [0.715,0.683,0,{ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2238     [0.828,0.683,0,{ic: 0.0278, krn: {'127': 0.0556}}],
2239     [0.738,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}],
2240     [0.643,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}],
2241     [0.786,0.683,0,{krn: {'127': 0.0833}}],
2242     [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2243     [0.44,0.683,0,{ic: 0.0785, krn: {'127': 0.111}}],
2244     [0.555,0.683,0,{ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}}],
2245     [0.849,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2246     [0.681,0.683,0,{krn: {'127': 0.0278}}],
2247     [0.97,0.683,0,{ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2248     [0.803,0.683,0,{ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2249     [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}],
2250
2251     [0.642,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}],
2252     [0.791,0.683,0.194,{krn: {'127': 0.0833}}],
2253     [0.759,0.683,0,{ic: 0.00773, krn: {'127': 0.0833}}],
2254     [0.613,0.683,0,{ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2255     [0.584,0.683,0,{ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2256     [0.683,0.683,0,{ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}}],
2257     [0.583,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}],
2258     [0.944,0.683,0,{ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}],
2259     [0.828,0.683,0,{ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2260     [0.581,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}],
2261     [0.683,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}],
2262     [0.389,0.75], [0.389,0.694,0.194], [0.389,0.694,0.194],
2263     [1,0.358,-0.142], [1,0.358,-0.142],
2264
2265     [0.417,0.694,0,{krn: {'127': 0.111}}],
2266     [0.529,0.431], [0.429,0.694], [0.433,0.431,0,{krn: {'127': 0.0556}}],
2267     [0.52,0.694,0,{krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}}],
2268     [0.466,0.431,0,{krn: {'127': 0.0556}}],
2269     [0.49,0.694,0.194,{ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}}],
2270     [0.477,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0278}}],
2271     [0.576,0.694,0,{krn: {'127': -0.0278}}], [0.345,0.66],
2272     [0.412,0.66,0.194,{ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}}],
2273     [0.521,0.694,0,{ic: 0.0315}], [0.298,0.694,0,{ic: 0.0197, krn: {'127': 0.0833}}],
2274     [0.878,0.431], [0.6,0.431], [0.485,0.431,0,{krn: {'127': 0.0556}}],
2275
2276     [0.503,0.431,0.194,{krn: {'127': 0.0833}}],
2277     [0.446,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0833}}],
2278     [0.451,0.431,0,{ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}],
2279     [0.469,0.431,0,{krn: {'127': 0.0556}}], [0.361,0.615,0,{krn: {'127': 0.0833}}],
2280     [0.572,0.431,0,{krn: {'127': 0.0278}}],
2281     [0.485,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}],
2282     [0.716,0.431,0,{ic: 0.0269, krn: {'127': 0.0833}}],
2283     [0.572,0.431,0,{krn: {'127': 0.0278}}],
2284     [0.49,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}],
2285     [0.465,0.431,0,{ic: 0.044, krn: {'127': 0.0556}}],
2286     [0.322,0.431,0,{krn: {'127': 0.0278}}],
2287     [0.384,0.431,0.194,{krn: {'127': 0.0833}}],
2288     [0.636,0.431,0.194,{krn: {'127': 0.111}}],
2289     [0.5,0.714,0,{ic: 0.154}], [0.278,0.694,0,{ic: 0.399}]
2290   ],
2291
2292   cmsy10: [
2293     [0.778,0.583,0.0833], [0.278,0.444,-0.0556], [0.778,0.583,0.0833],
2294     [0.5,0.465,-0.0347], [0.778,0.583,0.0833], [0.5,0.444,-0.0556],
2295     [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833],
2296     [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833],
2297     [0.778,0.583,0.0833], [1,0.694,0.194], [0.5,0.444,-0.0556], [0.5,0.444,-0.0556],
2298
2299     [0.778,0.464,-0.0363], [0.778,0.464,-0.0363], [0.778,0.636,0.136],
2300     [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.636,0.136],
2301     [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.367,-0.133],
2302     [0.778,0.483,-0.0169], [0.778,0.539,0.0391], [0.778,0.539,0.0391],
2303     [1,0.539,0.0391], [1,0.539,0.0391], [0.778,0.539,0.0391], [0.778,0.539,0.0391],
2304
2305     [1,0.367,-0.133], [1,0.367,-0.133], [0.5,0.694,0.194], [0.5,0.694,0.194],
2306     [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.464,-0.0363],
2307     [1,0.367,-0.133], [1,0.367,-0.133], [0.611,0.694,0.194], [0.611,0.694,0.194],
2308     [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.431],
2309
2310     [0.275,0.556], [1,0.431], [0.667,0.539,0.0391], [0.667,0.539,0.0391],
2311     [0.889,0.694,0.194], [0.889,0.694,0.194], [0,0.694,0.194], [0,0.367,-0.133],
2312     [0.556,0.694], [0.556,0.694], [0.667,0.431], [0.5,0.75,0.0556],
2313     [0.722,0.694], [0.722,0.694], [0.778,0.694], [0.778,0.694],
2314
2315     [0.611,0.694], [0.798,0.683,0,{krn: {'48': 0.194}}],
2316     [0.657,0.683,0,{ic: 0.0304, krn: {'48': 0.139}}],
2317     [0.527,0.683,0,{ic: 0.0583, krn: {'48': 0.139}}],
2318     [0.771,0.683,0,{ic: 0.0278, krn: {'48': 0.0833}}],
2319     [0.528,0.683,0,{ic: 0.0894, krn: {'48': 0.111}}],
2320     [0.719,0.683,0,{ic: 0.0993, krn: {'48': 0.111}}],
2321     [0.595,0.683,0.0972,{ic: 0.0593, krn: {'48': 0.111}}],
2322     [0.845,0.683,0,{ic: 0.00965, krn: {'48': 0.111}}],
2323     [0.545,0.683,0,{ic: 0.0738, krn: {'48': 0.0278}}],
2324     [0.678,0.683,0.0972,{ic: 0.185, krn: {'48': 0.167}}],
2325     [0.762,0.683,0,{ic: 0.0144, krn: {'48': 0.0556}}],
2326     [0.69,0.683,0,{krn: {'48': 0.139}}], [1.2,0.683,0,{krn: {'48': 0.139}}],
2327     [0.82,0.683,0,{ic: 0.147, krn: {'48': 0.0833}}],
2328     [0.796,0.683,0,{ic: 0.0278, krn: {'48': 0.111}}],
2329
2330     [0.696,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}],
2331     [0.817,0.683,0.0972,{krn: {'48': 0.111}}],
2332     [0.848,0.683,0,{krn: {'48': 0.0833}}],
2333     [0.606,0.683,0,{ic: 0.075, krn: {'48': 0.139}}],
2334     [0.545,0.683,0,{ic: 0.254, krn: {'48': 0.0278}}],
2335     [0.626,0.683,0,{ic: 0.0993, krn: {'48': 0.0833}}],
2336     [0.613,0.683,0,{ic: 0.0822, krn: {'48': 0.0278}}],
2337     [0.988,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}],
2338     [0.713,0.683,0,{ic: 0.146, krn: {'48': 0.139}}],
2339     [0.668,0.683,0.0972,{ic: 0.0822, krn: {'48': 0.0833}}],
2340     [0.725,0.683,0,{ic: 0.0794, krn: {'48': 0.139}}],
2341     [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556],
2342
2343     [0.611,0.694], [0.611,0.694], [0.444,0.75,0.25], [0.444,0.75,0.25],
2344     [0.444,0.75,0.25], [0.444,0.75,0.25], [0.5,0.75,0.25], [0.5,0.75,0.25],
2345     [0.389,0.75,0.25], [0.389,0.75,0.25], [0.278,0.75,0.25], [0.5,0.75,0.25],
2346     [0.5,0.75,0.25], [0.611,0.75,0.25], [0.5,0.75,0.25], [0.278,0.694,0.194],
2347
2348     [0.833,0.04,0.96], [0.75,0.683], [0.833,0.683], [0.417,0.694,0.194,{ic: 0.111}],
2349     [0.667,0.556], [0.667,0.556], [0.778,0.636,0.136], [0.778,0.636,0.136],
2350     [0.444,0.694,0.194], [0.444,0.694,0.194], [0.444,0.694,0.194],
2351     [0.611,0.694,0.194], [0.778,0.694,0.13], [0.778,0.694,0.13],
2352     [0.778,0.694,0.13], [0.778,0.694,0.13]
2353   ],
2354
2355   cmex10: [
2356     [0.458,0.04,1.16,{n: 16}], [0.458,0.04,1.16,{n: 17}],
2357     [0.417,0.04,1.16,{n: 104}], [0.417,0.04,1.16,{n: 105}],
2358     [0.472,0.04,1.16,{n: 106}], [0.472,0.04,1.16,{n: 107}],
2359     [0.472,0.04,1.16,{n: 108}], [0.472,0.04,1.16,{n: 109}],
2360     [0.583,0.04,1.16,{n: 110}], [0.583,0.04,1.16,{n: 111}],
2361     [0.472,0.04,1.16,{n: 68}], [0.472,0.04,1.16,{n: 69}],
2362     [0.333,0,0.6,{delim: {rep: 12}}], [0.556,0,0.6,{delim: {rep: 13}}],
2363     [0.578,0.04,1.16,{n: 46}], [0.578,0.04,1.16,{n: 47}],
2364
2365     [0.597,0.04,1.76,{n: 18}], [0.597,0.04,1.76,{n: 19}],
2366     [0.736,0.04,2.36,{n: 32}], [0.736,0.04,2.36,{n: 33}],
2367     [0.528,0.04,2.36,{n: 34}], [0.528,0.04,2.36,{n: 35}],
2368     [0.583,0.04,2.36,{n: 36}], [0.583,0.04,2.36,{n: 37}],
2369     [0.583,0.04,2.36,{n: 38}], [0.583,0.04,2.36,{n: 39}],
2370     [0.75,0.04,2.36,{n: 40}], [0.75,0.04,2.36,{n: 41}],
2371     [0.75,0.04,2.36,{n: 42}], [0.75,0.04,2.36,{n: 43}],
2372     [1.04,0.04,2.36,{n: 44}], [1.04,0.04,2.36,{n: 45}],
2373
2374     [0.792,0.04,2.96,{n: 48}], [0.792,0.04,2.96,{n: 49}],
2375     [0.583,0.04,2.96,{n: 50}], [0.583,0.04,2.96,{n: 51}],
2376     [0.639,0.04,2.96,{n: 52}], [0.639,0.04,2.96,{n: 53}],
2377     [0.639,0.04,2.96,{n: 54}], [0.639,0.04,2.96,{n: 55}],
2378     [0.806,0.04,2.96,{n: 56}], [0.806,0.04,2.96,{n: 57}],
2379     [0.806,0.04,2.96], [0.806,0.04,2.96],
2380     [1.28,0.04,2.96], [1.28,0.04,2.96],
2381     [0.811,0.04,1.76,{n: 30}], [0.811,0.04,1.76,{n: 31}],
2382
2383     [0.875,0.04,1.76,{delim: {top: 48, bot: 64, rep: 66}}],
2384     [0.875,0.04,1.76,{delim: {top: 49, bot: 65, rep: 67}}],
2385     [0.667,0.04,1.76,{delim: {top: 50, bot: 52, rep: 54}}],
2386     [0.667,0.04,1.76,{delim: {top: 51, bot: 53, rep: 55}}],
2387     [0.667,0.04,1.76,{delim: {bot: 52, rep: 54}}],
2388     [0.667,0.04,1.76,{delim: {bot: 53, rep: 55}}],
2389     [0.667,0,0.6,{delim: {top: 50, rep: 54}}],
2390     [0.667,0,0.6,{delim: {top: 51, rep: 55}}],
2391     [0.889,0,0.9,{delim: {top: 56, mid: 60, bot: 58, rep: 62}}],
2392     [0.889,0,0.9,{delim: {top: 57, mid: 61, bot: 59, rep: 62}}],
2393     [0.889,0,0.9,{delim: {top: 56, bot: 58, rep: 62}}],
2394     [0.889,0,0.9,{delim: {top: 57, bot: 59, rep: 62}}],
2395     [0.889,0,1.8,{delim: {rep: 63}}],
2396     [0.889,0,1.8,{delim: {rep: 119}}],
2397     [0.889,0,0.3,{delim: {rep: 62}}],
2398     [0.667,0,0.6,{delim: {top: 120, bot: 121, rep: 63}}],
2399
2400     [0.875,0.04,1.76,{delim: {top: 56, bot: 59, rep: 62}}],
2401     [0.875,0.04,1.76,{delim: {top: 57, bot: 58, rep: 62}}],
2402     [0.875,0,0.6,{delim: {rep: 66}}], [0.875,0,0.6,{delim: {rep: 67}}],
2403     [0.611,0.04,1.76,{n: 28}], [0.611,0.04,1.76,{n: 29}],
2404     [0.833,0,1,{n: 71}], [1.11,0.1,1.5], [0.472,0,1.11,{ic: 0.194, n: 73}],
2405     [0.556,0,2.22,{ic: 0.444}], [1.11,0,1,{n: 75}], [1.51,0.1,1.5],
2406     [1.11,0,1,{n: 77}], [1.51,0.1,1.5], [1.11,0,1,{n: 79}], [1.51,0.1,1.5],
2407
2408     [1.06,0,1,{n: 88}], [0.944,0,1,{n: 89}], [0.472,0,1.11,{ic: 0.194, n: 90}],
2409     [0.833,0,1,{n: 91}], [0.833,0,1,{n: 92}], [0.833,0,1,{n: 93}],
2410     [0.833,0,1,{n: 94}], [0.833,0,1,{n: 95}], [1.44,0.1,1.5],
2411     [1.28,0.1,1.5], [0.556,0,2.22,{ic: 0.444}], [1.11,0.1,1.5],
2412     [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5],
2413
2414     [0.944,0,1,{n: 97}], [1.28,0.1,1.5], [0.556,0.722,0,{n: 99}],
2415     [1,0.75,0,{n: 100}], [1.44,0.75], [0.556,0.722,0,{n: 102}],
2416     [1,0.75,0,{n: 103}], [1.44,0.75], [0.472,0.04,1.76,{n: 20}],
2417     [0.472,0.04,1.76,{n: 21}], [0.528,0.04,1.76,{n: 22}],
2418     [0.528,0.04,1.76,{n: 23}], [0.528,0.04,1.76,{n: 24}],
2419     [0.528,0.04,1.76,{n: 25}], [0.667,0.04,1.76,{n: 26}],
2420     [0.667,0.04,1.76,{n: 27}],
2421
2422     [1,0.04,1.16,{n: 113}], [1,0.04,1.76,{n: 114}], [1,0.04,2.36,{n: 115}],
2423     [1,0.04,2.96,{n: 116}], [1.06,0,1.8,{delim: {top: 118, bot: 116, rep: 117}}],
2424     [1.06,0,0.6], [1.06,0.04,0.56],
2425     [0.778,0,0.6,{delim: {top: 126, bot: 127, rep: 119}}],
2426     [0.667,0,0.6,{delim: {top: 120, rep: 63}}],
2427     [0.667,0,0.6,{delim: {bot: 121, rep: 63}}],
2428     [0.45,0.12], [0.45,0.12], [0.45,0.12], [0.45,0.12],
2429     [0.778,0,0.6,{delim: {top: 126, rep: 119}}],
2430     [0.778,0,0.6,{delim: {bot: 127, rep: 119}}]
2431   ],
2432   
2433   cmti10: [
2434     [0.627,0.683,0,{ic: 0.133}], [0.818,0.683], [0.767,0.683,0,{ic: 0.094}],
2435     [0.692,0.683], [0.664,0.683,0,{ic: 0.153}], [0.743,0.683,0,{ic: 0.164}],
2436     [0.716,0.683,0,{ic: 0.12}], [0.767,0.683,0,{ic: 0.111}],
2437     [0.716,0.683,0,{ic: 0.0599}], [0.767,0.683,0,{ic: 0.111}],
2438     [0.716,0.683,0,{ic: 0.103}],
2439     [0.613,0.694,0.194,{ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 14, '108': 15}}],
2440     [0.562,0.694,0.194,{ic: 0.103}], [0.588,0.694,0.194,{ic: 0.103}],
2441     [0.882,0.694,0.194,{ic: 0.103}], [0.894,0.694,0.194,{ic: 0.103}],
2442
2443     [0.307,0.431,0,{ic: 0.0767}], [0.332,0.431,0.194,{ic: 0.0374}],
2444     [0.511,0.694], [0.511,0.694,0,{ic: 0.0969}], [0.511,0.628,0,{ic: 0.083}],
2445     [0.511,0.694,0,{ic: 0.108}], [0.511,0.562,0,{ic: 0.103}], [0.831,0.694],
2446     [0.46,0,0.17], [0.537,0.694,0.194,{ic: 0.105}], [0.716,0.431,0,{ic: 0.0751}],
2447     [0.716,0.431,0,{ic: 0.0751}], [0.511,0.528,0.0972,{ic: 0.0919}],
2448     [0.883,0.683,0,{ic: 0.12}], [0.985,0.683,0,{ic: 0.12}],
2449     [0.767,0.732,0.0486,{ic: 0.094}],
2450
2451     [0.256,0.431,0,{krn: {'108': -0.256, '76': -0.321}}],
2452     [0.307,0.694,0,{ic: 0.124, lig: {'96': 60}}],
2453     [0.514,0.694,0,{ic: 0.0696}], [0.818,0.694,0.194,{ic: 0.0662}],
2454     [0.769,0.694], [0.818,0.75,0.0556,{ic: 0.136}],
2455     [0.767,0.694,0,{ic: 0.0969}],
2456     [0.307,0.694,0,{ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}}],
2457     [0.409,0.75,0.25,{ic: 0.162}], [0.409,0.75,0.25,{ic: 0.0369}],
2458     [0.511,0.75,0,{ic: 0.149}], [0.767,0.562,0.0567,{ic: 0.0369}],
2459     [0.307,0.106,0.194], [0.358,0.431,0,{ic: 0.0283, lig: {'45': 123}}],
2460     [0.307,0.106], [0.511,0.75,0.25,{ic: 0.162}],
2461
2462     [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}],
2463     [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}],
2464     [0.511,0.644,0.194,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}],
2465     [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0.194,{ic: 0.136}],
2466     [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}],
2467     [0.307,0.431,0,{ic: 0.0582}], [0.307,0.431,0.194,{ic: 0.0582}],
2468     [0.307,0.5,0.194,{ic: 0.0756}], [0.767,0.367,-0.133,{ic: 0.0662}],
2469     [0.511,0.5,0.194], [0.511,0.694,0,{ic: 0.122, lig: {'96': 62}}],
2470
2471     [0.767,0.694,0,{ic: 0.096}],
2472     [0.743,0.683,0,{krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2473     [0.704,0.683,0,{ic: 0.103}], [0.716,0.683,0,{ic: 0.145}],
2474     [0.755,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}],
2475     [0.678,0.683,0,{ic: 0.12}],
2476     [0.653,0.683,0,{ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}],
2477     [0.774,0.683,0,{ic: 0.0872}], [0.743,0.683,0,{ic: 0.164}],
2478     [0.386,0.683,0,{ic: 0.158}], [0.525,0.683,0,{ic: 0.14}],
2479     [0.769,0.683,0,{ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}],
2480     [0.627,0.683,0,{krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2481     [0.897,0.683,0,{ic: 0.164}], [0.743,0.683,0,{ic: 0.164}],
2482     [0.767,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}],
2483
2484     [0.678,0.683,0,{ic: 0.103, krn: {'65': -0.0767}}],
2485     [0.767,0.683,0.194,{ic: 0.094}],
2486     [0.729,0.683,0,{ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2487     [0.562,0.683,0,{ic: 0.12}],
2488     [0.716,0.683,0,{ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}],
2489     [0.743,0.683,0,{ic: 0.164}],
2490     [0.743,0.683,0,{ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}],
2491     [0.999,0.683,0,{ic: 0.184, krn: {'65': -0.0767}}],
2492     [0.743,0.683,0,{ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}],
2493     [0.743,0.683,0,{ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}],
2494     [0.613,0.683,0,{ic: 0.145}], [0.307,0.75,0.25,{ic: 0.188}],
2495     [0.514,0.694,0,{ic: 0.169}], [0.307,0.75,0.25,{ic: 0.105}],
2496     [0.511,0.694,0,{ic: 0.0665}], [0.307,0.668,0,{ic: 0.118}],
2497
2498     [0.307,0.694,0,{ic: 0.124, lig: {'96': 92}}], [0.511,0.431,0,{ic: 0.0767}],
2499     [0.46,0.694,0,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2500     [0.46,0.431,0,{ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2501     [0.511,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}],
2502     [0.46,0.431,0,{ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2503     [0.307,0.694,0.194,{ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}}],
2504     [0.46,0.431,0.194,{ic: 0.0885}], [0.511,0.694,0,{ic: 0.0767}],
2505     [0.307,0.655,0,{ic: 0.102}], [0.307,0.655,0.194,{ic: 0.145}],
2506     [0.46,0.694,0,{ic: 0.108}], [0.256,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}],
2507     [0.818,0.431,0,{ic: 0.0767}], [0.562,0.431,0,{ic: 0.0767, krn: {'39': -0.102}}],
2508     [0.511,0.431,0,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2509
2510     [0.511,0.431,0.194,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2511     [0.46,0.431,0.194,{ic: 0.0885}],
2512     [0.422,0.431,0,{ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}],
2513     [0.409,0.431,0,{ic: 0.0821}], [0.332,0.615,0,{ic: 0.0949}],
2514     [0.537,0.431,0,{ic: 0.0767}], [0.46,0.431,0,{ic: 0.108}],
2515     [0.664,0.431,0,{ic: 0.108, krn: {'108': 0.0511}}],
2516     [0.464,0.431,0,{ic: 0.12}], [0.486,0.431,0.194,{ic: 0.0885}],
2517     [0.409,0.431,0,{ic: 0.123}], [0.511,0.431,0,{ic: 0.0921, lig: {'45': 124}}],
2518     [1.02,0.431,0,{ic: 0.0921}], [0.511,0.694,0,{ic: 0.122}],
2519     [0.511,0.668,0,{ic: 0.116}], [0.511,0.668,0,{ic: 0.105}]
2520   ],
2521   
2522   cmbx10: [
2523     [0.692,0.686], [0.958,0.686], [0.894,0.686], [0.806,0.686],
2524     [0.767,0.686], [0.9,0.686], [0.831,0.686], [0.894,0.686],
2525     [0.831,0.686], [0.894,0.686], [0.831,0.686],
2526     [0.671,0.694,0,{ic: 0.109, krn: {'39': 0.109, '63': 0.109, '33': 0.109, '41': 0.109, '93': 0.109}, lig: {'105': 14, '108': 15}}],
2527     [0.639,0.694], [0.639,0.694], [0.958,0.694], [0.958,0.694],
2528
2529     [0.319,0.444], [0.351,0.444,0.194], [0.575,0.694], [0.575,0.694],
2530     [0.575,0.632], [0.575,0.694], [0.575,0.596], [0.869,0.694],
2531     [0.511,0,0.17], [0.597,0.694], [0.831,0.444], [0.894,0.444],
2532     [0.575,0.542,0.0972], [1.04,0.686], [1.17,0.686], [0.894,0.735,0.0486],
2533
2534     [0.319,0.444,0,{krn: {'108': -0.319, '76': -0.378}}],
2535     [0.35,0.694,0,{lig: {'96': 60}}], [0.603,0.694], [0.958,0.694,0.194],
2536     [0.575,0.75,0.0556], [0.958,0.75,0.0556], [0.894,0.694],
2537     [0.319,0.694,0,{krn: {'63': 0.128, '33': 0.128}, lig: {'39': 34}}],
2538     [0.447,0.75,0.25], [0.447,0.75,0.25], [0.575,0.75], [0.894,0.633,0.133],
2539     [0.319,0.156,0.194], [0.383,0.444,0,{lig: {'45': 123}}],
2540     [0.319,0.156], [0.575,0.75,0.25],
2541
2542     [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644],
2543     [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644],
2544     [0.575,0.644], [0.575,0.644], [0.319,0.444], [0.319,0.444,0.194],
2545     [0.35,0.5,0.194], [0.894,0.391,-0.109], [0.543,0.5,0.194],
2546     [0.543,0.694,0,{lig: {'96': 62}}],
2547
2548     [0.894,0.694],
2549     [0.869,0.686,0,{krn: {'116': -0.0319, '67': -0.0319, '79': -0.0319, '71': -0.0319, '85': -0.0319, '81': -0.0319, '84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}],
2550     [0.818,0.686], [0.831,0.686],
2551     [0.882,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}],
2552     [0.756,0.686],
2553     [0.724,0.686,0,{krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}],
2554     [0.904,0.686], [0.9,0.686], [0.436,0.686,0,{krn: {'73': 0.0319}}],
2555     [0.594,0.686],
2556     [0.901,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}],
2557     [0.692,0.686,0,{krn: {'84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}],
2558     [1.09,0.686], [0.9,0.686],
2559     [0.864,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}],
2560
2561     [0.786,0.686,0,{krn: {'65': -0.0958, '111': -0.0319, '101': -0.0319, '97': -0.0319, '46': -0.0958, '44': -0.0958}}],
2562     [0.864,0.686,0.194],
2563     [0.862,0.686,0,{krn: {'116': -0.0319, '67': -0.0319, '79': -0.0319, '71': -0.0319, '85': -0.0319, '81': -0.0319, '84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}],
2564     [0.639,0.686],
2565     [0.8,0.686,0,{krn: {'121': -0.0319, '101': -0.0958, '111': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.0958, '117': -0.0958}}],
2566     [0.885,0.686],
2567     [0.869,0.686,0,{ic: 0.016, krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}],
2568     [1.19,0.686,0,{ic: 0.016, krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}],
2569     [0.869,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}],
2570     [0.869,0.686,0,{ic: 0.0287, krn: {'101': -0.0958, '111': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.0958, '117': -0.0958}}],
2571     [0.703,0.686], [0.319,0.75,0.25], [0.603,0.694], [0.319,0.75,0.25],
2572     [0.575,0.694], [0.319,0.694],
2573
2574     [0.319,0.694,0,{lig: {'96': 92}}],
2575     [0.559,0.444,0,{krn: {'118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}],
2576     [0.639,0.694,0,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}],
2577     [0.511,0.444,0,{krn: {'104': -0.0319, '107': -0.0319}}],
2578     [0.639,0.694], [0.527,0.444],
2579     [0.351,0.694,0,{ic: 0.109, krn: {'39': 0.109, '63': 0.109, '33': 0.109, '41': 0.109, '93': 0.109}, lig: {'105': 12, '102': 11, '108': 13}}],
2580     [0.575,0.444,0.194,{ic: 0.016, krn: {'106': 0.0319}}],
2581     [0.639,0.694,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}],
2582     [0.319,0.694], [0.351,0.694,0.194],
2583     [0.607,0.694,0,{krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}],
2584     [0.319,0.694],
2585     [0.958,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}],
2586     [0.639,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}],
2587     [0.575,0.444,0,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}],
2588
2589     [0.639,0.444,0.194,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}],
2590     [0.607,0.444,0.194], [0.474,0.444], [0.454,0.444],
2591     [0.447,0.635,0,{krn: {'121': -0.0319, '119': -0.0319}}],
2592     [0.639,0.444,0,{krn: {'119': -0.0319}}],
2593     [0.607,0.444,0,{ic: 0.016, krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}],
2594     [0.831,0.444,0,{ic: 0.016, krn: {'101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}],
2595     [0.607,0.444],
2596     [0.607,0.444,0.194,{ic: 0.016, krn: {'111': -0.0319, '101': -0.0319, '97': -0.0319, '46': -0.0958, '44': -0.0958}}],
2597     [0.511,0.444], [0.575,0.444,0,{ic: 0.0319, lig: {'45': 124}}],
2598     [1.15,0.444,0,{ic: 0.0319}], [0.575,0.694], [0.575,0.694], [0.575,0.694]
2599   ]
2600 };
2601
2602 /***************************************************************************/
2603
2604 /*
2605  *  Implement image-based fonts for fallback method
2606  */
2607 jsMath.Img = {
2608   
2609   // font sizes available
2610   fonts: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249, 298, 358, 430],
2611     
2612   // em widths for the various font size directories
2613   w: {'50': 6.9, '60': 8.3, '70': 9.7, '85': 11.8, '100': 13.9,
2614       '120': 16.7, '144': 20.0, '173': 24.0, '207': 28.8, '249': 34.6,
2615       '298': 41.4, '358': 49.8, '430': 59.8},
2616         
2617   best: 4,     // index of best font size in the fonts list    
2618   update: {},  // fonts to update (see UpdateFonts below)
2619   factor: 1,   // factor by which to shrink images (for better printing)
2620   loaded: 0,   // image fonts are loaded
2621
2622   // add characters to be drawn using images
2623   SetFont: function (change) {
2624     for (var font in change) {
2625       if (!this.update[font]) {this.update[font] = []}
2626       this.update[font] = this.update[font].concat(change[font]);
2627     }
2628   },
2629
2630   /*
2631    *  Called by the exta-font definition files to add an image font
2632    *  into the mix
2633    */
2634   AddFont: function (size,def) {
2635     if (!jsMath.Img[size]) {jsMath.Img[size] = {}};
2636     jsMath.Add(jsMath.Img[size],def);
2637   },
2638     
2639   /*
2640    *  Update font(s) to use image data rather than native fonts
2641    *  It looks in the jsMath.Img.update array to find the names
2642    *  of the fonts to udpate, and the arrays of character codes
2643    *  to set (or 'all' to change every character);
2644    */
2645   UpdateFonts: function () {
2646     var change = this.update; if (!this.loaded) return;
2647     for (var font in change) {
2648       for (var i = 0; i < change[font].length; i++) {
2649         var c = change[font][i];
2650         if (c == 'all') {for (c in jsMath.TeX[font]) {jsMath.TeX[font][c].img = {}}}
2651           else {jsMath.TeX[font][c].img = {}}
2652       }
2653     }
2654     this.update = {};
2655   },
2656   
2657   /*
2658    *  Find the font size that best fits our current font
2659    *  (this is the directory name for the img files used
2660    *  in some fallback modes).
2661    */
2662   BestSize: function () {
2663     var w = jsMath.em * this.factor;
2664     var m = this.w[this.fonts[0]];
2665     for (var i = 1; i < this.fonts.length; i++) {
2666       if (w < (this.w[this.fonts[i]] + 2*m) / 3) {return i-1}
2667       m = this.w[this.fonts[i]];
2668     }
2669     return i-1;
2670   },
2671
2672   /*
2673    *  Get the scaling factor for the image fonts
2674    */
2675   Scale: function () {
2676     if (!this.loaded) return;
2677     this.best = this.BestSize();
2678     this.em = jsMath.Img.w[this.fonts[this.best]];
2679     this.scale = (jsMath.em/this.em);
2680     if (Math.abs(this.scale - 1) < .12) {this.scale = 1}
2681   },
2682
2683   /*
2684    *  Get URL to directory for given font and size, based on the
2685    *  user's alpha/plain setting
2686    */
2687   URL: function (name,size,C) {
2688     var type = (jsMath.Controls.cookie.alpha) ? '/alpha/': '/plain/';
2689     if (C == null) {C = "def.js"} else {C = 'char'+C+'.png'}
2690     if (size != "") {size += '/'}
2691     return this.root+name+type+size+C;
2692   },
2693
2694   /*
2695    *  Laod the data for an image font
2696    */
2697   LoadFont: function (name) {
2698     if (!this.loaded) this.Init();
2699     jsMath.Setup.Script(this.URL(name,""));
2700   },
2701   
2702   /*
2703    *  Setup for print mode, and create the hex code table
2704    */
2705   Init: function () {
2706     if (jsMath.Controls.cookie.print || jsMath.Controls.cookie.stayhires) {
2707       jsMath.Controls.cookie.print = jsMath.Controls.cookie.stayhires;
2708       this.factor *= 3;
2709       if (!jsMath.Controls.isLocalCookie || !jsMath.Global.isLocal) {jsMath.Controls.SetCookie(0)}
2710       if (jsMath.Browser.alphaPrintBug) {jsMath.Controls.cookie.alpha = 0}
2711     }
2712     var codes = '0123456789ABCDEF';
2713     this.HexCode = [];
2714     for (var i = 0; i < 128; i++) {
2715       var h = Math.floor(i/16); var l = i - 16*h;
2716       this.HexCode[i] = codes.charAt(h)+codes.charAt(l);
2717     }
2718     this.loaded = 1;
2719   }
2720   
2721 };
2722
2723 /***************************************************************************/
2724
2725 /*
2726  *  jsMath.HTML handles creation of most of the HTML needed for
2727  *  presenting mathematics in HTML pages.
2728  */
2729
2730 jsMath.HTML = {
2731   
2732   /*
2733    *  Produce a string version of a measurement in ems,
2734    *  showing only a limited number of digits, and 
2735    *  using 0 when the value is near zero.
2736    */
2737   Em: function (m) {
2738     if (Math.abs(m) < .000001) {m = 0}
2739     var s = String(m); s = s.replace(/(\.\d\d\d).+/,'$1');
2740     return s+'em'
2741   },
2742
2743   /*
2744    *  Create a horizontal space of width w
2745    */
2746   Spacer: function (w) {
2747     if (w == 0) {return ''};
2748     return jsMath.Browser.msieSpaceFix+'<span class="spacer" style="margin-left:'+this.Em(w)+'"></span>';
2749   },
2750   
2751   /*
2752    *  Create a blank rectangle of the given size
2753    *  If the height is small, it is converted to pixels so that it
2754    *  will not disappear at small font sizes.
2755    */
2756   
2757   Blank: function (w,h,d,isRule) {
2758     var backspace = ''; var style = ''
2759     if (isRule) {
2760       style += 'border-left:'+this.Em(w)+' solid;';
2761       if (jsMath.Browser.widthAddsBorder) {w = 0};
2762     }
2763     if (w == 0) {
2764       if (jsMath.Browser.blankWidthBug) {
2765         if (jsMath.Browser.quirks) {
2766           style += 'width:1px;';
2767           backspace = '<span class="spacer" style="margin-right:-1px"></span>'
2768         } else if (!isRule) {
2769           style += 'width:1px;margin-right:-1px;';
2770         }
2771       }
2772     } else {style += 'width:'+this.Em(w)+';'}
2773     if (d == null) {d = 0}
2774     if (h) {
2775       var H = this.Em(h+d);
2776       if (isRule && h*jsMath.em <= 1.5) {H = "1.5px"; h = 1.5/jsMath.em}
2777       style += 'height:'+H+';';
2778     }
2779     if (jsMath.Browser.mozInlineBlockBug) {d = -h}
2780     if (jsMath.Browser.msieBlockDepthBug && !isRule) {d -= jsMath.d}
2781     if (d) {style += 'vertical-align:'+this.Em(-d)}
2782     return backspace+'<span class="blank" style="'+style+'"></span>';
2783   },
2784
2785   /*
2786    *  Create a rule line for fractions, etc.
2787    */
2788   Rule: function (w,h) {
2789     if (h == null) {h = jsMath.TeX.default_rule_thickness}
2790     return this.Blank(w,h,0,1);
2791   },
2792
2793   /*
2794    *  Create a strut for measuring position of baseline
2795    */
2796   Strut: function (h) {return this.Blank(1,h,0,1)},
2797   msieStrut: function (h) {
2798     return '<img style="width:1px; height:'+this.Em(h)+'"/>'
2799   },
2800   
2801   /*
2802    *  Add a <SPAN> tag to activate a specific CSS class
2803    */
2804   Class: function (tclass,html) {
2805     return '<span class="'+tclass+'">'+html+'</span>';
2806   },
2807   
2808   /*
2809    *  Use a <SPAN> to place some HTML at a specific position.
2810    *  (This can be replaced by the ones below to overcome
2811    *   some browser-specific bugs.)
2812    */
2813   Place: function (html,x,y) {
2814     if (Math.abs(x) < .0001) {x = 0}
2815     if (Math.abs(y) < .0001) {y = 0}
2816     if (x || y) {
2817       var span = '<span style="position: relative;';
2818       if (x) {span += ' margin-left:'+this.Em(x)+';'}
2819       if (y) {span += ' top:'+this.Em(-y)+';'}
2820       html = span + '">' + html + '</span>';
2821     }
2822     return html;
2823   },
2824   
2825   /*
2826    *  For MSIE on Windows, backspacing must be done in a separate
2827    *  <SPAN>, otherwise the contents will be clipped.  Netscape
2828    *  also doesn't combine vertical and horizontal spacing well.
2829    *  Here the x and y positioning are done in separate <SPAN> tags
2830    */
2831   PlaceSeparateSkips: function (html,x,y,mw,Mw,w) {
2832     if (Math.abs(x) < .0001) {x = 0}
2833     if (Math.abs(y) < .0001) {y = 0}
2834     if (y) {
2835       var lw = 0; var rw = 0; var width = "";
2836       if (mw != null) {
2837         rw = Mw - w; lw = mw;
2838         width = ' width:'+this.Em(Mw-mw)+';';
2839       }
2840       html = 
2841         this.Spacer(lw-rw) +
2842         '<span style="position: relative; '
2843             + 'top:'+this.Em(-y)+';'
2844             + 'left:'+this.Em(rw)+';'
2845             + width + '">' +
2846           this.Spacer(-lw) +
2847           html +
2848           this.Spacer(rw) +
2849         '</span>'
2850     }
2851     if (x) {html = this.Spacer(x) + html}
2852     return html;
2853   },
2854   
2855   /*
2856    *  Place a SPAN with absolute coordinates
2857    */
2858   PlaceAbsolute: function (html,x,y,mw,Mw,w) {
2859     if (Math.abs(x) < .0001) {x = 0}
2860     if (Math.abs(y) < .0001) {y = 0}
2861     var leftSpace = ""; var rightSpace = ""; var width = "";
2862     if (jsMath.Browser.msieRelativeClipBug && mw != null) {
2863       leftSpace  = this.Spacer(-mw); x += mw;
2864       rightSpace = this.Spacer(Mw-w);
2865     }
2866     if (jsMath.Browser.operaAbsoluteWidthBug) {width = " width: "+this.Em(w+2)}
2867     html =
2868       '<span style="position:absolute; left:'+this.Em(x)+'; '
2869             + 'top:'+this.Em(y)+';'+width+'">' +
2870         leftSpace + html + rightSpace +
2871         '&nbsp;' + //  space normalizes line height in script styles
2872       '</span>';
2873     return html;
2874   },
2875
2876   Absolute: function(html,w,h,d,y) {
2877     if (y != "none") {
2878       if (Math.abs(y) < .0001) {y = 0}
2879       html = '<span style="position:absolute; '
2880                + 'top:'+jsMath.HTML.Em(y)+'; left:0em;">'
2881                + html + '&nbsp;' // space normalizes line height in script styles
2882              + '</span>';
2883     }
2884     if (d == "none") {d = 0}
2885     html += this.Blank((jsMath.Browser.lineBreakBug ? 0 : w),h-d,d);
2886     if (jsMath.Browser.msieAbsoluteBug) {           // for MSIE (Mac)
2887       html = '<span style="position:relative;">' + html + '</span>';
2888     }
2889     html = '<span style="position:relative;'
2890          +   jsMath.Browser.msieInlineBlockFix
2891          + '">' + html + '</span>';
2892     if (jsMath.Browser.lineBreakBug)
2893       {html = '<span style="display:inline-block; width:'+jsMath.HTML.Em(w)+'">'+html+'</span>'}
2894     return html;
2895   }
2896
2897 };
2898
2899
2900 /***************************************************************************/
2901
2902 /*
2903  *  jsMath.Box handles TeX's math boxes and jsMath's equivalent of hboxes.
2904  */
2905
2906 jsMath.Box = function (format,text,w,h,d) {
2907   if (d == null) {d = jsMath.d}
2908   this.type = 'typeset';
2909   this.w = w; this.h = h; this.d = d; this.bh = h; this.bd = d;
2910   this.x = 0; this.y = 0; this.mw = 0; this.Mw = w;
2911   this.html = text; this.format = format;
2912 };
2913
2914
2915 jsMath.Add(jsMath.Box,{
2916   
2917   defaultH: 0, // default height for characters with none specified
2918
2919   /*
2920    *  An empty box
2921    */
2922   Null: function () {return new jsMath.Box('null','',0,0,0)},
2923
2924   /*
2925    *  A box containing only text whose class and style haven't been added
2926    *  yet (so that we can combine ones with the same styles).  It gets
2927    *  the text dimensions, if needed.  (In general, this has been
2928    *  replaced by TeX() below, but is still used in fallback mode.)
2929    */
2930   Text: function (text,tclass,style,size,a,d) {
2931     var html = jsMath.Typeset.AddClass(tclass,text);
2932         html = jsMath.Typeset.AddStyle(style,size,html);
2933     var BB = jsMath.EmBoxFor(html); var TeX = jsMath.Typeset.TeX(style,size);
2934     var bd = ((tclass == 'cmsy10' || tclass == 'cmex10')? BB.h-TeX.h: TeX.d*BB.h/TeX.hd);
2935     var box = new jsMath.Box('text',text,BB.w,BB.h-bd,bd);
2936     box.style = style; box.size = size; box.tclass = tclass;
2937     if (d != null) {box.d = d*TeX.scale} else {box.d = 0}
2938     if (a == null || a == 1) {box.h = .9*TeX.M_height}
2939       else {box.h = 1.1*TeX.x_height + TeX.scale*a}
2940     return box;
2941   },
2942
2943   /*
2944    *  Produce a box containing a given TeX character from a given font.
2945    *  The box is a text box (like the ones above), so that characters from
2946    *  the same font can be combined.
2947    */
2948   TeX: function (C,font,style,size) {
2949     var c = jsMath.TeX[font][C];
2950     if (c.d == null) {c.d = 0}; if (c.h == null) {c.h = 0}
2951     if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size));
2952     var scale = jsMath.Typeset.TeX(style,size).scale;
2953     var box = new jsMath.Box('text',c.c,c.w*scale,c.h*scale,c.d*scale);
2954     box.style = style; box.size = size;
2955     if (c.tclass) {
2956       box.tclass = c.tclass;
2957       if (c.img) {box.bh = c.img.bh; box.bd = c.img.bd}
2958             else {box.bh = scale*jsMath.h; box.bd = scale*jsMath.d}
2959     } else {
2960       box.tclass = font;
2961       box.bh = scale*jsMath.TeX[font].h;
2962       box.bd = scale*jsMath.TeX[font].d;
2963       if (jsMath.Browser.msieFontBug && box.html.match(/&#/)) {
2964         // hack to avoid font changing back to the default
2965         // font when a unicode reference is not followed
2966         // by a letter or number
2967         box.html += '<span style="display:none">x</span>';
2968       }
2969     }
2970     return box;
2971   },
2972
2973   /*
2974    *  In fallback modes, handle the fact that we don't have the
2975    *  sizes of the characters precomputed
2976    */
2977   TeXfallback: function (C,font,style,size) {
2978     var c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font}
2979     if (c.img != null) {return this.TeXnonfallback(C,font,style,size)}
2980     if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height}
2981     var a = c.a; var d = c.d; // avoid Firefox warnings
2982     var box = this.Text(c.c,c.tclass,style,size,a,d);
2983     var scale = jsMath.Typeset.TeX(style,size).scale;
2984     if (c.bh != null) {
2985       box.bh = c.bh*scale;
2986       box.bd = c.bd*scale;
2987     } else {
2988       var h = box.bd+box.bh;
2989       var html = jsMath.Typeset.AddClass(box.tclass,box.html);
2990           html = jsMath.Typeset.AddStyle(style,size,html);
2991       box.bd = jsMath.EmBoxFor(html + jsMath.HTML.Strut(h)).h - h;
2992       box.bh = h - box.bd;
2993       if (scale == 1) {c.bh = box.bh; c.bd = box.bd}
2994     }
2995     if (jsMath.msieFontBug && box.html.match(/&#/))
2996       {box.html += '<span style="display:none">x</span>'}
2997     return box;
2998   },
2999
3000   /*
3001    *  Set the character's string to the appropriate image file
3002    */
3003   TeXIMG: function (font,C,size) {
3004     var c = jsMath.TeX[font][C];
3005     if (c.img.size != null && c.img.size == size &&
3006         c.img.best != null && c.img.best == jsMath.Img.best) return;
3007     var mustScale = (jsMath.Img.scale != 1);
3008     var id = jsMath.Img.best + size - 4;
3009     if (id < 0) {id = 0; mustScale = 1} else
3010     if (id >= jsMath.Img.fonts.length) {id = jsMath.Img.fonts.length-1; mustScale = 1}
3011     var imgFont = jsMath.Img[jsMath.Img.fonts[id]];
3012     var img = imgFont[font][C];
3013     var scale = 1/jsMath.Img.w[jsMath.Img.fonts[id]];
3014     if (id != jsMath.Img.best + size - 4) {
3015       if (c.w != null) {scale = c.w/img[0]} else {
3016         scale *= jsMath.Img.fonts[size]/jsMath.Img.fonts[4]
3017               *  jsMath.Img.fonts[jsMath.Img.best]/jsMath.Img.fonts[id];
3018       }
3019     }
3020     var w = img[0]*scale; var h = img[1]*scale; var d = -img[2]*scale; var v;
3021     var wadjust = (c.w == null || Math.abs(c.w-w) < .01)? "" : " margin-right:"+jsMath.HTML.Em(c.w-w)+';';
3022     var resize = ""; C = jsMath.Img.HexCode[C];
3023     if (!mustScale && !jsMath.Controls.cookie.scaleImg) {
3024       if (jsMath.Browser.mozImageSizeBug || 2*w < h || 
3025          (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha))
3026            {resize = "height:"+(img[1]*jsMath.Browser.imgScale)+'px;'}
3027       resize += " width:"+(img[0]*jsMath.Browser.imgScale)+'px;'
3028       v = -img[2]+'px';
3029     } else {
3030       if (jsMath.Browser.mozImageSizeBug || 2*w < h ||
3031          (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha))
3032            {resize = "height:"+jsMath.HTML.Em(h*jsMath.Browser.imgScale)+';'}
3033       resize += " width:"+jsMath.HTML.Em(w*jsMath.Browser.imgScale)+';'
3034       v = jsMath.HTML.Em(d);
3035     }
3036     var vadjust = (Math.abs(d) < .01 && !jsMath.Browser.valignBug)?
3037                          "": " vertical-align:"+v+';';
3038     var URL = jsMath.Img.URL(font,jsMath.Img.fonts[id],C);
3039     if (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha) {
3040       c.c = '<img src="'+jsMath.blank+'" '
3041                + 'style="'+jsMath.Browser.msieCenterBugFix
3042                + resize + vadjust + wadjust
3043                + ' filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' + "'"
3044                + URL + "', sizingMethod='scale'" + ');" />';
3045     } else {
3046       c.c = '<img src="'+URL+'" style="'+jsMath.Browser.msieCenterBugFix
3047                   + resize + vadjust + wadjust + '" />';
3048     }
3049     c.tclass = "normal";
3050     c.img.bh = h+d; c.img.bd = -d;
3051     c.img.size = size; c.img.best = jsMath.Img.best;
3052   },
3053   
3054   /*
3055    *  A box containing a spacer of a specific width
3056    */
3057   Space: function (w) {
3058     return new jsMath.Box('html',jsMath.HTML.Spacer(w),w,0,0);
3059   },
3060
3061   /*
3062    *  A box containing a horizontal rule
3063    */
3064   Rule: function (w,h) {
3065     if (h == null) {h = jsMath.TeX.default_rule_thickness}
3066     var html = jsMath.HTML.Rule(w,h);
3067     return new jsMath.Box('html',html,w,h,0);
3068   },
3069
3070   /*
3071    *  Get a character from a TeX font, and make sure that it has
3072    *  its metrics specified.
3073    */
3074   GetChar: function (code,font) {
3075     var c = jsMath.TeX[font][code];
3076     if (c.img != null) {this.TeXIMG(font,code,4)}
3077     if (c.tclass == null) {c.tclass = font}
3078     if (!c.computedW) {
3079       c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w;
3080       if (c.h == null) {c.h = jsMath.Box.defaultH}; if (c.d == null) {c.d = 0}
3081       c.computedW = 1;
3082     }
3083     return c;
3084   },
3085   
3086   /*
3087    *  Locate the TeX delimiter character that matches a given height.
3088    *  Return the character, font, style and actual height used.
3089    */
3090   DelimBestFit: function (H,c,font,style) {
3091     if (c == 0 && font == 0) return null;
3092     var C; var h; font = jsMath.TeX.fam[font];
3093     var isSS = (style.charAt(1) == 'S');
3094     var isS  = (style.charAt(0) == 'S');
3095     while (c != null) {
3096       C = jsMath.TeX[font][c];
3097       if (C.h == null) {C.h = jsMath.Box.defaultH}; if (C.d == null) {C.d = 0}
3098       h = C.h+C.d;
3099       if (C.delim) {return [c,font,'',H]}
3100       if (isSS && .5*h >= H) {return [c,font,'SS',.5*h]}
3101       if (isS  && .7*h >= H) {return [c,font,'S',.7*h]}
3102       if (h >= H || C.n == null) {return [c,font,'T',h]}
3103       c = C.n;
3104     }
3105     return null;
3106   },
3107   
3108   /*
3109    *  Create the HTML needed for a stretchable delimiter of a given height,
3110    *  either centered or not.  This version uses relative placement (i.e.,
3111    *  backspaces, not line-breaks).  This works with more browsers, but
3112    *  if the font size changes, the backspacing may not be right, so the
3113    *  delimiters may become jagged.
3114    */
3115   DelimExtendRelative: function (H,c,font,a,nocenter) {
3116     var C = jsMath.TeX[font][c];
3117     var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font);
3118     var rep = this.GetChar(C.delim.rep,font);
3119     var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font);
3120     var ext = jsMath.Typeset.AddClass(rep.tclass,rep.c);
3121     var w = rep.w; var h = rep.h+rep.d
3122     var y; var Y; var html; var dx; var i; var n;
3123     if (C.delim.mid) {// braces
3124       var mid = this.GetChar(C.delim.mid,font);
3125       n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d)-(bot.h+bot.d))/(2*(rep.h+rep.d)));
3126       H = 2*n*(rep.h+rep.d) + (top.h+top.d) + (mid.h+mid.d) + (bot.h+bot.d);
3127       if (nocenter) {y = 0} else {y = H/2+a}; Y = y;
3128       html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h)
3129            + jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(top.w+bot.w)/2,y-(H-bot.d))
3130            + jsMath.HTML.Place(jsMath.Typeset.AddClass(mid.tclass,mid.c),-(bot.w+mid.w)/2,y-(H+mid.h-mid.d)/2);
3131       dx = (w-mid.w)/2; if (Math.abs(dx) < .0001) {dx = 0}
3132       if (dx) {html += jsMath.HTML.Spacer(dx)}
3133       y -= top.h+top.d + rep.h;
3134       for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)}
3135       y -= H/2 - rep.h/2;
3136       for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)}
3137     } else {// everything else
3138       n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d))/(rep.h+rep.d));
3139       // make sure two-headed arrows have an extender
3140       if (top.h+top.d < .9*(rep.h+rep.d)) {n = Math.max(1,n)}
3141       H = n*(rep.h+rep.d) + (top.h+top.d) + (bot.h+bot.d);
3142       if (nocenter) {y = 0} else {y = H/2+a}; Y = y;
3143       html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h)
3144       dx = (w-top.w)/2; if (Math.abs(dx) < .0001) {dx = 0}
3145       if (dx) {html += jsMath.HTML.Spacer(dx)}
3146       y -= top.h+top.d + rep.h;
3147       for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)}
3148       html += jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(w+bot.w)/2,Y-(H-bot.d));
3149     }
3150     if (nocenter) {h = top.h} else {h = H/2+a}
3151     var box = new jsMath.Box('html',html,rep.w,h,H-h);
3152     box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d;
3153     return box;
3154   },
3155
3156   /*
3157    *  Create the HTML needed for a stretchable delimiter of a given height,
3158    *  either centered or not.  This version uses absolute placement (i.e.,
3159    *  line-breaks, not backspacing).  This gives more reliable results,
3160    *  but doesn't work with all browsers.
3161    */
3162   DelimExtendAbsolute: function (H,c,font,a,nocenter) {
3163     var Font = jsMath.TeX[font];
3164     var C = Font[c]; var html;
3165     var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font);
3166     var rep = this.GetChar(C.delim.rep,font);
3167     var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font);
3168     var n; var h; var y; var ext; var i;
3169     
3170     if (C.delim.mid) {// braces
3171       var mid = this.GetChar(C.delim.mid,font);
3172       n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d-.05)-(bot.h+bot.d-.05))/(2*(rep.h+rep.d-.05)));
3173       H = 2*n*(rep.h+rep.d-.05) + (top.h+top.d) + (mid.h+mid.d-.05) + (bot.h+bot.d-.05);
3174       
3175       html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0);
3176       h = rep.h+rep.d - .05; y = top.d-.05 + rep.h;
3177       ext = jsMath.Typeset.AddClass(rep.tclass,rep.c)
3178       for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)}
3179       html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(mid.tclass,mid.c),0,y+n*h-rep.h+mid.h);
3180       y += n*h + mid.h+mid.d - .05;
3181       for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)}
3182       html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h);
3183     } else {// all others
3184       n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d-.05))/(rep.h+rep.d-.05));
3185       H = n*(rep.h+rep.d-.05) + (top.h+top.d) + (bot.h+bot.d-.05);
3186
3187       html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0);
3188       h = rep.h+rep.d-.05; y = top.d-.05 + rep.h;
3189       ext = jsMath.Typeset.AddClass(rep.tclass,rep.c);
3190       for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)}
3191       html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h);
3192     }
3193     
3194     var w = top.w;
3195     if (nocenter) {h = top.h; y = 0} else {h = H/2 + a; y = h - top.h}
3196     if (jsMath.Controls.cookie.font === "unicode") {
3197       if (jsMath.Browser.msie8HeightBug) {y -= jsMath.hd}
3198       else if (jsMath.Browser.msieBlockDepthBug) {y += jsMath.d}
3199     }
3200     html = jsMath.HTML.Absolute(html,w,Font.h,"none",-y);
3201     var box = new jsMath.Box('html',html,rep.w,h,H-h);
3202     box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d;
3203     return box;
3204   },
3205   
3206   /*
3207    *  Get the HTML for a given delimiter of a given height.
3208    *  It will return either a single character, if one exists, or the
3209    *  more complex HTML needed for a stretchable delimiter.
3210    */
3211   Delimiter: function (H,delim,style,nocenter) {
3212     var size = 4;  //### pass this?
3213     var TeX = jsMath.Typeset.TeX(style,size);
3214     if (!delim) {return this.Space(TeX.nulldelimiterspace)}
3215     var CFSH = this.DelimBestFit(H,delim[2],delim[1],style);
3216     if (CFSH == null || CFSH[3] < H) 
3217       {CFSH = this.DelimBestFit(H,delim[4],delim[3],style)}
3218     if (CFSH == null) {return this.Space(TeX.nulldelimiterspace)}
3219     if (CFSH[2] == '')
3220       {return this.DelimExtend(H,CFSH[0],CFSH[1],TeX.axis_height,nocenter)}
3221     var box = jsMath.Box.TeX(CFSH[0],CFSH[1],CFSH[2],size).Styled();
3222     if (!nocenter) {box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height)}
3223     if (Math.abs(box.y) < .0001) {box.y = 0}
3224     if (box.y) {box = jsMath.Box.SetList([box],CFSH[2],size)}
3225     return box;
3226   },
3227   
3228   /*
3229    *  Get a character by its TeX charcode, and make sure its width
3230    *  is specified.
3231    */
3232   GetCharCode: function (code) {
3233     var font = jsMath.TeX.fam[code[0]];
3234     var Font = jsMath.TeX[font];
3235     var c = Font[code[1]];
3236     if (c.img != null) {this.TeXIMG(font,code[1],4)}
3237     if (c.w == null) {c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w}
3238     if (c.font == null) {c.font = font}
3239     return c;
3240   },
3241
3242   /*
3243    * Add the class to the html, and use the font if there isn't one
3244    * specified already
3245    */
3246
3247   AddClass: function (tclass,html,font) {
3248     if (tclass == null) {tclass = font}
3249     return jsMath.Typeset.AddClass(tclass,html);
3250   },
3251   
3252   /*
3253    *  Create the HTML for an alignment (e.g., array or matrix)
3254    *  Since the widths are not really accurate (they are based on pixel
3255    *  widths not the sub-pixel widths of the actual characters), there
3256    *  is some drift involved.  We lay out the table column by column
3257    *  to help reduce the problem.
3258    *  
3259    *  ###  still need to allow users to specify row and column attributes,
3260    *       and do things like \span and \multispan  ###
3261    */
3262   LayoutRelative: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) {
3263     if (align == null) {align = []}
3264     if (cspacing == null) {cspacing = []}
3265     if (rspacing == null) {rspacing = []}
3266     if (useStrut == null) {useStrut = 1}
3267     if (addWidth == null) {addWidth = 1}
3268     
3269     // get row and column maximum dimensions
3270     var scale = jsMath.sizes[size]/100;
3271     var W = []; var H = []; var D = [];
3272     var unset = -1000; var bh = unset; var bd = unset;
3273     var i; var j; var row;
3274     for (i = 0; i < table.length; i++) {
3275       if (rspacing[i] == null) {rspacing[i] = 0}
3276       row = table[i];
3277       H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale;
3278       for (j = 0; j < row.length; j++) {
3279         row[j] = row[j].Remeasured();
3280         if (row[j].h > H[i]) {H[i] = row[j].h}
3281         if (row[j].d > D[i]) {D[i] = row[j].d}
3282         if (j >= W.length) {W[j] = row[j].w}
3283         else if (row[j].w > W[j]) {W[j] = row[j].w}
3284         if (row[j].bh > bh) {bh = row[j].bh}
3285         if (row[j].bd > bd) {bd = row[j].bd}
3286       }
3287     }
3288     if (rspacing[table.length] == null) {rspacing[table.length] = 0}
3289     if (bh == unset) {bh = 0}; if (bd == unset) {bd = 0}
3290
3291     // lay out the columns
3292     var HD = useStrut*(jsMath.hd-.01)*scale;
3293     var dy = (vspace || 1) * scale/6;
3294     var html = ''; var pW = 0; var cW = 0;
3295     var w; var h; var y;
3296     var box; var mlist; var entry;
3297     for (j = 0; j < W.length; j++) {
3298       mlist = []; y = -H[0]-rspacing[0]; pW = 0;
3299       for (i = 0; i < table.length; i++) {
3300         entry = table[i][j];
3301         if (entry && entry.format != 'null') {
3302           if (align[j] == 'l') {w = 0} else
3303           if (align[j] == 'r') {w = W[j] - entry.w} else
3304             {w = (W[j] - entry.w)/2}
3305           entry.x = w - pW; pW = entry.w + w; entry.y = y;
3306           mlist[mlist.length] = entry;
3307         }
3308         if (i+1 < table.length) {y -= Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]}
3309       }
3310       if (cspacing[j] == null) cspacing[j] = scale;
3311       if (mlist.length > 0) {
3312         box = jsMath.Box.SetList(mlist,'T',size);
3313         html += jsMath.HTML.Place(box.html,cW,0);
3314         cW = W[j] - box.w + cspacing[j];
3315       } else {cW += cspacing[j]}
3316     }
3317     
3318     // get the full width and height
3319     w = -cspacing[W.length-1]; y = (H.length-1)*dy + rspacing[0];
3320     for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]}
3321     for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]}
3322     h = y/2 + jsMath.TeX.axis_height; var d = y-h;
3323     
3324     // adjust the final row width, and vcenter the table
3325     //   (add 1/6em at each side for the \,)
3326     html += jsMath.HTML.Spacer(cW-cspacing[W.length-1] + addWidth*scale/6);
3327     html = jsMath.HTML.Place(html,addWidth*scale/6,h);
3328     box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d);
3329     box.bh = bh; box.bd = bd;
3330     return box;
3331   },
3332
3333   /*
3334    *  Create the HTML for an alignment (e.g., array or matrix)
3335    *  Use absolute position for elements in the array.
3336    *  
3337    *  ###  still need to allow users to specify row and column attributes,
3338    *       and do things like \span and \multispan  ###
3339    */
3340   LayoutAbsolute: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) {
3341     if (align == null) {align = []}
3342     if (cspacing == null) {cspacing = []}
3343     if (rspacing == null) {rspacing = []}
3344     if (useStrut == null) {useStrut = 1}
3345     if (addWidth == null) {addWidth = 1}
3346     // get row and column maximum dimensions
3347     var scale = jsMath.sizes[size]/100;
3348     var HD = useStrut*(jsMath.hd-.01)*scale;
3349     var dy = (vspace || 1) * scale/6;
3350     var W = []; var H = []; var D = [];
3351     var w = 0; var h; var x; var y;
3352     var i; var j; var row;
3353     for (i = 0; i < table.length; i++) {
3354       if (rspacing[i] == null) {rspacing[i] = 0}
3355       row = table[i];
3356       H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale;
3357       for (j = 0; j < row.length; j++) {
3358         row[j] = row[j].Remeasured();
3359         if (row[j].h > H[i]) {H[i] = row[j].h}
3360         if (row[j].d > D[i]) {D[i] = row[j].d}
3361         if (j >= W.length) {W[j] = row[j].w}
3362         else if (row[j].w > W[j]) {W[j] = row[j].w}
3363       }
3364     }
3365     if (rspacing[table.length] == null) {rspacing[table.length] = 0}
3366
3367     // get the height and depth of the centered table
3368     y = (H.length-1)*dy + rspacing[0];
3369     for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]}
3370     h = y/2 + jsMath.TeX.axis_height; var d = y - h;
3371
3372     // lay out the columns
3373     var html = ''; var entry; w = addWidth*scale/6;
3374     for (j = 0; j < W.length; j++) {
3375       y = H[0]-h + rspacing[0];
3376       for (i = 0; i < table.length; i++) {
3377         entry = table[i][j];
3378         if (entry && entry.format != 'null') {
3379           if (align[j] && align[j] == 'l') {x = 0} else
3380           if (align[j] && align[j] == 'r') {x = W[j] - entry.w} else
3381             {x = (W[j] - entry.w)/2}
3382           html += jsMath.HTML.PlaceAbsolute(entry.html,w+x,
3383                     y-Math.max(0,entry.bh-jsMath.h*scale),
3384                     entry.mw,entry.Mw,entry.w);
3385         }
3386         if (i+1 < table.length) {y += Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]}
3387       }
3388       if (cspacing[j] == null) cspacing[j] = scale;
3389       w += W[j] + cspacing[j];
3390     }
3391     
3392     // get the full width
3393     w = -cspacing[W.length-1]+addWidth*scale/3;
3394     for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]}
3395
3396     html = jsMath.HTML.Spacer(addWidth*scale/6)+html+jsMath.HTML.Spacer(addWidth*scale/6);
3397     if (jsMath.Browser.spanHeightVaries) {y = h-jsMath.h} else {y = 0}
3398     if (jsMath.Browser.msie8HeightBug) {y = d-jsMath.d}
3399     html = jsMath.HTML.Absolute(html,w,h+d,d,y);
3400     var box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d);
3401     return box;
3402   },
3403
3404   /*
3405    *  Look for math within \hbox and other non-math text
3406    */
3407   InternalMath: function (text,size) {
3408     if (!jsMath.safeHBoxes) {text = text.replace(/@\(([^)]*)\)/g,'<$1>')}
3409     if (!text.match(/\$|\\\(/)) 
3410       {return this.Text(this.safeHTML(text),'normal','T',size).Styled()}
3411
3412     var i = 0; var k = 0; var c; var match = '';
3413     var mlist = []; var parse, s;
3414     while (i < text.length) {
3415       c = text.charAt(i++);
3416       if (c == '$') {
3417         if (match == '$') {
3418           parse = jsMath.Parse(text.slice(k,i-1),null,size);
3419           if (parse.error) {
3420             mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2);
3421           } else {
3422             parse.Atomize();
3423             mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled();
3424           }
3425           match = ''; k = i;
3426         } else {
3427           s = this.safeHTML(text.slice(k,i-1));
3428           mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2);
3429           match = '$'; k = i;
3430         }
3431       } else if (c == '\\') {
3432         c = text.charAt(i++);
3433         if (c == '(' && match == '') {
3434           s = this.safeHTML(text.slice(k,i-2));
3435           mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2);
3436           match = ')'; k = i;
3437         } else if (c == ')' && match == ')') {
3438           parse = jsMath.Parse(text.slice(k,i-2),null,size);
3439           if (parse.error) {
3440             mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2);
3441           } else {
3442             parse.Atomize();
3443             mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled();
3444           }
3445           match = ''; k = i;
3446         }
3447       }
3448     }
3449     s = this.safeHTML(text.slice(k));
3450     mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2);
3451     return this.SetList(mlist,'T',size);
3452   },
3453   
3454   /*
3455    *  Quote HTML characters if we are in safe mode
3456    */
3457   safeHTML: function (s) {
3458     if (jsMath.safeHBoxes) {
3459       s = s.replace(/&/g,'&amp;')
3460            .replace(/</g,'&lt;')
3461            .replace(/>/g,'&gt;');
3462     }
3463     return s;
3464   },
3465   
3466   /*
3467    *  Convert an abitrary box to a typeset box.  I.e., make an
3468    *  HTML version of the contents of the box, at its desired (x,y)
3469    *  position.
3470    */
3471   Set: function (box,style,size,addstyle) {
3472     if (box && box.type) {
3473       if (box.type == 'typeset') {return box}
3474       if (box.type == 'mlist') {
3475         box.mlist.Atomize(style,size);
3476         return box.mlist.Typeset(style,size);
3477       }
3478       if (box.type == 'text') {
3479         box = this.Text(box.text,box.tclass,style,size,box.ascend||null,box.descend||null);
3480         if (addstyle != 0) {box.Styled()}
3481         return box;
3482       }
3483       box = this.TeX(box.c,box.font,style,size);
3484       if (addstyle != 0) {box.Styled()}
3485       return box;
3486     }
3487     return jsMath.Box.Null();
3488   },
3489
3490   /*
3491    *  Convert a list of boxes to a single typeset box.  I.e., finalize
3492    *  the HTML for the list of boxes, properly spaced and positioned.
3493    */
3494   SetList: function (boxes,style,size) {
3495     var mlist = []; var box;
3496     for (var i = 0; i < boxes.length; i++) {
3497       box = boxes[i];
3498       if (box.type == 'typeset') {box = jsMath.mItem.Typeset(box)}
3499       mlist[mlist.length] = box;
3500     }
3501     var typeset = new jsMath.Typeset(mlist);
3502     return typeset.Typeset(style,size);
3503   }
3504
3505 });
3506
3507
3508 jsMath.Package(jsMath.Box,{
3509
3510   /*
3511    *  Add the class and style to a text box (i.e., finalize the
3512    *  unpositioned HTML for the box).
3513    */
3514   Styled: function () {
3515     if (this.format == 'text') {
3516       this.html = jsMath.Typeset.AddClass(this.tclass,this.html);
3517       this.html = jsMath.Typeset.AddStyle(this.style,this.size,this.html);
3518       delete this.tclass; delete this.style;
3519       this.format = 'html';
3520     }
3521     return this;
3522   },
3523   
3524   /*
3525    *  Recompute the box width to make it more accurate.
3526    */
3527   Remeasured: function () {
3528     if (this.w > 0) {
3529       var w = this.w; this.w = jsMath.EmBoxFor(this.html).w;
3530       if (this.w > this.Mw) {this.Mw = this.w}
3531       w = this.w/w; if (Math.abs(w-1) > .05) {this.h *= w; this.d *= w}
3532     }
3533     return this;
3534   }
3535
3536 });
3537
3538
3539 /***************************************************************************/
3540
3541 /*
3542  *  mItems are the building blocks of mLists (math lists) used to
3543  *  store the information about a mathematical expression.  These are
3544  *  basically the items listed in the TeXbook in Appendix G (plus some
3545  *  minor extensions).
3546  */
3547 jsMath.mItem = function (type,def) {
3548   this.type = type;
3549   jsMath.Add(this,def);
3550 }
3551
3552 jsMath.Add(jsMath.mItem,{
3553
3554   /*
3555    *  A general atom (given a nucleus for the atom)
3556    */
3557   Atom: function (type,nucleus) {
3558     return new jsMath.mItem(type,{atom: 1, nuc: nucleus});
3559   },
3560
3561   /*
3562    *  An atom whose nucleus is a piece of text, in a given
3563    *  class, with a given additional height and depth
3564    */
3565   TextAtom: function (type,text,tclass,a,d) {
3566     var atom = new jsMath.mItem(type,{
3567       atom: 1,
3568       nuc: {
3569         type: 'text',
3570         text: text,
3571         tclass: tclass
3572       }
3573     });
3574     if (a != null) {atom.nuc.ascend = a}
3575     if (d != null) {atom.nuc.descend = d}
3576     return atom;
3577   },
3578   
3579   /*
3580    *  An atom whose nucleus is a TeX character in a specific font
3581    */
3582   TeXAtom: function (type,c,font) {
3583     return new jsMath.mItem(type,{
3584       atom: 1,
3585       nuc: {
3586         type: 'TeX',
3587         c: c,
3588         font: font
3589       }
3590     });
3591   },
3592
3593   /*
3594    *  A generalized fraction atom, with given delimiters, rule
3595    *  thickness, and a numerator and denominator.
3596    */
3597   Fraction: function (name,num,den,thickness,left,right) {
3598     return new jsMath.mItem('fraction',{
3599       from: name, num: num, den: den,
3600       thickness: thickness, left: left, right: right
3601     });
3602   },
3603
3604   /*
3605    *  An atom that inserts some glue
3606    */
3607   Space: function (w) {return new jsMath.mItem('space',{w: w})},
3608
3609   /*
3610    *  An atom that contains a typeset box (like an hbox or vbox)
3611    */
3612   Typeset: function (box) {return new jsMath.mItem('ord',{atom:1, nuc: box})},
3613   
3614   /*
3615    *  An atom that contains some finished HTML (acts like a typeset box)
3616    */
3617   HTML: function (html) {return new jsMath.mItem('html',{html: html})}
3618
3619 });
3620
3621 /***************************************************************************/
3622
3623 /*
3624  *  mLists are lists of mItems, and encode the contents of
3625  *  mathematical expressions and sub-expressions.  They act as
3626  *  the expression "stack" as the mathematics is parsed, and
3627  *  contain some state information, like the position of the
3628  *  most recent open paren and \over command, and the current font.
3629  */
3630 jsMath.mList = function (list,font,size,style) {
3631   if (list) {this.mlist = list} else {this.mlist = []}
3632   if (style == null) {style = 'T'}; if (size == null) {size = 4}
3633   this.data = {openI: null, overI: null, overF: null,
3634                font: font, size: size, style: style};
3635   this.init = {size: size, style: style};
3636 }
3637
3638 jsMath.Package(jsMath.mList,{
3639
3640   /*
3641    *  Add an mItem to the list
3642    */
3643   Add: function (box) {return (this.mlist[this.mlist.length] = box)},
3644   
3645   /*
3646    *  Get the i-th mItem from the list
3647    */
3648   Get: function (i) {return this.mlist[i]},
3649   
3650   /*
3651    *  Get the length of the list
3652    */
3653   Length: function() {return this.mlist.length},
3654
3655   /*
3656    *  Get the tail mItem of the list
3657    */
3658   Last: function () {
3659     if (this.mlist.length == 0) {return null}
3660     return this.mlist[this.mlist.length-1]
3661   },
3662
3663   /*
3664    *  Get a sublist of an mList
3665    */
3666   Range: function (i,j) {
3667     if (j == null) {j = this.mlist.length}
3668     return new jsMath.mList(this.mlist.slice(i,j+1));
3669   },
3670
3671   /*
3672    *  Remove a range of mItems from the list.
3673    */
3674   Delete: function (i,j) {
3675     if (j == null) {j = i}
3676     if (this.mlist.splice) {this.mlist.splice(i,j-i+1)} else {
3677       var mlist = [];
3678       for (var k = 0; k < this.mlist.length; k++)
3679         {if (k < i || k > j) {mlist[mlist.length] = this.mlist[k]}}
3680       this.mlist = mlist;
3681     }
3682   },
3683
3684   /*
3685    *  Add an open brace and maintain the stack information
3686    *  about the previous open brace so we can recover it
3687    *  when this one os closed.
3688    */
3689   Open: function (left) {
3690     var box = this.Add(new jsMath.mItem('boundary',{data: this.data}));
3691     var olddata = this.data;
3692     this.data = {}; for (var i in olddata) {this.data[i] = olddata[i]}
3693     delete this.data.overI; delete this.data.overF;
3694     this.data.openI = this.mlist.length-1;
3695     if (left != null) {box.left = left}
3696     return box;
3697   },
3698
3699   /*
3700    *  Attempt to close a brace.  Recover the stack information
3701    *  about previous open braces and \over commands.  If there was an
3702    *  \over (or \above, etc) in this set of braces, create a fraction
3703    *  atom from the two halves, otherwise create an inner or ord
3704    *  from the contents of the braces.
3705    *  Remove the braced material from the list and add the newly
3706    *  created atom (the fraction, inner or ord).
3707    */
3708   Close: function (right) {
3709     if (right != null) {right = new jsMath.mItem('boundary',{right: right})}
3710     var atom; var open = this.data.openI;
3711     var over = this.data.overI; var from = this.data.overF;
3712     this.data  = this.mlist[open].data;
3713     if (over) {
3714       atom = jsMath.mItem.Fraction(from.name,
3715         {type: 'mlist', mlist: this.Range(open+1,over-1)},
3716         {type: 'mlist', mlist: this.Range(over)},
3717         from.thickness,from.left,from.right);
3718       if (right) {
3719         var mlist = new jsMath.mList([this.mlist[open],atom,right]);
3720         atom = jsMath.mItem.Atom('inner',{type: 'mlist', mlist: mlist});
3721       }
3722     } else {
3723       var openI = open+1; if (right) {this.Add(right); openI--}
3724       atom = jsMath.mItem.Atom((right)?'inner':'ord',
3725                   {type: 'mlist', mlist: this.Range(openI)});
3726     }
3727     this.Delete(open,this.Length());
3728     return this.Add(atom);
3729   },
3730
3731   /*
3732    *  Create a generalized fraction from an mlist that
3733    *  contains an \over (or \above, etc).
3734    */
3735   Over: function () {
3736     var over = this.data.overI; var from = this.data.overF;
3737     var atom = jsMath.mItem.Fraction(from.name,
3738       {type: 'mlist', mlist: this.Range(open+1,over-1)},
3739       {type: 'mlist', mlist: this.Range(over)},
3740       from.thickness,from.left,from.right);
3741     this.mlist = [atom];
3742   },
3743
3744   /*
3745    *  Take a raw mList (that has been produced by parsing some TeX
3746    *  expression), and perform the modifications outlined in
3747    *  Appendix G of the TeXbook.  
3748    */
3749   Atomize: function (style,size) {
3750     var mitem; var prev = '';
3751     this.style = style; this.size = size;
3752     for (var i = 0; i < this.mlist.length; i++) {
3753       mitem = this.mlist[i]; mitem.delta = 0;
3754       if (mitem.type == 'choice') 
3755         {this.mlist = this.Atomize.choice(this.style,mitem,i,this.mlist); i--}
3756       else if (this.Atomize[mitem.type]) {
3757         var f = this.Atomize[mitem.type]; // Opera needs separate name
3758         f(this.style,this.size,mitem,prev,this,i);
3759       }
3760       prev = mitem;
3761     }
3762     if (mitem && mitem.type == 'bin') {mitem.type = 'ord'}
3763     if (this.mlist.length >= 2 && mitem.type == 'boundary' &&
3764         this.mlist[0].type == 'boundary') {this.AddDelimiters(style,size)}
3765   },
3766
3767   /*
3768    *  For a list that has boundary delimiters as its first and last
3769    *  entries, we replace the boundary atoms by open and close
3770    *  atoms whose nuclii are the specified delimiters properly sized
3771    *  for the contents of the list.  (Rule 19)
3772    */
3773   AddDelimiters: function(style,size) {
3774     var unset = -10000; var h = unset; var d = unset;
3775     for (var i = 0; i < this.mlist.length; i++) {
3776       var mitem = this.mlist[i];
3777       if (mitem.atom || mitem.type == 'box') {
3778         h = Math.max(h,mitem.nuc.h+mitem.nuc.y);
3779         d = Math.max(d,mitem.nuc.d-mitem.nuc.y);
3780       }
3781     }
3782     var TeX = jsMath.TeX; var a = jsMath.Typeset.TeX(style,size).axis_height;
3783     var delta = Math.max(h-a,d+a);
3784     var H =  Math.max(Math.floor(TeX.integer*delta/500)*TeX.delimiterfactor,
3785                       TeX.integer*(2*delta-TeX.delimitershortfall))/TeX.integer;
3786     var left = this.mlist[0]; var right = this.mlist[this.mlist.length-1];
3787     left.nuc = jsMath.Box.Delimiter(H,left.left,style);
3788     right.nuc = jsMath.Box.Delimiter(H,right.right,style);
3789     left.type = 'open'; left.atom = 1; delete left.left;
3790     right.type = 'close'; right.atom = 1; delete right.right;
3791   },
3792   
3793   /*
3794    *  Typeset a math list to produce final HTML for the list.
3795    */
3796   Typeset: function (style,size) {
3797     var typeset = new jsMath.Typeset(this.mlist);
3798     return typeset.Typeset(style,size);
3799   }
3800
3801 });
3802
3803
3804 /*
3805  *  These routines implement the main rules given in Appendix G of the
3806  *  TeXbook
3807  */
3808
3809 jsMath.Add(jsMath.mList.prototype.Atomize,{
3810
3811   /*
3812    *  Handle \displaystyle, \textstyle, etc.
3813    */
3814   style: function (style,size,mitem,prev,mlist) {
3815     mlist.style = mitem.style;
3816   },
3817   
3818   /*
3819    *  Handle \tiny, \small, etc.
3820    */
3821   size: function (style,size,mitem,prev,mlist) {
3822     mlist.size = mitem.size;
3823   },
3824   
3825   /*
3826    *  Create empty boxes of the proper sizes for the various
3827    *  phantom-type commands
3828    */
3829   phantom: function (style,size,mitem) {
3830     var box = mitem.nuc = jsMath.Box.Set(mitem.phantom,style,size);
3831     if (mitem.h) {box.Remeasured(); box.html = jsMath.HTML.Spacer(box.w)}
3832       else {box.html = '', box.w = box.Mw = box.mw = 0;}
3833     if (!mitem.v) {box.h = box.d = 0}
3834     box.bd = box.bh = 0;
3835     delete mitem.phantom;
3836     mitem.type = 'box';
3837   },
3838   
3839   /*
3840    *  Create a box of zero height and depth containing the
3841    *  contents of the atom
3842    */
3843   smash: function (style,size,mitem) {
3844     var box = mitem.nuc = jsMath.Box.Set(mitem.smash,style,size).Remeasured();
3845     box.h = box.d = 0;
3846     delete mitem.smash;
3847     mitem.type = 'box';
3848   },
3849
3850   /*
3851    *  Move a box up or down vertically
3852    */
3853   raise: function (style,size,mitem) {
3854     mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size);
3855     var y = mitem.raise;
3856     mitem.nuc.html =
3857       jsMath.HTML.Place(mitem.nuc.html,0,y,mitem.nuc.mw,mitem.nuc.Mw,mitem.nuc.w);
3858     mitem.nuc.h += y; mitem.nuc.d -= y;
3859     mitem.type = 'ord'; mitem.atom = 1;
3860   },
3861
3862   /*
3863    *  Hide the size of a box so that it laps to the left or right, or
3864    *  up or down.
3865    */
3866   lap: function (style,size,mitem) {
3867     var box = jsMath.Box.Set(mitem.nuc,style,size).Remeasured();
3868     var mlist = [box];
3869     if (mitem.lap == 'llap') {box.x = -box.w} else
3870     if (mitem.lap == 'rlap') {mlist[1] = jsMath.mItem.Space(-box.w)} else
3871     if (mitem.lap == 'ulap') {box.y = box.d; box.h = box.d = 0} else
3872     if (mitem.lap == 'dlap') {box.y = -box.h; box.h = box.d = 0}
3873     mitem.nuc = jsMath.Box.SetList(mlist,style,size);
3874     if (mitem.lap == 'ulap' || mitem.lap == 'dlap') {mitem.nuc.h = mitem.nuc.d = 0}
3875     mitem.type = 'box'; delete mitem.atom;
3876   },
3877
3878   /*
3879    *  Handle a Bin atom. (Rule 5)
3880    */
3881   bin: function (style,size,mitem,prev) {
3882     if (prev && prev.type) {
3883       var type  = prev.type;
3884       if (type == 'bin' || type == 'op' || type == 'rel' ||
3885           type == 'open' || type == 'punct' || type == '' ||
3886           (type == 'boundary' && prev.left != '')) {mitem.type = 'ord'}
3887     } else {mitem.type = 'ord'}
3888     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3889   },
3890
3891   /*
3892    *  Handle a Rel atom.  (Rule 6)
3893    */
3894   rel: function (style,size,mitem,prev) {
3895     if (prev.type && prev.type == 'bin') {prev.type = 'ord'}
3896     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3897   },
3898
3899   /*
3900    *  Handle a Close atom.  (Rule 6)
3901    */
3902   close: function (style,size,mitem,prev) {
3903     if (prev.type && prev.type == 'bin') {prev.type = 'ord'}
3904     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3905   },
3906
3907   /*
3908    *  Handle a Punct atom.  (Rule 6)
3909    */
3910   punct: function (style,size,mitem,prev) {
3911     if (prev.type && prev.type == 'bin') {prev.type = 'ord'}
3912     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3913   },
3914
3915   /*
3916    *  Handle an Open atom.  (Rule 7)
3917    */
3918   open: function (style,size,mitem) {
3919     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3920   },
3921
3922   /*
3923    *  Handle an Inner atom.  (Rule 7)
3924    */
3925   inner: function (style,size,mitem) {
3926     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3927   },
3928
3929   /*
3930    *  Handle a Vcent atom.  (Rule 8)
3931    */
3932   vcenter: function (style,size,mitem) {
3933     var box = jsMath.Box.Set(mitem.nuc,style,size);
3934     var TeX = jsMath.Typeset.TeX(style,size);
3935     box.y = TeX.axis_height - (box.h-box.d)/2;
3936     mitem.nuc = box; mitem.type = 'ord';
3937     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3938   },
3939
3940   /*
3941    *  Handle an Over atom.  (Rule 9)
3942    */
3943   overline: function (style,size,mitem) {
3944     var TeX = jsMath.Typeset.TeX(style,size);
3945     var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured();
3946     var t = TeX.default_rule_thickness;
3947     var rule = jsMath.Box.Rule(box.w,t);
3948     rule.x = -rule.w; rule.y = box.h + 3*t;
3949     mitem.nuc = jsMath.Box.SetList([box,rule],style,size);
3950     mitem.nuc.h += t;
3951     mitem.type = 'ord';
3952     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3953   },
3954
3955   /*
3956    *  Handle an Under atom.  (Rule 10)
3957    */
3958   underline: function (style,size,mitem) {
3959     var TeX = jsMath.Typeset.TeX(style,size);
3960     var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured();
3961     var t = TeX.default_rule_thickness;
3962     var rule = jsMath.Box.Rule(box.w,t);
3963     rule.x = -rule.w; rule.y = -box.d - 3*t - t;
3964     mitem.nuc = jsMath.Box.SetList([box,rule],style,size);
3965     mitem.nuc.d += t;
3966     mitem.type = 'ord';
3967     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3968   },
3969
3970   /*
3971    *  Handle a Rad atom.  (Rule 11 plus stuff for \root..\of)
3972    */
3973   radical: function (style,size,mitem) {
3974     var TeX = jsMath.Typeset.TeX(style,size);
3975     var Cp = jsMath.Typeset.PrimeStyle(style);
3976     var box = jsMath.Box.Set(mitem.nuc,Cp,size).Remeasured();
3977     var t = TeX.default_rule_thickness;
3978     var p = t; if (style == 'D' || style == "D'") {p = TeX.x_height}
3979     var r = t + p/4; 
3980     var surd = jsMath.Box.Delimiter(box.h+box.d+r+t,[0,2,0x70,3,0x70],style,1);
3981 //    if (surd.h > 0) {t = surd.h} // thickness of rule is height of surd character
3982     if (surd.d > box.h+box.d+r) {r = (r+surd.d-box.h-box.d)/2}
3983     surd.y = box.h+r;
3984     var rule = jsMath.Box.Rule(box.w,t);
3985     rule.y = surd.y-t/2; rule.h += 3*t/2; box.x = -box.w;
3986     var Cr = jsMath.Typeset.UpStyle(jsMath.Typeset.UpStyle(style));
3987     var root = jsMath.Box.Set(mitem.root || null,Cr,size).Remeasured();
3988     if (mitem.root) {
3989       root.y = .55*(box.h+box.d+3*t+r)-box.d;
3990       surd.x = Math.max(root.w-(11/18)*surd.w,0);
3991       rule.x = (7/18)*surd.w;
3992       root.x = -(root.w+rule.x);
3993     }
3994     mitem.nuc = jsMath.Box.SetList([surd,root,rule,box],style,size);
3995     mitem.type = 'ord';
3996     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
3997   },
3998
3999   /*
4000    *  Handle an Acc atom.  (Rule 12)
4001    */
4002   accent: function (style,size,mitem) {
4003     var TeX = jsMath.Typeset.TeX(style,size);
4004     var Cp = jsMath.Typeset.PrimeStyle(style);
4005     var box = jsMath.Box.Set(mitem.nuc,Cp,size);
4006     var u = box.w; var s; var Font; var ic = 0;
4007     if (mitem.nuc.type == 'TeX') {
4008       Font = jsMath.TeX[mitem.nuc.font];
4009       if (Font[mitem.nuc.c].krn && Font.skewchar)
4010         {s = Font[mitem.nuc.c].krn[Font.skewchar]}
4011       ic = Font[mitem.nuc.c].ic; if (ic == null) {ic = 0}
4012     }
4013     if (s == null) {s = 0}
4014     
4015     var c = mitem.accent[2];
4016     var font = jsMath.TeX.fam[mitem.accent[1]]; Font = jsMath.TeX[font];
4017     while (Font[c].n && Font[Font[c].n].w <= u) {c = Font[c].n}
4018     
4019     var delta = Math.min(box.h,TeX.x_height);
4020     if (mitem.nuc.type == 'TeX') {
4021       var nitem = jsMath.mItem.Atom('ord',mitem.nuc);
4022       nitem.sup = mitem.sup; nitem.sub = mitem.sub; nitem.delta = 0;
4023       jsMath.mList.prototype.Atomize.SupSub(style,size,nitem);
4024       delta += (nitem.nuc.h - box.h);
4025       box = mitem.nuc = nitem.nuc;
4026       delete mitem.sup; delete mitem.sub;
4027     }
4028     var acc = jsMath.Box.TeX(c,font,style,size);
4029     acc.y = box.h - delta; acc.x = -box.w + s + (u-acc.w)/2;
4030     if (jsMath.Browser.msieAccentBug) 
4031       {acc.html += jsMath.HTML.Spacer(.1); acc.w += .1; acc.Mw += .1}
4032     if (Font[c].ic || ic) {acc.x += (ic - (Font[c].ic||0)) * TeX.scale}
4033
4034     mitem.nuc = jsMath.Box.SetList([box,acc],style,size);
4035     if (mitem.nuc.w != box.w) {
4036       var space = jsMath.mItem.Space(box.w-mitem.nuc.w);
4037       mitem.nuc = jsMath.Box.SetList([mitem.nuc,space],style,size);
4038     }
4039     mitem.type = 'ord';
4040     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
4041   },
4042
4043   /*
4044    *  Handle an Op atom.  (Rules 13 and 13a)
4045    */
4046   op: function (style,size,mitem) {
4047     var TeX = jsMath.Typeset.TeX(style,size); var box;
4048     mitem.delta = 0; var isD = (style.charAt(0) == 'D');
4049     if (mitem.limits == null && isD) {mitem.limits = 1}
4050
4051     if (mitem.nuc.type == 'TeX') {
4052       var C = jsMath.TeX[mitem.nuc.font][mitem.nuc.c];
4053       if (isD && C.n) {mitem.nuc.c = C.n; C = jsMath.TeX[mitem.nuc.font][C.n]}
4054       box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size);
4055       if (C.ic) {
4056         mitem.delta = C.ic * TeX.scale;
4057         if (mitem.limits || !mitem.sub || jsMath.Browser.msieIntegralBug) {
4058           box = mitem.nuc = jsMath.Box.SetList([box,jsMath.mItem.Space(mitem.delta)],style,size);
4059         }
4060       }
4061       box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height);
4062       if (Math.abs(box.y) < .0001) {box.y = 0}
4063     }
4064     
4065     if (!box) {box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size).Remeasured()}
4066     if (mitem.limits) {
4067       var W = box.w; var x = box.w;
4068       var mlist = [box]; var dh = 0; var dd = 0;
4069       if (mitem.sup) {
4070         var sup = jsMath.Box.Set(mitem.sup,jsMath.Typeset.UpStyle(style),size).Remeasured();
4071         sup.x = ((box.w-sup.w)/2 + mitem.delta/2) - x; dh = TeX.big_op_spacing5;
4072         W = Math.max(W,sup.w); x += sup.x + sup.w;
4073         sup.y = box.h+sup.d + box.y +
4074                     Math.max(TeX.big_op_spacing1,TeX.big_op_spacing3-sup.d);
4075         mlist[mlist.length] = sup; delete mitem.sup;
4076       }
4077       if (mitem.sub) {
4078         var sub = jsMath.Box.Set(mitem.sub,jsMath.Typeset.DownStyle(style),size).Remeasured();
4079         sub.x = ((box.w-sub.w)/2 - mitem.delta/2) - x; dd = TeX.big_op_spacing5;
4080         W = Math.max(W,sub.w); x += sub.x + sub.w;
4081         sub.y = -box.d-sub.h + box.y -
4082                    Math.max(TeX.big_op_spacing2,TeX.big_op_spacing4-sub.h);
4083         mlist[mlist.length] = sub; delete mitem.sub;
4084       }
4085       if (W > box.w) {box.x = (W-box.w)/2; x += box.x}
4086       if (x < W) {mlist[mlist.length] = jsMath.mItem.Space(W-x)}
4087       mitem.nuc = jsMath.Box.SetList(mlist,style,size);
4088       mitem.nuc.h += dh; mitem.nuc.d += dd;
4089     } else {
4090       if (jsMath.Browser.msieIntegralBug && mitem.sub && C && C.ic) 
4091         {mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(-C.ic*TeX.scale)],style,size)}
4092       else if (box.y) {mitem.nuc = jsMath.Box.SetList([box],style,size)}
4093       jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
4094     }
4095   },
4096
4097   /*
4098    *  Handle an Ord atom.  (Rule 14)
4099    */
4100   ord: function (style,size,mitem,prev,mList,i) {
4101     if (mitem.nuc.type == 'TeX' && !mitem.sup && !mitem.sub) {
4102       var nitem = mList.mlist[i+1];
4103       if (nitem && nitem.atom && nitem.type &&
4104           (nitem.type == 'ord' || nitem.type == 'op' || nitem.type == 'bin' ||
4105            nitem.type == 'rel' || nitem.type == 'open' ||
4106            nitem.type == 'close' || nitem.type == 'punct')) {
4107         if (nitem.nuc.type == 'TeX' && nitem.nuc.font == mitem.nuc.font) {
4108           mitem.textsymbol = 1;
4109           var krn = jsMath.TeX[mitem.nuc.font][mitem.nuc.c].krn;
4110           krn *= jsMath.Typeset.TeX(style,size).scale;
4111           if (krn && krn[nitem.nuc.c]) {
4112             for (var k = mList.mlist.length-1; k > i; k--)
4113               {mList.mlist[k+1] = mList.mlist[k]}
4114             mList.mlist[i+1] = jsMath.mItem.Space(krn[nitem.nuc.c]);
4115           }
4116         }
4117       }
4118     }
4119     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
4120   },
4121
4122   /*
4123    *  Handle a generalized fraction.  (Rules 15 to 15e)
4124    */
4125   fraction: function (style,size,mitem) {
4126     var TeX = jsMath.Typeset.TeX(style,size); var t = 0;
4127     if (mitem.thickness != null) {t = mitem.thickness}
4128     else if (mitem.from.match(/over/)) {t = TeX.default_rule_thickness}
4129     var isD = (style.charAt(0) == 'D');
4130     var Cn = (style == 'D')? 'T': (style == "D'")? "T'": jsMath.Typeset.UpStyle(style);
4131     var Cd = (isD)? "T'": jsMath.Typeset.DownStyle(style);
4132     var num = jsMath.Box.Set(mitem.num,Cn,size).Remeasured();
4133     var den = jsMath.Box.Set(mitem.den,Cd,size).Remeasured();
4134
4135     var u; var v; var w; var p; var r;
4136     var H = (isD)? TeX.delim1 : TeX.delim2;
4137     var mlist = [jsMath.Box.Delimiter(H,mitem.left,style)]
4138     var right = jsMath.Box.Delimiter(H,mitem.right,style);
4139
4140     if (num.w < den.w) {
4141       num.x = (den.w-num.w)/2;
4142       den.x = -(num.w + num.x);
4143       w = den.w; mlist[1] = num; mlist[2] = den;
4144     } else {
4145       den.x = (num.w-den.w)/2;
4146       num.x = -(den.w + den.x);
4147       w = num.w; mlist[1] = den; mlist[2] = num;
4148     }
4149     if (isD) {u = TeX.num1; v = TeX.denom1} else {
4150       u = (t != 0)? TeX.num2: TeX.num3;
4151       v = TeX.denom2;
4152     }
4153     if (t == 0) {// atop
4154       p = (isD)? 7*TeX.default_rule_thickness: 3*TeX.default_rule_thickness;
4155       r = (u - num.d) - (den.h - v);
4156       if (r < p) {u += (p-r)/2; v += (p-r)/2}
4157     } else {// over
4158       p = (isD)? 3*t: t; var a = TeX.axis_height;
4159       r = (u-num.d)-(a+t/2); if (r < p) {u += p-r}
4160       r = (a-t/2)-(den.h-v); if (r < p) {v += p-r}
4161       var rule = jsMath.Box.Rule(w,t); rule.x = -w; rule.y = a - t/2;
4162       mlist[mlist.length] = rule;
4163     }
4164     num.y = u; den.y = -v;
4165
4166     mlist[mlist.length] = right;
4167     mitem.nuc = jsMath.Box.SetList(mlist,style,size);
4168     mitem.type = 'ord'; mitem.atom = 1;
4169     delete mitem.num; delete mitem.den;
4170     jsMath.mList.prototype.Atomize.SupSub(style,size,mitem);
4171   },
4172
4173   /*
4174    *  Add subscripts and superscripts.  (Rules 17-18f)
4175    */
4176   SupSub: function (style,size,mitem) {
4177     var TeX = jsMath.Typeset.TeX(style,size);
4178     var nuc = mitem.nuc;
4179     var box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size,0);
4180     if (box.format == 'null') 
4181       {box = mitem.nuc = jsMath.Box.Text('','normal',style,size)}
4182
4183     if (nuc.type == 'TeX') {
4184       if (!mitem.textsymbol) {
4185         var C = jsMath.TeX[nuc.font][nuc.c];
4186         if (C.ic) {
4187           mitem.delta = C.ic * TeX.scale;
4188           if (!mitem.sub) {
4189             box = mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(mitem.delta)],style,size);
4190             mitem.delta = 0;
4191           }
4192         }
4193       } else {mitem.delta = 0}
4194     }
4195
4196     if (!mitem.sup && !mitem.sub) return;
4197     mitem.nuc.Styled();
4198     
4199     var Cd = jsMath.Typeset.DownStyle(style);
4200     var Cu = jsMath.Typeset.UpStyle(style);
4201     var q = jsMath.Typeset.TeX(Cu,size).sup_drop;
4202     var r = jsMath.Typeset.TeX(Cd,size).sub_drop;
4203     var u = 0; var v = 0; var p;
4204     if (nuc.type && nuc.type != 'text' && nuc.type != 'TeX' && nuc.type != 'null')
4205       {u = box.h - q; v = box.d + r}
4206
4207     if (mitem.sub) {
4208       var sub = jsMath.Box.Set(mitem.sub,Cd,size);
4209       sub = jsMath.Box.SetList([sub,jsMath.mItem.Space(TeX.scriptspace)],style,size);
4210     }
4211
4212     if (!mitem.sup) {
4213       sub.y = -Math.max(v,TeX.sub1,sub.h-(4/5)*jsMath.Typeset.TeX(Cd,size).x_height);
4214       mitem.nuc = jsMath.Box.SetList([box,sub],style,size).Styled(); delete mitem.sub;
4215       return;
4216     }
4217
4218     var sup = jsMath.Box.Set(mitem.sup,Cu,size);
4219     sup = jsMath.Box.SetList([sup,jsMath.mItem.Space(TeX.scriptspace)],style,size);
4220     if (style == 'D') {p = TeX.sup1}
4221     else if (style.charAt(style.length-1) == "'") {p = TeX.sup3}
4222     else {p = TeX.sup2}
4223     u = Math.max(u,p,sup.d+jsMath.Typeset.TeX(Cu,size).x_height/4);
4224
4225     if (!mitem.sub) {
4226       sup.y = u;
4227       mitem.nuc = jsMath.Box.SetList([box,sup],style,size); delete mitem.sup;
4228       return;
4229     }
4230
4231     v = Math.max(v,jsMath.Typeset.TeX(Cd,size).sub2);
4232     var t = TeX.default_rule_thickness;
4233     if ((u-sup.d) - (sub.h -v) < 4*t) {
4234       v = 4*t + sub.h - (u-sup.d);
4235       p = (4/5)*TeX.x_height - (u-sup.d);
4236       if (p > 0) {u += p; v -= p}
4237     }
4238     sup.Remeasured(); sub.Remeasured();
4239     sup.y = u; sub.y = -v; sup.x = mitem.delta;
4240     if (sup.w+sup.x > sub.w)
4241       {sup.x -= sub.w; mitem.nuc = jsMath.Box.SetList([box,sub,sup],style,size)} else
4242       {sub.x -= (sup.w+sup.x); mitem.nuc = jsMath.Box.SetList([box,sup,sub],style,size)}
4243
4244     delete mitem.sup; delete mitem.sub;
4245   }
4246
4247 });
4248
4249
4250 /***************************************************************************/
4251
4252 /*
4253  *  The Typeset object handles most of the TeX-specific processing
4254  */
4255
4256 jsMath.Typeset = function (mlist) {
4257   this.type = 'typeset';
4258   this.mlist = mlist;
4259 }
4260
4261 jsMath.Add(jsMath.Typeset,{
4262
4263   /*
4264    *  The "C-uparrow" style table (TeXbook, p. 441)
4265    */
4266   upStyle: {
4267     D: "S", T: "S",  "D'": "S'", "T'": "S'",
4268     S: "SS",  SS: "SS",  "S'": "SS'", "SS'": "SS'"
4269   },
4270
4271   /*
4272    *  The "C-downarrow" style table (TeXbook, p. 441)
4273    */
4274   downStyle: {
4275     D: "S'", T: "S'",  "D'": "S'", "T'": "S'",
4276     S: "SS'",  SS: "SS'",  "S'": "SS'", "SS'": "SS'"
4277   },
4278
4279   /*
4280    *  Get the various styles given the current style
4281    *  (see TeXbook, p. 441)
4282    */
4283   UpStyle: function (style) {return this.upStyle[style]},
4284   DownStyle: function (style) {return this.downStyle[style]},
4285   PrimeStyle: function (style) {
4286     if (style.charAt(style.length-1) == "'") {return style}
4287     return style + "'"
4288   },
4289
4290   /*
4291    *  A value scaled to the appropriate size for scripts
4292    */
4293   StyleValue: function (style,v) {
4294     if (style == "S" || style == "S'")   {return .7*v}
4295     if (style == "SS" || style == "SS'") {return .5*v}
4296     return v;
4297   },
4298   
4299   /*
4300    *  Return the size associated with a given style and size
4301    */
4302   StyleSize: function (style,size) {
4303     if      (style == "S" || style == "S'")   {size = Math.max(0,size-2)}
4304     else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)}
4305     return size;
4306   },
4307
4308   /*
4309    *  Return the font parameter table for the given style
4310    */
4311   TeX: function (style,size) {
4312     if      (style == "S" || style == "S'")   {size = Math.max(0,size-2)}
4313     else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)}
4314     return jsMath.TeXparams[size];
4315   },
4316
4317
4318   /*
4319    *  Add the CSS class for the given TeX style
4320    */
4321   AddStyle: function (style,size,html) {
4322     if      (style == "S" || style == "S'")   {size = Math.max(0,size-2)}
4323     else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)}
4324     if (size != 4) {html = '<span class="size'+size+'">' + html + '</span>'}
4325     return html;
4326   },
4327
4328   /*
4329    *  Add the font class, if needed
4330    */
4331   AddClass: function (tclass,html) {
4332     if (tclass != '' && tclass != 'normal') {html = jsMath.HTML.Class(tclass,html)}
4333     return html;
4334   }
4335
4336 });
4337
4338
4339 jsMath.Package(jsMath.Typeset,{
4340   
4341   /*
4342    *  The spacing tables for inter-atom spacing
4343    *  (See rule 20, and Chapter 18, p 170)
4344    */
4345   DTsep: {
4346     ord: {op: 1, bin: 2, rel: 3, inner: 1},
4347     op:  {ord: 1, op: 1, rel: 3, inner: 1},
4348     bin: {ord: 2, op: 2, open: 2, inner: 2},
4349     rel: {ord: 3, op: 3, open: 3, inner: 3},
4350     open: {},
4351     close: {op: 1, bin:2, rel: 3, inner: 1},
4352     punct: {ord: 1, op: 1, rel: 1, open: 1, close: 1, punct: 1, inner: 1},
4353     inner: {ord: 1, op: 1, bin: 2, rel: 3, open: 1, punct: 1, inner: 1}
4354   },
4355
4356   SSsep: {
4357     ord: {op: 1},
4358     op:  {ord: 1, op: 1},
4359     bin: {},
4360     rel: {},
4361     open: {},
4362     close: {op: 1},
4363     punct: {},
4364     inner: {op: 1}
4365   },
4366
4367   /*
4368    *  The sizes used in the tables above
4369    */
4370   sepW: ['','thinmuskip','medmuskip','thickmuskip'],
4371   
4372   
4373   /*
4374    *  Find the amount of separation to use between two adjacent
4375    *  atoms in the given style
4376    */
4377   GetSeparation: function (l,r,style) {
4378     if (l && l.atom && r.atom) {
4379       var table = this.DTsep; if (style.charAt(0) == "S") {table = this.SSsep}
4380       var row = table[l.type];
4381       if (row && row[r.type] != null) {return jsMath.TeX[this.sepW[row[r.type]]]}
4382     }
4383     return 0;
4384   },
4385
4386   /*
4387    *  Typeset an mlist (i.e., turn it into HTML).
4388    *  Here, text items of the same class and style are combined
4389    *  to reduce the number of <SPAN> tags used (though it is still
4390    *  huge).  Spaces are combined, when possible.
4391    *  ###  More needs to be done with that.  ###
4392    *  The width of the final box is recomputed at the end, since
4393    *  the final width is not necessarily the sum of the widths of
4394    *  the individual parts (widths are in pixels, but the browsers
4395    *  puts pieces together using sub-pixel accuracy).
4396    */
4397   Typeset: function (style,size) {
4398     this.style = style; this.size = size; var unset = -10000
4399     this.w = 0; this.mw = 0; this.Mw = 0;
4400     this.h = unset; this.d = unset;
4401     this.bh = this.h; this.bd = this.d;
4402     this.tbuf = ''; this.tx = 0; this.tclass = '';
4403     this.cbuf = ''; this.hbuf = ''; this.hx = 0;
4404     var mitem = null; var prev; this.x = 0; this.dx = 0;
4405
4406     for (var i = 0; i < this.mlist.length; i++) {
4407       prev = mitem; mitem = this.mlist[i];
4408       switch (mitem.type) {
4409
4410         case 'size':
4411           this.FlushClassed();
4412           this.size = mitem.size;
4413           mitem = prev; // hide this from TeX
4414           break;
4415
4416         case 'style':
4417           this.FlushClassed();
4418           if (this.style.charAt(this.style.length-1) == "'")
4419             {this.style = mitem.style + "'"} else {this.style = mitem.style}
4420           mitem = prev; // hide this from TeX
4421           break;
4422
4423         case 'space':
4424           if (typeof(mitem.w) == 'object') {
4425             if (this.style.charAt(1) == 'S') {mitem.w = .5*mitem.w[0]/18}
4426             else if (this.style.charAt(0) == 'S') {mitem.w = .7*mitem.w[0]/18}
4427             else {mitem.w = mitem.w[0]/18}
4428           }
4429           this.dx += mitem.w-0; // mitem.w is sometimes a string?
4430           mitem = prev; // hide this from TeX
4431           break;
4432           
4433         case 'html':
4434           this.FlushClassed();
4435           if (this.hbuf == '') {this.hx = this.x}
4436           this.hbuf += mitem.html;
4437           mitem = prev; // hide this from TeX
4438           break;
4439           
4440         default:   // atom
4441           if (!mitem.atom && mitem.type != 'box') break;
4442           mitem.nuc.x += this.dx + this.GetSeparation(prev,mitem,this.style);
4443           if (mitem.nuc.x || mitem.nuc.y) mitem.nuc.Styled();
4444           this.dx = 0; this.x = this.x + this.w;
4445           if (this.x + mitem.nuc.x + mitem.nuc.mw < this.mw) 
4446             {this.mw = this.x + mitem.nuc.x + mitem.nuc.mw}
4447           if (this.w + mitem.nuc.x + mitem.nuc.Mw > this.Mw)
4448             {this.Mw = this.w + mitem.nuc.x + mitem.nuc.Mw}
4449           this.w += mitem.nuc.w + mitem.nuc.x;
4450           if (mitem.nuc.format == 'text') {
4451             if (this.tclass != mitem.nuc.tclass && this.tclass != '') this.FlushText();
4452             if (this.tbuf == '' && this.cbuf == '') {this.tx = this.x}
4453             this.tbuf += mitem.nuc.html; this.tclass = mitem.nuc.tclass;
4454           } else  {
4455             this.FlushClassed();
4456             if (mitem.nuc.x || mitem.nuc.y) this.Place(mitem.nuc);
4457             if (this.hbuf == '') {this.hx = this.x}
4458             this.hbuf += mitem.nuc.html;
4459           }
4460           this.h = Math.max(this.h,mitem.nuc.h+mitem.nuc.y); this.bh = Math.max(this.bh,mitem.nuc.bh);
4461           this.d = Math.max(this.d,mitem.nuc.d-mitem.nuc.y); this.bd = Math.max(this.bd,mitem.nuc.bd);
4462           break;
4463       }
4464     }
4465
4466     this.FlushClassed(); // make sure scaling is included
4467     if (this.dx) {
4468       this.hbuf += jsMath.HTML.Spacer(this.dx); this.w += this.dx;
4469       if (this.w > this.Mw) {this.Mw = this.w}
4470       if (this.w < this.mw) {this.mw = this.w}
4471     }
4472     if (this.hbuf == '') {return jsMath.Box.Null()}
4473     if (this.h == unset) {this.h = 0}
4474     if (this.d == unset) {this.d = 0}
4475     var box = new jsMath.Box('html',this.hbuf,this.w,this.h,this.d);
4476     box.bh = this.bh; box.bd = this.bd;
4477     box.mw = this.mw; box.Mw = this.Mw;
4478     return box;
4479   },
4480
4481   /*
4482    *  Add the font to the buffered text and move it to the
4483    *  classed-text buffer.
4484    */
4485   FlushText: function () {
4486     if (this.tbuf == '') return;
4487     this.cbuf += jsMath.Typeset.AddClass(this.tclass,this.tbuf);
4488     this.tbuf = ''; this.tclass = '';
4489   },
4490
4491   /*
4492    *  Add the script or scriptscript style to the text and
4493    *  move it to the HTML buffer
4494    */
4495   FlushClassed: function () {
4496     this.FlushText();
4497     if (this.cbuf == '') return;
4498     if (this.hbuf == '') {this.hx = this.tx}
4499     this.hbuf += jsMath.Typeset.AddStyle(this.style,this.size,this.cbuf);
4500     this.cbuf = '';
4501   },
4502
4503   /*
4504    *  Add a <SPAN> to position an item's HTML, and
4505    *  adjust the item's height and depth.
4506    *  (This may be replaced buy one of the following browser-specific
4507    *   versions by Browser.Init().)
4508    */
4509   Place: function (item) {
4510     var html = '<span style="position: relative;';
4511     if (item.x) {html += ' margin-left:'+jsMath.HTML.Em(item.x)+';'}
4512     if (item.y) {html += ' top:'+jsMath.HTML.Em(-item.y)+';'}
4513     item.html = html + '">' + item.html + '</span>';
4514     item.h += item.y; item.d -= item.y;
4515     item.x = 0; item.y = 0;
4516   },
4517   
4518   /*
4519    *  For MSIE on Windows, backspacing must be done in a separate
4520    *  <SPAN>, otherwise the contents will be clipped.  Netscape
4521    *  also doesn't combine vertical and horizontal spacing well.
4522    *  Here, the horizontal and vertical spacing are done separately.
4523    */
4524
4525   PlaceSeparateSkips: function (item) {
4526     if (item.y) {
4527       var rw = item.Mw - item.w; var lw = item.mw;
4528       var W = item.Mw - item.mw;
4529       item.html = 
4530         jsMath.HTML.Spacer(lw-rw) +
4531         '<span style="position: relative; '
4532             + 'top:'+jsMath.HTML.Em(-item.y)+'; '
4533             + 'left:'+jsMath.HTML.Em(rw)+'; width:'+jsMath.HTML.Em(W)+';">' +
4534           jsMath.HTML.Spacer(-lw) +
4535           item.html +
4536           jsMath.HTML.Spacer(rw) + 
4537         '</span>'
4538     }
4539     if (item.x) {item.html = jsMath.HTML.Spacer(item.x) + item.html}
4540     item.h += item.y; item.d -= item.y;
4541     item.x = 0; item.y = 0;
4542   }
4543
4544 });
4545
4546
4547
4548 /***************************************************************************/
4549
4550 /*
4551  *  The Parse object handles the parsing of the TeX input string, and creates
4552  *  the mList to be typeset by the Typeset object above.
4553  */
4554
4555 jsMath.Parse = function (s,font,size,style) {
4556   var parse = new jsMath.Parser(s,font,size,style);
4557   parse.Parse();
4558   return parse;
4559 }
4560
4561 jsMath.Parser = function (s,font,size,style) {
4562   this.string = s; this.i = 0;
4563   this.mlist = new jsMath.mList(null,font,size,style);
4564 }
4565
4566 jsMath.Package(jsMath.Parser,{
4567   
4568   // special characters
4569   cmd:   '\\',
4570   open:  '{',
4571   close: '}',
4572   
4573   // patterns for letters and numbers
4574   letter:  /[a-z]/i,
4575   number:  /[0-9]/,
4576   //  pattern for macros to ^ and _ that should be read with arguments
4577   scriptargs: /^((math|text)..|mathcal|[hm]box)$/,
4578   
4579   //  the \mathchar definitions (see Appendix B of the TeXbook).
4580   mathchar: {
4581     '!': [5,0,0x21],
4582     '(': [4,0,0x28],
4583     ')': [5,0,0x29],
4584     '*': [2,2,0x03], // \ast
4585     '+': [2,0,0x2B],
4586     ',': [6,1,0x3B],
4587     '-': [2,2,0x00],
4588     '.': [0,1,0x3A],
4589     '/': [0,1,0x3D],
4590     ':': [3,0,0x3A],
4591     ';': [6,0,0x3B],
4592     '<': [3,1,0x3C],
4593     '=': [3,0,0x3D],
4594     '>': [3,1,0x3E],
4595     '?': [5,0,0x3F],
4596     '[': [4,0,0x5B],
4597     ']': [5,0,0x5D],
4598 //  '{': [4,2,0x66],
4599 //  '}': [5,2,0x67],
4600     '|': [0,2,0x6A]
4601   },
4602
4603   //  handle special \catcode characters
4604   special: {
4605     '~':   'Tilde',
4606     '^':   'HandleSuperscript',
4607     '_':   'HandleSubscript',
4608     ' ':   'Space',
4609     '\01': 'Space',
4610     "\t":  'Space',
4611     "\r":  'Space',
4612     "\n":  'Space',
4613     "'":   'Prime',
4614     '%':   'HandleComment',
4615     '&':   'HandleEntry',
4616     '#':   'Hash'
4617   },
4618
4619   // the \mathchardef table (see Appendix B of the TeXbook).
4620   mathchardef: {
4621   // brace parts
4622     braceld:      [0,3,0x7A],
4623     bracerd:      [0,3,0x7B],
4624     bracelu:      [0,3,0x7C],
4625     braceru:      [0,3,0x7D],
4626
4627   // Greek letters
4628     alpha:        [0,1,0x0B],
4629     beta:         [0,1,0x0C],
4630     gamma:        [0,1,0x0D],
4631     delta:        [0,1,0x0E],
4632     epsilon:      [0,1,0x0F],
4633     zeta:         [0,1,0x10],
4634     eta:          [0,1,0x11],
4635     theta:        [0,1,0x12],
4636     iota:         [0,1,0x13],
4637     kappa:        [0,1,0x14],
4638     lambda:       [0,1,0x15],
4639     mu:           [0,1,0x16],
4640     nu:           [0,1,0x17],
4641     xi:           [0,1,0x18],
4642     pi:           [0,1,0x19],
4643     rho:          [0,1,0x1A],
4644     sigma:        [0,1,0x1B],
4645     tau:          [0,1,0x1C],
4646     upsilon:      [0,1,0x1D],
4647     phi:          [0,1,0x1E],
4648     chi:          [0,1,0x1F],
4649     psi:          [0,1,0x20],
4650     omega:        [0,1,0x21],
4651     varepsilon:   [0,1,0x22],
4652     vartheta:     [0,1,0x23],
4653     varpi:        [0,1,0x24],
4654     varrho:       [0,1,0x25],
4655     varsigma:     [0,1,0x26],
4656     varphi:       [0,1,0x27],
4657     
4658     Gamma:        [7,0,0x00],
4659     Delta:        [7,0,0x01],
4660     Theta:        [7,0,0x02],
4661     Lambda:       [7,0,0x03],
4662     Xi:           [7,0,0x04],
4663     Pi:           [7,0,0x05],
4664     Sigma:        [7,0,0x06],
4665     Upsilon:      [7,0,0x07],
4666     Phi:          [7,0,0x08],
4667     Psi:          [7,0,0x09],
4668     Omega:        [7,0,0x0A],
4669
4670   // Ord symbols
4671     aleph:        [0,2,0x40],
4672     imath:        [0,1,0x7B],
4673     jmath:        [0,1,0x7C],
4674     ell:          [0,1,0x60],
4675     wp:           [0,1,0x7D],
4676     Re:           [0,2,0x3C],
4677     Im:           [0,2,0x3D],
4678     partial:      [0,1,0x40],
4679     infty:        [0,2,0x31],
4680     prime:        [0,2,0x30],
4681     emptyset:     [0,2,0x3B],
4682     nabla:        [0,2,0x72],
4683     surd:         [1,2,0x70],
4684     top:          [0,2,0x3E],
4685     bot:          [0,2,0x3F],
4686     triangle:     [0,2,0x34],
4687     forall:       [0,2,0x38],
4688     exists:       [0,2,0x39],
4689     neg:          [0,2,0x3A],
4690     lnot:         [0,2,0x3A],
4691     flat:         [0,1,0x5B],
4692     natural:      [0,1,0x5C],
4693     sharp:        [0,1,0x5D],
4694     clubsuit:     [0,2,0x7C],
4695     diamondsuit:  [0,2,0x7D],
4696     heartsuit:    [0,2,0x7E],
4697     spadesuit:    [0,2,0x7F],
4698
4699   // big ops
4700     coprod:      [1,3,0x60],
4701     bigvee:      [1,3,0x57],
4702     bigwedge:    [1,3,0x56],
4703     biguplus:    [1,3,0x55],
4704     bigcap:      [1,3,0x54],
4705     bigcup:      [1,3,0x53],
4706     intop:       [1,3,0x52], 
4707     prod:        [1,3,0x51],
4708     sum:         [1,3,0x50],
4709     bigotimes:   [1,3,0x4E],
4710     bigoplus:    [1,3,0x4C],
4711     bigodot:     [1,3,0x4A],
4712     ointop:      [1,3,0x48],
4713     bigsqcup:    [1,3,0x46],
4714     smallint:    [1,2,0x73],
4715
4716   // binary operations
4717     triangleleft:      [2,1,0x2F],
4718     triangleright:     [2,1,0x2E],
4719     bigtriangleup:     [2,2,0x34],
4720     bigtriangledown:   [2,2,0x35],
4721     wedge:       [2,2,0x5E],
4722     land:        [2,2,0x5E],
4723     vee:         [2,2,0x5F],
4724     lor:         [2,2,0x5F],
4725     cap:         [2,2,0x5C],
4726     cup:         [2,2,0x5B],
4727     ddagger:     [2,2,0x7A],
4728     dagger:      [2,2,0x79],
4729     sqcap:       [2,2,0x75],
4730     sqcup:       [2,2,0x74],
4731     uplus:       [2,2,0x5D],
4732     amalg:       [2,2,0x71],
4733     diamond:     [2,2,0x05],
4734     bullet:      [2,2,0x0F],
4735     wr:          [2,2,0x6F],
4736     div:         [2,2,0x04],
4737     odot:        [2,2,0x0C],
4738     oslash:      [2,2,0x0B],
4739     otimes:      [2,2,0x0A],
4740     ominus:      [2,2,0x09],
4741     oplus:       [2,2,0x08],
4742     mp:          [2,2,0x07],
4743     pm:          [2,2,0x06],
4744     circ:        [2,2,0x0E],
4745     bigcirc:     [2,2,0x0D],
4746     setminus:    [2,2,0x6E], // for set difference A\setminus B
4747     cdot:        [2,2,0x01],
4748     ast:         [2,2,0x03],
4749     times:       [2,2,0x02],
4750     star:        [2,1,0x3F],
4751
4752   // Relations
4753     propto:      [3,2,0x2F],
4754     sqsubseteq:  [3,2,0x76],
4755     sqsupseteq:  [3,2,0x77],
4756     parallel:    [3,2,0x6B],
4757     mid:         [3,2,0x6A],
4758     dashv:       [3,2,0x61],
4759     vdash:       [3,2,0x60],
4760     leq:         [3,2,0x14],
4761     le:          [3,2,0x14],
4762     geq:         [3,2,0x15],
4763     ge:          [3,2,0x15],
4764     lt:          [3,1,0x3C],  // extra since < and > are hard
4765     gt:          [3,1,0x3E],  //   to get in HTML
4766     succ:        [3,2,0x1F],
4767     prec:        [3,2,0x1E],
4768     approx:      [3,2,0x19],
4769     succeq:      [3,2,0x17],
4770     preceq:      [3,2,0x16],
4771     supset:      [3,2,0x1B],
4772     subset:      [3,2,0x1A],
4773     supseteq:    [3,2,0x13],
4774     subseteq:    [3,2,0x12],
4775     'in':        [3,2,0x32],
4776     ni:          [3,2,0x33],
4777     owns:        [3,2,0x33],
4778     gg:          [3,2,0x1D],
4779     ll:          [3,2,0x1C],
4780     not:         [3,2,0x36],
4781     sim:         [3,2,0x18],
4782     simeq:       [3,2,0x27],
4783     perp:        [3,2,0x3F],
4784     equiv:       [3,2,0x11],
4785     asymp:       [3,2,0x10],
4786     smile:       [3,1,0x5E],
4787     frown:       [3,1,0x5F],
4788
4789   // Arrows
4790     Leftrightarrow:   [3,2,0x2C],
4791     Leftarrow:        [3,2,0x28],
4792     Rightarrow:       [3,2,0x29],
4793     leftrightarrow:   [3,2,0x24],
4794     leftarrow:        [3,2,0x20],
4795     gets:             [3,2,0x20],
4796     rightarrow:       [3,2,0x21],
4797     to:               [3,2,0x21],
4798     mapstochar:       [3,2,0x37],
4799     leftharpoonup:    [3,1,0x28],
4800     leftharpoondown:  [3,1,0x29],
4801     rightharpoonup:   [3,1,0x2A],
4802     rightharpoondown: [3,1,0x2B],
4803     nearrow:          [3,2,0x25],
4804     searrow:          [3,2,0x26],
4805     nwarrow:          [3,2,0x2D],
4806     swarrow:          [3,2,0x2E],
4807
4808     minuschar:  [3,2,0x00], // for longmapsto
4809     hbarchar:   [0,0,0x16], // for \hbar
4810     lhook:      [3,1,0x2C],
4811     rhook:      [3,1,0x2D],
4812
4813     ldotp:      [6,1,0x3A], // ldot as a punctuation mark
4814     cdotp:      [6,2,0x01], // cdot as a punctuation mark
4815     colon:      [6,0,0x3A], // colon as a punctuation mark
4816
4817     '#':        [7,0,0x23],
4818     '$':        [7,0,0x24],
4819     '%':        [7,0,0x25],
4820     '&':        [7,0,0x26]
4821   },
4822   
4823   // The delimiter table (see Appendix B of the TeXbook)
4824   delimiter: {
4825     '(':                [0,0,0x28,3,0x00],
4826     ')':                [0,0,0x29,3,0x01],
4827     '[':                [0,0,0x5B,3,0x02],
4828     ']':                [0,0,0x5D,3,0x03],
4829     '<':                [0,2,0x68,3,0x0A],
4830     '>':                [0,2,0x69,3,0x0B],
4831     '\\lt':             [0,2,0x68,3,0x0A],  // extra since < and > are
4832     '\\gt':             [0,2,0x69,3,0x0B],  //  hard to get in HTML
4833     '/':                [0,0,0x2F,3,0x0E],
4834     '|':                [0,2,0x6A,3,0x0C],
4835     '.':                [0,0,0x00,0,0x00],
4836     '\\':               [0,2,0x6E,3,0x0F],
4837     '\\lmoustache':     [4,3,0x7A,3,0x40],  // top from (, bottom from )
4838     '\\rmoustache':     [5,3,0x7B,3,0x41],  // top from ), bottom from (
4839     '\\lgroup':         [4,6,0x28,3,0x3A],  // extensible ( with sharper tips
4840     '\\rgroup':         [5,6,0x29,3,0x3B],  // extensible ) with sharper tips
4841     '\\arrowvert':      [0,2,0x6A,3,0x3C],  // arrow without arrowheads
4842     '\\Arrowvert':      [0,2,0x6B,3,0x3D],  // double arrow without arrowheads
4843 //  '\\bracevert':      [0,7,0x7C,3,0x3E],  // the vertical bar that extends braces
4844     '\\bracevert':      [0,2,0x6A,3,0x3E],  // we don't load tt, so use | instead
4845     '\\Vert':           [0,2,0x6B,3,0x0D],
4846     '\\|':              [0,2,0x6B,3,0x0D],
4847     '\\vert':           [0,2,0x6A,3,0x0C],
4848     '\\uparrow':        [3,2,0x22,3,0x78],
4849     '\\downarrow':      [3,2,0x23,3,0x79],
4850     '\\updownarrow':    [3,2,0x6C,3,0x3F],
4851     '\\Uparrow':        [3,2,0x2A,3,0x7E],
4852     '\\Downarrow':      [3,2,0x2B,3,0x7F],
4853     '\\Updownarrow':    [3,2,0x6D,3,0x77],
4854     '\\backslash':      [0,2,0x6E,3,0x0F],  // for double coset G\backslash H
4855     '\\rangle':         [5,2,0x69,3,0x0B],
4856     '\\langle':         [4,2,0x68,3,0x0A],
4857     '\\rbrace':         [5,2,0x67,3,0x09],
4858     '\\lbrace':         [4,2,0x66,3,0x08],
4859     '\\}':              [5,2,0x67,3,0x09],
4860     '\\{':              [4,2,0x66,3,0x08],
4861     '\\rceil':          [5,2,0x65,3,0x07],
4862     '\\lceil':          [4,2,0x64,3,0x06],
4863     '\\rfloor':         [5,2,0x63,3,0x05],
4864     '\\lfloor':         [4,2,0x62,3,0x04],
4865     '\\lbrack':         [0,0,0x5B,3,0x02],
4866     '\\rbrack':         [0,0,0x5D,3,0x03]
4867   },
4868
4869   /*
4870    *  The basic macros for plain TeX.
4871    *
4872    *  When the control sequence on the left is called, the JavaScript
4873    *  funtion on the right is called, with the name of the control sequence
4874    *  as its first parameter (this way, the same function can be called by
4875    *  several different control sequences to do similar actions, and the
4876    *  function can still tell which TeX command was issued).  If the right
4877    *  is an array, the first entry is the routine to call, and the
4878    *  remaining entries in the array are parameters to pass to the function
4879    *  as the second parameter (they are in an array reference).
4880    *  
4881    *  Note:  TeX macros as defined by the user are discussed below.
4882    */
4883   macros: {
4884     displaystyle:      ['HandleStyle','D'],
4885     textstyle:         ['HandleStyle','T'],
4886     scriptstyle:       ['HandleStyle','S'],
4887     scriptscriptstyle: ['HandleStyle','SS'],
4888     
4889     rm:                ['HandleFont',0],
4890     mit:               ['HandleFont',1],
4891     oldstyle:          ['HandleFont',1],
4892     cal:               ['HandleFont',2],
4893     it:                ['HandleFont',4],
4894     bf:                ['HandleFont',6],
4895     
4896     font:              ['Extension','font'],
4897     
4898     left:              'HandleLeft',
4899     right:             'HandleRight',
4900
4901     arcsin:       ['NamedOp',0],
4902     arccos:       ['NamedOp',0],
4903     arctan:       ['NamedOp',0],
4904     arg:          ['NamedOp',0],
4905     cos:          ['NamedOp',0],
4906     cosh:         ['NamedOp',0],
4907     cot:          ['NamedOp',0],
4908     coth:         ['NamedOp',0],
4909     csc:          ['NamedOp',0],
4910     deg:          ['NamedOp',0],
4911     det:           'NamedOp',
4912     dim:          ['NamedOp',0],
4913     exp:          ['NamedOp',0],
4914     gcd:           'NamedOp',
4915     hom:          ['NamedOp',0],
4916     inf:           'NamedOp',
4917     ker:          ['NamedOp',0],
4918     lg:           ['NamedOp',0],
4919     lim:           'NamedOp',
4920     liminf:       ['NamedOp',null,'lim<span style="margin-left: '+1/6+'em"></span>inf'],
4921     limsup:       ['NamedOp',null,'lim<span style="margin-left: '+1/6+'em"></span>sup'],
4922     ln:           ['NamedOp',0],
4923     log:          ['NamedOp',0],
4924     max:           'NamedOp',
4925     min:           'NamedOp',
4926     Pr:            'NamedOp',
4927     sec:          ['NamedOp',0],
4928     sin:          ['NamedOp',0],
4929     sinh:         ['NamedOp',0],
4930     sup:           'NamedOp',
4931     tan:          ['NamedOp',0],
4932     tanh:         ['NamedOp',0],
4933
4934     vcenter:        ['HandleAtom','vcenter'],
4935     overline:       ['HandleAtom','overline'],
4936     underline:      ['HandleAtom','underline'],
4937     over:            'HandleOver',
4938     overwithdelims:  'HandleOver',
4939     atop:            'HandleOver',
4940     atopwithdelims:  'HandleOver',
4941     above:           'HandleOver',
4942     abovewithdelims: 'HandleOver',
4943     brace:           ['HandleOver','\\{','\\}'],
4944     brack:           ['HandleOver','[',']'],
4945     choose:          ['HandleOver','(',')'],
4946     
4947     overbrace:       ['Extension','leaders'],
4948     underbrace:      ['Extension','leaders'],
4949     overrightarrow:  ['Extension','leaders'],
4950     underrightarrow: ['Extension','leaders'],
4951     overleftarrow:   ['Extension','leaders'],
4952     underleftarrow:  ['Extension','leaders'],
4953     overleftrightarrow:  ['Extension','leaders'],
4954     underleftrightarrow: ['Extension','leaders'],
4955     overset:         ['Extension','underset-overset'],
4956     underset:        ['Extension','underset-overset'],
4957
4958     llap:            'HandleLap',
4959     rlap:            'HandleLap',
4960     ulap:            'HandleLap',
4961     dlap:            'HandleLap',
4962     raise:           'RaiseLower',
4963     lower:           'RaiseLower',
4964     moveleft:        'MoveLeftRight',
4965     moveright:       'MoveLeftRight',
4966
4967     frac:            'Frac',
4968     root:            'Root',
4969     sqrt:            'Sqrt',
4970
4971     //  TeX substitution macros
4972     hbar:               ['Macro','\\hbarchar\\kern-.5em h'],
4973     ne:                 ['Macro','\\not='],
4974     neq:                ['Macro','\\not='],
4975     notin:              ['Macro','\\mathrel{\\rlap{\\kern2mu/}}\\in'],
4976     cong:               ['Macro','\\mathrel{\\lower2mu{\\mathrel{{\\rlap{=}\\raise6mu\\sim}}}}'],
4977     bmod:               ['Macro','\\mathbin{\\rm mod}'],
4978     pmod:               ['Macro','\\kern 18mu ({\\rm mod}\\,\\,#1)',1],
4979     'int':              ['Macro','\\intop\\nolimits'],
4980     oint:               ['Macro','\\ointop\\nolimits'],
4981     doteq:              ['Macro','\\buildrel\\textstyle.\\over='],
4982     ldots:              ['Macro','\\mathinner{\\ldotp\\ldotp\\ldotp}'],
4983     cdots:              ['Macro','\\mathinner{\\cdotp\\cdotp\\cdotp}'],
4984     vdots:              ['Macro','\\mathinner{\\rlap{\\raise8pt{.\\rule 0pt 6pt 0pt}}\\rlap{\\raise4pt{.}}.}'],
4985     ddots:              ['Macro','\\mathinner{\\kern1mu\\raise7pt{\\rule 0pt 7pt 0pt .}\\kern2mu\\raise4pt{.}\\kern2mu\\raise1pt{.}\\kern1mu}'],
4986     joinrel:            ['Macro','\\mathrel{\\kern-4mu}'],
4987     relbar:             ['Macro','\\mathrel{\\smash-}'], // \smash, because - has the same height as +
4988     Relbar:             ['Macro','\\mathrel='],
4989     bowtie:             ['Macro','\\mathrel\\triangleright\\joinrel\\mathrel\\triangleleft'],
4990     models:             ['Macro','\\mathrel|\\joinrel='],
4991     mapsto:             ['Macro','\\mathrel{\\mapstochar\\rightarrow}'],
4992     rightleftharpoons:  ['Macro','\\vcenter{\\mathrel{\\rlap{\\raise3mu{\\rightharpoonup}}}\\leftharpoondown}'],
4993     hookrightarrow:     ['Macro','\\lhook\\joinrel\\rightarrow'],
4994     hookleftarrow:      ['Macro','\\leftarrow\\joinrel\\rhook'],
4995     Longrightarrow:     ['Macro','\\Relbar\\joinrel\\Rightarrow'],
4996     longrightarrow:     ['Macro','\\relbar\\joinrel\\rightarrow'],
4997     longleftarrow:      ['Macro','\\leftarrow\\joinrel\\relbar'],
4998     Longleftarrow:      ['Macro','\\Leftarrow\\joinrel\\Relbar'],
4999     longmapsto:         ['Macro','\\mathrel{\\mapstochar\\minuschar\\joinrel\\rightarrow}'],
5000     longleftrightarrow: ['Macro','\\leftarrow\\joinrel\\rightarrow'],
5001     Longleftrightarrow: ['Macro','\\Leftarrow\\joinrel\\Rightarrow'],
5002     iff:                ['Macro','\\;\\Longleftrightarrow\\;'],
5003     mathcal:            ['Macro','{\\cal #1}',1],
5004     mathrm:             ['Macro','{\\rm #1}',1],
5005     mathbf:             ['Macro','{\\bf #1}',1],
5006     mathbb:             ['Macro','{\\bf #1}',1],
5007     mathit:             ['Macro','{\\it #1}',1],
5008     textrm:             ['Macro','\\mathord{\\hbox{#1}}',1],
5009     textit:             ['Macro','\\mathord{\\class{textit}{\\hbox{#1}}}',1],
5010     textbf:             ['Macro','\\mathord{\\class{textbf}{\\hbox{#1}}}',1],
5011     pmb:                ['Macro','\\rlap{#1}\\kern1px{#1}',1],
5012
5013     TeX:                ['Macro','T\\kern-.1667em\\lower.5ex{E}\\kern-.125em X'],
5014
5015     limits:       ['Limits',1],
5016     nolimits:     ['Limits',0],
5017
5018     ',':          ['Spacer',1/6],
5019     ':':          ['Spacer',1/6],  // for LaTeX
5020     '>':          ['Spacer',2/9],
5021     ';':          ['Spacer',5/18],
5022     '!':          ['Spacer',-1/6],
5023     enspace:      ['Spacer',1/2],
5024     quad:         ['Spacer',1],
5025     qquad:        ['Spacer',2],
5026     thinspace:    ['Spacer',1/6],
5027     negthinspace: ['Spacer',-1/6],
5028     
5029     hskip:         'Hskip',
5030     kern:          'Hskip',
5031     rule:          ['Rule','colored'],
5032     space:         ['Rule','blank'],
5033     
5034     big:        ['MakeBig','ord',0.85],
5035     Big:        ['MakeBig','ord',1.15],
5036     bigg:       ['MakeBig','ord',1.45],
5037     Bigg:       ['MakeBig','ord',1.75],
5038     bigl:       ['MakeBig','open',0.85],
5039     Bigl:       ['MakeBig','open',1.15],
5040     biggl:      ['MakeBig','open',1.45],
5041     Biggl:      ['MakeBig','open',1.75],
5042     bigr:       ['MakeBig','close',0.85],
5043     Bigr:       ['MakeBig','close',1.15],
5044     biggr:      ['MakeBig','close',1.45],
5045     Biggr:      ['MakeBig','close',1.75],
5046     bigm:       ['MakeBig','rel',0.85],
5047     Bigm:       ['MakeBig','rel',1.15],
5048     biggm:      ['MakeBig','rel',1.45],
5049     Biggm:      ['MakeBig','rel',1.75],
5050     
5051     mathord:    ['HandleAtom','ord'],
5052     mathop:     ['HandleAtom','op'],
5053     mathopen:   ['HandleAtom','open'],
5054     mathclose:  ['HandleAtom','close'],
5055     mathbin:    ['HandleAtom','bin'],
5056     mathrel:    ['HandleAtom','rel'],
5057     mathpunct:  ['HandleAtom','punct'],
5058     mathinner:  ['HandleAtom','inner'],
5059     
5060     mathchoice: ['Extension','mathchoice'],
5061     buildrel:   'BuildRel',
5062     
5063     hbox:       'HBox',
5064     text:       'HBox',
5065     mbox:       'HBox',
5066     fbox:       ['Extension','fbox'],
5067
5068     strut:      'Strut',
5069     mathstrut:  ['Macro','\\vphantom{(}'],
5070     phantom:    ['Phantom',1,1],
5071     vphantom:   ['Phantom',1,0],
5072     hphantom:   ['Phantom',0,1],
5073     smash:      'Smash',
5074     
5075     acute:      ['MathAccent', [7,0,0x13]],
5076     grave:      ['MathAccent', [7,0,0x12]],
5077     ddot:       ['MathAccent', [7,0,0x7F]],
5078     tilde:      ['MathAccent', [7,0,0x7E]],
5079     bar:        ['MathAccent', [7,0,0x16]],
5080     breve:      ['MathAccent', [7,0,0x15]],
5081     check:      ['MathAccent', [7,0,0x14]],
5082     hat:        ['MathAccent', [7,0,0x5E]],
5083     vec:        ['MathAccent', [0,1,0x7E]],
5084     dot:        ['MathAccent', [7,0,0x5F]],
5085     widetilde:  ['MathAccent', [0,3,0x65]],
5086     widehat:    ['MathAccent', [0,3,0x62]],
5087
5088     '_':        ['Replace','ord','_','normal',-.4,.1],
5089     ' ':        ['Replace','ord','&nbsp;','normal'],
5090 //  angle:      ['Replace','ord','&#x2220;','normal'],
5091     angle:      ['Macro','\\kern2.5mu\\raise1.54pt{\\rlap{\\scriptstyle \\char{cmsy10}{54}}\\kern1pt\\rule{.45em}{-1.2pt}{1.54pt}\\kern2.5mu}'],
5092         
5093     matrix:     'Matrix',
5094     array:      'Matrix',  // ### still need to do alignment options ###
5095     pmatrix:    ['Matrix','(',')','c'],
5096     cases:      ['Matrix','\\{','.',['l','l'],null,2],
5097     eqalign:    ['Matrix',null,null,['r','l'],[5/18],3,'D'],
5098     displaylines: ['Matrix',null,null,['c'],null,3,'D'],
5099     cr:         'HandleRow',
5100     '\\':       'HandleRow',
5101     newline:    'HandleRow',
5102     noalign:    'HandleNoAlign',
5103     eqalignno:  ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'],
5104     leqalignno: ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'],
5105     
5106     //  LaTeX
5107     begin:      'Begin',
5108     end:        'End',
5109     tiny:       ['HandleSize',0],
5110     Tiny:       ['HandleSize',1],  // non-standard
5111     scriptsize: ['HandleSize',2],
5112     small:      ['HandleSize',3],
5113     normalsize: ['HandleSize',4],
5114     large:      ['HandleSize',5],
5115     Large:      ['HandleSize',6],
5116     LARGE:      ['HandleSize',7],
5117     huge:       ['HandleSize',8],
5118     Huge:       ['HandleSize',9],
5119     dots:       ['Macro','\\ldots'],
5120     
5121     newcommand:     ['Extension','newcommand'],
5122     newenvironment: ['Extension','newcommand'],
5123     def:            ['Extension','newcommand'],
5124
5125     //  Extensions to TeX
5126     color:      ['Extension','HTML'],
5127     href:       ['Extension','HTML'],
5128     'class':    ['Extension','HTML'],
5129     style:      ['Extension','HTML'],
5130     cssId:      ['Extension','HTML'],
5131     unicode:    ['Extension','HTML'],
5132     bbox:       ['Extension','bbox'],
5133     
5134     require:    'Require',
5135     
5136     //  debugging and test routines
5137     'char':     'Char'
5138   },
5139   
5140   /*
5141    *  LaTeX environments
5142    */
5143   environments: {
5144     array:        'Array',
5145     matrix:       ['Array',null,null,'c'],
5146     pmatrix:      ['Array','(',')','c'],
5147     bmatrix:      ['Array','[',']','c'],
5148     Bmatrix:      ['Array','\\{','\\}','c'],
5149     vmatrix:      ['Array','\\vert','\\vert','c'],
5150     Vmatrix:      ['Array','\\Vert','\\Vert','c'],
5151     cases:        ['Array','\\{','.','ll',null,2],
5152     eqnarray:     ['Array',null,null,'rcl',[5/18,5/18],3,'D'],
5153     'eqnarray*':  ['Array',null,null,'rcl',[5/18,5/18],3,'D'],
5154     equation:     'Equation',
5155     'equation*':  'Equation',
5156
5157     align:        ['Extension','AMSmath'],
5158     'align*':     ['Extension','AMSmath'],
5159     aligned:      ['Extension','AMSmath'],
5160     multline:     ['Extension','AMSmath'],
5161     'multline*':  ['Extension','AMSmath'],
5162     split:        ['Extension','AMSmath'],
5163     gather:       ['Extension','AMSmath'],
5164     'gather*':    ['Extension','AMSmath'],
5165     gathered:     ['Extension','AMSmath']
5166   },
5167
5168
5169   /***************************************************************************/
5170
5171   /*
5172    *  Add special characters to list above.  (This makes it possible
5173    *  to define them in a variable that the user can change.)
5174    */
5175   AddSpecial: function (obj) {
5176     for (var id in obj) {
5177       jsMath.Parser.prototype.special[jsMath.Parser.prototype[id]] = obj[id];
5178     }
5179   },
5180
5181   /*
5182    *  Throw an error
5183    */
5184   Error: function (s) {
5185     this.i = this.string.length;
5186     if (s.error) {this.error = s.error} else {
5187       if (!this.error) {this.error = s}
5188     }
5189   },
5190
5191   /***************************************************************************/
5192
5193   /*
5194    *  Check if the next character is a space
5195    */
5196   nextIsSpace: function () {
5197     return this.string.charAt(this.i).match(/[ \n\r\t]/);
5198   },
5199   
5200   /*
5201    *  Trim spaces from a string
5202    */
5203   trimSpaces: function (text) {
5204     if (typeof(text) != 'string') {return text}
5205     return text.replace(/^\s+|\s+$/g,'');
5206   },
5207
5208   /*
5209    *  Parse a substring to get its mList, and return it.
5210    *  Check that no errors occured
5211    */
5212   Process: function (arg) {
5213     var data = this.mlist.data;
5214     arg = jsMath.Parse(arg,data.font,data.size,data.style);
5215       if (arg.error) {this.Error(arg); return null}
5216     if (arg.mlist.Length() == 0) {return null}
5217     if (arg.mlist.Length() == 1) {
5218       var atom = arg.mlist.Last();
5219       if (atom.atom && atom.type == 'ord' && atom.nuc &&
5220          !atom.sub && !atom.sup && (atom.nuc.type == 'text' || atom.nuc.type == 'TeX'))
5221              {return atom.nuc}
5222     }
5223     return {type: 'mlist', mlist: arg.mlist};
5224   },
5225
5226   /*
5227    *  Get and return a control-sequence name from the TeX string
5228    */
5229   GetCommand: function () {
5230     var letter = /^([a-z]+|.) ?/i;
5231     var cmd = letter.exec(this.string.slice(this.i));
5232     if (cmd) {this.i += cmd[1].length; return cmd[1]}
5233     this.i++; return " ";
5234   },
5235
5236   /*
5237    *  Get and return a TeX argument (either a single character or control sequence,
5238    *  or the contents of the next set of braces).
5239    */
5240   GetArgument: function (name,noneOK) {
5241     while (this.nextIsSpace()) {this.i++}
5242     if (this.i >= this.string.length) {if (!noneOK) this.Error("Missing argument for "+name); return null}
5243     if (this.string.charAt(this.i) == this.close) {if (!noneOK) this.Error("Extra close brace"); return null}
5244     if (this.string.charAt(this.i) == this.cmd) {this.i++; return this.cmd+this.GetCommand()}
5245     if (this.string.charAt(this.i) != this.open) {return this.string.charAt(this.i++)}
5246     var j = ++this.i; var pcount = 1; var c = '';
5247     while (this.i < this.string.length) {
5248       c = this.string.charAt(this.i++);
5249       if (c == this.cmd) {this.i++}
5250       else if (c == this.open) {pcount++}
5251       else if (c == this.close) {
5252         if (pcount == 0) {this.Error("Extra close brace"); return null}
5253         if (--pcount == 0) {return this.string.slice(j,this.i-1)}
5254       }
5255     }
5256     this.Error("Missing close brace");
5257     return null;
5258   },
5259
5260   /*
5261    *  Get an argument and process it into an mList
5262    */
5263   ProcessArg: function (name) {
5264     var arg = this.GetArgument(name); if (this.error) {return null}
5265     return this.Process(arg);
5266   },
5267   
5268   /*
5269    *  Get and process an argument for a super- or subscript.
5270    *  (read extra args for \frac, \sqrt, \mathrm, etc.)
5271    *  This handles these macros as special cases, so is really
5272    *  rather a hack.  A more general method for indicating
5273    *  how to handle macros in scripts needs to be developed.
5274    */
5275   ProcessScriptArg: function (name) {
5276     var arg = this.GetArgument(name); if (this.error) {return null}
5277     if (arg.charAt(0) == this.cmd) {
5278       var csname = arg.substr(1);
5279       if (csname == "frac") {
5280         arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null}
5281         arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null}
5282       } else if (csname == "sqrt") {
5283         arg += '['+this.GetBrackets(csname)+']'; if (this.error) {return null}
5284         arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null}
5285       } else if (csname.match(this.scriptargs)) {
5286         arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null}
5287       }
5288     }
5289     return this.Process(arg);
5290   },
5291
5292   /*
5293    *  Get the name of a delimiter (check it in the delimiter list).
5294    */
5295   GetDelimiter: function (name) {
5296     while (this.nextIsSpace()) {this.i++}
5297     var c = this.string.charAt(this.i);
5298     if (this.i < this.string.length) {
5299       this.i++;
5300       if (c == this.cmd) {c = '\\'+this.GetCommand(name); if (this.error) return null}
5301       if (this.delimiter[c] != null) {return this.delimiter[c]}
5302     }
5303     this.Error("Missing or unrecognized delimiter for "+name);
5304     return null;
5305   },
5306   
5307   /*
5308    *  Get a dimension (including its units).
5309    *  Convert the dimen to em's, except for mu's, which must be
5310    *  converted when typeset.
5311    */
5312   GetDimen: function (name,nomu) {
5313     var rest; var advance = 0;
5314     if (this.nextIsSpace()) {this.i++}
5315     if (this.string.charAt(this.i) == '{') {
5316       rest = this.GetArgument(name);
5317     } else {
5318       rest = this.string.slice(this.i);
5319       advance = 1;
5320     }
5321     return this.ParseDimen(rest,name,advance,nomu);
5322   },
5323   
5324   ParseDimen: function (dimen,name,advance,nomu) {
5325     var match = dimen.match(/^\s*([-+]?(\.\d+|\d+(\.\d*)?))(pt|em|ex|mu|px)/);
5326     if (!match) {this.Error("Missing dimension or its units for "+name); return null}
5327     if (advance) {
5328       this.i += match[0].length;
5329       if (this.nextIsSpace()) {this.i++}
5330     }
5331     var d = match[1]-0;
5332     if (match[4] == 'px') {d /= jsMath.em}
5333     else if (match[4] == 'pt') {d /= 10}
5334     else if (match[4] == 'ex') {d *= jsMath.TeX.x_height}
5335     else if (match[4] == 'mu') {if (nomu) {d = d/18} else {d = [d,'mu']}}
5336     return d;
5337   },
5338
5339   /*
5340    *  Get the next non-space character
5341    */
5342   GetNext: function () {
5343     while (this.nextIsSpace()) {this.i++}
5344     return this.string.charAt(this.i);
5345   },
5346   
5347   /*
5348    *  Get an optional LaTeX argument in brackets
5349    */
5350   GetBrackets: function (name) {
5351     var c = this.GetNext(); if (c != '[') return '';
5352     var start = ++this.i; var pcount = 0;
5353     while (this.i < this.string.length) {
5354       c = this.string.charAt(this.i++);
5355       if (c == '{') {pcount++}
5356       else if (c == '}') {
5357         if (pcount == 0)
5358           {this.Error("Extra close brace while looking for ']'"); return null}
5359         pcount --;
5360       } else if (c == this.cmd) {
5361         this.i++;
5362       } else if (c == ']') {
5363         if (pcount == 0) {return this.string.slice(start,this.i-1)}
5364       }
5365     }
5366     this.Error("Couldn't find closing ']' for argument to "+this.cmd+name);
5367     return null;
5368   },
5369   
5370   /*
5371    *  Get everything up to the given control sequence name (token)
5372    */
5373   GetUpto: function (name,token) {
5374     while (this.nextIsSpace()) {this.i++}
5375     var start = this.i; var pcount = 0;
5376     while (this.i < this.string.length) {
5377       var c = this.string.charAt(this.i++);
5378       if (c == '{') {pcount++}
5379       else if (c == '}') {
5380         if (pcount == 0)
5381           {this.Error("Extra close brace while looking for "+this.cmd+token); return null}
5382         pcount --;
5383       } else if (c == this.cmd) {
5384         // really need separate counter for begin/end
5385         // and it should really be a stack (new pcount for each begin)
5386         if (this.string.slice(this.i,this.i+5) == "begin") {pcount++; this.i+=4}
5387         else if (this.string.slice(this.i,this.i+3) == "end") {
5388           if (pcount > 0) {pcount--; this.i += 2}
5389         }
5390         if (pcount == 0)  {
5391           if (this.string.slice(this.i,this.i+token.length) == token) {
5392             c = this.string.charAt(this.i+token.length);
5393             if (c.match(/[^a-z]/i) || !token.match(/[a-z]/i)) {
5394               var arg = this.string.slice(start,this.i-1);
5395               this.i += token.length;
5396               return arg;
5397             }
5398           }
5399         }
5400         this.i++;
5401       }
5402     }
5403     this.Error("Couldn't find "+this.cmd+token+" for "+name);
5404     return null;
5405   },
5406
5407   /*
5408    *  Get a parameter delimited by a control sequence, and
5409    *  process it to get its mlist
5410    */
5411   ProcessUpto: function (name,token) {
5412     var arg = this.GetUpto(name,token); if (this.error) return null;
5413     return this.Process(arg);
5414   },
5415
5416   /*
5417    *  Get everything up to \end{env}
5418    */
5419   GetEnd: function (env) {
5420     var body = ''; var name = '';
5421     while (name != env) {
5422       body += this.GetUpto('begin{'+env+'}','end'); if (this.error) return null;
5423       name = this.GetArgument(this.cmd+'end'); if (this.error) return null;
5424     }
5425     return body;
5426   },
5427   
5428
5429   /***************************************************************************/
5430
5431
5432   /*
5433    *  Ignore spaces
5434    */
5435   Space: function () {},
5436
5437   /*
5438    *  Collect together any primes and convert them to a superscript
5439    */
5440   Prime: function (c) {
5441     var base = this.mlist.Last();
5442     if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac'))
5443        {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))}
5444     if (base.sup) {this.Error("Prime causes double exponent: use braces to clarify"); return}
5445     var sup = '';
5446     while (c == "'") {sup += this.cmd+'prime'; c = this.GetNext(); if (c == "'") {this.i++}}
5447     base.sup = this.Process(sup);
5448     base.sup.isPrime = 1;
5449   },
5450
5451   /*
5452    *  Raise or lower its parameter by a given amount
5453    *  @@@ Note that this is different from TeX, which requires an \hbox @@@
5454    *  ### make this work with mu's ###
5455    */
5456   RaiseLower: function (name) {
5457     var h = this.GetDimen(this.cmd+name,1); if (this.error) return;
5458     var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return;
5459     if (name == 'lower') {h = -h}
5460     this.mlist.Add(new jsMath.mItem('raise',{nuc: box, raise: h}));
5461   },
5462   
5463   /*
5464    *  Shift an expression to the right or left
5465    *  @@@ Note that this is different from TeX, which requires a \vbox @@@
5466    *  ### make this work with mu's ###
5467    */
5468   MoveLeftRight: function (name) {
5469     var x = this.GetDimen(this.cmd+name,1); if (this.error) return;
5470     var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return;
5471     if (name == 'moveleft') {x = -x}
5472     this.mlist.Add(jsMath.mItem.Space(x));
5473     this.mlist.Add(jsMath.mItem.Atom('ord',box));
5474     this.mlist.Add(jsMath.mItem.Space(-x));
5475   },
5476
5477   /*
5478    *  Load an extension if it has not already been loaded
5479    */
5480   Require: function (name) {
5481     var file = this.GetArgument(this.cmd+name); if (this.error) return;
5482     file = jsMath.Extension.URL(file);
5483     if (jsMath.Setup.loaded[file]) return;
5484     this.Extension(null,[file]);
5485   },
5486   
5487   /*
5488    *  Load an extension file and restart processing the math
5489    */
5490   Extension: function (name,data) {
5491     jsMath.Translate.restart = 1;
5492     if (name != null) {delete jsMath.Parser.prototype[data[1]||'macros'][name]}
5493     jsMath.Extension.Require(data[0],jsMath.Translate.asynchronous);
5494     throw "restart";
5495   },
5496   
5497   /*
5498    *  Implements \frac{num}{den}
5499    */
5500   Frac: function (name) {
5501     var num = this.ProcessArg(this.cmd+name); if (this.error) return;
5502     var den = this.ProcessArg(this.cmd+name); if (this.error) return;
5503     this.mlist.Add(jsMath.mItem.Fraction('over',num,den));
5504   },
5505   
5506   /*
5507    *  Implements \sqrt[n]{...}
5508    */
5509   Sqrt: function (name) {
5510     var n = this.GetBrackets(this.cmd+name); if (this.error) return;
5511     var arg = this.ProcessArg(this.cmd+name); if (this.error) return;
5512     var box = jsMath.mItem.Atom('radical',arg);
5513     if (n != '') {box.root = this.Process(n); if (this.error) return}
5514     this.mlist.Add(box);
5515   },
5516
5517   /*
5518    *  Implements \root...\of{...}
5519    */
5520   Root: function (name) {
5521     var n = this.ProcessUpto(this.cmd+name,'of'); if (this.error) return;
5522     var arg = this.ProcessArg(this.cmd+name); if (this.error) return;
5523     var box = jsMath.mItem.Atom('radical',arg);
5524     box.root = n; this.mlist.Add(box);
5525   },
5526   
5527
5528   /*
5529    *  Implements \buildrel...\over{...}
5530    */
5531   BuildRel: function (name) {
5532     var top = this.ProcessUpto(this.cmd+name,'over'); if (this.error) return;
5533     var bot = this.ProcessArg(this.cmd+name); if (this.error) return;
5534     var op = jsMath.mItem.Atom('op',bot);
5535     op.limits = 1; op.sup = top;
5536     this.mlist.Add(op);
5537   },
5538
5539   /*
5540    *  Create a delimiter of the type and size specified in the parameters
5541    */
5542   MakeBig: function (name,data) {
5543     var type = data[0]; var h = data[1] * jsMath.p_height;
5544     var delim = this.GetDelimiter(this.cmd+name); if (this.error) return;
5545     this.mlist.Add(jsMath.mItem.Atom(type,jsMath.Box.Delimiter(h,delim,'T')));
5546   },
5547   
5548   /*
5549    *  Insert the specified character in the given font.
5550    *  (Try to load the font if it is not already available.)
5551    */
5552   Char: function (name) {
5553     var font = this.GetArgument(this.cmd+name); if (this.error) return;
5554     var n = this.GetArgument(this.cmd+name); if (this.error) return;
5555     if (!jsMath.TeX[font]) {
5556       jsMath.TeX[font] = [];
5557       this.Extension(null,[jsMath.Font.URL(font)]);
5558     } else {
5559       this.mlist.Add(jsMath.mItem.Typeset(jsMath.Box.TeX(n-0,font,this.mlist.data.style,this.mlist.data.size)));
5560     }
5561   },
5562   
5563   /*
5564    *  Create an array or matrix.
5565    */
5566   Matrix: function (name,delim) {
5567     var data = this.mlist.data;
5568     var arg = this.GetArgument(this.cmd+name); if (this.error) return;
5569     var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,delim[5] || 'T');
5570     parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = [];
5571     parse.Parse(); if (parse.error) {this.Error(parse); return}
5572     parse.HandleRow(name,1);  // be sure the last row is recorded
5573     var box = jsMath.Box.Layout(data.size,parse.table,delim[2]||null,delim[3]||null,parse.rspacing,delim[4]||null);
5574     // Add parentheses, if needed
5575     if (delim[0] && delim[1]) {
5576       var left  = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T');
5577       var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T');
5578       box = jsMath.Box.SetList([left,box,right],data.style,data.size);
5579     }
5580     this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box));
5581   },
5582   
5583   /*
5584    *  When we see an '&', try to add a matrix entry to the row data.
5585    *  (Use all the data in the current mList, and then clear it)
5586    */
5587   HandleEntry: function (name) {
5588     if (!this.matrix) 
5589       {this.Error(name+" can only appear in a matrix or array"); return}
5590     if (this.mlist.data.openI != null) {
5591       var open = this.mlist.Get(this.mlist.data.openI);
5592       if (open.left) {this.Error("Missing "+this.cmd+"right")}
5593         else {this.Error("Missing close brace")}
5594     }
5595     if (this.mlist.data.overI != null) {this.mlist.Over()}
5596     var data = this.mlist.data;
5597     this.mlist.Atomize(data.style,data.size);
5598     var box = this.mlist.Typeset(data.style,data.size);
5599     box.entry = data.entry; delete data.entry; if (!box.entry) {box.entry = {}};
5600     this.row[this.row.length] = box;
5601     this.mlist = new jsMath.mList(null,null,data.size,data.style); 
5602   },
5603   
5604   /*
5605    *  When we see a \cr or \\, try to add a row to the table
5606    */
5607   HandleRow: function (name,last) {
5608     var dimen;
5609     if (!this.matrix) {this.Error(this.cmd+name+" can only appear in a matrix or array"); return}
5610     if (name == "\\") {
5611       dimen = this.GetBrackets(this.cmd+name); if (this.error) return;
5612       if (dimen) {dimen = this.ParseDimen(dimen,this.cmd+name,0,1)}
5613     }
5614     this.HandleEntry(name);
5615     if (!last || this.row.length > 1 || this.row[0].format != 'null')
5616       {this.table[this.table.length] = this.row}
5617     if (dimen) {this.rspacing[this.table.length] = dimen}
5618     this.row = [];
5619   },
5620   
5621   /*
5622    *  Look for \vskip or \vspace in \noalign parameters
5623    */
5624   HandleNoAlign: function (name) {
5625     var arg = this.GetArgument(this.cmd+name); if (this.error) return;
5626     var skip = arg.replace(/^.*(vskip|vspace)([^a-z])/i,'$2');
5627     if (skip.length == arg.length) return;
5628     var d = this.ParseDimen(skip,this.cmd+RegExp.$1,0,1); if (this.error) return;
5629     this.rspacing[this.table.length] = (this.rspacing[this.table.length] || 0) + d;
5630   },
5631   
5632   /*
5633    *  LaTeX array environment
5634    */
5635   Array: function (name,delim) {
5636     var columns = delim[2]; var cspacing = delim[3];
5637     if (!columns) {
5638       columns = this.GetArgument(this.cmd+'begin{'+name+'}');
5639       if (this.error) return;
5640     }
5641     columns = columns.replace(/[^clr]/g,'');
5642     columns = columns.split('');
5643     var data = this.mlist.data; var style = delim[5] || 'T';
5644     var arg = this.GetEnd(name); if (this.error) return;
5645     var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,style);
5646     parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = [];
5647     parse.Parse(); if (parse.error) {this.Error(parse); return}
5648     parse.HandleRow(name,1);  // be sure the last row is recorded
5649     var box = jsMath.Box.Layout(data.size,parse.table,columns,cspacing,parse.rspacing,delim[4],delim[6],delim[7]);
5650     // Add parentheses, if needed
5651     if (delim[0] && delim[1]) {
5652       var left  = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T');
5653       var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T');
5654       box = jsMath.Box.SetList([left,box,right],data.style,data.size);
5655     }
5656     this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box));
5657   },
5658   
5659   /*
5660    *  LaTeX \begin{env}
5661    */
5662   Begin: function (name) {
5663     var env = this.GetArgument(this.cmd+name); if (this.error) return;
5664     if (env.match(/[^a-z*]/i)) {this.Error('Invalid environment name "'+env+'"'); return}
5665     if (!this.environments[env]) {this.Error('Unknown environment "'+env+'"'); return}
5666     var cmd = this.environments[env];
5667     if (typeof(cmd) == "string") {cmd = [cmd]}
5668     this[cmd[0]](env,cmd.slice(1));
5669   },
5670   
5671   /*
5672    *  LaTeX \end{env}
5673    */
5674   End: function (name) {
5675     var env = this.GetArgument(this.cmd+name); if (this.error) return;
5676     this.Error(this.cmd+name+'{'+env+'} without matching '+this.cmd+'begin');
5677   },
5678   
5679   /*
5680    *  LaTeX equation environment (just remove the environment)
5681    */
5682   Equation: function (name) {
5683     var arg = this.GetEnd(name); if (this.error) return;
5684     this.string = arg+this.string.slice(this.i); this.i = 0;
5685   },
5686
5687   /*
5688    *  Add a fixed amount of horizontal space
5689    */
5690   Spacer: function (name,w) {
5691     this.mlist.Add(jsMath.mItem.Space(w-0));
5692   },
5693   
5694   /*
5695    *  Add horizontal space given by the argument
5696    */
5697   Hskip: function (name) {
5698     var w = this.GetDimen(this.cmd+name); if (this.error) return;
5699     this.mlist.Add(jsMath.mItem.Space(w));
5700   },
5701
5702   /*
5703    *  Typeset the argument as plain text rather than math.
5704    */
5705   HBox: function (name) {
5706     var text = this.GetArgument(this.cmd+name); if (this.error) return;
5707     var box = jsMath.Box.InternalMath(text,this.mlist.data.size);
5708     this.mlist.Add(jsMath.mItem.Typeset(box));
5709   },
5710   
5711   /*
5712    *  Insert a rule of a particular width, height and depth
5713    *  This replaces \hrule and \vrule
5714    *  @@@ not a standard TeX command, and all three parameters must be given @@@
5715    */
5716   Rule: function (name,style) {
5717     var w = this.GetDimen(this.cmd+name,1); if (this.error) return;
5718     var h = this.GetDimen(this.cmd+name,1); if (this.error) return;
5719     var d = this.GetDimen(this.cmd+name,1); if (this.error) return;
5720     h += d; var html;
5721     if (h != 0) {h = Math.max(1.05/jsMath.em,h)}
5722     if (h == 0 || w == 0 || style == "blank")
5723       {html = jsMath.HTML.Blank(w,h)} else {html = jsMath.HTML.Rule(w,h)}
5724     if (d) {
5725       html = '<span style="vertical-align:'+jsMath.HTML.Em(-d)+'">'
5726            +  html + '</span>';
5727     }
5728     this.mlist.Add(jsMath.mItem.Typeset(new jsMath.Box('html',html,w,h-d,d)));
5729   },
5730   
5731   /*
5732    *  Inserts an empty box of a specific height and depth
5733    */
5734   Strut: function () {
5735     var size = this.mlist.data.size;
5736     var box = jsMath.Box.Text('','normal','T',size).Styled();
5737     box.bh = box.bd = 0; box.h = .8; box.d = .3; box.w = box.Mw = 0;
5738     this.mlist.Add(jsMath.mItem.Typeset(box));
5739   },
5740   
5741   /*
5742    *  Handles \phantom, \vphantom and \hphantom
5743    */
5744   Phantom: function (name,data) {
5745     var arg = this.ProcessArg(this.cmd+name); if (this.error) return;
5746     this.mlist.Add(new jsMath.mItem('phantom',{phantom: arg, v: data[0], h: data[1]}));
5747   },
5748   
5749   /*
5750    *  Implements \smash
5751    */
5752   Smash: function (name,data) {
5753     var arg = this.ProcessArg(this.cmd+name); if (this.error) return;
5754     this.mlist.Add(new jsMath.mItem('smash',{smash: arg}));
5755   },
5756   
5757   /*
5758    *  Puts an accent on the following argument
5759    */
5760   MathAccent: function (name,accent) {
5761     var c = this.ProcessArg(this.cmd+name); if (this.error) return;
5762     var atom = jsMath.mItem.Atom('accent',c); atom.accent = accent[0];
5763     this.mlist.Add(atom);
5764   },
5765
5766   /*
5767    *  Handles functions and operators like sin, cos, sum, etc.
5768    */
5769   NamedOp: function (name,data) {
5770     var a = (name.match(/[^acegm-su-z]/)) ? 1: 0;
5771     var d = (name.match(/[gjpqy]/)) ? .2: 0;
5772     if (data[1]) {name = data[1]}
5773     var box = jsMath.mItem.TextAtom('op',name,jsMath.TeX.fam[0],a,d);
5774     if (data[0] != null) {box.limits = data[0]}
5775     this.mlist.Add(box);
5776   },
5777
5778   /*
5779    *  Implements \limits
5780    */
5781   Limits: function (name,data) {
5782     var atom = this.mlist.Last();
5783     if (!atom || atom.type != 'op') 
5784       {this.Error(this.cmd+name+" is allowed only on operators"); return}
5785     atom.limits = data[0];
5786   },
5787
5788   /*
5789    *  Implements macros like those created by \def.  The named control
5790    *  sequence is replaced by the string given as the first data value.
5791    *  If there is a second data value, this specifies how many arguments
5792    *  the macro uses, and in this case, those arguments are substituted
5793    *  for #1, #2, etc. within the replacement string.
5794    *  
5795    *  See the jsMath.Macro() command below for more details.
5796    *  The "newcommand" extension implements \newcommand and \def
5797    *  and are loaded automatically if needed.
5798    */
5799   Macro: function (name,data) {
5800     var text = data[0];
5801     if (data[1]) {
5802       var args = [];
5803       for (var i = 0; i < data[1]; i++)
5804         {args[args.length] = this.GetArgument(this.cmd+name); if (this.error) return}
5805       text = this.SubstituteArgs(args,text);
5806     }
5807     this.string = this.AddArgs(text,this.string.slice(this.i));
5808     this.i = 0;
5809   },
5810   
5811   /*
5812    *  Replace macro paramters with their values
5813    */
5814   SubstituteArgs: function (args,string) {
5815     var text = ''; var newstring = ''; var c; var i = 0;
5816     while (i < string.length) {
5817       c = string.charAt(i++);
5818       if (c == this.cmd) {text += c + string.charAt(i++)}
5819       else if (c == '#') {
5820         c = string.charAt(i++);
5821         if (c == "#") {text += c} else {
5822           if (!c.match(/[1-9]/) || c > args.length)
5823             {this.Error("Illegal macro parameter reference"); return null}
5824           newstring = this.AddArgs(this.AddArgs(newstring,text),args[c-1]);
5825           text = '';
5826         }
5827       } else {text += c}
5828     }
5829     return this.AddArgs(newstring,text);
5830   },
5831   
5832   /*
5833    *  Make sure that macros are followed by a space if their names
5834    *  could accidentally be continued into the following text.
5835    */
5836   AddArgs: function (s1,s2) {
5837     if (s2.match(/^[a-z]/i) && s1.match(/(^|[^\\])(\\\\)*\\[a-z]+$/i)) {s1 += ' '}
5838     return s1+s2;
5839   },
5840   
5841   /*
5842    *  Replace the control sequence with the given text
5843    */
5844   Replace: function (name,data) {
5845     this.mlist.Add(jsMath.mItem.TextAtom(data[0],data[1],data[2],data[3]));
5846   },
5847   
5848   /*
5849    *  Error for # (must use \#)
5850    */
5851   Hash: function (name) {
5852     this.Error("You can't use 'macro parameter character #' in math mode");
5853   },
5854   
5855   /*
5856    *  Insert space for ~
5857    */
5858   Tilde: function (name) {
5859     this.mlist.Add(jsMath.mItem.TextAtom('ord','&nbsp;','normal'));
5860   },
5861
5862   /*
5863    *  Implements \llap, \rlap, etc.
5864    */
5865   HandleLap: function (name) {
5866     var box = this.ProcessArg(); if (this.error) return;
5867     box = this.mlist.Add(new jsMath.mItem('lap',{nuc: box, lap: name}));
5868   },
5869
5870   /*
5871    *  Adds the argument as a specific type of atom (for commands like
5872    *  \overline, etc.)
5873    */
5874   HandleAtom: function (name,data) {
5875     var arg = this.ProcessArg(this.cmd+name); if (this.error) return;
5876     this.mlist.Add(jsMath.mItem.Atom(data[0],arg));
5877   },
5878
5879
5880   /*
5881    *  Process the character associated with a specific \mathcharcode
5882    */
5883   HandleMathCode: function (name,code) {
5884     this.HandleTeXchar(code[0],code[1],code[2]);
5885   },
5886   
5887   /*
5888    *  Add a specific character from a TeX font (use the current
5889    *  font if the type is 7 (variable) or the font is not specified)
5890    *  Load the font if it is not already loaded.
5891    */
5892   HandleTeXchar: function (type,font,code) {
5893     if (type == 7 && this.mlist.data.font != null) {font = this.mlist.data.font}
5894     font = jsMath.TeX.fam[font];
5895     if (!jsMath.TeX[font]) {
5896       jsMath.TeX[font] = [];
5897       this.Extension(null,[jsMath.Font.URL(font)]);
5898     } else {
5899       this.mlist.Add(jsMath.mItem.TeXAtom(jsMath.TeX.atom[type],code,font));
5900     }
5901   },
5902
5903   /*
5904    *  Add a TeX variable character or number
5905    */
5906   HandleVariable: function (c) {this.HandleTeXchar(7,1,c.charCodeAt(0))},
5907   HandleNumber: function (c) {this.HandleTeXchar(7,0,c.charCodeAt(0))},
5908
5909   /*
5910    *  For unmapped characters, just add them in as normal
5911    *  (non-TeX) characters
5912    */
5913   HandleOther: function (c) {
5914     this.mlist.Add(jsMath.mItem.TextAtom('ord',c,'normal'));
5915   },
5916   
5917   /*
5918    *  Ignore comments in TeX data
5919    *  ### Some browsers remove the newlines, so this might cause
5920    *      extra stuff to be ignored; look into this ###
5921    */
5922   HandleComment: function () {
5923     var c;
5924     while (this.i < this.string.length) {
5925       c = this.string.charAt(this.i++);
5926       if (c == "\r" || c == "\n") return;
5927     }
5928   },
5929
5930   /*
5931    *  Add a style change (e.g., \displaystyle, etc)
5932    */
5933   HandleStyle: function (name,style) {
5934     this.mlist.data.style = style[0];
5935     this.mlist.Add(new jsMath.mItem('style',{style: style[0]}));
5936   },
5937   
5938   /*
5939    *  Implements \small, \large, etc.
5940    */
5941   HandleSize: function (name,size) {
5942     this.mlist.data.size = size[0];
5943     this.mlist.Add(new jsMath.mItem('size',{size: size[0]}));
5944   },
5945
5946   /*
5947    *  Set the current font (e.g., \rm, etc)
5948    */
5949   HandleFont: function (name,font) {
5950     this.mlist.data.font = font[0];
5951   },
5952
5953   /*
5954    *  Look for and process a control sequence
5955    */
5956   HandleCS: function () {
5957     var cmd = this.GetCommand(); if (this.error) return;
5958     if (this.macros[cmd]) {
5959       var macro = this.macros[cmd];
5960       if (typeof(macro) == "string") {macro = [macro]}
5961       this[macro[0]](cmd,macro.slice(1)); return;
5962     }
5963     if (this.mathchardef[cmd]) {
5964       this.HandleMathCode(cmd,this.mathchardef[cmd]);
5965       return;
5966     }
5967     if (this.delimiter[this.cmd+cmd]) {
5968       this.HandleMathCode(cmd,this.delimiter[this.cmd+cmd].slice(0,3))
5969       return;
5970     }
5971     this.Error("Unknown control sequence '"+this.cmd+cmd+"'");
5972   },
5973
5974   /*
5975    *  Process open and close braces
5976    */
5977   HandleOpen: function () {this.mlist.Open()},
5978   HandleClose: function () {
5979     if (this.mlist.data.openI == null) {this.Error("Extra close brace"); return}
5980     var open = this.mlist.Get(this.mlist.data.openI);
5981     if (!open || open.left == null) {this.mlist.Close()}
5982       else {this.Error("Extra close brace or missing "+this.cmd+"right"); return}
5983   },
5984
5985   /*
5986    *  Implements \left
5987    */
5988   HandleLeft: function (name) {
5989     var left = this.GetDelimiter(this.cmd+name); if (this.error) return;
5990     this.mlist.Open(left);
5991   },
5992
5993   /*
5994    *  Implements \right
5995    */
5996   HandleRight: function (name) {
5997     var right = this.GetDelimiter(this.cmd+name); if (this.error) return;
5998     var open = this.mlist.Get(this.mlist.data.openI);
5999     if (open && open.left != null) {this.mlist.Close(right)}
6000       else {this.Error("Extra open brace or missing "+this.cmd+"left");}
6001   },
6002
6003   /*
6004    *  Implements generalized fractions (\over, \above, etc.)
6005    */
6006   HandleOver: function (name,data) {
6007     if (this.mlist.data.overI != null) 
6008       {this.Error('Ambiguous use of '+this.cmd+name); return}
6009     this.mlist.data.overI = this.mlist.Length();
6010     this.mlist.data.overF = {name: name};
6011     if (data.length > 0) {
6012       this.mlist.data.overF.left  = this.delimiter[data[0]];
6013       this.mlist.data.overF.right = this.delimiter[data[1]];
6014     } else if (name.match(/withdelims$/)) {
6015       this.mlist.data.overF.left  = this.GetDelimiter(this.cmd+name); if (this.error) return;
6016       this.mlist.data.overF.right = this.GetDelimiter(this.cmd+name); if (this.error) return;
6017     } else {
6018       this.mlist.data.overF.left  = null;
6019       this.mlist.data.overF.right = null;
6020     }
6021     if (name.match(/^above/)) {
6022       this.mlist.data.overF.thickness = this.GetDimen(this.cmd+name,1);
6023       if (this.error) return;
6024     } else {
6025       this.mlist.data.overF.thickness = null; 
6026     }
6027   },
6028
6029   /*
6030    *  Add a superscript to the preceeding atom
6031    */
6032   HandleSuperscript: function () {
6033     var base = this.mlist.Last();
6034     if (this.mlist.data.overI == this.mlist.Length()) {base = null}
6035     if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac'))
6036        {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))}
6037     if (base.sup) {
6038       if (base.sup.isPrime) {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))}
6039         else {this.Error("Double exponent: use braces to clarify"); return}
6040     }
6041     base.sup = this.ProcessScriptArg('superscript'); if (this.error) return;
6042   },
6043
6044   /*
6045    *  Add a subscript to the preceeding atom
6046    */
6047   HandleSubscript: function () {
6048     var base = this.mlist.Last();
6049     if (this.mlist.data.overI == this.mlist.Length()) {base = null}
6050     if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac'))
6051        {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))}
6052     if (base.sub) {this.Error("Double subscripts: use braces to clarify"); return}
6053     base.sub = this.ProcessScriptArg('subscript'); if (this.error) return;
6054   },
6055
6056   /*
6057    *  Parse a TeX math string, handling macros, etc.
6058    */
6059   Parse: function () {
6060     var c;
6061     while (this.i < this.string.length) {
6062       c = this.string.charAt(this.i++);
6063       if (this.mathchar[c]) {this.HandleMathCode(c,this.mathchar[c])}
6064       else if (this.special[c]) {this[this.special[c]](c)}
6065       else if (this.letter.test(c)) {this.HandleVariable(c)}
6066       else if (this.number.test(c)) {this.HandleNumber(c)}
6067       else {this.HandleOther(c)}
6068     }
6069     if (this.mlist.data.openI != null) {
6070       var open = this.mlist.Get(this.mlist.data.openI);
6071       if (open.left) {this.Error("Missing "+this.cmd+"right")}
6072         else {this.Error("Missing close brace")}
6073     }
6074     if (this.mlist.data.overI != null) {this.mlist.Over()}
6075   },
6076
6077   /*
6078    *  Perform the processing of Appendix G
6079    */
6080   Atomize: function () {
6081     var data = this.mlist.init;
6082     if (!this.error) this.mlist.Atomize(data.style,data.size)
6083   },
6084
6085   /*
6086    *  Produce the final HTML.
6087    *  
6088    *  We have to wrap the HTML it appropriate <SPAN> tags to hide its
6089    *  actual dimensions when these don't match the TeX dimensions of the
6090    *  results.  We also include an image to force the results to take up
6091    *  the right amount of space.  The results may need to be vertically
6092    *  adjusted to make the baseline appear in the correct place.
6093    */
6094   Typeset: function () {
6095     var data = this.mlist.init;
6096     var box = this.typeset = this.mlist.Typeset(data.style,data.size);
6097     if (this.error) {return '<span class="error">'+this.error+'</span>'}
6098     if (box.format == 'null') {return ''};
6099
6100     box.Styled().Remeasured(); var isSmall = 0; var isBig = 0;
6101     if (box.bh > box.h && box.bh > jsMath.h+.001) {isSmall = 1}
6102     if (box.bd > box.d && box.bd > jsMath.d+.001) {isSmall = 1}
6103     if (box.h > jsMath.h || box.d > jsMath.d) {isBig = 1}
6104     
6105     var html = box.html;
6106     if (isSmall) {// hide the extra size
6107       if (jsMath.Browser.allowAbsolute) {
6108         var y = (box.bh > jsMath.h+.001 ? jsMath.h - box.bh : 0);
6109         html = jsMath.HTML.Absolute(html,box.w,jsMath.h,0,y);
6110       } else if (jsMath.Browser.valignBug) {
6111         // remove line height
6112         html = '<span style="line-height:'+jsMath.HTML.Em(jsMath.d)+';">'
6113              +    html + '</span>';
6114       } else if (!jsMath.Browser.operaLineHeightBug) {
6115         // remove line height and try to hide the depth
6116         var dy = jsMath.HTML.Em(Math.max(0,box.bd-jsMath.hd)/3);
6117         html = '<span style="line-height:'+jsMath.HTML.Em(jsMath.d)+';'
6118                + ' position:relative; top:'+dy+'; vertical-align:'+dy
6119                + '">' + html + '</span>';
6120       }
6121       isBig = 1;
6122     }
6123     if (isBig) {
6124       // add height and depth to the line
6125       //   (force a little extra to separate lines if needed)
6126       html += jsMath.HTML.Blank(0,box.h+.05,box.d+.05);
6127     }
6128     return '<nobr><span class="scale">'+html+'</span></nobr>';
6129   }
6130
6131 });
6132
6133 /*
6134  *  Make these characters special (and call the given routines)
6135  */
6136 jsMath.Parser.prototype.AddSpecial({
6137   cmd:   'HandleCS',
6138   open:  'HandleOpen',
6139   close: 'HandleClose'
6140 });
6141
6142
6143 /*
6144  *  The web-page author can call jsMath.Macro to create additional
6145  *  TeX macros for use within his or her mathematics.  See the
6146  *  author's documentation for more details.
6147  */
6148
6149 jsMath.Add(jsMath,{
6150   Macro: function (name) {
6151     var macro = jsMath.Parser.prototype.macros;
6152     macro[name] = ['Macro'];
6153     for (var i = 1; i < arguments.length; i++) 
6154       {macro[name][macro[name].length] = arguments[i]}
6155   }
6156 });
6157
6158 /*
6159  *  Use these commands to create macros that load
6160  *  JavaScript files and reprocess the mathematics when
6161  *  the file is loaded.  This lets you to have macros or
6162  *  LaTeX environments that autoload their own definitions
6163  *  only when they are needed, saving initial download time
6164  *  on pages where they are not used.  See the author's
6165  *  documentation for more details.
6166  *
6167  */
6168
6169 jsMath.Extension = {
6170
6171   safeRequire: 1,   // disables access to files outside of jsMath/extensions
6172   
6173   Macro: function (name,file) {
6174     var macro = jsMath.Parser.prototype.macros;
6175     if (file == null) {file = name}
6176     macro[name] = ['Extension',file];
6177   },
6178
6179   LaTeX: function (env,file) {
6180     var latex = jsMath.Parser.prototype.environments;
6181     latex[env] = ['Extension',file,'environments'];
6182   },
6183
6184   Font: function (name,font) {
6185     if (font == null) {font = name + "10"}
6186     var macro = jsMath.Parser.prototype.macros;
6187     macro[name] = ['Extension',jsMath.Font.URL(font)];
6188   },
6189   
6190   MathChar: function (font,defs) {
6191     var fam = jsMath.TeX.famName[font];
6192     if (fam == null) {
6193       fam = jsMath.TeX.fam.length;
6194       jsMath.TeX.fam[fam] = font;
6195       jsMath.TeX.famName[font] = fam;
6196     }
6197     var mathchardef = jsMath.Parser.prototype.mathchardef;
6198     for (var c in defs) {mathchardef[c] = [defs[c][0],fam,defs[c][1]]}
6199   },
6200
6201   Require: function (file,show) {
6202     if (this.safeRequire && (file.match(/\.\.\/|[^-a-z0-9.\/:_+=%~]/i) ||
6203          (file.match(/:/) && file.substr(0,jsMath.root.length) != jsMath.root))) {
6204       jsMath.Setup.loaded[file] = 1;
6205       return;
6206     }
6207     jsMath.Setup.Script(this.URL(file),show);
6208   },
6209   
6210   URL: function (file) {
6211     file = file.replace(/^\s+|\s+$/g,'');
6212     if (!file.match(/^([a-z]+:|\/|fonts|extensions\/)/i)) {file = 'extensions/'+file}
6213     if (!file.match(/\.js$/)) {file += '.js'}
6214     return file;
6215   }
6216 }
6217
6218
6219 /***************************************************************************/
6220
6221 /*
6222  *  These routines look through the web page for math elements to process.
6223  *  There are two main entry points you can call:
6224  *  
6225  *      <script> jsMath.Process() </script>
6226  *  or
6227  *      <script> jsMath.ProcessBeforeShowing() </script>
6228  *
6229  *  The first will process the page asynchronously (so the user can start
6230  *  reading the top of the file while jsMath is still processing the bottom)
6231  *  while the second does not update until all the mathematics is typeset.
6232  */
6233
6234 jsMath.Add(jsMath,{
6235   /*
6236    *  Call this at the bottom of your HTML page to have the
6237    *  mathematics typeset asynchronously.  This lets the user
6238    *  start reading the mathematics while the rest of the page
6239    *  is being processed.
6240    */
6241   Process: function (obj) {
6242     jsMath.Setup.Body();
6243     jsMath.Script.Push(jsMath.Translate,'Asynchronous',obj);
6244   },
6245   
6246   /*
6247    *  Call this at the bottom of your HTML page to have the
6248    *  mathematics typeset before the page is displayed.
6249    *  This can take a long time, so the user could cancel the
6250    *  page before it is complete; use it with caution, and only
6251    *  when there is a relatively small amount of math on the page.
6252    */
6253   ProcessBeforeShowing: function (obj) {
6254     jsMath.Setup.Body();
6255     var method = (jsMath.Controls.cookie.asynch ? "Asynchronous": "Synchronous");
6256     jsMath.Script.Push(jsMath.Translate,method,obj);
6257   },
6258   
6259   /*
6260    *  Process the contents of a single element.  It must be of
6261    *  class "math".
6262    */
6263   ProcessElement: function (obj) {
6264     jsMath.Setup.Body();
6265     jsMath.Script.Push(jsMath.Translate,'ProcessOne',obj);
6266   }
6267   
6268 });
6269
6270 jsMath.Translate = {
6271
6272   element: [],  // the list of math elements on the page
6273   cancel: 0,    // set to 1 to cancel asynchronous processing
6274   
6275   /*
6276    *  Parse a TeX string in Text or Display mode and return
6277    *  the HTML for it (taking it from the cache, if available)
6278    */
6279   Parse: function (style,s,noCache) {
6280     var cache = jsMath.Global.cache[style];
6281     if (!cache[jsMath.em]) {cache[jsMath.em] = {}}
6282     var HTML = cache[jsMath.em][s];
6283     if (!HTML || noCache) {
6284       var parse = jsMath.Parse(s,null,null,style);
6285       parse.Atomize(); HTML = parse.Typeset();
6286       if (!noCache) {cache[jsMath.em][s] = HTML}
6287     }
6288     return HTML;
6289   },
6290
6291   TextMode:    function (s,noCache) {this.Parse('T',s,noCache)},
6292   DisplayMode: function (s,noCache) {this.Parse('D',s,noCache)},
6293   
6294   /*
6295    *  Return the text of a given DOM element
6296    */
6297   GetElementText: function (element) {
6298     if (element.childNodes.length == 1 && element.childNodes[0].nodeName === "#comment") {
6299       var result = element.childNodes[0].nodeValue.match(/^\[CDATA\[(.*)\]\]$/);
6300       if (result != null) {return result[1]};
6301     }
6302     var text = this.recursiveElementText(element);
6303     element.alt = text;
6304     if (text.search('&') >= 0) {
6305       text = text.replace(/&lt;/g,'<');
6306       text = text.replace(/&gt;/g,'>');
6307       text = text.replace(/&quot;/g,'"');
6308       text = text.replace(/&amp;/g,'&');
6309     }
6310     return text;
6311   },
6312   recursiveElementText: function (element) {
6313     if (element.nodeValue != null) {
6314       if (element.nodeName !== "#comment") {return element.nodeValue}
6315       return element.nodeValue.replace(/^\[CDATA\[((.|\n)*)\]\]$/,"$1");
6316     }
6317     if (element.childNodes.length === 0) {return " "}
6318     var text = '';
6319     for (var i = 0; i < element.childNodes.length; i++) 
6320       {text += this.recursiveElementText(element.childNodes[i])}
6321     return text;
6322   },
6323   
6324   /*
6325    *  Move hidden to the location of the math element to be
6326    *  processed and reinitialize sizes for that location.
6327    */
6328   ResetHidden: function (element) {
6329     element.innerHTML =
6330       '<span id="jsMath_hiddenSpan" style="position:absolute"></span>'
6331         + jsMath.Browser.operaHiddenFix; // needed by Opera in tables
6332     element.className = '';
6333     jsMath.hidden = element.firstChild;
6334     if (!jsMath.BBoxFor("x").w) {jsMath.hidden = jsMath.hiddenTop}
6335     jsMath.ReInit();
6336   },
6337
6338   
6339   /*
6340    *  Typeset the contents of an element in \textstyle or \displaystyle
6341    */
6342   ConvertMath: function (style,element,noCache) {
6343     var text = this.GetElementText(element);
6344     this.ResetHidden(element);
6345     if (text.match(/^\s*\\nocache([^a-zA-Z])/))
6346       {noCache = true; text = text.replace(/\s*\\nocache/,'')}
6347     text = this.Parse(style,text,noCache);
6348     element.className = 'typeset';
6349     element.innerHTML = text;
6350   },
6351
6352   /*
6353    *  Process a math element
6354    */
6355   ProcessElement: function (element) {
6356     this.restart = 0;
6357     if (!element.className.match(/(^| )math( |$)/)) return; // don't reprocess elements
6358     var noCache = (element.className.toLowerCase().match(/(^| )nocache( |$)/) != null);
6359     try {
6360       var style = (element.tagName.toLowerCase() == 'div' ? 'D' : 'T');
6361       this.ConvertMath(style,element,noCache);
6362       element.onclick = jsMath.Click.CheckClick;
6363       element.ondblclick = jsMath.Click.CheckDblClick;
6364     } catch (err) {
6365       if (element.alt) {
6366         var tex = element.alt;
6367         tex = tex.replace(/&/g,'&amp;')
6368                  .replace(/</g,'&lt;')
6369                  .replace(/>/g,'&gt;');
6370         element.innerHTML = tex;
6371         element.className = 'math';
6372         if (noCache) {element.className += ' nocache'}
6373       }
6374       jsMath.hidden = jsMath.hiddenTop;
6375     }
6376   },
6377
6378   /*
6379    *  Asynchronously process all the math elements starting with
6380    *  the k-th one.  Do them in blocks of 8 (more efficient than one
6381    *  at a time, but still allows screen updates periodically).
6382    */
6383   ProcessElements: function (k) {
6384     jsMath.Script.blocking = 1;
6385     for (var i = 0; i < jsMath.Browser.processAtOnce; i++, k++) {
6386       if (k >= this.element.length || this.cancel) {
6387         this.ProcessComplete();
6388         if (this.cancel) {
6389           jsMath.Message.Set("Process Math: Canceled");
6390           jsMath.Message.Clear()
6391         }
6392         jsMath.Script.blocking = 0;
6393         jsMath.Script.Process();
6394         return;
6395       } else {
6396         var savedQueue = jsMath.Script.SaveQueue();
6397         this.ProcessElement(this.element[k]);
6398         if (this.restart) {
6399           jsMath.Script.Push(this,'ProcessElements',k);
6400           jsMath.Script.RestoreQueue(savedQueue);
6401           jsMath.Script.blocking = 0;
6402           setTimeout('jsMath.Script.Process()',jsMath.Browser.delay);
6403           return;
6404         }
6405       }
6406     }
6407     jsMath.Script.RestoreQueue(savedQueue);
6408     var p = Math.floor(100 * k / this.element.length);
6409     jsMath.Message.Set('Processing Math: '+p+'%');
6410     setTimeout('jsMath.Translate.ProcessElements('+k+')',jsMath.Browser.delay);
6411   },
6412
6413   /*
6414    *  Start the asynchronous processing of mathematics
6415    */
6416   Asynchronous: function (obj) {
6417     if (!jsMath.initialized) {jsMath.Init()}
6418     this.element = this.GetMathElements(obj);
6419     jsMath.Script.blocking = 1;
6420     this.cancel = 0; this.asynchronous = 1;
6421     jsMath.Message.Set('Processing Math: 0%',1);
6422     setTimeout('jsMath.Translate.ProcessElements(0)',jsMath.Browser.delay);
6423   },
6424   
6425   /*
6426    *  Do synchronous processing of mathematics
6427    */
6428   Synchronous: function (obj,i) {
6429     if (i == null) {
6430       if (!jsMath.initialized) {jsMath.Init()}
6431       this.element = this.GetMathElements(obj);
6432       i = 0;
6433     }
6434     this.asynchronous = 0;
6435     while (i < this.element.length) {
6436       this.ProcessElement(this.element[i]);
6437       if (this.restart) {
6438         jsMath.Synchronize('jsMath.Translate.Synchronous(null,'+i+')');
6439         jsMath.Script.Process();
6440         return;
6441       }
6442       i++;
6443     }
6444     this.ProcessComplete(1);
6445   },
6446
6447   /*
6448    *  Synchronously process the contents of a single element
6449    */
6450   ProcessOne: function (obj) {
6451     if (!jsMath.initialized) {jsMath.Init()}
6452     this.element = [obj];
6453     this.Synchronous(null,0);
6454   },
6455   
6456   /*
6457    *  Look up all the math elements on the page and
6458    *  put them in a list sorted from top to bottom of the page
6459    */
6460   GetMathElements: function (obj) {
6461     var element = []; var k;
6462     if (!obj) {obj = jsMath.document}
6463     if (typeof(obj) == 'string') {obj = jsMath.document.getElementById(obj)}
6464     if (!obj.getElementsByTagName) return null;
6465     var math = obj.getElementsByTagName('div');
6466     for (k = 0; k < math.length; k++) {
6467       if (math[k].className && math[k].className.match(/(^| )math( |$)/)) {
6468         if (jsMath.Browser.renameOK && obj.getElementsByName) 
6469                {math[k].setAttribute('name','_jsMath_')}
6470           else {element[element.length] = math[k]}
6471       }
6472     }
6473     math = obj.getElementsByTagName('span');
6474     for (k = 0; k < math.length; k++) {
6475       if (math[k].className && math[k].className.match(/(^| )math( |$)/)) {
6476         if (jsMath.Browser.renameOK && obj.getElementsByName) 
6477                {math[k].setAttribute('name','_jsMath_')}
6478           else {element[element.length] = math[k]}
6479       }
6480     }
6481     // this gets the SPAN and DIV elements interleaved in order
6482     if (jsMath.Browser.renameOK && obj.getElementsByName) {
6483       element = obj.getElementsByName('_jsMath_');
6484     } else if (jsMath.hidden.sourceIndex) {
6485       element.sort(function (a,b) {return a.sourceIndex - b.sourceIndex});
6486     }
6487     return element;
6488   },
6489
6490   /*
6491    *  Remove the window message about processing math
6492    *  and clean up any marked <SPAN> or <DIV> tags
6493    */
6494   ProcessComplete: function (noMessage) {
6495     if (jsMath.Browser.renameOK) {
6496       var element = jsMath.document.getElementsByName('_jsMath_');
6497       for (var i = element.length-1; i >= 0; i--) {
6498         element[i].removeAttribute('name');
6499       }
6500     }
6501     jsMath.hidden = jsMath.hiddenTop;
6502     this.element = []; this.restart = null;
6503     if (!noMessage) {
6504       jsMath.Message.Set('Processing Math: Done');
6505       jsMath.Message.Clear();
6506     }
6507     jsMath.Message.UnBlank();
6508     if (jsMath.Browser.safariImgBug &&
6509         (jsMath.Controls.cookie.font == 'symbol' ||
6510          jsMath.Controls.cookie.font == 'image')) {
6511       //
6512       //  For Safari, the images don't always finish
6513       //  updating, so nudge the window to cause a
6514       //  redraw.  (Hack!)
6515       //
6516       if (this.timeout) {clearTimeout(this.timeout)}
6517       this.timeout = setTimeout("jsMath.window.resizeBy(-1,0); "
6518                               + "jsMath.window.resizeBy(1,0); "
6519                               + "jsMath.Translate.timeout = null",2000);
6520     }
6521   },
6522   
6523   /*
6524    *  Cancel procesing elements
6525    */
6526   Cancel: function () {
6527     jsMath.Translate.cancel = 1;
6528     if (jsMath.Script.cancelTimer) {jsMath.Script.cancelLoad()}
6529   }
6530   
6531 };
6532
6533 jsMath.Add(jsMath,{
6534   //
6535   //  Synchronize these with the loading of the tex2math plugin.
6536   //
6537   ConvertTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX',element)},
6538   ConvertTeX2: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX2',element)},
6539   ConvertLaTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertLaTeX',element)},
6540   ConvertCustom: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertCustom',element)},
6541   CustomSearch: function (om,cm,od,cd) {jsMath.Script.Push(null,function () {jsMath.tex2math.CustomSearch(om,cm,od,cd)})},
6542   tex2math: {
6543     ConvertTeX: function () {},
6544     ConvertTeX2: function () {},
6545     ConvertLaTeX: function () {},
6546     ConvertCustom: function () {},
6547     CustomSearch: function () {}
6548   }
6549 });
6550 jsMath.Synchronize = jsMath.Script.Synchronize;
6551
6552 /***************************************************************************/
6553
6554
6555 /*
6556  *  Initialize things
6557  */
6558 try {
6559   if (window.parent != window && window.jsMathAutoload) {
6560     window.parent.jsMath = jsMath;
6561     jsMath.document = window.parent.document;
6562     jsMath.window = window.parent;
6563   }
6564 } catch (err) {}
6565
6566  
6567 jsMath.Global.Register();
6568 jsMath.Loaded();
6569 jsMath.Controls.GetCookie();
6570 jsMath.Setup.Source();
6571 jsMath.Global.Init();
6572 jsMath.Script.Init();
6573 jsMath.Setup.Fonts();
6574 if (jsMath.document.body) {jsMath.Setup.Body()}
6575 jsMath.Setup.User("onload");
6576
6577 }}