X-Git-Url: http://lambda.jimpryor.net/git/gitweb.cgi?a=blobdiff_plain;f=jsMath%2Funcompressed%2FjsMath.js;fp=jsMath%2Funcompressed%2FjsMath.js;h=3080cea643cef0dfedfdca9d1aca1ed66a52786d;hb=f084723ab17e56d39e05a8a8da8d976e670dfd42;hp=0000000000000000000000000000000000000000;hpb=b3b3c1c988e48e53f295529431d640528bb17927;p=lambda.git diff --git a/jsMath/uncompressed/jsMath.js b/jsMath/uncompressed/jsMath.js new file mode 100644 index 00000000..3080cea6 --- /dev/null +++ b/jsMath/uncompressed/jsMath.js @@ -0,0 +1,6577 @@ +/***************************************************************************** + * + * jsMath: Mathematics on the Web + * + * This jsMath package makes it possible to display mathematics in HTML pages + * that are viewable by a wide range of browsers on both the Mac and the IBM PC, + * including browsers that don't process MathML. See + * + * http://www.math.union.edu/locate/jsMath + * + * for the latest version, and for documentation on how to use jsMath. + * + * Copyright 2004-2010 by Davide P. Cervone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *****************************************************************************/ + +/* + * Prevent running everything again if this file is loaded twice + */ +if (!window.jsMath || !window.jsMath.loaded) { + +var jsMath_old = window.jsMath; // save user customizations + +// +// debugging routine +// +/* + * function ShowObject (obj,spaces) { + * var s = ''; if (!spaces) {spaces = ""} + * for (var i in obj) { + * if (obj[i] != null) { + * if (typeof(obj[i]) == "object") { + * s += spaces + i + ": {\n" + * + ShowObject(obj[i],spaces + ' ') + * + spaces + "}\n"; + * } else if (typeof(obj[i]) != "function") { + * s += spaces + i + ': ' + obj[i] + "\n"; + * } + * } + * } + * return s; + * } + */ + +/***************************************************************************/ +// +// Check for DOM support +// +if (!document.getElementById || !document.childNodes || !document.createElement) { + alert('The mathematics on this page requires W3C DOM support in its JavaScript. ' + + 'Unfortunately, your browser doesn\'t seem to have this.'); +} else { + +/***************************************************************************/ + +window.jsMath = { + + version: "3.6e", // change this if you edit the file, but don't edit this file + + document: document, // the document loading jsMath + window: window, // the window of the of loading document + + platform: (navigator.platform.match(/Mac/) ? "mac" : + navigator.platform.match(/Win/) ? "pc" : "unix"), + + // Font sizes for \tiny, \small, etc. (must match styles below) + sizes: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249], + + // + // The styles needed for the TeX fonts and other jsMath elements + // + styles: { + '.math': { // unprocessed mathematics + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal' + }, + + '.typeset': { // final typeset mathematics + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal', + 'line-height': 'normal', + 'text-indent': '0px', + 'white-space': 'normal' + }, + + '.typeset .normal': { // \hbox contents style + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal' + }, + + 'div.typeset': { // display mathematics + 'text-align': 'center', + margin: '1em 0px' + }, + + 'span.typeset': { // in-line mathematics + 'text-align': 'left' + }, + + '.typeset span': { // prevent outside CSS from setting these + 'text-align': 'left', + border: '0px', + margin: '0px', + padding: '0px' + }, + + 'a .typeset img, .typeset a img': { // links in image mode + border: '0px', + 'border-bottom': '1px solid blue;' + }, + + // Font sizes + '.typeset .size0': {'font-size': '50%'}, // tiny (\scriptscriptsize) + '.typeset .size1': {'font-size': '60%'}, // (50% of \large for consistency) + '.typeset .size2': {'font-size': '70%'}, // scriptsize + '.typeset .size3': {'font-size': '85%'}, // small (70% of \large for consistency) + '.typeset .size4': {'font-size': '100%'}, // normalsize + '.typeset .size5': {'font-size': '120%'}, // large + '.typeset .size6': {'font-size': '144%'}, // Large + '.typeset .size7': {'font-size': '173%'}, // LARGE + '.typeset .size8': {'font-size': '207%'}, // huge + '.typeset .size9': {'font-size': '249%'}, // Huge + + // TeX fonts + '.typeset .cmr10': {'font-family': 'jsMath-cmr10, serif'}, + '.typeset .cmbx10': {'font-family': 'jsMath-cmbx10, jsMath-cmr10'}, + '.typeset .cmti10': {'font-family': 'jsMath-cmti10, jsMath-cmr10'}, + '.typeset .cmmi10': {'font-family': 'jsMath-cmmi10'}, + '.typeset .cmsy10': {'font-family': 'jsMath-cmsy10'}, + '.typeset .cmex10': {'font-family': 'jsMath-cmex10'}, + + '.typeset .textit': {'font-family': 'serif', 'font-style': 'italic'}, + '.typeset .textbf': {'font-family': 'serif', 'font-weight': 'bold'}, + + '.typeset .link': {'text-decoration': 'none'}, // links in mathematics + + '.typeset .error': { // in-line error messages + 'font-size': '90%', + 'font-style': 'italic', + 'background-color': '#FFFFCC', + padding: '1px', + border: '1px solid #CC0000' + }, + + '.typeset .blank': { // internal use + display: 'inline-block', + overflow: 'hidden', + border: '0px none', + width: '0px', + height: '0px' + }, + '.typeset .spacer': { // internal use + display: 'inline-block' + }, + + '#jsMath_hiddenSpan': { // used for measuring BBoxes + visibility: 'hidden', + position: 'absolute', + top: '0px', + left: '0px', + 'line-height': 'normal', + 'text-indent': '0px' + }, + + '#jsMath_message': { // percentage complete message + position: 'fixed', + bottom: '1px', + left: '2px', + 'background-color': '#E6E6E6', + border: 'solid 1px #959595', + margin: '0px', + padding: '1px 8px', + 'z-index': '102', + color: 'black', + 'font-size': 'small', + width: 'auto' + }, + + '#jsMath_panel': { // control panel + position: 'fixed', + bottom: '1.75em', + right: '1.5em', + padding: '.8em 1.6em', + 'background-color': '#DDDDDD', + border: 'outset 2px', + 'z-index': '103', + width: 'auto', + color: 'black', + 'font-size': '10pt', + 'font-style': 'normal' + }, + '#jsMath_panel .disabled': {color: '#888888'}, // disabled items in the panel + '#jsMath_panel .infoLink': {'font-size': '85%'}, // links to web pages + + // avoid CSS polution from outside the panel + '#jsMath_panel *': { + 'font-size': 'inherit', + 'font-style': 'inherit', + 'font-family': 'inherit', + 'line-height': 'normal' + }, + '#jsMath_panel div': {'background-color': 'inherit', color: 'inherit'}, + '#jsMath_panel span': {'background-color': 'inherit', color: 'inherit'}, + '#jsMath_panel td': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit' + }, + '#jsMath_panel tr': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit' + }, + '#jsMath_panel table': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit', + height: 'auto', width: 'auto' + }, + + '#jsMath_button': { // the jsMath floating button (to open control panel) + position: 'fixed', + bottom: '1px', + right: '2px', + 'background-color': 'white', + border: 'solid 1px #959595', + margin: '0px', + padding: '0px 3px 1px 3px', + 'z-index': '102', + color: 'black', + 'text-decoration': 'none', + 'font-size': 'x-small', + width: 'auto', + cursor: 'hand' + }, + '#jsMath_button *': { + padding: '0px', border: '0px', margin: '0px', 'line-height': 'normal', + 'font-size': 'inherit', 'font-style': 'inherit', 'font-family': 'inherit' + }, + + '#jsMath_global': {'font-style': 'italic'}, // 'global' in jsMath button + + '#jsMath_noFont .message': { // missing font message window + 'text-align': 'center', + padding: '.8em 1.6em', + border: '3px solid #DD0000', + 'background-color': '#FFF8F8', + color: '#AA0000', + 'font-size': 'small', + width: 'auto' + }, + '#jsMath_noFont .link': { + padding: '0px 5px 2px 5px', + border: '2px outset', + 'background-color': '#E8E8E8', + color: 'black', + 'font-size': '80%', + width: 'auto', + cursor: 'hand' + }, + + '#jsMath_PrintWarning .message': { // warning on print pages + 'text-align': 'center', + padding: '.8em 1.6em', + border: '3px solid #DD0000', + 'background-color': '#FFF8F8', + color: '#AA0000', + 'font-size': 'x-small', + width: 'auto' + }, + + '@media print': { + '#jsMath_button': {display: 'none'}, + '#jsMath_Warning': {display: 'none'} + }, + + '@media screen': { + '#jsMath_PrintWarning': {display:'none'} + } + + }, + + + /***************************************************************************/ + + /* + * Get a jsMath DOM element + */ + Element: function (name) {return jsMath.document.getElementById('jsMath_'+name)}, + + /* + * Get the width and height (in pixels) of an HTML string + */ + BBoxFor: function (s) { + this.hidden.innerHTML = + ''+s+''; + var math = (jsMath.Browser.msieBBoxBug ? this.hidden.firstChild.firstChild : this.hidden); + var bbox = {w: math.offsetWidth, h: this.hidden.offsetHeight}; + this.hidden.innerHTML = ''; + return bbox; + }, + + /* + * Get the width and height (in ems) of an HTML string. + * Check the cache first to see if we've already measured it. + */ + EmBoxFor: function (s) { + var cache = jsMath.Global.cache.R; + if (!cache[this.em]) {cache[this.em] = {}} + if (!cache[this.em][s]) { + var bbox = this.BBoxFor(s); + cache[this.em][s] = {w: bbox.w/this.em, h: bbox.h/this.em}; + } + return cache[this.em][s]; + }, + + /* + * Initialize jsMath. This determines the em size, and a variety + * of other parameters used throughout jsMath. + */ + Init: function () { + if (jsMath.Setup.inited != 1) { + if (!jsMath.Setup.inited) {jsMath.Setup.Body()} + if (jsMath.Setup.inited != 1) { + if (jsMath.Setup.inited == -100) return; + alert("It looks like jsMath failed to set up properly (error code " + + jsMath.Setup.inited + "). " + + "I will try to keep going, but it could get ugly."); + jsMath.Setup.inited = 1; + } + } + this.em = this.CurrentEm(); + var cache = jsMath.Global.cache.B; + if (!cache[this.em]) { + cache[this.em] = {}; + cache[this.em].bb = this.BBoxFor('x'); var hh = cache[this.em].bb.h; + cache[this.em].d = this.BBoxFor('x'+jsMath.HTML.Strut(hh/this.em)).h - hh; + } + jsMath.Browser.italicCorrection = cache[this.em].ic; + var bb = cache[this.em].bb; var h = bb.h; var d = cache[this.em].d + this.h = (h-d)/this.em; this.d = d/this.em; + this.hd = this.h + this.d; + + this.Setup.TeXfonts(); + + var x_height = this.EmBoxFor('M').w/2; + this.TeX.M_height = x_height*(26/14); + this.TeX.h = this.h; this.TeX.d = this.d; this.TeX.hd = this.hd; + + this.Img.Scale(); + if (!this.initialized) { + this.Setup.Sizes(); + this.Img.UpdateFonts(); + } + + // factor for \big and its brethren + this.p_height = (this.TeX.cmex10[0].h + this.TeX.cmex10[0].d) / .85; + + this.initialized = 1; + }, + + /* + * Get the x size and if it has changed, reinitialize the sizes + */ + ReInit: function () { + if (this.em != this.CurrentEm()) {this.Init()} + }, + + /* + * Find the em size in effect at the current text location + */ + CurrentEm: function () { + var em = this.BBoxFor('').w/27; + if (em > 0) {return em} + // handle older browsers + return this.BBoxFor('').w/13; + }, + + /* + * Mark jsMath as loaded and copy any user-provided overrides + */ + Loaded: function () { + if (jsMath_old) { + var override = ['Process', 'ProcessBeforeShowing','ProcessElement', + 'ConvertTeX','ConvertTeX2','ConvertLaTeX','ConvertCustom', + 'CustomSearch', 'Synchronize', 'Macro', 'document']; + for (var i = 0; i < override.length; i++) { + if (jsMath_old[override[i]]) {delete jsMath_old[override[i]]} + } + } + if (jsMath_old) {this.Insert(jsMath,jsMath_old)} + jsMath_old = null; + jsMath.loaded = 1; + }, + + /* + * Manage JavaScript objects: + * + * Add: add/replace items in an object + * Insert: add items to an object + * Package: add items to an object prototype + */ + Add: function (dst,src) {for (var id in src) {dst[id] = src[id]}}, + Insert: function (dst,src) { + for (var id in src) { + if (dst[id] && typeof(src[id]) == 'object' + && (typeof(dst[id]) == 'object' + || typeof(dst[id]) == 'function')) { + this.Insert(dst[id],src[id]); + } else { + dst[id] = src[id]; + } + } + }, + Package: function (obj,def) {this.Insert(obj.prototype,def)} + +}; + + +/***************************************************************************/ + + /* + * Implements items associated with the global cache. + * + * This object will be replaced by a global version when + * (and if) jsMath-global.html is loaded. + */ +jsMath.Global = { + isLocal: 1, // a local copy if jsMath-global.html hasn't been loaded + cache: {T: {}, D: {}, R: {}, B: {}}, + + /* + * Clear the global (or local) cache + */ + ClearCache: function () {jsMath.Global.cache = {T: {}, D: {}, R: {}, B: {}}}, + + /* + * Initiate global mode + */ + GoGlobal: function (cookie) { + var url = String(jsMath.window.location); + var c = (jsMath.isCHMmode ? '#' : '?'); + if (cookie) {url = url.replace(/\?.*/,'') + '?' + cookie} + jsMath.Controls.Reload(jsMath.root + "jsMath-global.html" + c +escape(url)); + }, + + /* + * Check if we need to go to global mode + */ + Init: function () { + if (jsMath.Controls.cookie.global == "always" && !jsMath.noGoGlobal) { + if (navigator.accentColorName) return; // OmniWeb crashes on GoGlobal + if (!jsMath.window) {jsMath.window = window} + jsMath.Controls.loaded = 1; + jsMath.Controls.defaults.hiddenGlobal = null; + this.GoGlobal(jsMath.Controls.SetCookie(2)); + } + }, + + /* + * Try to register with a global.html window that contains us + */ + Register: function () { + var parent = jsMath.window.parent; + if (!jsMath.isCHMmode) + {jsMath.isCHMmode = (jsMath.window.location.protocol == 'mk:')} + try { + if (!jsMath.isCHMmode) this.Domain(); + if (parent.jsMath && parent.jsMath.isGlobal) + {parent.jsMath.Register(jsMath.window)} + } catch (err) {jsMath.noGoGlobal = 1} + }, + + /* + * If we're not the parent window, try to set the domain to + * match the parent's domain (so we can use the Global data + * if the surrounding frame is a Global frame). + */ + Domain: function () { + // MSIE/Mac can't do domain changes, so don't bother trying + if (navigator.appName == 'Microsoft Internet Explorer' && + jsMath.platform == 'mac' && navigator.userProfile != null) return; + // MSIE/PC can do domain change, but gets mixed up if we don't + // find a domain that works, and then can't look in window.location + // any longer. So don't try, since we can't afford to leave it confused. + if (jsMath.document.all && !jsMath.window.opera) return; + + if (window == parent) return; + var oldDomain = jsMath.document.domain; + try { + while (true) { + try {if (parent.document.title != null) return} catch (err) {} + if (!document.domain.match(/\..*\./)) break; + jsMath.document.domain = jsMath.document.domain.replace(/^[^.]*\./,''); + } + } catch (err) {} + jsMath.document.domain = oldDomain; + } + +}; + + + +/***************************************************************************/ + +/* + * + * Implement loading of remote scripts using XMLHttpRequest, if + * possible, otherwise use a hidden IFRAME and fake it. That + * method runs asynchronously, which causes lots of headaches. + * Solve these using Push command, which queues actions + * until files have loaded. + */ + +jsMath.Script = { + + request: null, // the XMLHttpRequest object + + /* + * Create the XMLHttpRequest object, if we can. + * Otherwise, use the iframe-based fallback method. + */ + Init: function () { + if (!(jsMath.Controls.cookie.asynch && jsMath.Controls.cookie.progress)) { + if (window.XMLHttpRequest) { + try {this.request = new XMLHttpRequest} catch (err) {} + // MSIE and FireFox3 can't use xmlRequest on local files, + // but we don't have jsMath.browser yet to tell, so use this check + if (this.request && jsMath.root.match(/^file:\/\//)) { + try { + this.request.open("GET",jsMath.root+"jsMath.js",false); + this.request.send(null); + } catch (err) { + this.request = null; + // Firefox3 has window.postMessage for inter-window communication. + // It can be used to handle the new file:// security model, + // so set up the listener. + if (window.postMessage && window.addEventListener) { + this.mustPost = 1; + jsMath.window.addEventListener("message",jsMath.Post.Listener,false); + } + } + } + } + if (!this.request && window.ActiveXObject && !this.mustPost) { + var xml = ["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0", + "MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"]; + for (var i = 0; i < xml.length && !this.request; i++) { + try {this.request = new ActiveXObject(xml[i])} catch (err) {} + } + } + } + // + // Use the delayed-script fallback for MSIE/Mac and old versions + // of several browsers (Opera 7.5, OmniWeb 4.5). + // + if (!this.request || jsMath.Setup.domainChanged) + {this.Load = this.delayedLoad; this.needsBody = 1} + }, + + /* + * Load a script and evaluate it in the window's context + */ + Load: function (url,show) { + if (show) { + jsMath.Message.Set("Loading "+url); + jsMath.Script.Delay(1); + jsMath.Script.Push(this,'xmlRequest',url); + jsMath.Script.Push(jsMath.Message,'Clear'); + } else { + jsMath.Script.Push(this,'xmlRequest',url); + } + }, + + /* + * Load a URL and run the contents of the file + */ + xmlRequest: function (url) { + this.blocking = 1; +// this.debug('xmlRequest: '+url); + try { + this.request.open("GET",url,false); + this.request.send(null); + } catch (err) { + this.blocking = 0; + if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""} + throw Error("jsMath can't load the file '"+url+"'\n" + + "Message: "+err.message); + } + if (this.request.status != null && (this.request.status >= 400 || this.request.status < 0)) { + // Do we need to deal with redirected links? + this.blocking = 0; + if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""} + throw Error("jsMath can't load the file '"+url+"'\n" + + "Error status: "+this.request.status); + } + if (!url.match(/\.js$/)) {return(this.request.responseText)} + var tmpQueue = this.queue; this.queue = []; +// this.debug('xml Eval ['+tmpQueue.length+']'); + jsMath.window.eval(this.request.responseText); +// this.debug('xml Done ['+this.queue.length+' + '+tmpQueue.length+']'); + this.blocking = 0; this.queue = this.queue.concat(tmpQueue); + this.Process(); + return ""; + }, + + /******************************************************************** + * + * Implement asynchronous loading and execution of scripts + * (via hidden IFRAME) interleved with other JavaScript commands + * that must be synchronized with the file loading. (Basically, this + * is for MSIE/Mac and Opera 7.5, which don't have XMLHttpRequest.) + */ + + cancelTimeout: 30*1000, // delay for canceling load (30 sec) + + blocking: 0, // true when an asynchronous action is being performed + cancelTimer: null, // timer to cancel load if it takes too long + needsBody: 0, // true if loading files requires BODY to be present + + queue: [], // the stack of pending actions + + /* + * Provide mechanism for synchronizing with the asynchronous jsMath + * file-loading mechanism. 'code' can be a string or a function. + */ + Synchronize: function (code,data) { + if (typeof(code) != 'string') {jsMath.Script.Push(null,code,data)} + else {jsMath.Script.Push(jsMath.window,'eval',code)} + }, + + /* + * Queue a function to be processed. + * If nothing is being loaded, do the pending commands. + */ + Push: function (object,method,data) { +// this.debug('Pushing: '+method+' at '+this.queue.length); // debug + this.queue[this.queue.length] = [object,method,data]; + if (!(this.blocking || (this.needsBody && !jsMath.document.body))) this.Process(); + }, + + /* + * Do any pending functions (stopping if a file load is started) + */ + Process: function () { + while (this.queue.length && !this.blocking) { + var call = this.queue[0]; this.queue = this.queue.slice(1); + var savedQueue = this.SaveQueue(); + var object = call[0]; var method = call[1]; var data = call[2]; +// this.debug('Calling: '+method+' ['+savedQueue.length+']'); // debug + if (object) {object[method](data)} else if (method) {method(data)} +// this.debug('Done: '+method+' ['+this.queue.length+' + '+savedQueue.length+'] ('+this.blocking+')'); // debug + this.RestoreQueue(savedQueue); + } + }, + + /* + * Allows pushes to occur at the FRONT of the queue + * (so a command acts as a single unit, including anything + * that it pushes on to the command stack) + */ + SaveQueue: function () { + var queue = this.queue; + this.queue = []; + return queue; + }, + RestoreQueue: function (queue) { + this.queue = this.queue.concat(queue); + }, + + /* + * Handle loading of scripts that run asynchronously + */ + delayedLoad: function (url) { +// this.debug('Loading: '+url); + this.Push(this,'startLoad',url); + }, + startLoad: function (url) { + var iframe = jsMath.document.createElement('iframe'); + iframe.style.visibility = 'hidden'; + iframe.style.position = 'absolute'; + iframe.style.width = '0px'; + iframe.style.height = '0px'; + if (jsMath.document.body.firstChild) { + jsMath.document.body.insertBefore(iframe,jsMath.document.body.firstChild); + } else { + jsMath.document.body.appendChild(iframe); + } + this.blocking = 1; this.url = url; + if (url.substr(0,jsMath.root.length) == jsMath.root) + {url = url.substr(jsMath.root.length)} + jsMath.Message.Set("Loading "+url); + this.cancelTimer = setTimeout('jsMath.Script.cancelLoad()',this.cancelTimeout); + if (this.mustPost) {iframe.src = jsMath.Post.startLoad(url,iframe)} + else if (url.match(/\.js$/)) {iframe.src = jsMath.root+"jsMath-loader.html"} + else {iframe.src = this.url} + }, + endLoad: function (action) { + if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null} + jsMath.Post.endLoad(); + jsMath.Message.Clear(); + if (action != 'cancel') {this.blocking = 0; this.Process()} + }, + + Start: function () { +// this.debug('Starting: ['+this.queue.length+'] '+this.url); + this.tmpQueue = this.queue; this.queue = []; + }, + End: function () { +// this.debug('Ending: ['+this.queue.length+' + '+this.tmpQueue.length+'] '+this.url); + this.queue = this.queue.concat(this.tmpQueue); delete this.tmpQueue; + }, + + /* + * If the loading takes too long, cancel it and end the load. + */ + cancelLoad: function (message,delay) { + if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null} + if (message == null) {message = "Can't load file"} + if (delay == null) {delay = 2000} + jsMath.Message.Set(message); + setTimeout('jsMath.Script.endLoad("cancel")',delay); + }, + + /* + * Perform a delay (to let the browser catch up) + */ + Delay: function (time) { + this.blocking = 1; + setTimeout('jsMath.Script.endDelay()',time); + }, + endDelay: function () { +// this.debug('endDelay'); + this.blocking = 0; + this.Process(); + }, + + /* + * Load an image and wait for it + * (so MSIE won't load extra copies of it) + */ + imageCount: 0, + WaitForImage: function (file) { + this.blocking = 1; this.imageCount++; + if (this.img == null) {this.img = []} + var img = new Image(); this.img[this.img.length] = img; + img.onload = function () {if (--jsMath.Script.imageCount == 0) jsMath.Script.endDelay()} + img.onerror = img.onload; img.onabort = img.onload; + img.src = file; + }, + + /* + * The code uncompressor + */ + Uncompress: function (data) { + for (var k = 0; k < data.length; k++) { + var d = data[k]; var n = d.length; + for (var i = 0; i < n; i++) {if (typeof(d[i]) == 'number') {d[i] = d[d[i]]}} + data[k] = d.join(''); + } + window.eval(data.join('')); + } + + /* + * for debugging the event queue + */ + /* + * ,debug: function (message) { + * if (jsMath.document.body && jsMath.window.debug) {jsMath.window.debug(message)} + * else {alert(message)} + * } + */ + +}; + +/***************************************************************************/ + +/* + * Handle window.postMessage() events in Firefox3 + */ + +jsMath.Post = { + window: null, // iframe we are listening to + + Listener: function (event) { + if (event.source != jsMath.Post.window) return; + var domain = event.origin.replace(/^file:\/\//,''); + var ddomain = document.domain.replace(/^file:\/\//,''); + if (domain == null || domain == "" || domain == "null") {domain = "localhost"} + if (ddomain == null || ddomain == "" || ddomain == "null") {ddomain = "localhost"} + if (domain != ddomain || !event.data.substr(0,6).match(/jsM(CP|LD|AL):/)) return; + var type = event.data.substr(6,3).replace(/ /g,''); + var message = event.data.substr(10); + if (jsMath.Post.Commands[type]) (jsMath.Post.Commands[type])(message); + // cancel event? + }, + + /* + * Commands that can be performed by the listener + */ + Commands: { + SCR: function (message) {jsMath.window.eval(message)}, + ERR: function (message) {jsMath.Script.cancelLoad(message,3000)}, + BGN: function (message) {jsMath.Script.Start()}, + END: function (message) {if (message) jsMath.Script.End(); jsMath.Script.endLoad()} + }, + + startLoad: function (url,iframe) { + this.window = iframe.contentWindow; + if (!url.match(/\.js$/)) {return jsMath.root+url} + return jsMath.root+"jsMath-loader-post.html?"+url; + }, + endLoad: function () {this.window = null} +}; + +/***************************************************************************/ + +/* + * Message and screen blanking facility + */ + +jsMath.Message = { + + blank: null, // the div to blank out the screen + message: null, // the div for the messages + text: null, // the text node for messages + clear: null, // timer for clearing message + + /* + * Create the elements needed for the message box + */ + Init: function () { + if (!jsMath.document.body || !jsMath.Controls.cookie.progress) return; + this.message = jsMath.Element('message'); + if (!this.message) { + if (jsMath.Setup.stylesReady) { + this.message = jsMath.Setup.DIV('message',{visibility:'hidden'},jsMath.fixedDiv); + } else { + this.message = jsMath.Setup.DIV('message',{ + visibility:'hidden', position:'absolute', bottom:'1px', left:'2px', + backgroundColor:'#E6E6E6', border:'solid 1px #959595', + margin:'0px', padding:'1px 8px', zIndex:102, + color:'black', fontSize:'small', width:'auto' + },jsMath.fixedDiv); + } + } + this.text = jsMath.document.createTextNode(''); + this.message.appendChild(this.text); + this.message.onmousedown = jsMath.Translate.Cancel; + }, + + /* + * Set the contents of the message box, or use the window status line + */ + Set: function (text,canCancel) { + if (this.clear) {clearTimeout(this.clear); this.clear = null} + if (jsMath.Controls.cookie.progress) { + if (!this.text) {this.Init(); if (!this.text) return} + if (jsMath.Browser.textNodeBug) {this.message.innerHTML = text} + else {this.text.nodeValue = text} + this.message.style.visibility = 'visible'; + if (canCancel) { + this.message.style.cursor = 'pointer'; + if (!this.message.style.cursor) {this.message.style.cursor = 'hand'} + this.message.title = ' Cancel Processing of Math '; + } else { + this.message.style.cursor = ''; + this.message.title = ''; + } + } else { + if (text.substr(0,8) != "Loading ") {jsMath.window.status = text} + } + }, + + /* + * Clear the message box or status line + */ + Clear: function () { + if (this.clear) {clearTimeout(this.clear)} + this.clear = setTimeout("jsMath.Message.doClear()",1000); + }, + doClear: function () { + if (this.clear) { + this.clear = null; + jsMath.window.status = ''; + if (this.text) {this.text.nodeValue = ''} + if (this.message) {this.message.style.visibility = 'hidden'} + } + }, + + + /* + * Put up a DIV that covers the window so that the + * "flicker" of processing the mathematics will not be visible + */ + Blank: function () { + if (this.blank || !jsMath.document.body) return; + this.blank = jsMath.Setup.DIV("blank",{ + position:(jsMath.Browser.msiePositionFixedBug? 'absolute': 'fixed'), + top:'0px', left:'0px', bottom:'0px', right:'0px', + zIndex:101, backgroundColor:'white' + },jsMath.fixedDiv); + if (jsMath.Browser.msieBlankBug) { + this.blank.innerHTML = ' '; + this.blank.style.width = "110%"; + this.blank.style.height = "110%"; + } + }, + + UnBlank: function () { + if (this.blank) {jsMath.document.body.removeChild(this.blank)} + this.blank = null; + } +}; + + +/***************************************************************************/ + +/* + * Miscellaneous setup and initialization + */ +jsMath.Setup = { + + loaded: [], // array of files already loaded + + /* + * Insert a DIV at the top of the page with given ID, + * attributes, and style settings + */ + DIV: function (id,styles,parent) { + if (parent == null) {parent = jsMath.document.body} + var div = jsMath.document.createElement('div'); + div.id = 'jsMath_'+id; + for (var i in styles) {div.style[i]= styles[i]} + if (!parent.hasChildNodes) {parent.appendChild(div)} + else {parent.insertBefore(div,parent.firstChild)} + return div; + }, + + /* + * Source a jsMath JavaScript file (only load any given file once) + */ + Script: function (file,show) { + if (this.loaded[file]) {return} else {this.loaded[file] = 1} + if (!file.match('^([a-zA-Z]+:/?)?/')) {file = jsMath.root + file} + jsMath.Script.Load(file,show); + }, + + /* + * Use a hidden
for measuring the BBoxes of things + */ + Hidden: function () { + jsMath.hidden = this.DIV("Hidden",{ + visibility: 'hidden', position:"absolute", + top:0, left:0, border:0, padding:0, margin:0 + }); + jsMath.hiddenTop = jsMath.hidden; + return; + }, + + /* + * Find the root URL for the jsMath files (so we can load + * the other .js and .gif files) + */ + Source: function () { + if (jsMath.Autoload && jsMath.Autoload.root) { + jsMath.root = jsMath.Autoload.root; + } else { + jsMath.root = ''; + var script = jsMath.document.getElementsByTagName('script'); + if (script) { + for (var i = 0; i < script.length; i++) { + var src = script[i].src; + if (src && src.match('(^|/|\\\\)jsMath.js$')) { + jsMath.root = src.replace(/jsMath.js$/,''); + break; + } + } + } + } + if (jsMath.root.charAt(0) == '\\') {jsMath.root = jsMath.root.replace(/\\/g,'/')} + if (jsMath.root.charAt(0) == '/') { + if (jsMath.root.charAt(1) != '/') + {jsMath.root = '//' + jsMath.document.location.host + jsMath.root} + jsMath.root = jsMath.document.location.protocol + jsMath.root; + } else if (!jsMath.root.match(/^[a-z]+:/i)) { + var src = new String(jsMath.document.location); + var pattern = new RegExp('/[^/]*/\\.\\./') + jsMath.root = src.replace(new RegExp('[^/]*$'),'') + jsMath.root; + while (jsMath.root.match(pattern)) + {jsMath.root = jsMath.root.replace(pattern,'/')} + } + jsMath.Img.root = jsMath.root + "fonts/"; + jsMath.blank = jsMath.root + "blank.gif"; + this.Domain(); + }, + + /* + * Find the most restricted common domain for the main + * page and jsMath. Report an error if jsMath is outside + * the domain of the calling page. + */ + Domain: function () { + try {jsMath.document.domain} catch (err) {return} + var jsDomain = ''; var pageDomain = jsMath.document.domain; + if (jsMath.root.match('://([^/]*)/')) {jsDomain = RegExp.$1} + jsDomain = jsDomain.replace(/:\d+$/,''); + if (jsDomain == "" || jsDomain == pageDomain) return; + // + // MSIE on the Mac can't change jsMath.document.domain and 'try' won't + // catch the error (Grrr!), so exit for them. + // + if (navigator.appName == 'Microsoft Internet Explorer' && + jsMath.platform == 'mac' && navigator.onLine && + navigator.userProfile && jsMath.document.all) return; + jsDomain = jsDomain.split(/\./); pageDomain = pageDomain.split(/\./); + if (jsDomain.length < 2 || pageDomain.length < 2 || + jsDomain[jsDomain.length-1] != pageDomain[pageDomain.length-1] || + jsDomain[jsDomain.length-2] != pageDomain[pageDomain.length-2]) { + this.DomainWarning(); + return; + } + var domain = jsDomain[jsDomain.length-2] + '.' + jsDomain[jsDomain.length-1]; + for (var i = 3; i <= jsDomain.length && i <= pageDomain.length; i++) { + if (jsDomain[jsDomain.length-i] != pageDomain[pageDomain.length-i]) break; + domain = jsDomain[jsDomain.length-i] + '.' + domain; + } + jsMath.document.domain = domain; + this.domainChanged = 1; + }, + + DomainWarning: function () { + alert("In order for jsMath to be able to load the additional " + + "components that it may need, the jsMath.js file must be " + + "loaded from a server in the same domain as the page that " + + "contains it. Because that is not the case for this page, " + + "the mathematics displayed here may not appear correctly."); + }, + + /* + * Initialize a font's encoding array + */ + EncodeFont: function (name) { + var font = jsMath.TeX[name]; + if (font[0].c != null) return; + for (var k = 0; k < 128; k++) { + var data = font[k]; font[k] = data[3]; + if (font[k] == null) {font[k] = {}}; + font[k].w = data[0]; font[k].h = data[1]; + if (data[2] != null) {font[k].d = data[2]} + font[k].c = jsMath.TeX.encoding[k]; + } + }, + + /* + * Initialize the encodings for all fonts + */ + Fonts: function () { + for (var i = 0; i < jsMath.TeX.fam.length; i++) { + var name = jsMath.TeX.fam[i]; + if (name) {this.EncodeFont(name)} + } + }, + + /* + * Look up the default height and depth for a TeX font + * and set the skewchar + */ + TeXfont: function (name) { + var font = jsMath.TeX[name]; if (font == null) return; + font.hd = jsMath.EmBoxFor(''+font[65].c+'').h; + font.d = jsMath.EmBoxFor(''+font[65].c+jsMath.HTML.Strut(font.hd)+'').h - font.hd; + font.h = font.hd - font.d; + if (name == 'cmmi10') {font.skewchar = 0177} + else if (name == 'cmsy10') {font.skewchar = 060} + }, + + /* + * Init all the TeX fonts + */ + TeXfonts: function () { + for (var i = 0; i < jsMath.TeX.fam.length; i++) + {if (jsMath.TeX.fam[i]) {this.TeXfont(jsMath.TeX.fam[i])}} + }, + + /* + * Compute font parameters for various sizes + */ + Sizes: function () { + jsMath.TeXparams = []; var i; var j; + for (j=0; j < jsMath.sizes.length; j++) {jsMath.TeXparams[j] = {}} + for (i in jsMath.TeX) { + if (typeof(jsMath.TeX[i]) != 'object') { + for (j=0; j < jsMath.sizes.length; j++) { + jsMath.TeXparams[j][i] = jsMath.sizes[j]*jsMath.TeX[i]/100; + } + } + } + }, + + /* + * Send the style definitions to the browser (these may be adjusted + * by the browser-specific code) + */ + Styles: function (styles) { + if (!styles) { + styles = jsMath.styles; + styles['.typeset .scale'] = {'font-size': jsMath.Controls.cookie.scale+'%'}; + this.stylesReady = 1; + } + jsMath.Script.Push(this,'AddStyleSheet',styles); + if (jsMath.Browser.styleChangeDelay) {jsMath.Script.Push(jsMath.Script,'Delay',1)} + }, + + /* + * Make a style string from a hash of style definitions, which are + * either strings themselves or hashes of style settings. + */ + StyleString: function (styles) { + var styleString = {}, id; + for (id in styles) { + if (typeof styles[id] === 'string') { + styleString[id] = styles[id]; + } else if (id.substr(0,1) === '@') { + styleString[id] = this.StyleString(styles[id]); + } else if (styles[id] != null) { + var style = []; + for (var name in styles[id]) { + if (styles[id][name] != null) + {style[style.length] = name + ': ' + styles[id][name]} + } + styleString[id] = style.join('; '); + } + } + var string = ''; + for (id in styleString) {string += id + " {"+styleString[id]+"}\n"} + return string; + }, + + AddStyleSheet: function (styles) { + var head = jsMath.document.getElementsByTagName('head')[0]; + if (head) { + var string = this.StyleString(styles); + if (jsMath.document.createStyleSheet) {// check for MSIE + head.insertAdjacentHTML('beforeEnd', + 'x' // MSIE needs this for some reason + + ''); + } else { + var style = jsMath.document.createElement('style'); style.type = "text/css"; + style.appendChild(jsMath.document.createTextNode(string)); + head.appendChild(style); + } + } else if (!jsMath.noHEAD) { + jsMath.noHEAD = 1; + alert("Document is missing its section. Style sheet can't be created without it."); + } + }, + + /* + * Do the initialization that requires the to be in place. + */ + Body: function () { + if (this.inited) return; + + this.inited = -1; + + jsMath.Setup.Hidden(); this.inited = -2; + jsMath.Browser.Init(); this.inited = -3; + + // blank screen if necessary + if (jsMath.Controls.cookie.blank) {jsMath.Message.Blank()}; this.inited = -4; + + jsMath.Setup.Styles(); this.inited = -5; + jsMath.Controls.Init(); this.inited = -6; + + // do user-specific initialization + jsMath.Script.Push(jsMath.Setup,'User','pre-font'); this.inited = -7; + + // make sure browser-specific loads are done before this + jsMath.Script.Push(jsMath.Font,'Check'); + if (jsMath.Font.register.length) + {jsMath.Script.Push(jsMath.Font,'LoadRegistered')} + + this.inited = 1; + }, + + /* + * Web page author can override the entries to the UserEvent hash + * functions that will be run at various times during jsMath's setup + * process. + */ + User: function (when) { + if (jsMath.Setup.UserEvent[when]) {(jsMath.Setup.UserEvent[when])()} + }, + + UserEvent: { + "pre-font": null, // after browser is set up but before fonts are tested + "onload": null // after jsMath.js is loaded and finished running + } + +}; + +jsMath.Update = { + + /* + * Update specific parameters for a limited number of font entries + */ + TeXfonts: function (change) { + for (var font in change) { + for (var code in change[font]) { + for (var id in change[font][code]) { + jsMath.TeX[font][code][id] = change[font][code][id]; + } + } + } + }, + + /* + * Update the character code for every character in a list + * of fonts + */ + TeXfontCodes: function (change) { + for (var font in change) { + for (var i = 0; i < change[font].length; i++) { + jsMath.TeX[font][i].c = change[font][i]; + } + } + } + +}; + +/***************************************************************************/ + +/* + * Implement browser-specific checks + */ + +jsMath.Browser = { + + allowAbsolute: 1, // tells if browser can nest absolutely positioned + // SPANs inside relative SPANs + allowAbsoluteDelim: 0, // OK to use absolute placement for building delims? + separateSkips: 0, // MSIE doesn't do negative left margins, and + // Netscape doesn't combine skips well + + valignBug: 0, // Konqueror doesn't nest vertical-align + operaHiddenFix: '', // for Opera to fix bug with math in tables + msieCenterBugFix: '', // for MSIE centering bug with image fonts + msieInlineBlockFix: '', // for MSIE alignment bug in non-quirks mode + msieSpaceFix: '', // for MSIE to avoid dropping empty spans + imgScale: 1, // MSI scales images for 120dpi screens, so compensate + + renameOK: 1, // tells if brower will find a tag whose name + // has been set via setAttributes + styleChangeDelay: 0, // true if style changes need a delay in order + // for them to be available + + delay: 1, // delay for asynchronous math processing + processAtOnce: 16, // number of math elements to process before screen update + + version: 0, // browser version number (when needed) + + /* + * Determine if the "top" of a is always at the same height + * or varies with the height of the rest of the line (MSIE). + */ + TestSpanHeight: function () { + jsMath.hidden.innerHTML = 'x'; + var span = jsMath.hidden.firstChild; + var img = span.firstChild; + this.spanHeightVaries = (span.offsetHeight >= img.offsetHeight && span.offsetHeight > 0); + this.spanHeightTooBig = (span.offsetHeight > img.offsetHeight); + jsMath.hidden.innerHTML = ''; + }, + + /* + * Determine if an inline-block with 0 width is OK or not + * and decide whether to use spans or images for spacing + */ + TestInlineBlock: function () { + this.block = "display:-moz-inline-box"; + this.hasInlineBlock = jsMath.BBoxFor('').w > 0; + if (this.hasInlineBlock) { + jsMath.styles['.typeset .blank'].display = '-moz-inline-box'; + delete jsMath.styles['.typeset .spacer'].display; + } else { + this.block = "display:inline-block"; + this.hasInlineBlock = jsMath.BBoxFor('').w > 0; + if (!this.hasInlineBlock) return; + } + this.block += ';overflow:hidden'; + var h = jsMath.BBoxFor('x').h; + this.mozInlineBlockBug = jsMath.BBoxFor( + 'x'+ + '').h > 2*h; + this.widthAddsBorder = jsMath.BBoxFor('').w > 10; + var h1 = jsMath.BBoxFor('x').h, + h2 = jsMath.BBoxFor('x').h, + h3 = jsMath.BBoxFor('').h; + this.msieBlockDepthBug = (h1 == h); + this.msieRuleDepthBug = (h2 == h); + this.blankWidthBug = (h3 == 0); + }, + + /* + * Determine if the NAME attribute of a tag can be changed + * using the setAttribute function, and then be properly + * returned by getElementByName. + */ + TestRenameOK: function () { + jsMath.hidden.innerHTML = ''; + var test = jsMath.hidden.firstChild; + test.setAttribute('name','jsMath_test'); + this.renameOK = (jsMath.document.getElementsByName('jsMath_test').length > 0); + jsMath.hidden.innerHTML = ''; + }, + + /* + * See if style changes occur immediately, or if we need to delay + * in order to let them take effect. + */ + TestStyleChange: function () { + jsMath.hidden.innerHTML = 'x'; + var span = jsMath.hidden.firstChild; + var w = span.offsetWidth; + jsMath.Setup.AddStyleSheet({'#jsMath_test': 'font-size:200%'}); + this.styleChangeDelay = (span.offsetWidth == w); + jsMath.hidden.innerHTML = ''; + }, + + /* + * Perform a version check on a standard version string + */ + VersionAtLeast: function (v) { + var bv = new String(this.version).split('.'); + v = new String(v).split('.'); if (v[1] == null) {v[1] = '0'} + return bv[0] > v[0] || (bv[0] == v[0] && bv[1] >= v[1]); + }, + + /* + * Test for browser characteristics, and adjust things + * to overcome specific browser bugs + */ + Init: function () { + jsMath.browser = 'unknown'; + this.TestInlineBlock(); + this.TestSpanHeight(); + this.TestRenameOK(); + this.TestStyleChange(); + + this.MSIE(); + this.Mozilla(); + this.Opera(); + this.OmniWeb(); + this.Safari(); + this.Konqueror(); + + // + // Change some routines depending on the browser + // + if (this.allowAbsoluteDelim) { + jsMath.Box.DelimExtend = jsMath.Box.DelimExtendAbsolute; + jsMath.Box.Layout = jsMath.Box.LayoutAbsolute; + } else { + jsMath.Box.DelimExtend = jsMath.Box.DelimExtendRelative; + jsMath.Box.Layout = jsMath.Box.LayoutRelative; + } + + if (this.separateSkips) { + jsMath.HTML.Place = jsMath.HTML.PlaceSeparateSkips; + jsMath.Typeset.prototype.Place = jsMath.Typeset.prototype.PlaceSeparateSkips; + } + }, + + // + // Handle bug-filled Internet Explorer + // + MSIE: function () { + if (jsMath.BBoxFor("").w) { + jsMath.browser = 'MSIE'; + if (jsMath.platform == 'pc') { + this.IE7 = (window.XMLHttpRequest != null); + this.IE8 = (jsMath.BBoxFor("").w > 0); + this.isReallyIE8 = (jsMath.document.documentMode != null); + this.quirks = (jsMath.document.compatMode == "BackCompat"); + this.msieMode = (jsMath.document.documentMode || (this.quirks ? 5 : 7)); + this.msieStandard6 = !this.quirks && !this.IE7; + this.allowAbsoluteDelim = 1; this.separateSkips = 1; + this.buttonCheck = 1; this.msieBlankBug = 1; + this.msieAccentBug = 1; this.msieRelativeClipBug = 1; + this.msieDivWidthBug = 1; this.msiePositionFixedBug = 1; + this.msieIntegralBug = 1; this.waitForImages = 1; + this.msieAlphaBug = !this.IE7; this.alphaPrintBug = !this.IE7; + this.msieCenterBugFix = 'position:relative; '; + this.msieInlineBlockFix = ' display:inline-block;'; + this.msie8HeightBug = this.msieBBoxBug = (this.msieMode == 8); + this.blankWidthBug = (this.msieMode != 8); + this.msieSpaceFix = (this.isReallyIE8 ? + '' : + ''); + jsMath.Macro('joinrel','\\mathrel{\\kern-5mu}'), + jsMath.Parser.prototype.mathchardef.mapstocharOrig = jsMath.Parser.prototype.mathchardef.mapstochar; + delete jsMath.Parser.prototype.mathchardef.mapstochar; + jsMath.Macro('mapstochar','\\rlap{\\mapstocharOrig\\,}\\kern1mu'), + jsMath.styles['.typeset .arial'] = {'font-family': "'Arial unicode MS'"}; + if (!this.IE7 || this.quirks) { + // MSIE doesn't implement fixed positioning, so use absolute + jsMath.styles['#jsMath_message'].position = 'absolute'; + delete jsMath.styles['#jsMath_message'].width; + jsMath.styles['#jsMath_panel'].position = 'absolute'; + delete jsMath.styles['#jsMath_panel'].width; + jsMath.styles['#jsMath_button'].width = '1px'; + jsMath.styles['#jsMath_button'].position = 'absolute' + delete jsMath.styles['#jsMath_button'].width; + jsMath.fixedDiv = jsMath.Setup.DIV("fixedDiv",{position:'absolute', zIndex: 101}); + jsMath.window.attachEvent("onscroll",jsMath.Controls.MoveButton); + jsMath.window.attachEvent("onresize",jsMath.Controls.MoveButton); + jsMath.Controls.MoveButton(); + } + // Make MSIE put borders around the whole button + jsMath.styles['#jsMath_noFont .link'].display = "inline-block"; + // MSIE needs this NOT to be inline-block + delete jsMath.styles['.typeset .spacer'].display; + // MSIE can't insert DIV's into text nodes, so tex2math must use SPAN's to fake DIV's + jsMath.styles['.tex2math_div'] = {}; jsMath.Add(jsMath.styles['.tex2math_div'],jsMath.styles['div.typeset']); + jsMath.styles['.tex2math_div'].width = '100%'; + jsMath.styles['.tex2math_div'].display = 'inline-block'; + // Reduce occurrance of zoom bug in IE7 + jsMath.styles['.typeset']['letter-spacing'] = '0'; + // MSIE will rescale images if the DPIs differ + if (screen.deviceXDPI && screen.logicalXDPI + && screen.deviceXDPI != screen.logicalXDPI) { + this.imgScale *= screen.logicalXDPI/screen.deviceXDPI; + jsMath.Controls.cookie.alpha = 0; + } + // IE8 doesn't puts ALL boxes at the bottom rather than on the baseline + if (this.msieRuleDepthBug) {jsMath.HTML.Strut = jsMath.HTML.msieStrut} + } else if (jsMath.platform == 'mac') { + this.msieAbsoluteBug = 1; this.msieButtonBug = 1; + this.msieDivWidthBug = 1; this.msieBlankBug = 1; + this.quirks = 1; + jsMath.Setup.Script('jsMath-msie-mac.js'); + jsMath.Parser.prototype.macros.angle = ['Replace','ord','','normal']; + jsMath.styles['#jsMath_panel'].width = '42em'; + jsMath.Controls.cookie.printwarn = 0; // MSIE/Mac doesn't handle '@media screen' + } + this.processAtOnce = Math.max(Math.floor((this.processAtOnce+1)/2),1); + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}'); + jsMath.Macro('angle','\\raise1.84pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\kern.5pt\\rule{.4em}{-1.5pt}{1.84pt}\\kern2.5mu}'); + } + }, + + // + // Handle Netscape/Mozilla (any flavor) + // + Mozilla: function () { + if (jsMath.hidden.ATTRIBUTE_NODE && jsMath.window.directories) { + jsMath.browser = 'Mozilla'; + if (jsMath.platform == 'pc') {this.alphaPrintBug = 1} + this.allowAbsoluteDelim = 1; + jsMath.styles['#jsMath_button'].cursor = jsMath.styles['#jsMath_noFont .link'].cursor = 'pointer', + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}'); + jsMath.Macro('angle','\\raise1.34pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\rule{.4em}{-1pt}{1.34pt}\\kern2.5mu}'); + if (navigator.vendor == 'Firefox') { + this.version = navigator.vendorSub; + } else if (navigator.userAgent.match(' Firefox/([0-9.]+)([a-z ]|$)')) { + this.version = RegExp.$1; + } + if (this.VersionAtLeast("3.0")) {this.mozImageSizeBug = this.lineBreakBug = 1} + } + }, + + // + // Handle OmniWeb + // + OmniWeb: function () { + if (navigator.accentColorName) { + jsMath.browser = 'OmniWeb'; + this.allowAbsolute = this.hasInlineBlock; + this.allowAbsoluteDelim = this.allowAbsolute; + this.valignBug = !this.allowAbsolute; + this.buttonCheck = 1; this.textNodeBug = 1; + jsMath.noChangeGlobal = 1; // OmniWeb craches on GoGlobal + if (!this.hasInlineBlock) {jsMath.Setup.Script('jsMath-old-browsers.js')} + } + }, + + // + // Handle Opera + // + Opera: function () { + if (this.spanHeightTooBig) { + jsMath.browser = 'Opera'; + var isOld = navigator.userAgent.match("Opera 7"); + this.allowAbsolute = 0; + this.delay = 10; + this.operaHiddenFix = '[Processing]'; + if (isOld) {jsMath.Setup.Script('jsMath-old-browsers.js')} + var version = navigator.appVersion.match(/^(\d+\.\d+)/); + if (version) {this.version = version[1]} else {this.vesion = 0} + this.operaAbsoluteWidthBug = this.operaLineHeightBug = (version[1] >= 9.5 && version[1] < 9.6); + } + }, + + // + // Handle Safari + // + Safari: function () { + if (navigator.appVersion.match(/Safari\//)) { + jsMath.browser = 'Safari'; + if (navigator.vendor.match(/Google/)) {jsMath.browser = 'Chrome'} + var version = navigator.userAgent.match("Safari/([0-9]+)"); + version = (version)? version[1] : 400; this.version = version; + jsMath.TeX.axis_height += .05; + this.allowAbsoluteDelim = version >= 125; + this.safariIFRAMEbug = version >= 312 && version < 412; + this.safariButtonBug = version < 412; + this.safariImgBug = 1; this.textNodeBug = 1; + this.lineBreakBug = version >= 526; + this.buttonCheck = version < 500; + this.styleChangeDelay = 1; + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3.25mu/}}'); + } + }, + + // + // Handle Konqueror + // + Konqueror: function () { + if (navigator.product && navigator.product.match("Konqueror")) { + jsMath.browser = 'Konqueror'; + this.allowAbsolute = 0; + this.allowAbsoluteDelim = 0; + if (navigator.userAgent.match(/Konqueror\/(\d+)\.(\d+)/)) { + if (RegExp.$1 < 3 || (RegExp.$1 == 3 && RegExp.$2 < 3)) { + this.separateSkips = 1; + this.valignBug = 1; + jsMath.Setup.Script('jsMath-old-browsers.js'); + } + } + // Apparently, Konqueror wants the names without the hyphen + jsMath.Add(jsMath.styles,{ + '.typeset .cmr10': 'font-family: jsMath-cmr10, jsMath cmr10, serif', + '.typeset .cmbx10': 'font-family: jsMath-cmbx10, jsMath cmbx10, jsMath-cmr10, jsMath cmr10', + '.typeset .cmti10': 'font-family: jsMath-cmti10, jsMath cmti10, jsMath-cmr10, jsMath cmr10', + '.typeset .cmmi10': 'font-family: jsMath-cmmi10, jsMath cmmi10', + '.typeset .cmsy10': 'font-family: jsMath-cmsy10, jsMath cmsy10', + '.typeset .cmex10': 'font-family: jsMath-cmex10, jsMath cmex10' + }); + jsMath.Font.testFont = "jsMath-cmex10, jsMath cmex10"; + } + } + +}; + +/***************************************************************************/ + +/* + * Implement font check and messages + */ +jsMath.Font = { + + testFont: "jsMath-cmex10", + fallback: "symbol", // the default fallback method + register: [], // list of fonts registered before jsMath.Init() + + // the HTML for the missing font message + message: + 'No jsMath TeX fonts found -- using image fonts instead.
\n' + + 'These may be slow and might not print well.
\n' + + 'Use the jsMath control panel to get additional information.', + + extra_message: + 'Extra TeX fonts not found:
' + + 'Using image fonts instead. This may be slow and might not print well.
\n' + + 'Use the jsMath control panel to get additional information.', + + print_message: + 'To print higher-resolution math symbols, click the
\n' + + 'Hi-Res Fonts for Printing button on the jsMath control panel.
\n', + + alpha_message: + 'If the math symbols print as black boxes, turn off image alpha channels
\n' + + 'using the Options pane of the jsMath control panel.
\n', + + /* + * Look to see if a font is found. + * Check the character in a given position, and see if it is + * wider than the usual one in that position. + */ + Test1: function (name,n,factor,prefix) { + if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''} + var wh1 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + var wh2 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + //alert([wh1.w,wh2.w,wh1.h,factor*wh2.w]); + return (wh1.w > factor*wh2.w && wh1.h != 0); + }, + + Test2: function (name,n,factor,prefix) { + if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''} + var wh1 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + var wh2 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + //alert([wh2.w,wh1.w,wh1.h,factor*wh1.w]); + return (wh2.w > factor*wh1.w && wh1.h != 0); + }, + + /* + * Check for the new jsMath versions of the fonts (blacker with + * different encoding) and if not found, look for old-style fonts. + * If they are found, load the BaKoMa encoding information. + */ + CheckTeX: function () { + var wh = jsMath.BBoxFor(''+jsMath.TeX.cmex10[1].c+''); + jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10',null,null,'jsMath-')); + if (!jsMath.nofonts) return; + /* + * if (jsMath.browser != 'Mozilla' || + * (jsMath.platform == "mac" && + * (!jsMath.Browser.VersionAtLeast(1.5) || jsMath.Browser.VersionAtLeast(3.0))) || + * (jsMath.platform != "mac" && !jsMath.Browser.VersionAtLeast(3.0))) { + * wh = jsMath.BBoxFor(''+jsMath.TeX.cmex10[1].c+''); + * jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10')); + * if (!jsMath.nofonts) {jsMath.Setup.Script("jsMath-BaKoMa-fonts.js")} + * } + */ + }, + + /* + * Check for the availability of TeX fonts. We do this by looking at + * the width and height of a character in the cmex10 font. The cmex10 + * font has depth considerably greater than most characters' widths (the + * whole font has the depth of the character with greatest depth). This + * is not the case for most fonts, so if we can access cmex10, the + * height of a character should be much bigger than the width. + * Otherwise, if we don't have cmex10, we'll get a character in another + * font with normal height and width. In this case, we insert a message + * pointing the user to the jsMath site, and load one of the fallback + * definitions. + * + */ + Check: function () { + var cookie = jsMath.Controls.cookie; + this.CheckTeX(); + if (jsMath.nofonts) { + if (cookie.autofont || cookie.font == 'tex') { + cookie.font = this.fallback; + if (cookie.warn) { + jsMath.nofontMessage = 1; + cookie.warn = 0; jsMath.Controls.SetCookie(0); + if (jsMath.window.NoFontMessage) {jsMath.window.NoFontMessage()} + else {this.Message(this.message)} + } + } + } else { + if (cookie.autofont) {cookie.font = 'tex'} + if (cookie.font == 'tex') return; + } + if (jsMath.noImgFonts) {cookie.font = 'unicode'} + if (cookie.font == 'unicode') { + jsMath.Setup.Script('jsMath-fallback-'+jsMath.platform+'.js'); + jsMath.Box.TeXnonfallback = jsMath.Box.TeX; + jsMath.Box.TeX = jsMath.Box.TeXfallback; + return; + } + if (!cookie.print && cookie.printwarn) { + this.PrintMessage( + (jsMath.Browser.alphaPrintBug && jsMath.Controls.cookie.alpha) ? + this.print_message + this.alpha_message : this.print_message); + } + if (jsMath.Browser.waitForImages) { + jsMath.Script.Push(jsMath.Script,"WaitForImage",jsMath.blank); + } + if (cookie.font == 'symbol') { + jsMath.Setup.Script('jsMath-fallback-symbols.js'); + jsMath.Box.TeXnonfallback = jsMath.Box.TeX; + jsMath.Box.TeX = jsMath.Box.TeXfallback; + return; + } + jsMath.Img.SetFont({ + cmr10: ['all'], cmmi10: ['all'], cmsy10: ['all'], + cmex10: ['all'], cmbx10: ['all'], cmti10: ['all'] + }); + jsMath.Img.LoadFont('cm-fonts'); + }, + + /* + * The message for when no TeX fonts. You can eliminate this message + * by including + * + * + * + * in your HTML file, before loading jsMath.js, if you want. But this + * means the user may not know that he or she can get a better version + * of your page. + */ + Message: function (message) { + if (jsMath.Element("Warning")) return; + var div = jsMath.Setup.DIV("Warning",{}); + div.innerHTML = + '
' + + '
' + message + + '
' + + 'jsMath Control Panel' + + '' + + 'Hide this Message' + + '

' + + '
' + + '

'; + }, + + HideMessage: function () { + var message = jsMath.Element("Warning"); + if (message) {message.style.display = "none"} + }, + + PrintMessage: function (message) { + if (jsMath.Element("PrintWarning")) return; + var div = jsMath.Setup.DIV("PrintWarning",{}); + div.innerHTML = + '
' + + '
' + message + '
' + + '
' + + '

'; + }, + + /* + * Register an extra font so jsMath knows about it + */ + Register: function (data,force) { + if (typeof(data) == 'string') {data = {name: data}} + if (!jsMath.Setup.inited && !force) { + this.register[this.register.length] = data; + return; + } + var fontname = data.name; var name = fontname.replace(/10$/,''); + var fontfam = jsMath.TeX.fam.length; + if (data.prefix == null) {data.prefix = ""} + if (!data.style) {data.style = "font-family: "+data.prefix+fontname+", serif"} + if (!data.styles) {data.styles = {}} + if (!data.macros) {data.macros = {}} + /* + * Register font family + */ + jsMath.TeX.fam[fontfam] = fontname; + jsMath.TeX.famName[fontname] = fontfam; + data.macros[name] = ['HandleFont',fontfam]; + jsMath.Add(jsMath.Parser.prototype.macros,data.macros); + /* + * Set up styles + */ + data.styles['.typeset .'+fontname] = data.style; + jsMath.Setup.Styles(data.styles); + if (jsMath.initialized) {jsMath.Script.Push(jsMath.Setup,'TeXfont',fontname)} + /* + * Check for font and give message if missing + */ + var cookie = jsMath.Controls.cookie; + var hasTeXfont = !jsMath.nofonts && + data.test(fontname,data.testChar,data.testFactor,data.prefix); + if (hasTeXfont && cookie.font == 'tex') { + if (data.tex) {data.tex(fontname,fontfam,data)} + return; + } + if (!hasTeXfont && cookie.warn && cookie.font == 'tex' && !jsMath.nofonts) { + if (!cookie.fonts.match("/"+fontname+"/")) { + cookie.fonts += fontname + "/"; jsMath.Controls.SetCookie(0); + if (!jsMath.Element("Warning")) this.Message(this.extra_message); + var extra = jsMath.Element("ExtraFonts"); + if (extra) { + if (extra.innerHTML != "") {extra.innerHTML += ','} + extra.innerHTML += " " + data.prefix+fontname; + } + } + } + if (cookie.font == 'unicode' || jsMath.noImgFonts) { + if (data.fallback) {data.fallback(fontname,fontfam,data)} + return; + } + // Image fonts + var font = {}; + if (cookie.font == 'symbol' && data.symbol != null) { + font[fontname] = data.symbol(fontname,fontfam,data); + } else { + font[fontname] = ['all']; + } + jsMath.Img.SetFont(font); + jsMath.Img.LoadFont(fontname); + if (jsMath.initialized) { + jsMath.Script.Push(jsMath.Img,'Scale'); + jsMath.Script.Push(jsMath.Img,'UpdateFonts'); + } + }, + /* + * If fonts are registered before jsMath.Init() is called, jsMath.em + * will not be available, so they need to be delayed. + */ + LoadRegistered: function () { + var i = 0; + while (i < this.register.length) {this.Register(this.register[i++],1)} + this.register = []; + }, + + /* + * Load a font + */ + Load: function (name) {jsMath.Setup.Script(this.URL(name))}, + URL: function (name) {return jsMath.Img.root+name+'/def.js'} + +}; + +/***************************************************************************/ + +/* + * Implements the jsMath control panel. + * Much of the code is in jsMath-controls.html, which is + * loaded into a hidden IFRAME on demand + */ +jsMath.Controls = { + + // Data stored in the jsMath cookie + cookie: { + scale: 100, + font: 'tex', autofont: 1, scaleImg: 0, alpha: 1, + warn: 1, fonts: '/', printwarn: 1, stayhires: 0, + button: 1, progress: 1, asynch: 0, blank: 0, + print: 0, keep: '0D', global: 'auto', hiddenGlobal: 1 + }, + + cookiePath: '/', // can also set cookieDomain + noCookiePattern: /^(file|mk):$/, // pattern for handling cookies locally + + + /* + * Create the HTML needed for control panel + */ + Init: function () { + this.panel = jsMath.Setup.DIV("panel",{display:'none'},jsMath.fixedDiv); + if (!jsMath.Browser.msieButtonBug) {this.Button()} + else {setTimeout("jsMath.Controls.Button()",500)} + }, + + /* + * Load the control panel + */ + Panel: function () { + jsMath.Translate.Cancel(); + if (this.loaded) {this.Main()} + else {jsMath.Script.delayedLoad(jsMath.root+"jsMath-controls.html")} + }, + + /* + * Create the control panel button + */ + Button: function () { + var button = jsMath.Setup.DIV("button",{},jsMath.fixedDiv); + button.title = ' Open jsMath Control Panel '; + button.innerHTML = + 'jsMath'; + if (!jsMath.Global.isLocal && !jsMath.noShowGlobal) { + button.innerHTML += + 'Global '; + } + if (button.offsetWidth < 30) {button.style.width = "auto"} + if (!this.cookie.button) {button.style.display = "none"} + }, + + /* + * Since MSIE doesn't handle position:float, we need to have the + * window repositioned every time the window scrolls. We do that + * putting the floating elements into a window-sized DIV, but + * absolutely positioned, and then move the DIV. + */ + MoveButton: function () { + var body = (jsMath.Browser.quirks ? document.body : document.documentElement); + jsMath.fixedDiv.style.left = body.scrollLeft + 'px'; + jsMath.fixedDiv.style.top = body.scrollTop + body.clientHeight + 'px'; + jsMath.fixedDiv.style.width = body.clientWidth + 'px'; +// jsMath.fixedDiv.style.top = body.scrollTop + 'px'; +// jsMath.fixedDiv.style.height = body.clientHeight + 'px'; + }, + + /* + * Get the cookie data from the browser + * (for file: references, use url '?' syntax) + */ + GetCookie: function () { + // save the current cookie settings as the defaults + if (this.defaults == null) {this.defaults = {}} + jsMath.Add(this.defaults,this.cookie); this.userSet = {}; + // get the browser's cookie data + var cookies = jsMath.document.cookie; + if (jsMath.window.location.protocol.match(this.noCookiePattern)) { + cookies = this.localGetCookie(); + this.isLocalCookie = 1; + } + if (cookies.match(/jsMath=([^;]+)/)) { + var data = unescape(RegExp.$1).split(/,/); + for (var i = 0; i < data.length; i++) { + var x = data[i].match(/(.*):(.*)/); + if (x[2].match(/^\d+$/)) {x[2] = 1*x[2]} // convert from string + this.cookie[x[1]] = x[2]; + this.userSet[x[1]] = 1; + } + } + }, + localGetCookie: function () { + return jsMath.window.location.search.substr(1); + }, + + /* + * Save the cookie data in the browser + * (for file: urls, append data like CGI reference) + */ + SetCookie: function (warn) { + var cookie = []; + for (var id in this.cookie) { + if (this.defaults[id] == null || this.cookie[id] != this.defaults[id]) + {cookie[cookie.length] = id + ':' + this.cookie[id]} + } + cookie = cookie.join(','); + if (this.isLocalCookie) { + if (warn == 2) {return 'jsMath='+escape(cookie)} + this.localSetCookie(cookie,warn); + } else { + cookie = escape(cookie); + if (cookie == '') {warn = 0} + if (this.cookiePath) {cookie += '; path='+this.cookiePath} + if (this.cookieDomain) {cookie += '; domain='+this.cookieDomain} + if (this.cookie.keep != '0D') { + var ms = { + D: 1000*60*60*24, + W: 1000*60*60*24*7, + M: 1000*60*60*24*30, + Y: 1000*60*60*24*365 + }; + var exp = new Date; + exp.setTime(exp.getTime() + + this.cookie.keep.substr(0,1) * ms[this.cookie.keep.substr(1,1)]); + cookie += '; expires=' + exp.toGMTString(); + } + if (cookie != '') { + jsMath.document.cookie = 'jsMath='+cookie; + var cookies = jsMath.document.cookie; + if (warn && !cookies.match(/jsMath=/)) + {alert("Cookies must be enabled in order to save jsMath options")} + } + } + return null; + }, + localSetCookie: function (cookie,warn) { + if (!warn) return; + var href = String(jsMath.window.location).replace(/\?.*/,""); + if (cookie != '') {href += '?jsMath=' + escape(cookie)} + if (href != jsMath.window.location.href) {this.Reload(href)} + }, + + /* + * Reload the page (with the given URL) + */ + Reload: function (url) { + if (!this.loaded) return; + this.loaded = 0; jsMath.Setup.inited = -100; + jsMath.Global.ClearCache(); + if (url) {jsMath.window.location.replace(url)} + else {jsMath.window.location.reload()} + } + +}; + +/***************************************************************************/ + +/* + * Implements the actions for clicking and double-clicking + * on math formulas + */ +jsMath.Click = { + + /* + * Handle clicking on math to get control panel + */ + CheckClick: function (event) { + if (!event) {event = jsMath.window.event} + if (event.altKey) jsMath.Controls.Panel(); + }, + + /* + * Handle double-click for seeing TeX code + */ + + CheckDblClick: function (event) { + if (!event) {event = jsMath.window.event} + if (!jsMath.Click.DblClick) { + jsMath.Extension.Require('double-click',1); + // Firefox clears the event, so copy it + var tmpEvent = event; event = {}; + for (var id in tmpEvent) {event[id] = tmpEvent[id]} + } + jsMath.Script.Push(jsMath.Click,'DblClick',[event,this.alt]); + } + +}; + +/***************************************************************************/ + +/* + * The TeX font information + */ +jsMath.TeX = { + + // + // The TeX font parameters + // + thinmuskip: 3/18, + medmuskip: 4/18, + thickmuskip: 5/18, + + x_height: .430554, + quad: 1, + num1: .676508, + num2: .393732, + num3: .44373, + denom1: .685951, + denom2: .344841, + sup1: .412892, + sup2: .362892, + sup3: .288888, + sub1: .15, + sub2: .247217, + sup_drop: .386108, + sub_drop: .05, + delim1: 2.39, + delim2: 1.0, + axis_height: .25, + default_rule_thickness: .06, + big_op_spacing1: .111111, + big_op_spacing2: .166666, + big_op_spacing3: .2, + big_op_spacing4: .6, + big_op_spacing5: .1, + + integer: 6553.6, // conversion of em's to TeX internal integer + scriptspace: .05, + nulldelimiterspace: .12, + delimiterfactor: 901, + delimitershortfall: .5, + scale: 1, // scaling factor for font dimensions + + // The TeX math atom types (see Appendix G of the TeXbook) + atom: ['ord', 'op', 'bin', 'rel', 'open', 'close', 'punct', 'ord'], + + // The TeX font families + fam: ['cmr10','cmmi10','cmsy10','cmex10','cmti10','','cmbx10',''], + famName: {cmr10:0, cmmi10:1, cmsy10:2, cmex10:3, cmti10:4, cmbx10:6}, + + // Encoding used by jsMath fonts + encoding: [ + 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', + 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', + + '°', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '·', + 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'µ', '¶', 'ß', + + 'ï', '!', '"', '#', '$', '%', '&', ''', + '(', ')', '*', '+', ',', '-', '.', '/', + + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\', ']', '^', '_', + + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', 'ÿ' + ], + + /* + * The following are the TeX font mappings and metrics. The metric + * information comes directly from the TeX .tfm files. Browser-specific + * adjustments are made to these tables in the Browser.Init() routine + */ + cmr10: [ + [0.625,0.683], [0.833,0.683], [0.778,0.683], [0.694,0.683], + [0.667,0.683], [0.75,0.683], [0.722,0.683], [0.778,0.683], + [0.722,0.683], [0.778,0.683], [0.722,0.683], + [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}}], + [0.556,0.694], [0.556,0.694], [0.833,0.694], [0.833,0.694], + + [0.278,0.431], [0.306,0.431,0.194], [0.5,0.694], [0.5,0.694], + [0.5,0.628], [0.5,0.694], [0.5,0.568], [0.75,0.694], + [0.444,0,0.17], [0.5,0.694], [0.722,0.431], [0.778,0.431], + [0.5,0.528,0.0972], [0.903,0.683], [1.01,0.683], [0.778,0.732,0.0486], + + [0.278,0.431,0,{krn: {'108': -0.278, '76': -0.319}}], + [0.278,0.694,0,{lig: {'96': 60}}], + [0.5,0.694], [0.833,0.694,0.194], [0.5,0.75,0.0556], + [0.833,0.75,0.0556], [0.778,0.694], + [0.278,0.694,0,{krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}}], + [0.389,0.75,0.25], [0.389,0.75,0.25], [0.5,0.75], + [0.778,0.583,0.0833], [0.278,0.106,0.194], + [0.333,0.431,0,{lig: {'45': 123}}], + [0.278,0.106], [0.5,0.75,0.25], + + [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644], + [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644], + [0.5,0.644], [0.5,0.644], [0.278,0.431], [0.278,0.431,0.194], + [0.278,0.5,0.194], [0.778,0.367,-0.133], [0.472,0.5,0.194], + [0.472,0.694,0,{lig: {'96': 62}}], + + [0.778,0.694], + [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}}], + [0.708,0.683], [0.722,0.683], + [0.764,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}], + [0.681,0.683], + [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}}], + [0.785,0.683], [0.75,0.683], [0.361,0.683,0,{krn: {'73': 0.0278}}], + [0.514,0.683], + [0.778,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [0.625,0.683,0,{krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}], + [0.917,0.683], [0.75,0.683], + [0.778,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}], + + [0.681,0.683,0,{krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}], + [0.778,0.683,0.194], + [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}}], + [0.556,0.683], + [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}}], + [0.75,0.683], + [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}}], + [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}}], + [0.75,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [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}}], + [0.611,0.683], [0.278,0.75,0.25], [0.5,0.694], + [0.278,0.75,0.25], [0.5,0.694], [0.278,0.668], + + [0.278,0.694,0,{lig: {'96': 92}}], + [0.5,0.431,0,{krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}], + [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}}], + [0.444,0.431,0,{krn: {'104': -0.0278, '107': -0.0278}}], + [0.556,0.694], [0.444,0.431], + [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}}], + [0.5,0.431,0.194,{ic: 0.0139, krn: {'106': 0.0278}}], + [0.556,0.694,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [0.278,0.668], [0.306,0.668,0.194], + [0.528,0.694,0,{krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.278,0.694], + [0.833,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [0.556,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [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}}], + + [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}}], + [0.528,0.431,0.194], [0.392,0.431], [0.394,0.431], + [0.389,0.615,0,{krn: {'121': -0.0278, '119': -0.0278}}], + [0.556,0.431,0,{krn: {'119': -0.0278}}], + [0.528,0.431,0,{ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.722,0.431,0,{ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.528,0.431], + [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}}], + [0.444,0.431], [0.5,0.431,0,{ic: 0.0278, lig: {'45': 124}}], + [1,0.431,0,{ic: 0.0278}], [0.5,0.694], [0.5,0.668], [0.5,0.668] + ], + + cmmi10: [ + [0.615,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.833,0.683,0,{krn: {'127': 0.167}}], + [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}], + [0.694,0.683,0,{krn: {'127': 0.167}}], + [0.742,0.683,0,{ic: 0.0757, krn: {'127': 0.0833}}], + [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.78,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}], + [0.583,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}}], + [0.667,0.683,0,{krn: {'127': 0.0833}}], + [0.612,0.683,0,{ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.772,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}], + [0.64,0.431,0,{ic: 0.0037, krn: {'127': 0.0278}}], + [0.566,0.694,0.194,{ic: 0.0528, krn: {'127': 0.0833}}], + [0.518,0.431,0.194,{ic: 0.0556}], + [0.444,0.694,0,{ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.406,0.431,0,{krn: {'127': 0.0556}}], + + [0.438,0.694,0.194,{ic: 0.0738, krn: {'127': 0.0833}}], + [0.497,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}], + [0.469,0.694,0,{ic: 0.0278, krn: {'127': 0.0833}}], + [0.354,0.431,0,{krn: {'127': 0.0556}}], + [0.576,0.431], [0.583,0.694], + [0.603,0.431,0.194,{krn: {'127': 0.0278}}], + [0.494,0.431,0,{ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}], + [0.438,0.694,0.194,{ic: 0.046, krn: {'127': 0.111}}], + [0.57,0.431,0,{ic: 0.0359}], + [0.517,0.431,0.194,{krn: {'127': 0.0833}}], + [0.571,0.431,0,{ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}}], + [0.437,0.431,0,{ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}], + [0.54,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.596,0.694,0.194,{krn: {'127': 0.0833}}], + [0.626,0.431,0.194,{krn: {'127': 0.0556}}], + + [0.651,0.694,0.194,{ic: 0.0359, krn: {'127': 0.111}}], + [0.622,0.431,0,{ic: 0.0359}], + [0.466,0.431,0,{krn: {'127': 0.0833}}], + [0.591,0.694,0,{krn: {'127': 0.0833}}], + [0.828,0.431,0,{ic: 0.0278}], + [0.517,0.431,0.194,{krn: {'127': 0.0833}}], + [0.363,0.431,0.0972,{ic: 0.0799, krn: {'127': 0.0833}}], + [0.654,0.431,0.194,{krn: {'127': 0.0833}}], + [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], + [0.278,0.464,-0.0363], [0.278,0.464,-0.0363], [0.5,0.465,-0.0347], [0.5,0.465,-0.0347], + + [0.5,0.431], [0.5,0.431], [0.5,0.431], [0.5,0.431,0.194], + [0.5,0.431,0.194], [0.5,0.431,0.194], [0.5,0.644], [0.5,0.431,0.194], + [0.5,0.644], [0.5,0.431,0.194], [0.278,0.106], [0.278,0.106,0.194], + [0.778,0.539,0.0391], + [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}}], + [0.778,0.539,0.0391], [0.5,0.465,-0.0347], + + [0.531,0.694,0,{ic: 0.0556, krn: {'127': 0.0833}}], + [0.75,0.683,0,{krn: {'127': 0.139}}], + [0.759,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}], + [0.715,0.683,0,{ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.828,0.683,0,{ic: 0.0278, krn: {'127': 0.0556}}], + [0.738,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}], + [0.643,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.786,0.683,0,{krn: {'127': 0.0833}}], + [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.44,0.683,0,{ic: 0.0785, krn: {'127': 0.111}}], + [0.555,0.683,0,{ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}}], + [0.849,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.681,0.683,0,{krn: {'127': 0.0278}}], + [0.97,0.683,0,{ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.803,0.683,0,{ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}], + + [0.642,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.791,0.683,0.194,{krn: {'127': 0.0833}}], + [0.759,0.683,0,{ic: 0.00773, krn: {'127': 0.0833}}], + [0.613,0.683,0,{ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.584,0.683,0,{ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.683,0.683,0,{ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}}], + [0.583,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.944,0.683,0,{ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.828,0.683,0,{ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.581,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.683,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.389,0.75], [0.389,0.694,0.194], [0.389,0.694,0.194], + [1,0.358,-0.142], [1,0.358,-0.142], + + [0.417,0.694,0,{krn: {'127': 0.111}}], + [0.529,0.431], [0.429,0.694], [0.433,0.431,0,{krn: {'127': 0.0556}}], + [0.52,0.694,0,{krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}}], + [0.466,0.431,0,{krn: {'127': 0.0556}}], + [0.49,0.694,0.194,{ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}}], + [0.477,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.576,0.694,0,{krn: {'127': -0.0278}}], [0.345,0.66], + [0.412,0.66,0.194,{ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}}], + [0.521,0.694,0,{ic: 0.0315}], [0.298,0.694,0,{ic: 0.0197, krn: {'127': 0.0833}}], + [0.878,0.431], [0.6,0.431], [0.485,0.431,0,{krn: {'127': 0.0556}}], + + [0.503,0.431,0.194,{krn: {'127': 0.0833}}], + [0.446,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0833}}], + [0.451,0.431,0,{ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.469,0.431,0,{krn: {'127': 0.0556}}], [0.361,0.615,0,{krn: {'127': 0.0833}}], + [0.572,0.431,0,{krn: {'127': 0.0278}}], + [0.485,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.716,0.431,0,{ic: 0.0269, krn: {'127': 0.0833}}], + [0.572,0.431,0,{krn: {'127': 0.0278}}], + [0.49,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}], + [0.465,0.431,0,{ic: 0.044, krn: {'127': 0.0556}}], + [0.322,0.431,0,{krn: {'127': 0.0278}}], + [0.384,0.431,0.194,{krn: {'127': 0.0833}}], + [0.636,0.431,0.194,{krn: {'127': 0.111}}], + [0.5,0.714,0,{ic: 0.154}], [0.278,0.694,0,{ic: 0.399}] + ], + + cmsy10: [ + [0.778,0.583,0.0833], [0.278,0.444,-0.0556], [0.778,0.583,0.0833], + [0.5,0.465,-0.0347], [0.778,0.583,0.0833], [0.5,0.444,-0.0556], + [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833], + [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833], + [0.778,0.583,0.0833], [1,0.694,0.194], [0.5,0.444,-0.0556], [0.5,0.444,-0.0556], + + [0.778,0.464,-0.0363], [0.778,0.464,-0.0363], [0.778,0.636,0.136], + [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.636,0.136], + [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.367,-0.133], + [0.778,0.483,-0.0169], [0.778,0.539,0.0391], [0.778,0.539,0.0391], + [1,0.539,0.0391], [1,0.539,0.0391], [0.778,0.539,0.0391], [0.778,0.539,0.0391], + + [1,0.367,-0.133], [1,0.367,-0.133], [0.5,0.694,0.194], [0.5,0.694,0.194], + [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.464,-0.0363], + [1,0.367,-0.133], [1,0.367,-0.133], [0.611,0.694,0.194], [0.611,0.694,0.194], + [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.431], + + [0.275,0.556], [1,0.431], [0.667,0.539,0.0391], [0.667,0.539,0.0391], + [0.889,0.694,0.194], [0.889,0.694,0.194], [0,0.694,0.194], [0,0.367,-0.133], + [0.556,0.694], [0.556,0.694], [0.667,0.431], [0.5,0.75,0.0556], + [0.722,0.694], [0.722,0.694], [0.778,0.694], [0.778,0.694], + + [0.611,0.694], [0.798,0.683,0,{krn: {'48': 0.194}}], + [0.657,0.683,0,{ic: 0.0304, krn: {'48': 0.139}}], + [0.527,0.683,0,{ic: 0.0583, krn: {'48': 0.139}}], + [0.771,0.683,0,{ic: 0.0278, krn: {'48': 0.0833}}], + [0.528,0.683,0,{ic: 0.0894, krn: {'48': 0.111}}], + [0.719,0.683,0,{ic: 0.0993, krn: {'48': 0.111}}], + [0.595,0.683,0.0972,{ic: 0.0593, krn: {'48': 0.111}}], + [0.845,0.683,0,{ic: 0.00965, krn: {'48': 0.111}}], + [0.545,0.683,0,{ic: 0.0738, krn: {'48': 0.0278}}], + [0.678,0.683,0.0972,{ic: 0.185, krn: {'48': 0.167}}], + [0.762,0.683,0,{ic: 0.0144, krn: {'48': 0.0556}}], + [0.69,0.683,0,{krn: {'48': 0.139}}], [1.2,0.683,0,{krn: {'48': 0.139}}], + [0.82,0.683,0,{ic: 0.147, krn: {'48': 0.0833}}], + [0.796,0.683,0,{ic: 0.0278, krn: {'48': 0.111}}], + + [0.696,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.817,0.683,0.0972,{krn: {'48': 0.111}}], + [0.848,0.683,0,{krn: {'48': 0.0833}}], + [0.606,0.683,0,{ic: 0.075, krn: {'48': 0.139}}], + [0.545,0.683,0,{ic: 0.254, krn: {'48': 0.0278}}], + [0.626,0.683,0,{ic: 0.0993, krn: {'48': 0.0833}}], + [0.613,0.683,0,{ic: 0.0822, krn: {'48': 0.0278}}], + [0.988,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.713,0.683,0,{ic: 0.146, krn: {'48': 0.139}}], + [0.668,0.683,0.0972,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.725,0.683,0,{ic: 0.0794, krn: {'48': 0.139}}], + [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556], + + [0.611,0.694], [0.611,0.694], [0.444,0.75,0.25], [0.444,0.75,0.25], + [0.444,0.75,0.25], [0.444,0.75,0.25], [0.5,0.75,0.25], [0.5,0.75,0.25], + [0.389,0.75,0.25], [0.389,0.75,0.25], [0.278,0.75,0.25], [0.5,0.75,0.25], + [0.5,0.75,0.25], [0.611,0.75,0.25], [0.5,0.75,0.25], [0.278,0.694,0.194], + + [0.833,0.04,0.96], [0.75,0.683], [0.833,0.683], [0.417,0.694,0.194,{ic: 0.111}], + [0.667,0.556], [0.667,0.556], [0.778,0.636,0.136], [0.778,0.636,0.136], + [0.444,0.694,0.194], [0.444,0.694,0.194], [0.444,0.694,0.194], + [0.611,0.694,0.194], [0.778,0.694,0.13], [0.778,0.694,0.13], + [0.778,0.694,0.13], [0.778,0.694,0.13] + ], + + cmex10: [ + [0.458,0.04,1.16,{n: 16}], [0.458,0.04,1.16,{n: 17}], + [0.417,0.04,1.16,{n: 104}], [0.417,0.04,1.16,{n: 105}], + [0.472,0.04,1.16,{n: 106}], [0.472,0.04,1.16,{n: 107}], + [0.472,0.04,1.16,{n: 108}], [0.472,0.04,1.16,{n: 109}], + [0.583,0.04,1.16,{n: 110}], [0.583,0.04,1.16,{n: 111}], + [0.472,0.04,1.16,{n: 68}], [0.472,0.04,1.16,{n: 69}], + [0.333,0,0.6,{delim: {rep: 12}}], [0.556,0,0.6,{delim: {rep: 13}}], + [0.578,0.04,1.16,{n: 46}], [0.578,0.04,1.16,{n: 47}], + + [0.597,0.04,1.76,{n: 18}], [0.597,0.04,1.76,{n: 19}], + [0.736,0.04,2.36,{n: 32}], [0.736,0.04,2.36,{n: 33}], + [0.528,0.04,2.36,{n: 34}], [0.528,0.04,2.36,{n: 35}], + [0.583,0.04,2.36,{n: 36}], [0.583,0.04,2.36,{n: 37}], + [0.583,0.04,2.36,{n: 38}], [0.583,0.04,2.36,{n: 39}], + [0.75,0.04,2.36,{n: 40}], [0.75,0.04,2.36,{n: 41}], + [0.75,0.04,2.36,{n: 42}], [0.75,0.04,2.36,{n: 43}], + [1.04,0.04,2.36,{n: 44}], [1.04,0.04,2.36,{n: 45}], + + [0.792,0.04,2.96,{n: 48}], [0.792,0.04,2.96,{n: 49}], + [0.583,0.04,2.96,{n: 50}], [0.583,0.04,2.96,{n: 51}], + [0.639,0.04,2.96,{n: 52}], [0.639,0.04,2.96,{n: 53}], + [0.639,0.04,2.96,{n: 54}], [0.639,0.04,2.96,{n: 55}], + [0.806,0.04,2.96,{n: 56}], [0.806,0.04,2.96,{n: 57}], + [0.806,0.04,2.96], [0.806,0.04,2.96], + [1.28,0.04,2.96], [1.28,0.04,2.96], + [0.811,0.04,1.76,{n: 30}], [0.811,0.04,1.76,{n: 31}], + + [0.875,0.04,1.76,{delim: {top: 48, bot: 64, rep: 66}}], + [0.875,0.04,1.76,{delim: {top: 49, bot: 65, rep: 67}}], + [0.667,0.04,1.76,{delim: {top: 50, bot: 52, rep: 54}}], + [0.667,0.04,1.76,{delim: {top: 51, bot: 53, rep: 55}}], + [0.667,0.04,1.76,{delim: {bot: 52, rep: 54}}], + [0.667,0.04,1.76,{delim: {bot: 53, rep: 55}}], + [0.667,0,0.6,{delim: {top: 50, rep: 54}}], + [0.667,0,0.6,{delim: {top: 51, rep: 55}}], + [0.889,0,0.9,{delim: {top: 56, mid: 60, bot: 58, rep: 62}}], + [0.889,0,0.9,{delim: {top: 57, mid: 61, bot: 59, rep: 62}}], + [0.889,0,0.9,{delim: {top: 56, bot: 58, rep: 62}}], + [0.889,0,0.9,{delim: {top: 57, bot: 59, rep: 62}}], + [0.889,0,1.8,{delim: {rep: 63}}], + [0.889,0,1.8,{delim: {rep: 119}}], + [0.889,0,0.3,{delim: {rep: 62}}], + [0.667,0,0.6,{delim: {top: 120, bot: 121, rep: 63}}], + + [0.875,0.04,1.76,{delim: {top: 56, bot: 59, rep: 62}}], + [0.875,0.04,1.76,{delim: {top: 57, bot: 58, rep: 62}}], + [0.875,0,0.6,{delim: {rep: 66}}], [0.875,0,0.6,{delim: {rep: 67}}], + [0.611,0.04,1.76,{n: 28}], [0.611,0.04,1.76,{n: 29}], + [0.833,0,1,{n: 71}], [1.11,0.1,1.5], [0.472,0,1.11,{ic: 0.194, n: 73}], + [0.556,0,2.22,{ic: 0.444}], [1.11,0,1,{n: 75}], [1.51,0.1,1.5], + [1.11,0,1,{n: 77}], [1.51,0.1,1.5], [1.11,0,1,{n: 79}], [1.51,0.1,1.5], + + [1.06,0,1,{n: 88}], [0.944,0,1,{n: 89}], [0.472,0,1.11,{ic: 0.194, n: 90}], + [0.833,0,1,{n: 91}], [0.833,0,1,{n: 92}], [0.833,0,1,{n: 93}], + [0.833,0,1,{n: 94}], [0.833,0,1,{n: 95}], [1.44,0.1,1.5], + [1.28,0.1,1.5], [0.556,0,2.22,{ic: 0.444}], [1.11,0.1,1.5], + [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5], + + [0.944,0,1,{n: 97}], [1.28,0.1,1.5], [0.556,0.722,0,{n: 99}], + [1,0.75,0,{n: 100}], [1.44,0.75], [0.556,0.722,0,{n: 102}], + [1,0.75,0,{n: 103}], [1.44,0.75], [0.472,0.04,1.76,{n: 20}], + [0.472,0.04,1.76,{n: 21}], [0.528,0.04,1.76,{n: 22}], + [0.528,0.04,1.76,{n: 23}], [0.528,0.04,1.76,{n: 24}], + [0.528,0.04,1.76,{n: 25}], [0.667,0.04,1.76,{n: 26}], + [0.667,0.04,1.76,{n: 27}], + + [1,0.04,1.16,{n: 113}], [1,0.04,1.76,{n: 114}], [1,0.04,2.36,{n: 115}], + [1,0.04,2.96,{n: 116}], [1.06,0,1.8,{delim: {top: 118, bot: 116, rep: 117}}], + [1.06,0,0.6], [1.06,0.04,0.56], + [0.778,0,0.6,{delim: {top: 126, bot: 127, rep: 119}}], + [0.667,0,0.6,{delim: {top: 120, rep: 63}}], + [0.667,0,0.6,{delim: {bot: 121, rep: 63}}], + [0.45,0.12], [0.45,0.12], [0.45,0.12], [0.45,0.12], + [0.778,0,0.6,{delim: {top: 126, rep: 119}}], + [0.778,0,0.6,{delim: {bot: 127, rep: 119}}] + ], + + cmti10: [ + [0.627,0.683,0,{ic: 0.133}], [0.818,0.683], [0.767,0.683,0,{ic: 0.094}], + [0.692,0.683], [0.664,0.683,0,{ic: 0.153}], [0.743,0.683,0,{ic: 0.164}], + [0.716,0.683,0,{ic: 0.12}], [0.767,0.683,0,{ic: 0.111}], + [0.716,0.683,0,{ic: 0.0599}], [0.767,0.683,0,{ic: 0.111}], + [0.716,0.683,0,{ic: 0.103}], + [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}}], + [0.562,0.694,0.194,{ic: 0.103}], [0.588,0.694,0.194,{ic: 0.103}], + [0.882,0.694,0.194,{ic: 0.103}], [0.894,0.694,0.194,{ic: 0.103}], + + [0.307,0.431,0,{ic: 0.0767}], [0.332,0.431,0.194,{ic: 0.0374}], + [0.511,0.694], [0.511,0.694,0,{ic: 0.0969}], [0.511,0.628,0,{ic: 0.083}], + [0.511,0.694,0,{ic: 0.108}], [0.511,0.562,0,{ic: 0.103}], [0.831,0.694], + [0.46,0,0.17], [0.537,0.694,0.194,{ic: 0.105}], [0.716,0.431,0,{ic: 0.0751}], + [0.716,0.431,0,{ic: 0.0751}], [0.511,0.528,0.0972,{ic: 0.0919}], + [0.883,0.683,0,{ic: 0.12}], [0.985,0.683,0,{ic: 0.12}], + [0.767,0.732,0.0486,{ic: 0.094}], + + [0.256,0.431,0,{krn: {'108': -0.256, '76': -0.321}}], + [0.307,0.694,0,{ic: 0.124, lig: {'96': 60}}], + [0.514,0.694,0,{ic: 0.0696}], [0.818,0.694,0.194,{ic: 0.0662}], + [0.769,0.694], [0.818,0.75,0.0556,{ic: 0.136}], + [0.767,0.694,0,{ic: 0.0969}], + [0.307,0.694,0,{ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}}], + [0.409,0.75,0.25,{ic: 0.162}], [0.409,0.75,0.25,{ic: 0.0369}], + [0.511,0.75,0,{ic: 0.149}], [0.767,0.562,0.0567,{ic: 0.0369}], + [0.307,0.106,0.194], [0.358,0.431,0,{ic: 0.0283, lig: {'45': 123}}], + [0.307,0.106], [0.511,0.75,0.25,{ic: 0.162}], + + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0.194,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0.194,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.307,0.431,0,{ic: 0.0582}], [0.307,0.431,0.194,{ic: 0.0582}], + [0.307,0.5,0.194,{ic: 0.0756}], [0.767,0.367,-0.133,{ic: 0.0662}], + [0.511,0.5,0.194], [0.511,0.694,0,{ic: 0.122, lig: {'96': 62}}], + + [0.767,0.694,0,{ic: 0.096}], + [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}}], + [0.704,0.683,0,{ic: 0.103}], [0.716,0.683,0,{ic: 0.145}], + [0.755,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}], + [0.678,0.683,0,{ic: 0.12}], + [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}}], + [0.774,0.683,0,{ic: 0.0872}], [0.743,0.683,0,{ic: 0.164}], + [0.386,0.683,0,{ic: 0.158}], [0.525,0.683,0,{ic: 0.14}], + [0.769,0.683,0,{ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [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}}], + [0.897,0.683,0,{ic: 0.164}], [0.743,0.683,0,{ic: 0.164}], + [0.767,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}], + + [0.678,0.683,0,{ic: 0.103, krn: {'65': -0.0767}}], + [0.767,0.683,0.194,{ic: 0.094}], + [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}}], + [0.562,0.683,0,{ic: 0.12}], + [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}}], + [0.743,0.683,0,{ic: 0.164}], + [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}}], + [0.999,0.683,0,{ic: 0.184, krn: {'65': -0.0767}}], + [0.743,0.683,0,{ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [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}}], + [0.613,0.683,0,{ic: 0.145}], [0.307,0.75,0.25,{ic: 0.188}], + [0.514,0.694,0,{ic: 0.169}], [0.307,0.75,0.25,{ic: 0.105}], + [0.511,0.694,0,{ic: 0.0665}], [0.307,0.668,0,{ic: 0.118}], + + [0.307,0.694,0,{ic: 0.124, lig: {'96': 92}}], [0.511,0.431,0,{ic: 0.0767}], + [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}}], + [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}}], + [0.511,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}], + [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}}], + [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}}], + [0.46,0.431,0.194,{ic: 0.0885}], [0.511,0.694,0,{ic: 0.0767}], + [0.307,0.655,0,{ic: 0.102}], [0.307,0.655,0.194,{ic: 0.145}], + [0.46,0.694,0,{ic: 0.108}], [0.256,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}], + [0.818,0.431,0,{ic: 0.0767}], [0.562,0.431,0,{ic: 0.0767, krn: {'39': -0.102}}], + [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}}], + + [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}}], + [0.46,0.431,0.194,{ic: 0.0885}], + [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}}], + [0.409,0.431,0,{ic: 0.0821}], [0.332,0.615,0,{ic: 0.0949}], + [0.537,0.431,0,{ic: 0.0767}], [0.46,0.431,0,{ic: 0.108}], + [0.664,0.431,0,{ic: 0.108, krn: {'108': 0.0511}}], + [0.464,0.431,0,{ic: 0.12}], [0.486,0.431,0.194,{ic: 0.0885}], + [0.409,0.431,0,{ic: 0.123}], [0.511,0.431,0,{ic: 0.0921, lig: {'45': 124}}], + [1.02,0.431,0,{ic: 0.0921}], [0.511,0.694,0,{ic: 0.122}], + [0.511,0.668,0,{ic: 0.116}], [0.511,0.668,0,{ic: 0.105}] + ], + + cmbx10: [ + [0.692,0.686], [0.958,0.686], [0.894,0.686], [0.806,0.686], + [0.767,0.686], [0.9,0.686], [0.831,0.686], [0.894,0.686], + [0.831,0.686], [0.894,0.686], [0.831,0.686], + [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}}], + [0.639,0.694], [0.639,0.694], [0.958,0.694], [0.958,0.694], + + [0.319,0.444], [0.351,0.444,0.194], [0.575,0.694], [0.575,0.694], + [0.575,0.632], [0.575,0.694], [0.575,0.596], [0.869,0.694], + [0.511,0,0.17], [0.597,0.694], [0.831,0.444], [0.894,0.444], + [0.575,0.542,0.0972], [1.04,0.686], [1.17,0.686], [0.894,0.735,0.0486], + + [0.319,0.444,0,{krn: {'108': -0.319, '76': -0.378}}], + [0.35,0.694,0,{lig: {'96': 60}}], [0.603,0.694], [0.958,0.694,0.194], + [0.575,0.75,0.0556], [0.958,0.75,0.0556], [0.894,0.694], + [0.319,0.694,0,{krn: {'63': 0.128, '33': 0.128}, lig: {'39': 34}}], + [0.447,0.75,0.25], [0.447,0.75,0.25], [0.575,0.75], [0.894,0.633,0.133], + [0.319,0.156,0.194], [0.383,0.444,0,{lig: {'45': 123}}], + [0.319,0.156], [0.575,0.75,0.25], + + [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644], + [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644], + [0.575,0.644], [0.575,0.644], [0.319,0.444], [0.319,0.444,0.194], + [0.35,0.5,0.194], [0.894,0.391,-0.109], [0.543,0.5,0.194], + [0.543,0.694,0,{lig: {'96': 62}}], + + [0.894,0.694], + [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}}], + [0.818,0.686], [0.831,0.686], + [0.882,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}], + [0.756,0.686], + [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}}], + [0.904,0.686], [0.9,0.686], [0.436,0.686,0,{krn: {'73': 0.0319}}], + [0.594,0.686], + [0.901,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [0.692,0.686,0,{krn: {'84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}], + [1.09,0.686], [0.9,0.686], + [0.864,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}], + + [0.786,0.686,0,{krn: {'65': -0.0958, '111': -0.0319, '101': -0.0319, '97': -0.0319, '46': -0.0958, '44': -0.0958}}], + [0.864,0.686,0.194], + [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}}], + [0.639,0.686], + [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}}], + [0.885,0.686], + [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}}], + [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}}], + [0.869,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [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}}], + [0.703,0.686], [0.319,0.75,0.25], [0.603,0.694], [0.319,0.75,0.25], + [0.575,0.694], [0.319,0.694], + + [0.319,0.694,0,{lig: {'96': 92}}], + [0.559,0.444,0,{krn: {'118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}], + [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}}], + [0.511,0.444,0,{krn: {'104': -0.0319, '107': -0.0319}}], + [0.639,0.694], [0.527,0.444], + [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}}], + [0.575,0.444,0.194,{ic: 0.016, krn: {'106': 0.0319}}], + [0.639,0.694,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [0.319,0.694], [0.351,0.694,0.194], + [0.607,0.694,0,{krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.319,0.694], + [0.958,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [0.639,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [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}}], + + [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}}], + [0.607,0.444,0.194], [0.474,0.444], [0.454,0.444], + [0.447,0.635,0,{krn: {'121': -0.0319, '119': -0.0319}}], + [0.639,0.444,0,{krn: {'119': -0.0319}}], + [0.607,0.444,0,{ic: 0.016, krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.831,0.444,0,{ic: 0.016, krn: {'101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.607,0.444], + [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}}], + [0.511,0.444], [0.575,0.444,0,{ic: 0.0319, lig: {'45': 124}}], + [1.15,0.444,0,{ic: 0.0319}], [0.575,0.694], [0.575,0.694], [0.575,0.694] + ] +}; + +/***************************************************************************/ + +/* + * Implement image-based fonts for fallback method + */ +jsMath.Img = { + + // font sizes available + fonts: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249, 298, 358, 430], + + // em widths for the various font size directories + w: {'50': 6.9, '60': 8.3, '70': 9.7, '85': 11.8, '100': 13.9, + '120': 16.7, '144': 20.0, '173': 24.0, '207': 28.8, '249': 34.6, + '298': 41.4, '358': 49.8, '430': 59.8}, + + best: 4, // index of best font size in the fonts list + update: {}, // fonts to update (see UpdateFonts below) + factor: 1, // factor by which to shrink images (for better printing) + loaded: 0, // image fonts are loaded + + // add characters to be drawn using images + SetFont: function (change) { + for (var font in change) { + if (!this.update[font]) {this.update[font] = []} + this.update[font] = this.update[font].concat(change[font]); + } + }, + + /* + * Called by the exta-font definition files to add an image font + * into the mix + */ + AddFont: function (size,def) { + if (!jsMath.Img[size]) {jsMath.Img[size] = {}}; + jsMath.Add(jsMath.Img[size],def); + }, + + /* + * Update font(s) to use image data rather than native fonts + * It looks in the jsMath.Img.update array to find the names + * of the fonts to udpate, and the arrays of character codes + * to set (or 'all' to change every character); + */ + UpdateFonts: function () { + var change = this.update; if (!this.loaded) return; + for (var font in change) { + for (var i = 0; i < change[font].length; i++) { + var c = change[font][i]; + if (c == 'all') {for (c in jsMath.TeX[font]) {jsMath.TeX[font][c].img = {}}} + else {jsMath.TeX[font][c].img = {}} + } + } + this.update = {}; + }, + + /* + * Find the font size that best fits our current font + * (this is the directory name for the img files used + * in some fallback modes). + */ + BestSize: function () { + var w = jsMath.em * this.factor; + var m = this.w[this.fonts[0]]; + for (var i = 1; i < this.fonts.length; i++) { + if (w < (this.w[this.fonts[i]] + 2*m) / 3) {return i-1} + m = this.w[this.fonts[i]]; + } + return i-1; + }, + + /* + * Get the scaling factor for the image fonts + */ + Scale: function () { + if (!this.loaded) return; + this.best = this.BestSize(); + this.em = jsMath.Img.w[this.fonts[this.best]]; + this.scale = (jsMath.em/this.em); + if (Math.abs(this.scale - 1) < .12) {this.scale = 1} + }, + + /* + * Get URL to directory for given font and size, based on the + * user's alpha/plain setting + */ + URL: function (name,size,C) { + var type = (jsMath.Controls.cookie.alpha) ? '/alpha/': '/plain/'; + if (C == null) {C = "def.js"} else {C = 'char'+C+'.png'} + if (size != "") {size += '/'} + return this.root+name+type+size+C; + }, + + /* + * Laod the data for an image font + */ + LoadFont: function (name) { + if (!this.loaded) this.Init(); + jsMath.Setup.Script(this.URL(name,"")); + }, + + /* + * Setup for print mode, and create the hex code table + */ + Init: function () { + if (jsMath.Controls.cookie.print || jsMath.Controls.cookie.stayhires) { + jsMath.Controls.cookie.print = jsMath.Controls.cookie.stayhires; + this.factor *= 3; + if (!jsMath.Controls.isLocalCookie || !jsMath.Global.isLocal) {jsMath.Controls.SetCookie(0)} + if (jsMath.Browser.alphaPrintBug) {jsMath.Controls.cookie.alpha = 0} + } + var codes = '0123456789ABCDEF'; + this.HexCode = []; + for (var i = 0; i < 128; i++) { + var h = Math.floor(i/16); var l = i - 16*h; + this.HexCode[i] = codes.charAt(h)+codes.charAt(l); + } + this.loaded = 1; + } + +}; + +/***************************************************************************/ + +/* + * jsMath.HTML handles creation of most of the HTML needed for + * presenting mathematics in HTML pages. + */ + +jsMath.HTML = { + + /* + * Produce a string version of a measurement in ems, + * showing only a limited number of digits, and + * using 0 when the value is near zero. + */ + Em: function (m) { + if (Math.abs(m) < .000001) {m = 0} + var s = String(m); s = s.replace(/(\.\d\d\d).+/,'$1'); + return s+'em' + }, + + /* + * Create a horizontal space of width w + */ + Spacer: function (w) { + if (w == 0) {return ''}; + return jsMath.Browser.msieSpaceFix+''; + }, + + /* + * Create a blank rectangle of the given size + * If the height is small, it is converted to pixels so that it + * will not disappear at small font sizes. + */ + + Blank: function (w,h,d,isRule) { + var backspace = ''; var style = '' + if (isRule) { + style += 'border-left:'+this.Em(w)+' solid;'; + if (jsMath.Browser.widthAddsBorder) {w = 0}; + } + if (w == 0) { + if (jsMath.Browser.blankWidthBug) { + if (jsMath.Browser.quirks) { + style += 'width:1px;'; + backspace = '' + } else if (!isRule) { + style += 'width:1px;margin-right:-1px;'; + } + } + } else {style += 'width:'+this.Em(w)+';'} + if (d == null) {d = 0} + if (h) { + var H = this.Em(h+d); + if (isRule && h*jsMath.em <= 1.5) {H = "1.5px"; h = 1.5/jsMath.em} + style += 'height:'+H+';'; + } + if (jsMath.Browser.mozInlineBlockBug) {d = -h} + if (jsMath.Browser.msieBlockDepthBug && !isRule) {d -= jsMath.d} + if (d) {style += 'vertical-align:'+this.Em(-d)} + return backspace+''; + }, + + /* + * Create a rule line for fractions, etc. + */ + Rule: function (w,h) { + if (h == null) {h = jsMath.TeX.default_rule_thickness} + return this.Blank(w,h,0,1); + }, + + /* + * Create a strut for measuring position of baseline + */ + Strut: function (h) {return this.Blank(1,h,0,1)}, + msieStrut: function (h) { + return '' + }, + + /* + * Add a tag to activate a specific CSS class + */ + Class: function (tclass,html) { + return ''+html+''; + }, + + /* + * Use a to place some HTML at a specific position. + * (This can be replaced by the ones below to overcome + * some browser-specific bugs.) + */ + Place: function (html,x,y) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + if (x || y) { + var span = '' + html + ''; + } + return html; + }, + + /* + * For MSIE on Windows, backspacing must be done in a separate + * , otherwise the contents will be clipped. Netscape + * also doesn't combine vertical and horizontal spacing well. + * Here the x and y positioning are done in separate tags + */ + PlaceSeparateSkips: function (html,x,y,mw,Mw,w) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + if (y) { + var lw = 0; var rw = 0; var width = ""; + if (mw != null) { + rw = Mw - w; lw = mw; + width = ' width:'+this.Em(Mw-mw)+';'; + } + html = + this.Spacer(lw-rw) + + '' + + this.Spacer(-lw) + + html + + this.Spacer(rw) + + '' + } + if (x) {html = this.Spacer(x) + html} + return html; + }, + + /* + * Place a SPAN with absolute coordinates + */ + PlaceAbsolute: function (html,x,y,mw,Mw,w) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + var leftSpace = ""; var rightSpace = ""; var width = ""; + if (jsMath.Browser.msieRelativeClipBug && mw != null) { + leftSpace = this.Spacer(-mw); x += mw; + rightSpace = this.Spacer(Mw-w); + } + if (jsMath.Browser.operaAbsoluteWidthBug) {width = " width: "+this.Em(w+2)} + html = + '' + + leftSpace + html + rightSpace + + ' ' + // space normalizes line height in script styles + ''; + return html; + }, + + Absolute: function(html,w,h,d,y) { + if (y != "none") { + if (Math.abs(y) < .0001) {y = 0} + html = '' + + html + ' ' // space normalizes line height in script styles + + ''; + } + if (d == "none") {d = 0} + html += this.Blank((jsMath.Browser.lineBreakBug ? 0 : w),h-d,d); + if (jsMath.Browser.msieAbsoluteBug) { // for MSIE (Mac) + html = '' + html + ''; + } + html = '' + html + ''; + if (jsMath.Browser.lineBreakBug) + {html = ''+html+''} + return html; + } + +}; + + +/***************************************************************************/ + +/* + * jsMath.Box handles TeX's math boxes and jsMath's equivalent of hboxes. + */ + +jsMath.Box = function (format,text,w,h,d) { + if (d == null) {d = jsMath.d} + this.type = 'typeset'; + this.w = w; this.h = h; this.d = d; this.bh = h; this.bd = d; + this.x = 0; this.y = 0; this.mw = 0; this.Mw = w; + this.html = text; this.format = format; +}; + + +jsMath.Add(jsMath.Box,{ + + defaultH: 0, // default height for characters with none specified + + /* + * An empty box + */ + Null: function () {return new jsMath.Box('null','',0,0,0)}, + + /* + * A box containing only text whose class and style haven't been added + * yet (so that we can combine ones with the same styles). It gets + * the text dimensions, if needed. (In general, this has been + * replaced by TeX() below, but is still used in fallback mode.) + */ + Text: function (text,tclass,style,size,a,d) { + var html = jsMath.Typeset.AddClass(tclass,text); + html = jsMath.Typeset.AddStyle(style,size,html); + var BB = jsMath.EmBoxFor(html); var TeX = jsMath.Typeset.TeX(style,size); + var bd = ((tclass == 'cmsy10' || tclass == 'cmex10')? BB.h-TeX.h: TeX.d*BB.h/TeX.hd); + var box = new jsMath.Box('text',text,BB.w,BB.h-bd,bd); + box.style = style; box.size = size; box.tclass = tclass; + if (d != null) {box.d = d*TeX.scale} else {box.d = 0} + if (a == null || a == 1) {box.h = .9*TeX.M_height} + else {box.h = 1.1*TeX.x_height + TeX.scale*a} + return box; + }, + + /* + * Produce a box containing a given TeX character from a given font. + * The box is a text box (like the ones above), so that characters from + * the same font can be combined. + */ + TeX: function (C,font,style,size) { + var c = jsMath.TeX[font][C]; + if (c.d == null) {c.d = 0}; if (c.h == null) {c.h = 0} + if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); + var scale = jsMath.Typeset.TeX(style,size).scale; + var box = new jsMath.Box('text',c.c,c.w*scale,c.h*scale,c.d*scale); + box.style = style; box.size = size; + if (c.tclass) { + box.tclass = c.tclass; + if (c.img) {box.bh = c.img.bh; box.bd = c.img.bd} + else {box.bh = scale*jsMath.h; box.bd = scale*jsMath.d} + } else { + box.tclass = font; + box.bh = scale*jsMath.TeX[font].h; + box.bd = scale*jsMath.TeX[font].d; + if (jsMath.Browser.msieFontBug && box.html.match(/&#/)) { + // hack to avoid font changing back to the default + // font when a unicode reference is not followed + // by a letter or number + box.html += 'x'; + } + } + return box; + }, + + /* + * In fallback modes, handle the fact that we don't have the + * sizes of the characters precomputed + */ + TeXfallback: function (C,font,style,size) { + var c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} + if (c.img != null) {return this.TeXnonfallback(C,font,style,size)} + if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} + var a = c.a; var d = c.d; // avoid Firefox warnings + var box = this.Text(c.c,c.tclass,style,size,a,d); + var scale = jsMath.Typeset.TeX(style,size).scale; + if (c.bh != null) { + box.bh = c.bh*scale; + box.bd = c.bd*scale; + } else { + var h = box.bd+box.bh; + var html = jsMath.Typeset.AddClass(box.tclass,box.html); + html = jsMath.Typeset.AddStyle(style,size,html); + box.bd = jsMath.EmBoxFor(html + jsMath.HTML.Strut(h)).h - h; + box.bh = h - box.bd; + if (scale == 1) {c.bh = box.bh; c.bd = box.bd} + } + if (jsMath.msieFontBug && box.html.match(/&#/)) + {box.html += 'x'} + return box; + }, + + /* + * Set the character's string to the appropriate image file + */ + TeXIMG: function (font,C,size) { + var c = jsMath.TeX[font][C]; + if (c.img.size != null && c.img.size == size && + c.img.best != null && c.img.best == jsMath.Img.best) return; + var mustScale = (jsMath.Img.scale != 1); + var id = jsMath.Img.best + size - 4; + if (id < 0) {id = 0; mustScale = 1} else + if (id >= jsMath.Img.fonts.length) {id = jsMath.Img.fonts.length-1; mustScale = 1} + var imgFont = jsMath.Img[jsMath.Img.fonts[id]]; + var img = imgFont[font][C]; + var scale = 1/jsMath.Img.w[jsMath.Img.fonts[id]]; + if (id != jsMath.Img.best + size - 4) { + if (c.w != null) {scale = c.w/img[0]} else { + scale *= jsMath.Img.fonts[size]/jsMath.Img.fonts[4] + * jsMath.Img.fonts[jsMath.Img.best]/jsMath.Img.fonts[id]; + } + } + var w = img[0]*scale; var h = img[1]*scale; var d = -img[2]*scale; var v; + var wadjust = (c.w == null || Math.abs(c.w-w) < .01)? "" : " margin-right:"+jsMath.HTML.Em(c.w-w)+';'; + var resize = ""; C = jsMath.Img.HexCode[C]; + if (!mustScale && !jsMath.Controls.cookie.scaleImg) { + if (jsMath.Browser.mozImageSizeBug || 2*w < h || + (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha)) + {resize = "height:"+(img[1]*jsMath.Browser.imgScale)+'px;'} + resize += " width:"+(img[0]*jsMath.Browser.imgScale)+'px;' + v = -img[2]+'px'; + } else { + if (jsMath.Browser.mozImageSizeBug || 2*w < h || + (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha)) + {resize = "height:"+jsMath.HTML.Em(h*jsMath.Browser.imgScale)+';'} + resize += " width:"+jsMath.HTML.Em(w*jsMath.Browser.imgScale)+';' + v = jsMath.HTML.Em(d); + } + var vadjust = (Math.abs(d) < .01 && !jsMath.Browser.valignBug)? + "": " vertical-align:"+v+';'; + var URL = jsMath.Img.URL(font,jsMath.Img.fonts[id],C); + if (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha) { + c.c = ''; + } else { + c.c = ''; + } + c.tclass = "normal"; + c.img.bh = h+d; c.img.bd = -d; + c.img.size = size; c.img.best = jsMath.Img.best; + }, + + /* + * A box containing a spacer of a specific width + */ + Space: function (w) { + return new jsMath.Box('html',jsMath.HTML.Spacer(w),w,0,0); + }, + + /* + * A box containing a horizontal rule + */ + Rule: function (w,h) { + if (h == null) {h = jsMath.TeX.default_rule_thickness} + var html = jsMath.HTML.Rule(w,h); + return new jsMath.Box('html',html,w,h,0); + }, + + /* + * Get a character from a TeX font, and make sure that it has + * its metrics specified. + */ + GetChar: function (code,font) { + var c = jsMath.TeX[font][code]; + if (c.img != null) {this.TeXIMG(font,code,4)} + if (c.tclass == null) {c.tclass = font} + if (!c.computedW) { + c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w; + if (c.h == null) {c.h = jsMath.Box.defaultH}; if (c.d == null) {c.d = 0} + c.computedW = 1; + } + return c; + }, + + /* + * Locate the TeX delimiter character that matches a given height. + * Return the character, font, style and actual height used. + */ + DelimBestFit: function (H,c,font,style) { + if (c == 0 && font == 0) return null; + var C; var h; font = jsMath.TeX.fam[font]; + var isSS = (style.charAt(1) == 'S'); + var isS = (style.charAt(0) == 'S'); + while (c != null) { + C = jsMath.TeX[font][c]; + if (C.h == null) {C.h = jsMath.Box.defaultH}; if (C.d == null) {C.d = 0} + h = C.h+C.d; + if (C.delim) {return [c,font,'',H]} + if (isSS && .5*h >= H) {return [c,font,'SS',.5*h]} + if (isS && .7*h >= H) {return [c,font,'S',.7*h]} + if (h >= H || C.n == null) {return [c,font,'T',h]} + c = C.n; + } + return null; + }, + + /* + * Create the HTML needed for a stretchable delimiter of a given height, + * either centered or not. This version uses relative placement (i.e., + * backspaces, not line-breaks). This works with more browsers, but + * if the font size changes, the backspacing may not be right, so the + * delimiters may become jagged. + */ + DelimExtendRelative: function (H,c,font,a,nocenter) { + var C = jsMath.TeX[font][c]; + var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font); + var rep = this.GetChar(C.delim.rep,font); + var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font); + var ext = jsMath.Typeset.AddClass(rep.tclass,rep.c); + var w = rep.w; var h = rep.h+rep.d + var y; var Y; var html; var dx; var i; var n; + if (C.delim.mid) {// braces + var mid = this.GetChar(C.delim.mid,font); + n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d)-(bot.h+bot.d))/(2*(rep.h+rep.d))); + H = 2*n*(rep.h+rep.d) + (top.h+top.d) + (mid.h+mid.d) + (bot.h+bot.d); + if (nocenter) {y = 0} else {y = H/2+a}; Y = y; + html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h) + + jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(top.w+bot.w)/2,y-(H-bot.d)) + + jsMath.HTML.Place(jsMath.Typeset.AddClass(mid.tclass,mid.c),-(bot.w+mid.w)/2,y-(H+mid.h-mid.d)/2); + dx = (w-mid.w)/2; if (Math.abs(dx) < .0001) {dx = 0} + if (dx) {html += jsMath.HTML.Spacer(dx)} + y -= top.h+top.d + rep.h; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + y -= H/2 - rep.h/2; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + } else {// everything else + n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d))/(rep.h+rep.d)); + // make sure two-headed arrows have an extender + if (top.h+top.d < .9*(rep.h+rep.d)) {n = Math.max(1,n)} + H = n*(rep.h+rep.d) + (top.h+top.d) + (bot.h+bot.d); + if (nocenter) {y = 0} else {y = H/2+a}; Y = y; + html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h) + dx = (w-top.w)/2; if (Math.abs(dx) < .0001) {dx = 0} + if (dx) {html += jsMath.HTML.Spacer(dx)} + y -= top.h+top.d + rep.h; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + html += jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(w+bot.w)/2,Y-(H-bot.d)); + } + if (nocenter) {h = top.h} else {h = H/2+a} + var box = new jsMath.Box('html',html,rep.w,h,H-h); + box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d; + return box; + }, + + /* + * Create the HTML needed for a stretchable delimiter of a given height, + * either centered or not. This version uses absolute placement (i.e., + * line-breaks, not backspacing). This gives more reliable results, + * but doesn't work with all browsers. + */ + DelimExtendAbsolute: function (H,c,font,a,nocenter) { + var Font = jsMath.TeX[font]; + var C = Font[c]; var html; + var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font); + var rep = this.GetChar(C.delim.rep,font); + var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font); + var n; var h; var y; var ext; var i; + + if (C.delim.mid) {// braces + var mid = this.GetChar(C.delim.mid,font); + n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d-.05)-(bot.h+bot.d-.05))/(2*(rep.h+rep.d-.05))); + H = 2*n*(rep.h+rep.d-.05) + (top.h+top.d) + (mid.h+mid.d-.05) + (bot.h+bot.d-.05); + + html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0); + h = rep.h+rep.d - .05; y = top.d-.05 + rep.h; + ext = jsMath.Typeset.AddClass(rep.tclass,rep.c) + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(mid.tclass,mid.c),0,y+n*h-rep.h+mid.h); + y += n*h + mid.h+mid.d - .05; + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h); + } else {// all others + n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d-.05))/(rep.h+rep.d-.05)); + H = n*(rep.h+rep.d-.05) + (top.h+top.d) + (bot.h+bot.d-.05); + + html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0); + h = rep.h+rep.d-.05; y = top.d-.05 + rep.h; + ext = jsMath.Typeset.AddClass(rep.tclass,rep.c); + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h); + } + + var w = top.w; + if (nocenter) {h = top.h; y = 0} else {h = H/2 + a; y = h - top.h} + if (jsMath.Controls.cookie.font === "unicode") { + if (jsMath.Browser.msie8HeightBug) {y -= jsMath.hd} + else if (jsMath.Browser.msieBlockDepthBug) {y += jsMath.d} + } + html = jsMath.HTML.Absolute(html,w,Font.h,"none",-y); + var box = new jsMath.Box('html',html,rep.w,h,H-h); + box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d; + return box; + }, + + /* + * Get the HTML for a given delimiter of a given height. + * It will return either a single character, if one exists, or the + * more complex HTML needed for a stretchable delimiter. + */ + Delimiter: function (H,delim,style,nocenter) { + var size = 4; //### pass this? + var TeX = jsMath.Typeset.TeX(style,size); + if (!delim) {return this.Space(TeX.nulldelimiterspace)} + var CFSH = this.DelimBestFit(H,delim[2],delim[1],style); + if (CFSH == null || CFSH[3] < H) + {CFSH = this.DelimBestFit(H,delim[4],delim[3],style)} + if (CFSH == null) {return this.Space(TeX.nulldelimiterspace)} + if (CFSH[2] == '') + {return this.DelimExtend(H,CFSH[0],CFSH[1],TeX.axis_height,nocenter)} + var box = jsMath.Box.TeX(CFSH[0],CFSH[1],CFSH[2],size).Styled(); + if (!nocenter) {box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height)} + if (Math.abs(box.y) < .0001) {box.y = 0} + if (box.y) {box = jsMath.Box.SetList([box],CFSH[2],size)} + return box; + }, + + /* + * Get a character by its TeX charcode, and make sure its width + * is specified. + */ + GetCharCode: function (code) { + var font = jsMath.TeX.fam[code[0]]; + var Font = jsMath.TeX[font]; + var c = Font[code[1]]; + if (c.img != null) {this.TeXIMG(font,code[1],4)} + if (c.w == null) {c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w} + if (c.font == null) {c.font = font} + return c; + }, + + /* + * Add the class to the html, and use the font if there isn't one + * specified already + */ + + AddClass: function (tclass,html,font) { + if (tclass == null) {tclass = font} + return jsMath.Typeset.AddClass(tclass,html); + }, + + /* + * Create the HTML for an alignment (e.g., array or matrix) + * Since the widths are not really accurate (they are based on pixel + * widths not the sub-pixel widths of the actual characters), there + * is some drift involved. We lay out the table column by column + * to help reduce the problem. + * + * ### still need to allow users to specify row and column attributes, + * and do things like \span and \multispan ### + */ + LayoutRelative: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) { + if (align == null) {align = []} + if (cspacing == null) {cspacing = []} + if (rspacing == null) {rspacing = []} + if (useStrut == null) {useStrut = 1} + if (addWidth == null) {addWidth = 1} + + // get row and column maximum dimensions + var scale = jsMath.sizes[size]/100; + var W = []; var H = []; var D = []; + var unset = -1000; var bh = unset; var bd = unset; + var i; var j; var row; + for (i = 0; i < table.length; i++) { + if (rspacing[i] == null) {rspacing[i] = 0} + row = table[i]; + H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale; + for (j = 0; j < row.length; j++) { + row[j] = row[j].Remeasured(); + if (row[j].h > H[i]) {H[i] = row[j].h} + if (row[j].d > D[i]) {D[i] = row[j].d} + if (j >= W.length) {W[j] = row[j].w} + else if (row[j].w > W[j]) {W[j] = row[j].w} + if (row[j].bh > bh) {bh = row[j].bh} + if (row[j].bd > bd) {bd = row[j].bd} + } + } + if (rspacing[table.length] == null) {rspacing[table.length] = 0} + if (bh == unset) {bh = 0}; if (bd == unset) {bd = 0} + + // lay out the columns + var HD = useStrut*(jsMath.hd-.01)*scale; + var dy = (vspace || 1) * scale/6; + var html = ''; var pW = 0; var cW = 0; + var w; var h; var y; + var box; var mlist; var entry; + for (j = 0; j < W.length; j++) { + mlist = []; y = -H[0]-rspacing[0]; pW = 0; + for (i = 0; i < table.length; i++) { + entry = table[i][j]; + if (entry && entry.format != 'null') { + if (align[j] == 'l') {w = 0} else + if (align[j] == 'r') {w = W[j] - entry.w} else + {w = (W[j] - entry.w)/2} + entry.x = w - pW; pW = entry.w + w; entry.y = y; + mlist[mlist.length] = entry; + } + if (i+1 < table.length) {y -= Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]} + } + if (cspacing[j] == null) cspacing[j] = scale; + if (mlist.length > 0) { + box = jsMath.Box.SetList(mlist,'T',size); + html += jsMath.HTML.Place(box.html,cW,0); + cW = W[j] - box.w + cspacing[j]; + } else {cW += cspacing[j]} + } + + // get the full width and height + w = -cspacing[W.length-1]; y = (H.length-1)*dy + rspacing[0]; + for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]} + for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]} + h = y/2 + jsMath.TeX.axis_height; var d = y-h; + + // adjust the final row width, and vcenter the table + // (add 1/6em at each side for the \,) + html += jsMath.HTML.Spacer(cW-cspacing[W.length-1] + addWidth*scale/6); + html = jsMath.HTML.Place(html,addWidth*scale/6,h); + box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d); + box.bh = bh; box.bd = bd; + return box; + }, + + /* + * Create the HTML for an alignment (e.g., array or matrix) + * Use absolute position for elements in the array. + * + * ### still need to allow users to specify row and column attributes, + * and do things like \span and \multispan ### + */ + LayoutAbsolute: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) { + if (align == null) {align = []} + if (cspacing == null) {cspacing = []} + if (rspacing == null) {rspacing = []} + if (useStrut == null) {useStrut = 1} + if (addWidth == null) {addWidth = 1} + // get row and column maximum dimensions + var scale = jsMath.sizes[size]/100; + var HD = useStrut*(jsMath.hd-.01)*scale; + var dy = (vspace || 1) * scale/6; + var W = []; var H = []; var D = []; + var w = 0; var h; var x; var y; + var i; var j; var row; + for (i = 0; i < table.length; i++) { + if (rspacing[i] == null) {rspacing[i] = 0} + row = table[i]; + H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale; + for (j = 0; j < row.length; j++) { + row[j] = row[j].Remeasured(); + if (row[j].h > H[i]) {H[i] = row[j].h} + if (row[j].d > D[i]) {D[i] = row[j].d} + if (j >= W.length) {W[j] = row[j].w} + else if (row[j].w > W[j]) {W[j] = row[j].w} + } + } + if (rspacing[table.length] == null) {rspacing[table.length] = 0} + + // get the height and depth of the centered table + y = (H.length-1)*dy + rspacing[0]; + for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]} + h = y/2 + jsMath.TeX.axis_height; var d = y - h; + + // lay out the columns + var html = ''; var entry; w = addWidth*scale/6; + for (j = 0; j < W.length; j++) { + y = H[0]-h + rspacing[0]; + for (i = 0; i < table.length; i++) { + entry = table[i][j]; + if (entry && entry.format != 'null') { + if (align[j] && align[j] == 'l') {x = 0} else + if (align[j] && align[j] == 'r') {x = W[j] - entry.w} else + {x = (W[j] - entry.w)/2} + html += jsMath.HTML.PlaceAbsolute(entry.html,w+x, + y-Math.max(0,entry.bh-jsMath.h*scale), + entry.mw,entry.Mw,entry.w); + } + if (i+1 < table.length) {y += Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]} + } + if (cspacing[j] == null) cspacing[j] = scale; + w += W[j] + cspacing[j]; + } + + // get the full width + w = -cspacing[W.length-1]+addWidth*scale/3; + for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]} + + html = jsMath.HTML.Spacer(addWidth*scale/6)+html+jsMath.HTML.Spacer(addWidth*scale/6); + if (jsMath.Browser.spanHeightVaries) {y = h-jsMath.h} else {y = 0} + if (jsMath.Browser.msie8HeightBug) {y = d-jsMath.d} + html = jsMath.HTML.Absolute(html,w,h+d,d,y); + var box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d); + return box; + }, + + /* + * Look for math within \hbox and other non-math text + */ + InternalMath: function (text,size) { + if (!jsMath.safeHBoxes) {text = text.replace(/@\(([^)]*)\)/g,'<$1>')} + if (!text.match(/\$|\\\(/)) + {return this.Text(this.safeHTML(text),'normal','T',size).Styled()} + + var i = 0; var k = 0; var c; var match = ''; + var mlist = []; var parse, s; + while (i < text.length) { + c = text.charAt(i++); + if (c == '$') { + if (match == '$') { + parse = jsMath.Parse(text.slice(k,i-1),null,size); + if (parse.error) { + mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2); + } else { + parse.Atomize(); + mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled(); + } + match = ''; k = i; + } else { + s = this.safeHTML(text.slice(k,i-1)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + match = '$'; k = i; + } + } else if (c == '\\') { + c = text.charAt(i++); + if (c == '(' && match == '') { + s = this.safeHTML(text.slice(k,i-2)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + match = ')'; k = i; + } else if (c == ')' && match == ')') { + parse = jsMath.Parse(text.slice(k,i-2),null,size); + if (parse.error) { + mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2); + } else { + parse.Atomize(); + mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled(); + } + match = ''; k = i; + } + } + } + s = this.safeHTML(text.slice(k)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + return this.SetList(mlist,'T',size); + }, + + /* + * Quote HTML characters if we are in safe mode + */ + safeHTML: function (s) { + if (jsMath.safeHBoxes) { + s = s.replace(/&/g,'&') + .replace(//g,'>'); + } + return s; + }, + + /* + * Convert an abitrary box to a typeset box. I.e., make an + * HTML version of the contents of the box, at its desired (x,y) + * position. + */ + Set: function (box,style,size,addstyle) { + if (box && box.type) { + if (box.type == 'typeset') {return box} + if (box.type == 'mlist') { + box.mlist.Atomize(style,size); + return box.mlist.Typeset(style,size); + } + if (box.type == 'text') { + box = this.Text(box.text,box.tclass,style,size,box.ascend||null,box.descend||null); + if (addstyle != 0) {box.Styled()} + return box; + } + box = this.TeX(box.c,box.font,style,size); + if (addstyle != 0) {box.Styled()} + return box; + } + return jsMath.Box.Null(); + }, + + /* + * Convert a list of boxes to a single typeset box. I.e., finalize + * the HTML for the list of boxes, properly spaced and positioned. + */ + SetList: function (boxes,style,size) { + var mlist = []; var box; + for (var i = 0; i < boxes.length; i++) { + box = boxes[i]; + if (box.type == 'typeset') {box = jsMath.mItem.Typeset(box)} + mlist[mlist.length] = box; + } + var typeset = new jsMath.Typeset(mlist); + return typeset.Typeset(style,size); + } + +}); + + +jsMath.Package(jsMath.Box,{ + + /* + * Add the class and style to a text box (i.e., finalize the + * unpositioned HTML for the box). + */ + Styled: function () { + if (this.format == 'text') { + this.html = jsMath.Typeset.AddClass(this.tclass,this.html); + this.html = jsMath.Typeset.AddStyle(this.style,this.size,this.html); + delete this.tclass; delete this.style; + this.format = 'html'; + } + return this; + }, + + /* + * Recompute the box width to make it more accurate. + */ + Remeasured: function () { + if (this.w > 0) { + var w = this.w; this.w = jsMath.EmBoxFor(this.html).w; + if (this.w > this.Mw) {this.Mw = this.w} + w = this.w/w; if (Math.abs(w-1) > .05) {this.h *= w; this.d *= w} + } + return this; + } + +}); + + +/***************************************************************************/ + +/* + * mItems are the building blocks of mLists (math lists) used to + * store the information about a mathematical expression. These are + * basically the items listed in the TeXbook in Appendix G (plus some + * minor extensions). + */ +jsMath.mItem = function (type,def) { + this.type = type; + jsMath.Add(this,def); +} + +jsMath.Add(jsMath.mItem,{ + + /* + * A general atom (given a nucleus for the atom) + */ + Atom: function (type,nucleus) { + return new jsMath.mItem(type,{atom: 1, nuc: nucleus}); + }, + + /* + * An atom whose nucleus is a piece of text, in a given + * class, with a given additional height and depth + */ + TextAtom: function (type,text,tclass,a,d) { + var atom = new jsMath.mItem(type,{ + atom: 1, + nuc: { + type: 'text', + text: text, + tclass: tclass + } + }); + if (a != null) {atom.nuc.ascend = a} + if (d != null) {atom.nuc.descend = d} + return atom; + }, + + /* + * An atom whose nucleus is a TeX character in a specific font + */ + TeXAtom: function (type,c,font) { + return new jsMath.mItem(type,{ + atom: 1, + nuc: { + type: 'TeX', + c: c, + font: font + } + }); + }, + + /* + * A generalized fraction atom, with given delimiters, rule + * thickness, and a numerator and denominator. + */ + Fraction: function (name,num,den,thickness,left,right) { + return new jsMath.mItem('fraction',{ + from: name, num: num, den: den, + thickness: thickness, left: left, right: right + }); + }, + + /* + * An atom that inserts some glue + */ + Space: function (w) {return new jsMath.mItem('space',{w: w})}, + + /* + * An atom that contains a typeset box (like an hbox or vbox) + */ + Typeset: function (box) {return new jsMath.mItem('ord',{atom:1, nuc: box})}, + + /* + * An atom that contains some finished HTML (acts like a typeset box) + */ + HTML: function (html) {return new jsMath.mItem('html',{html: html})} + +}); + +/***************************************************************************/ + +/* + * mLists are lists of mItems, and encode the contents of + * mathematical expressions and sub-expressions. They act as + * the expression "stack" as the mathematics is parsed, and + * contain some state information, like the position of the + * most recent open paren and \over command, and the current font. + */ +jsMath.mList = function (list,font,size,style) { + if (list) {this.mlist = list} else {this.mlist = []} + if (style == null) {style = 'T'}; if (size == null) {size = 4} + this.data = {openI: null, overI: null, overF: null, + font: font, size: size, style: style}; + this.init = {size: size, style: style}; +} + +jsMath.Package(jsMath.mList,{ + + /* + * Add an mItem to the list + */ + Add: function (box) {return (this.mlist[this.mlist.length] = box)}, + + /* + * Get the i-th mItem from the list + */ + Get: function (i) {return this.mlist[i]}, + + /* + * Get the length of the list + */ + Length: function() {return this.mlist.length}, + + /* + * Get the tail mItem of the list + */ + Last: function () { + if (this.mlist.length == 0) {return null} + return this.mlist[this.mlist.length-1] + }, + + /* + * Get a sublist of an mList + */ + Range: function (i,j) { + if (j == null) {j = this.mlist.length} + return new jsMath.mList(this.mlist.slice(i,j+1)); + }, + + /* + * Remove a range of mItems from the list. + */ + Delete: function (i,j) { + if (j == null) {j = i} + if (this.mlist.splice) {this.mlist.splice(i,j-i+1)} else { + var mlist = []; + for (var k = 0; k < this.mlist.length; k++) + {if (k < i || k > j) {mlist[mlist.length] = this.mlist[k]}} + this.mlist = mlist; + } + }, + + /* + * Add an open brace and maintain the stack information + * about the previous open brace so we can recover it + * when this one os closed. + */ + Open: function (left) { + var box = this.Add(new jsMath.mItem('boundary',{data: this.data})); + var olddata = this.data; + this.data = {}; for (var i in olddata) {this.data[i] = olddata[i]} + delete this.data.overI; delete this.data.overF; + this.data.openI = this.mlist.length-1; + if (left != null) {box.left = left} + return box; + }, + + /* + * Attempt to close a brace. Recover the stack information + * about previous open braces and \over commands. If there was an + * \over (or \above, etc) in this set of braces, create a fraction + * atom from the two halves, otherwise create an inner or ord + * from the contents of the braces. + * Remove the braced material from the list and add the newly + * created atom (the fraction, inner or ord). + */ + Close: function (right) { + if (right != null) {right = new jsMath.mItem('boundary',{right: right})} + var atom; var open = this.data.openI; + var over = this.data.overI; var from = this.data.overF; + this.data = this.mlist[open].data; + if (over) { + atom = jsMath.mItem.Fraction(from.name, + {type: 'mlist', mlist: this.Range(open+1,over-1)}, + {type: 'mlist', mlist: this.Range(over)}, + from.thickness,from.left,from.right); + if (right) { + var mlist = new jsMath.mList([this.mlist[open],atom,right]); + atom = jsMath.mItem.Atom('inner',{type: 'mlist', mlist: mlist}); + } + } else { + var openI = open+1; if (right) {this.Add(right); openI--} + atom = jsMath.mItem.Atom((right)?'inner':'ord', + {type: 'mlist', mlist: this.Range(openI)}); + } + this.Delete(open,this.Length()); + return this.Add(atom); + }, + + /* + * Create a generalized fraction from an mlist that + * contains an \over (or \above, etc). + */ + Over: function () { + var over = this.data.overI; var from = this.data.overF; + var atom = jsMath.mItem.Fraction(from.name, + {type: 'mlist', mlist: this.Range(open+1,over-1)}, + {type: 'mlist', mlist: this.Range(over)}, + from.thickness,from.left,from.right); + this.mlist = [atom]; + }, + + /* + * Take a raw mList (that has been produced by parsing some TeX + * expression), and perform the modifications outlined in + * Appendix G of the TeXbook. + */ + Atomize: function (style,size) { + var mitem; var prev = ''; + this.style = style; this.size = size; + for (var i = 0; i < this.mlist.length; i++) { + mitem = this.mlist[i]; mitem.delta = 0; + if (mitem.type == 'choice') + {this.mlist = this.Atomize.choice(this.style,mitem,i,this.mlist); i--} + else if (this.Atomize[mitem.type]) { + var f = this.Atomize[mitem.type]; // Opera needs separate name + f(this.style,this.size,mitem,prev,this,i); + } + prev = mitem; + } + if (mitem && mitem.type == 'bin') {mitem.type = 'ord'} + if (this.mlist.length >= 2 && mitem.type == 'boundary' && + this.mlist[0].type == 'boundary') {this.AddDelimiters(style,size)} + }, + + /* + * For a list that has boundary delimiters as its first and last + * entries, we replace the boundary atoms by open and close + * atoms whose nuclii are the specified delimiters properly sized + * for the contents of the list. (Rule 19) + */ + AddDelimiters: function(style,size) { + var unset = -10000; var h = unset; var d = unset; + for (var i = 0; i < this.mlist.length; i++) { + var mitem = this.mlist[i]; + if (mitem.atom || mitem.type == 'box') { + h = Math.max(h,mitem.nuc.h+mitem.nuc.y); + d = Math.max(d,mitem.nuc.d-mitem.nuc.y); + } + } + var TeX = jsMath.TeX; var a = jsMath.Typeset.TeX(style,size).axis_height; + var delta = Math.max(h-a,d+a); + var H = Math.max(Math.floor(TeX.integer*delta/500)*TeX.delimiterfactor, + TeX.integer*(2*delta-TeX.delimitershortfall))/TeX.integer; + var left = this.mlist[0]; var right = this.mlist[this.mlist.length-1]; + left.nuc = jsMath.Box.Delimiter(H,left.left,style); + right.nuc = jsMath.Box.Delimiter(H,right.right,style); + left.type = 'open'; left.atom = 1; delete left.left; + right.type = 'close'; right.atom = 1; delete right.right; + }, + + /* + * Typeset a math list to produce final HTML for the list. + */ + Typeset: function (style,size) { + var typeset = new jsMath.Typeset(this.mlist); + return typeset.Typeset(style,size); + } + +}); + + +/* + * These routines implement the main rules given in Appendix G of the + * TeXbook + */ + +jsMath.Add(jsMath.mList.prototype.Atomize,{ + + /* + * Handle \displaystyle, \textstyle, etc. + */ + style: function (style,size,mitem,prev,mlist) { + mlist.style = mitem.style; + }, + + /* + * Handle \tiny, \small, etc. + */ + size: function (style,size,mitem,prev,mlist) { + mlist.size = mitem.size; + }, + + /* + * Create empty boxes of the proper sizes for the various + * phantom-type commands + */ + phantom: function (style,size,mitem) { + var box = mitem.nuc = jsMath.Box.Set(mitem.phantom,style,size); + if (mitem.h) {box.Remeasured(); box.html = jsMath.HTML.Spacer(box.w)} + else {box.html = '', box.w = box.Mw = box.mw = 0;} + if (!mitem.v) {box.h = box.d = 0} + box.bd = box.bh = 0; + delete mitem.phantom; + mitem.type = 'box'; + }, + + /* + * Create a box of zero height and depth containing the + * contents of the atom + */ + smash: function (style,size,mitem) { + var box = mitem.nuc = jsMath.Box.Set(mitem.smash,style,size).Remeasured(); + box.h = box.d = 0; + delete mitem.smash; + mitem.type = 'box'; + }, + + /* + * Move a box up or down vertically + */ + raise: function (style,size,mitem) { + mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size); + var y = mitem.raise; + mitem.nuc.html = + jsMath.HTML.Place(mitem.nuc.html,0,y,mitem.nuc.mw,mitem.nuc.Mw,mitem.nuc.w); + mitem.nuc.h += y; mitem.nuc.d -= y; + mitem.type = 'ord'; mitem.atom = 1; + }, + + /* + * Hide the size of a box so that it laps to the left or right, or + * up or down. + */ + lap: function (style,size,mitem) { + var box = jsMath.Box.Set(mitem.nuc,style,size).Remeasured(); + var mlist = [box]; + if (mitem.lap == 'llap') {box.x = -box.w} else + if (mitem.lap == 'rlap') {mlist[1] = jsMath.mItem.Space(-box.w)} else + if (mitem.lap == 'ulap') {box.y = box.d; box.h = box.d = 0} else + if (mitem.lap == 'dlap') {box.y = -box.h; box.h = box.d = 0} + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + if (mitem.lap == 'ulap' || mitem.lap == 'dlap') {mitem.nuc.h = mitem.nuc.d = 0} + mitem.type = 'box'; delete mitem.atom; + }, + + /* + * Handle a Bin atom. (Rule 5) + */ + bin: function (style,size,mitem,prev) { + if (prev && prev.type) { + var type = prev.type; + if (type == 'bin' || type == 'op' || type == 'rel' || + type == 'open' || type == 'punct' || type == '' || + (type == 'boundary' && prev.left != '')) {mitem.type = 'ord'} + } else {mitem.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Rel atom. (Rule 6) + */ + rel: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Close atom. (Rule 6) + */ + close: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Punct atom. (Rule 6) + */ + punct: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Open atom. (Rule 7) + */ + open: function (style,size,mitem) { + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Inner atom. (Rule 7) + */ + inner: function (style,size,mitem) { + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Vcent atom. (Rule 8) + */ + vcenter: function (style,size,mitem) { + var box = jsMath.Box.Set(mitem.nuc,style,size); + var TeX = jsMath.Typeset.TeX(style,size); + box.y = TeX.axis_height - (box.h-box.d)/2; + mitem.nuc = box; mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Over atom. (Rule 9) + */ + overline: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured(); + var t = TeX.default_rule_thickness; + var rule = jsMath.Box.Rule(box.w,t); + rule.x = -rule.w; rule.y = box.h + 3*t; + mitem.nuc = jsMath.Box.SetList([box,rule],style,size); + mitem.nuc.h += t; + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Under atom. (Rule 10) + */ + underline: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured(); + var t = TeX.default_rule_thickness; + var rule = jsMath.Box.Rule(box.w,t); + rule.x = -rule.w; rule.y = -box.d - 3*t - t; + mitem.nuc = jsMath.Box.SetList([box,rule],style,size); + mitem.nuc.d += t; + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Rad atom. (Rule 11 plus stuff for \root..\of) + */ + radical: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var Cp = jsMath.Typeset.PrimeStyle(style); + var box = jsMath.Box.Set(mitem.nuc,Cp,size).Remeasured(); + var t = TeX.default_rule_thickness; + var p = t; if (style == 'D' || style == "D'") {p = TeX.x_height} + var r = t + p/4; + var surd = jsMath.Box.Delimiter(box.h+box.d+r+t,[0,2,0x70,3,0x70],style,1); +// if (surd.h > 0) {t = surd.h} // thickness of rule is height of surd character + if (surd.d > box.h+box.d+r) {r = (r+surd.d-box.h-box.d)/2} + surd.y = box.h+r; + var rule = jsMath.Box.Rule(box.w,t); + rule.y = surd.y-t/2; rule.h += 3*t/2; box.x = -box.w; + var Cr = jsMath.Typeset.UpStyle(jsMath.Typeset.UpStyle(style)); + var root = jsMath.Box.Set(mitem.root || null,Cr,size).Remeasured(); + if (mitem.root) { + root.y = .55*(box.h+box.d+3*t+r)-box.d; + surd.x = Math.max(root.w-(11/18)*surd.w,0); + rule.x = (7/18)*surd.w; + root.x = -(root.w+rule.x); + } + mitem.nuc = jsMath.Box.SetList([surd,root,rule,box],style,size); + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Acc atom. (Rule 12) + */ + accent: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var Cp = jsMath.Typeset.PrimeStyle(style); + var box = jsMath.Box.Set(mitem.nuc,Cp,size); + var u = box.w; var s; var Font; var ic = 0; + if (mitem.nuc.type == 'TeX') { + Font = jsMath.TeX[mitem.nuc.font]; + if (Font[mitem.nuc.c].krn && Font.skewchar) + {s = Font[mitem.nuc.c].krn[Font.skewchar]} + ic = Font[mitem.nuc.c].ic; if (ic == null) {ic = 0} + } + if (s == null) {s = 0} + + var c = mitem.accent[2]; + var font = jsMath.TeX.fam[mitem.accent[1]]; Font = jsMath.TeX[font]; + while (Font[c].n && Font[Font[c].n].w <= u) {c = Font[c].n} + + var delta = Math.min(box.h,TeX.x_height); + if (mitem.nuc.type == 'TeX') { + var nitem = jsMath.mItem.Atom('ord',mitem.nuc); + nitem.sup = mitem.sup; nitem.sub = mitem.sub; nitem.delta = 0; + jsMath.mList.prototype.Atomize.SupSub(style,size,nitem); + delta += (nitem.nuc.h - box.h); + box = mitem.nuc = nitem.nuc; + delete mitem.sup; delete mitem.sub; + } + var acc = jsMath.Box.TeX(c,font,style,size); + acc.y = box.h - delta; acc.x = -box.w + s + (u-acc.w)/2; + if (jsMath.Browser.msieAccentBug) + {acc.html += jsMath.HTML.Spacer(.1); acc.w += .1; acc.Mw += .1} + if (Font[c].ic || ic) {acc.x += (ic - (Font[c].ic||0)) * TeX.scale} + + mitem.nuc = jsMath.Box.SetList([box,acc],style,size); + if (mitem.nuc.w != box.w) { + var space = jsMath.mItem.Space(box.w-mitem.nuc.w); + mitem.nuc = jsMath.Box.SetList([mitem.nuc,space],style,size); + } + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Op atom. (Rules 13 and 13a) + */ + op: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); var box; + mitem.delta = 0; var isD = (style.charAt(0) == 'D'); + if (mitem.limits == null && isD) {mitem.limits = 1} + + if (mitem.nuc.type == 'TeX') { + var C = jsMath.TeX[mitem.nuc.font][mitem.nuc.c]; + if (isD && C.n) {mitem.nuc.c = C.n; C = jsMath.TeX[mitem.nuc.font][C.n]} + box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size); + if (C.ic) { + mitem.delta = C.ic * TeX.scale; + if (mitem.limits || !mitem.sub || jsMath.Browser.msieIntegralBug) { + box = mitem.nuc = jsMath.Box.SetList([box,jsMath.mItem.Space(mitem.delta)],style,size); + } + } + box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height); + if (Math.abs(box.y) < .0001) {box.y = 0} + } + + if (!box) {box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size).Remeasured()} + if (mitem.limits) { + var W = box.w; var x = box.w; + var mlist = [box]; var dh = 0; var dd = 0; + if (mitem.sup) { + var sup = jsMath.Box.Set(mitem.sup,jsMath.Typeset.UpStyle(style),size).Remeasured(); + sup.x = ((box.w-sup.w)/2 + mitem.delta/2) - x; dh = TeX.big_op_spacing5; + W = Math.max(W,sup.w); x += sup.x + sup.w; + sup.y = box.h+sup.d + box.y + + Math.max(TeX.big_op_spacing1,TeX.big_op_spacing3-sup.d); + mlist[mlist.length] = sup; delete mitem.sup; + } + if (mitem.sub) { + var sub = jsMath.Box.Set(mitem.sub,jsMath.Typeset.DownStyle(style),size).Remeasured(); + sub.x = ((box.w-sub.w)/2 - mitem.delta/2) - x; dd = TeX.big_op_spacing5; + W = Math.max(W,sub.w); x += sub.x + sub.w; + sub.y = -box.d-sub.h + box.y - + Math.max(TeX.big_op_spacing2,TeX.big_op_spacing4-sub.h); + mlist[mlist.length] = sub; delete mitem.sub; + } + if (W > box.w) {box.x = (W-box.w)/2; x += box.x} + if (x < W) {mlist[mlist.length] = jsMath.mItem.Space(W-x)} + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + mitem.nuc.h += dh; mitem.nuc.d += dd; + } else { + if (jsMath.Browser.msieIntegralBug && mitem.sub && C && C.ic) + {mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(-C.ic*TeX.scale)],style,size)} + else if (box.y) {mitem.nuc = jsMath.Box.SetList([box],style,size)} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + } + }, + + /* + * Handle an Ord atom. (Rule 14) + */ + ord: function (style,size,mitem,prev,mList,i) { + if (mitem.nuc.type == 'TeX' && !mitem.sup && !mitem.sub) { + var nitem = mList.mlist[i+1]; + if (nitem && nitem.atom && nitem.type && + (nitem.type == 'ord' || nitem.type == 'op' || nitem.type == 'bin' || + nitem.type == 'rel' || nitem.type == 'open' || + nitem.type == 'close' || nitem.type == 'punct')) { + if (nitem.nuc.type == 'TeX' && nitem.nuc.font == mitem.nuc.font) { + mitem.textsymbol = 1; + var krn = jsMath.TeX[mitem.nuc.font][mitem.nuc.c].krn; + krn *= jsMath.Typeset.TeX(style,size).scale; + if (krn && krn[nitem.nuc.c]) { + for (var k = mList.mlist.length-1; k > i; k--) + {mList.mlist[k+1] = mList.mlist[k]} + mList.mlist[i+1] = jsMath.mItem.Space(krn[nitem.nuc.c]); + } + } + } + } + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a generalized fraction. (Rules 15 to 15e) + */ + fraction: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); var t = 0; + if (mitem.thickness != null) {t = mitem.thickness} + else if (mitem.from.match(/over/)) {t = TeX.default_rule_thickness} + var isD = (style.charAt(0) == 'D'); + var Cn = (style == 'D')? 'T': (style == "D'")? "T'": jsMath.Typeset.UpStyle(style); + var Cd = (isD)? "T'": jsMath.Typeset.DownStyle(style); + var num = jsMath.Box.Set(mitem.num,Cn,size).Remeasured(); + var den = jsMath.Box.Set(mitem.den,Cd,size).Remeasured(); + + var u; var v; var w; var p; var r; + var H = (isD)? TeX.delim1 : TeX.delim2; + var mlist = [jsMath.Box.Delimiter(H,mitem.left,style)] + var right = jsMath.Box.Delimiter(H,mitem.right,style); + + if (num.w < den.w) { + num.x = (den.w-num.w)/2; + den.x = -(num.w + num.x); + w = den.w; mlist[1] = num; mlist[2] = den; + } else { + den.x = (num.w-den.w)/2; + num.x = -(den.w + den.x); + w = num.w; mlist[1] = den; mlist[2] = num; + } + if (isD) {u = TeX.num1; v = TeX.denom1} else { + u = (t != 0)? TeX.num2: TeX.num3; + v = TeX.denom2; + } + if (t == 0) {// atop + p = (isD)? 7*TeX.default_rule_thickness: 3*TeX.default_rule_thickness; + r = (u - num.d) - (den.h - v); + if (r < p) {u += (p-r)/2; v += (p-r)/2} + } else {// over + p = (isD)? 3*t: t; var a = TeX.axis_height; + r = (u-num.d)-(a+t/2); if (r < p) {u += p-r} + r = (a-t/2)-(den.h-v); if (r < p) {v += p-r} + var rule = jsMath.Box.Rule(w,t); rule.x = -w; rule.y = a - t/2; + mlist[mlist.length] = rule; + } + num.y = u; den.y = -v; + + mlist[mlist.length] = right; + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + mitem.type = 'ord'; mitem.atom = 1; + delete mitem.num; delete mitem.den; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Add subscripts and superscripts. (Rules 17-18f) + */ + SupSub: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var nuc = mitem.nuc; + var box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size,0); + if (box.format == 'null') + {box = mitem.nuc = jsMath.Box.Text('','normal',style,size)} + + if (nuc.type == 'TeX') { + if (!mitem.textsymbol) { + var C = jsMath.TeX[nuc.font][nuc.c]; + if (C.ic) { + mitem.delta = C.ic * TeX.scale; + if (!mitem.sub) { + box = mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(mitem.delta)],style,size); + mitem.delta = 0; + } + } + } else {mitem.delta = 0} + } + + if (!mitem.sup && !mitem.sub) return; + mitem.nuc.Styled(); + + var Cd = jsMath.Typeset.DownStyle(style); + var Cu = jsMath.Typeset.UpStyle(style); + var q = jsMath.Typeset.TeX(Cu,size).sup_drop; + var r = jsMath.Typeset.TeX(Cd,size).sub_drop; + var u = 0; var v = 0; var p; + if (nuc.type && nuc.type != 'text' && nuc.type != 'TeX' && nuc.type != 'null') + {u = box.h - q; v = box.d + r} + + if (mitem.sub) { + var sub = jsMath.Box.Set(mitem.sub,Cd,size); + sub = jsMath.Box.SetList([sub,jsMath.mItem.Space(TeX.scriptspace)],style,size); + } + + if (!mitem.sup) { + sub.y = -Math.max(v,TeX.sub1,sub.h-(4/5)*jsMath.Typeset.TeX(Cd,size).x_height); + mitem.nuc = jsMath.Box.SetList([box,sub],style,size).Styled(); delete mitem.sub; + return; + } + + var sup = jsMath.Box.Set(mitem.sup,Cu,size); + sup = jsMath.Box.SetList([sup,jsMath.mItem.Space(TeX.scriptspace)],style,size); + if (style == 'D') {p = TeX.sup1} + else if (style.charAt(style.length-1) == "'") {p = TeX.sup3} + else {p = TeX.sup2} + u = Math.max(u,p,sup.d+jsMath.Typeset.TeX(Cu,size).x_height/4); + + if (!mitem.sub) { + sup.y = u; + mitem.nuc = jsMath.Box.SetList([box,sup],style,size); delete mitem.sup; + return; + } + + v = Math.max(v,jsMath.Typeset.TeX(Cd,size).sub2); + var t = TeX.default_rule_thickness; + if ((u-sup.d) - (sub.h -v) < 4*t) { + v = 4*t + sub.h - (u-sup.d); + p = (4/5)*TeX.x_height - (u-sup.d); + if (p > 0) {u += p; v -= p} + } + sup.Remeasured(); sub.Remeasured(); + sup.y = u; sub.y = -v; sup.x = mitem.delta; + if (sup.w+sup.x > sub.w) + {sup.x -= sub.w; mitem.nuc = jsMath.Box.SetList([box,sub,sup],style,size)} else + {sub.x -= (sup.w+sup.x); mitem.nuc = jsMath.Box.SetList([box,sup,sub],style,size)} + + delete mitem.sup; delete mitem.sub; + } + +}); + + +/***************************************************************************/ + +/* + * The Typeset object handles most of the TeX-specific processing + */ + +jsMath.Typeset = function (mlist) { + this.type = 'typeset'; + this.mlist = mlist; +} + +jsMath.Add(jsMath.Typeset,{ + + /* + * The "C-uparrow" style table (TeXbook, p. 441) + */ + upStyle: { + D: "S", T: "S", "D'": "S'", "T'": "S'", + S: "SS", SS: "SS", "S'": "SS'", "SS'": "SS'" + }, + + /* + * The "C-downarrow" style table (TeXbook, p. 441) + */ + downStyle: { + D: "S'", T: "S'", "D'": "S'", "T'": "S'", + S: "SS'", SS: "SS'", "S'": "SS'", "SS'": "SS'" + }, + + /* + * Get the various styles given the current style + * (see TeXbook, p. 441) + */ + UpStyle: function (style) {return this.upStyle[style]}, + DownStyle: function (style) {return this.downStyle[style]}, + PrimeStyle: function (style) { + if (style.charAt(style.length-1) == "'") {return style} + return style + "'" + }, + + /* + * A value scaled to the appropriate size for scripts + */ + StyleValue: function (style,v) { + if (style == "S" || style == "S'") {return .7*v} + if (style == "SS" || style == "SS'") {return .5*v} + return v; + }, + + /* + * Return the size associated with a given style and size + */ + StyleSize: function (style,size) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + return size; + }, + + /* + * Return the font parameter table for the given style + */ + TeX: function (style,size) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + return jsMath.TeXparams[size]; + }, + + + /* + * Add the CSS class for the given TeX style + */ + AddStyle: function (style,size,html) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + if (size != 4) {html = '' + html + ''} + return html; + }, + + /* + * Add the font class, if needed + */ + AddClass: function (tclass,html) { + if (tclass != '' && tclass != 'normal') {html = jsMath.HTML.Class(tclass,html)} + return html; + } + +}); + + +jsMath.Package(jsMath.Typeset,{ + + /* + * The spacing tables for inter-atom spacing + * (See rule 20, and Chapter 18, p 170) + */ + DTsep: { + ord: {op: 1, bin: 2, rel: 3, inner: 1}, + op: {ord: 1, op: 1, rel: 3, inner: 1}, + bin: {ord: 2, op: 2, open: 2, inner: 2}, + rel: {ord: 3, op: 3, open: 3, inner: 3}, + open: {}, + close: {op: 1, bin:2, rel: 3, inner: 1}, + punct: {ord: 1, op: 1, rel: 1, open: 1, close: 1, punct: 1, inner: 1}, + inner: {ord: 1, op: 1, bin: 2, rel: 3, open: 1, punct: 1, inner: 1} + }, + + SSsep: { + ord: {op: 1}, + op: {ord: 1, op: 1}, + bin: {}, + rel: {}, + open: {}, + close: {op: 1}, + punct: {}, + inner: {op: 1} + }, + + /* + * The sizes used in the tables above + */ + sepW: ['','thinmuskip','medmuskip','thickmuskip'], + + + /* + * Find the amount of separation to use between two adjacent + * atoms in the given style + */ + GetSeparation: function (l,r,style) { + if (l && l.atom && r.atom) { + var table = this.DTsep; if (style.charAt(0) == "S") {table = this.SSsep} + var row = table[l.type]; + if (row && row[r.type] != null) {return jsMath.TeX[this.sepW[row[r.type]]]} + } + return 0; + }, + + /* + * Typeset an mlist (i.e., turn it into HTML). + * Here, text items of the same class and style are combined + * to reduce the number of tags used (though it is still + * huge). Spaces are combined, when possible. + * ### More needs to be done with that. ### + * The width of the final box is recomputed at the end, since + * the final width is not necessarily the sum of the widths of + * the individual parts (widths are in pixels, but the browsers + * puts pieces together using sub-pixel accuracy). + */ + Typeset: function (style,size) { + this.style = style; this.size = size; var unset = -10000 + this.w = 0; this.mw = 0; this.Mw = 0; + this.h = unset; this.d = unset; + this.bh = this.h; this.bd = this.d; + this.tbuf = ''; this.tx = 0; this.tclass = ''; + this.cbuf = ''; this.hbuf = ''; this.hx = 0; + var mitem = null; var prev; this.x = 0; this.dx = 0; + + for (var i = 0; i < this.mlist.length; i++) { + prev = mitem; mitem = this.mlist[i]; + switch (mitem.type) { + + case 'size': + this.FlushClassed(); + this.size = mitem.size; + mitem = prev; // hide this from TeX + break; + + case 'style': + this.FlushClassed(); + if (this.style.charAt(this.style.length-1) == "'") + {this.style = mitem.style + "'"} else {this.style = mitem.style} + mitem = prev; // hide this from TeX + break; + + case 'space': + if (typeof(mitem.w) == 'object') { + if (this.style.charAt(1) == 'S') {mitem.w = .5*mitem.w[0]/18} + else if (this.style.charAt(0) == 'S') {mitem.w = .7*mitem.w[0]/18} + else {mitem.w = mitem.w[0]/18} + } + this.dx += mitem.w-0; // mitem.w is sometimes a string? + mitem = prev; // hide this from TeX + break; + + case 'html': + this.FlushClassed(); + if (this.hbuf == '') {this.hx = this.x} + this.hbuf += mitem.html; + mitem = prev; // hide this from TeX + break; + + default: // atom + if (!mitem.atom && mitem.type != 'box') break; + mitem.nuc.x += this.dx + this.GetSeparation(prev,mitem,this.style); + if (mitem.nuc.x || mitem.nuc.y) mitem.nuc.Styled(); + this.dx = 0; this.x = this.x + this.w; + if (this.x + mitem.nuc.x + mitem.nuc.mw < this.mw) + {this.mw = this.x + mitem.nuc.x + mitem.nuc.mw} + if (this.w + mitem.nuc.x + mitem.nuc.Mw > this.Mw) + {this.Mw = this.w + mitem.nuc.x + mitem.nuc.Mw} + this.w += mitem.nuc.w + mitem.nuc.x; + if (mitem.nuc.format == 'text') { + if (this.tclass != mitem.nuc.tclass && this.tclass != '') this.FlushText(); + if (this.tbuf == '' && this.cbuf == '') {this.tx = this.x} + this.tbuf += mitem.nuc.html; this.tclass = mitem.nuc.tclass; + } else { + this.FlushClassed(); + if (mitem.nuc.x || mitem.nuc.y) this.Place(mitem.nuc); + if (this.hbuf == '') {this.hx = this.x} + this.hbuf += mitem.nuc.html; + } + this.h = Math.max(this.h,mitem.nuc.h+mitem.nuc.y); this.bh = Math.max(this.bh,mitem.nuc.bh); + this.d = Math.max(this.d,mitem.nuc.d-mitem.nuc.y); this.bd = Math.max(this.bd,mitem.nuc.bd); + break; + } + } + + this.FlushClassed(); // make sure scaling is included + if (this.dx) { + this.hbuf += jsMath.HTML.Spacer(this.dx); this.w += this.dx; + if (this.w > this.Mw) {this.Mw = this.w} + if (this.w < this.mw) {this.mw = this.w} + } + if (this.hbuf == '') {return jsMath.Box.Null()} + if (this.h == unset) {this.h = 0} + if (this.d == unset) {this.d = 0} + var box = new jsMath.Box('html',this.hbuf,this.w,this.h,this.d); + box.bh = this.bh; box.bd = this.bd; + box.mw = this.mw; box.Mw = this.Mw; + return box; + }, + + /* + * Add the font to the buffered text and move it to the + * classed-text buffer. + */ + FlushText: function () { + if (this.tbuf == '') return; + this.cbuf += jsMath.Typeset.AddClass(this.tclass,this.tbuf); + this.tbuf = ''; this.tclass = ''; + }, + + /* + * Add the script or scriptscript style to the text and + * move it to the HTML buffer + */ + FlushClassed: function () { + this.FlushText(); + if (this.cbuf == '') return; + if (this.hbuf == '') {this.hx = this.tx} + this.hbuf += jsMath.Typeset.AddStyle(this.style,this.size,this.cbuf); + this.cbuf = ''; + }, + + /* + * Add a to position an item's HTML, and + * adjust the item's height and depth. + * (This may be replaced buy one of the following browser-specific + * versions by Browser.Init().) + */ + Place: function (item) { + var html = '' + item.html + ''; + item.h += item.y; item.d -= item.y; + item.x = 0; item.y = 0; + }, + + /* + * For MSIE on Windows, backspacing must be done in a separate + * , otherwise the contents will be clipped. Netscape + * also doesn't combine vertical and horizontal spacing well. + * Here, the horizontal and vertical spacing are done separately. + */ + + PlaceSeparateSkips: function (item) { + if (item.y) { + var rw = item.Mw - item.w; var lw = item.mw; + var W = item.Mw - item.mw; + item.html = + jsMath.HTML.Spacer(lw-rw) + + '' + + jsMath.HTML.Spacer(-lw) + + item.html + + jsMath.HTML.Spacer(rw) + + '' + } + if (item.x) {item.html = jsMath.HTML.Spacer(item.x) + item.html} + item.h += item.y; item.d -= item.y; + item.x = 0; item.y = 0; + } + +}); + + + +/***************************************************************************/ + +/* + * The Parse object handles the parsing of the TeX input string, and creates + * the mList to be typeset by the Typeset object above. + */ + +jsMath.Parse = function (s,font,size,style) { + var parse = new jsMath.Parser(s,font,size,style); + parse.Parse(); + return parse; +} + +jsMath.Parser = function (s,font,size,style) { + this.string = s; this.i = 0; + this.mlist = new jsMath.mList(null,font,size,style); +} + +jsMath.Package(jsMath.Parser,{ + + // special characters + cmd: '\\', + open: '{', + close: '}', + + // patterns for letters and numbers + letter: /[a-z]/i, + number: /[0-9]/, + // pattern for macros to ^ and _ that should be read with arguments + scriptargs: /^((math|text)..|mathcal|[hm]box)$/, + + // the \mathchar definitions (see Appendix B of the TeXbook). + mathchar: { + '!': [5,0,0x21], + '(': [4,0,0x28], + ')': [5,0,0x29], + '*': [2,2,0x03], // \ast + '+': [2,0,0x2B], + ',': [6,1,0x3B], + '-': [2,2,0x00], + '.': [0,1,0x3A], + '/': [0,1,0x3D], + ':': [3,0,0x3A], + ';': [6,0,0x3B], + '<': [3,1,0x3C], + '=': [3,0,0x3D], + '>': [3,1,0x3E], + '?': [5,0,0x3F], + '[': [4,0,0x5B], + ']': [5,0,0x5D], +// '{': [4,2,0x66], +// '}': [5,2,0x67], + '|': [0,2,0x6A] + }, + + // handle special \catcode characters + special: { + '~': 'Tilde', + '^': 'HandleSuperscript', + '_': 'HandleSubscript', + ' ': 'Space', + '\01': 'Space', + "\t": 'Space', + "\r": 'Space', + "\n": 'Space', + "'": 'Prime', + '%': 'HandleComment', + '&': 'HandleEntry', + '#': 'Hash' + }, + + // the \mathchardef table (see Appendix B of the TeXbook). + mathchardef: { + // brace parts + braceld: [0,3,0x7A], + bracerd: [0,3,0x7B], + bracelu: [0,3,0x7C], + braceru: [0,3,0x7D], + + // Greek letters + alpha: [0,1,0x0B], + beta: [0,1,0x0C], + gamma: [0,1,0x0D], + delta: [0,1,0x0E], + epsilon: [0,1,0x0F], + zeta: [0,1,0x10], + eta: [0,1,0x11], + theta: [0,1,0x12], + iota: [0,1,0x13], + kappa: [0,1,0x14], + lambda: [0,1,0x15], + mu: [0,1,0x16], + nu: [0,1,0x17], + xi: [0,1,0x18], + pi: [0,1,0x19], + rho: [0,1,0x1A], + sigma: [0,1,0x1B], + tau: [0,1,0x1C], + upsilon: [0,1,0x1D], + phi: [0,1,0x1E], + chi: [0,1,0x1F], + psi: [0,1,0x20], + omega: [0,1,0x21], + varepsilon: [0,1,0x22], + vartheta: [0,1,0x23], + varpi: [0,1,0x24], + varrho: [0,1,0x25], + varsigma: [0,1,0x26], + varphi: [0,1,0x27], + + Gamma: [7,0,0x00], + Delta: [7,0,0x01], + Theta: [7,0,0x02], + Lambda: [7,0,0x03], + Xi: [7,0,0x04], + Pi: [7,0,0x05], + Sigma: [7,0,0x06], + Upsilon: [7,0,0x07], + Phi: [7,0,0x08], + Psi: [7,0,0x09], + Omega: [7,0,0x0A], + + // Ord symbols + aleph: [0,2,0x40], + imath: [0,1,0x7B], + jmath: [0,1,0x7C], + ell: [0,1,0x60], + wp: [0,1,0x7D], + Re: [0,2,0x3C], + Im: [0,2,0x3D], + partial: [0,1,0x40], + infty: [0,2,0x31], + prime: [0,2,0x30], + emptyset: [0,2,0x3B], + nabla: [0,2,0x72], + surd: [1,2,0x70], + top: [0,2,0x3E], + bot: [0,2,0x3F], + triangle: [0,2,0x34], + forall: [0,2,0x38], + exists: [0,2,0x39], + neg: [0,2,0x3A], + lnot: [0,2,0x3A], + flat: [0,1,0x5B], + natural: [0,1,0x5C], + sharp: [0,1,0x5D], + clubsuit: [0,2,0x7C], + diamondsuit: [0,2,0x7D], + heartsuit: [0,2,0x7E], + spadesuit: [0,2,0x7F], + + // big ops + coprod: [1,3,0x60], + bigvee: [1,3,0x57], + bigwedge: [1,3,0x56], + biguplus: [1,3,0x55], + bigcap: [1,3,0x54], + bigcup: [1,3,0x53], + intop: [1,3,0x52], + prod: [1,3,0x51], + sum: [1,3,0x50], + bigotimes: [1,3,0x4E], + bigoplus: [1,3,0x4C], + bigodot: [1,3,0x4A], + ointop: [1,3,0x48], + bigsqcup: [1,3,0x46], + smallint: [1,2,0x73], + + // binary operations + triangleleft: [2,1,0x2F], + triangleright: [2,1,0x2E], + bigtriangleup: [2,2,0x34], + bigtriangledown: [2,2,0x35], + wedge: [2,2,0x5E], + land: [2,2,0x5E], + vee: [2,2,0x5F], + lor: [2,2,0x5F], + cap: [2,2,0x5C], + cup: [2,2,0x5B], + ddagger: [2,2,0x7A], + dagger: [2,2,0x79], + sqcap: [2,2,0x75], + sqcup: [2,2,0x74], + uplus: [2,2,0x5D], + amalg: [2,2,0x71], + diamond: [2,2,0x05], + bullet: [2,2,0x0F], + wr: [2,2,0x6F], + div: [2,2,0x04], + odot: [2,2,0x0C], + oslash: [2,2,0x0B], + otimes: [2,2,0x0A], + ominus: [2,2,0x09], + oplus: [2,2,0x08], + mp: [2,2,0x07], + pm: [2,2,0x06], + circ: [2,2,0x0E], + bigcirc: [2,2,0x0D], + setminus: [2,2,0x6E], // for set difference A\setminus B + cdot: [2,2,0x01], + ast: [2,2,0x03], + times: [2,2,0x02], + star: [2,1,0x3F], + + // Relations + propto: [3,2,0x2F], + sqsubseteq: [3,2,0x76], + sqsupseteq: [3,2,0x77], + parallel: [3,2,0x6B], + mid: [3,2,0x6A], + dashv: [3,2,0x61], + vdash: [3,2,0x60], + leq: [3,2,0x14], + le: [3,2,0x14], + geq: [3,2,0x15], + ge: [3,2,0x15], + lt: [3,1,0x3C], // extra since < and > are hard + gt: [3,1,0x3E], // to get in HTML + succ: [3,2,0x1F], + prec: [3,2,0x1E], + approx: [3,2,0x19], + succeq: [3,2,0x17], + preceq: [3,2,0x16], + supset: [3,2,0x1B], + subset: [3,2,0x1A], + supseteq: [3,2,0x13], + subseteq: [3,2,0x12], + 'in': [3,2,0x32], + ni: [3,2,0x33], + owns: [3,2,0x33], + gg: [3,2,0x1D], + ll: [3,2,0x1C], + not: [3,2,0x36], + sim: [3,2,0x18], + simeq: [3,2,0x27], + perp: [3,2,0x3F], + equiv: [3,2,0x11], + asymp: [3,2,0x10], + smile: [3,1,0x5E], + frown: [3,1,0x5F], + + // Arrows + Leftrightarrow: [3,2,0x2C], + Leftarrow: [3,2,0x28], + Rightarrow: [3,2,0x29], + leftrightarrow: [3,2,0x24], + leftarrow: [3,2,0x20], + gets: [3,2,0x20], + rightarrow: [3,2,0x21], + to: [3,2,0x21], + mapstochar: [3,2,0x37], + leftharpoonup: [3,1,0x28], + leftharpoondown: [3,1,0x29], + rightharpoonup: [3,1,0x2A], + rightharpoondown: [3,1,0x2B], + nearrow: [3,2,0x25], + searrow: [3,2,0x26], + nwarrow: [3,2,0x2D], + swarrow: [3,2,0x2E], + + minuschar: [3,2,0x00], // for longmapsto + hbarchar: [0,0,0x16], // for \hbar + lhook: [3,1,0x2C], + rhook: [3,1,0x2D], + + ldotp: [6,1,0x3A], // ldot as a punctuation mark + cdotp: [6,2,0x01], // cdot as a punctuation mark + colon: [6,0,0x3A], // colon as a punctuation mark + + '#': [7,0,0x23], + '$': [7,0,0x24], + '%': [7,0,0x25], + '&': [7,0,0x26] + }, + + // The delimiter table (see Appendix B of the TeXbook) + delimiter: { + '(': [0,0,0x28,3,0x00], + ')': [0,0,0x29,3,0x01], + '[': [0,0,0x5B,3,0x02], + ']': [0,0,0x5D,3,0x03], + '<': [0,2,0x68,3,0x0A], + '>': [0,2,0x69,3,0x0B], + '\\lt': [0,2,0x68,3,0x0A], // extra since < and > are + '\\gt': [0,2,0x69,3,0x0B], // hard to get in HTML + '/': [0,0,0x2F,3,0x0E], + '|': [0,2,0x6A,3,0x0C], + '.': [0,0,0x00,0,0x00], + '\\': [0,2,0x6E,3,0x0F], + '\\lmoustache': [4,3,0x7A,3,0x40], // top from (, bottom from ) + '\\rmoustache': [5,3,0x7B,3,0x41], // top from ), bottom from ( + '\\lgroup': [4,6,0x28,3,0x3A], // extensible ( with sharper tips + '\\rgroup': [5,6,0x29,3,0x3B], // extensible ) with sharper tips + '\\arrowvert': [0,2,0x6A,3,0x3C], // arrow without arrowheads + '\\Arrowvert': [0,2,0x6B,3,0x3D], // double arrow without arrowheads +// '\\bracevert': [0,7,0x7C,3,0x3E], // the vertical bar that extends braces + '\\bracevert': [0,2,0x6A,3,0x3E], // we don't load tt, so use | instead + '\\Vert': [0,2,0x6B,3,0x0D], + '\\|': [0,2,0x6B,3,0x0D], + '\\vert': [0,2,0x6A,3,0x0C], + '\\uparrow': [3,2,0x22,3,0x78], + '\\downarrow': [3,2,0x23,3,0x79], + '\\updownarrow': [3,2,0x6C,3,0x3F], + '\\Uparrow': [3,2,0x2A,3,0x7E], + '\\Downarrow': [3,2,0x2B,3,0x7F], + '\\Updownarrow': [3,2,0x6D,3,0x77], + '\\backslash': [0,2,0x6E,3,0x0F], // for double coset G\backslash H + '\\rangle': [5,2,0x69,3,0x0B], + '\\langle': [4,2,0x68,3,0x0A], + '\\rbrace': [5,2,0x67,3,0x09], + '\\lbrace': [4,2,0x66,3,0x08], + '\\}': [5,2,0x67,3,0x09], + '\\{': [4,2,0x66,3,0x08], + '\\rceil': [5,2,0x65,3,0x07], + '\\lceil': [4,2,0x64,3,0x06], + '\\rfloor': [5,2,0x63,3,0x05], + '\\lfloor': [4,2,0x62,3,0x04], + '\\lbrack': [0,0,0x5B,3,0x02], + '\\rbrack': [0,0,0x5D,3,0x03] + }, + + /* + * The basic macros for plain TeX. + * + * When the control sequence on the left is called, the JavaScript + * funtion on the right is called, with the name of the control sequence + * as its first parameter (this way, the same function can be called by + * several different control sequences to do similar actions, and the + * function can still tell which TeX command was issued). If the right + * is an array, the first entry is the routine to call, and the + * remaining entries in the array are parameters to pass to the function + * as the second parameter (they are in an array reference). + * + * Note: TeX macros as defined by the user are discussed below. + */ + macros: { + displaystyle: ['HandleStyle','D'], + textstyle: ['HandleStyle','T'], + scriptstyle: ['HandleStyle','S'], + scriptscriptstyle: ['HandleStyle','SS'], + + rm: ['HandleFont',0], + mit: ['HandleFont',1], + oldstyle: ['HandleFont',1], + cal: ['HandleFont',2], + it: ['HandleFont',4], + bf: ['HandleFont',6], + + font: ['Extension','font'], + + left: 'HandleLeft', + right: 'HandleRight', + + arcsin: ['NamedOp',0], + arccos: ['NamedOp',0], + arctan: ['NamedOp',0], + arg: ['NamedOp',0], + cos: ['NamedOp',0], + cosh: ['NamedOp',0], + cot: ['NamedOp',0], + coth: ['NamedOp',0], + csc: ['NamedOp',0], + deg: ['NamedOp',0], + det: 'NamedOp', + dim: ['NamedOp',0], + exp: ['NamedOp',0], + gcd: 'NamedOp', + hom: ['NamedOp',0], + inf: 'NamedOp', + ker: ['NamedOp',0], + lg: ['NamedOp',0], + lim: 'NamedOp', + liminf: ['NamedOp',null,'liminf'], + limsup: ['NamedOp',null,'limsup'], + ln: ['NamedOp',0], + log: ['NamedOp',0], + max: 'NamedOp', + min: 'NamedOp', + Pr: 'NamedOp', + sec: ['NamedOp',0], + sin: ['NamedOp',0], + sinh: ['NamedOp',0], + sup: 'NamedOp', + tan: ['NamedOp',0], + tanh: ['NamedOp',0], + + vcenter: ['HandleAtom','vcenter'], + overline: ['HandleAtom','overline'], + underline: ['HandleAtom','underline'], + over: 'HandleOver', + overwithdelims: 'HandleOver', + atop: 'HandleOver', + atopwithdelims: 'HandleOver', + above: 'HandleOver', + abovewithdelims: 'HandleOver', + brace: ['HandleOver','\\{','\\}'], + brack: ['HandleOver','[',']'], + choose: ['HandleOver','(',')'], + + overbrace: ['Extension','leaders'], + underbrace: ['Extension','leaders'], + overrightarrow: ['Extension','leaders'], + underrightarrow: ['Extension','leaders'], + overleftarrow: ['Extension','leaders'], + underleftarrow: ['Extension','leaders'], + overleftrightarrow: ['Extension','leaders'], + underleftrightarrow: ['Extension','leaders'], + overset: ['Extension','underset-overset'], + underset: ['Extension','underset-overset'], + + llap: 'HandleLap', + rlap: 'HandleLap', + ulap: 'HandleLap', + dlap: 'HandleLap', + raise: 'RaiseLower', + lower: 'RaiseLower', + moveleft: 'MoveLeftRight', + moveright: 'MoveLeftRight', + + frac: 'Frac', + root: 'Root', + sqrt: 'Sqrt', + + // TeX substitution macros + hbar: ['Macro','\\hbarchar\\kern-.5em h'], + ne: ['Macro','\\not='], + neq: ['Macro','\\not='], + notin: ['Macro','\\mathrel{\\rlap{\\kern2mu/}}\\in'], + cong: ['Macro','\\mathrel{\\lower2mu{\\mathrel{{\\rlap{=}\\raise6mu\\sim}}}}'], + bmod: ['Macro','\\mathbin{\\rm mod}'], + pmod: ['Macro','\\kern 18mu ({\\rm mod}\\,\\,#1)',1], + 'int': ['Macro','\\intop\\nolimits'], + oint: ['Macro','\\ointop\\nolimits'], + doteq: ['Macro','\\buildrel\\textstyle.\\over='], + ldots: ['Macro','\\mathinner{\\ldotp\\ldotp\\ldotp}'], + cdots: ['Macro','\\mathinner{\\cdotp\\cdotp\\cdotp}'], + vdots: ['Macro','\\mathinner{\\rlap{\\raise8pt{.\\rule 0pt 6pt 0pt}}\\rlap{\\raise4pt{.}}.}'], + ddots: ['Macro','\\mathinner{\\kern1mu\\raise7pt{\\rule 0pt 7pt 0pt .}\\kern2mu\\raise4pt{.}\\kern2mu\\raise1pt{.}\\kern1mu}'], + joinrel: ['Macro','\\mathrel{\\kern-4mu}'], + relbar: ['Macro','\\mathrel{\\smash-}'], // \smash, because - has the same height as + + Relbar: ['Macro','\\mathrel='], + bowtie: ['Macro','\\mathrel\\triangleright\\joinrel\\mathrel\\triangleleft'], + models: ['Macro','\\mathrel|\\joinrel='], + mapsto: ['Macro','\\mathrel{\\mapstochar\\rightarrow}'], + rightleftharpoons: ['Macro','\\vcenter{\\mathrel{\\rlap{\\raise3mu{\\rightharpoonup}}}\\leftharpoondown}'], + hookrightarrow: ['Macro','\\lhook\\joinrel\\rightarrow'], + hookleftarrow: ['Macro','\\leftarrow\\joinrel\\rhook'], + Longrightarrow: ['Macro','\\Relbar\\joinrel\\Rightarrow'], + longrightarrow: ['Macro','\\relbar\\joinrel\\rightarrow'], + longleftarrow: ['Macro','\\leftarrow\\joinrel\\relbar'], + Longleftarrow: ['Macro','\\Leftarrow\\joinrel\\Relbar'], + longmapsto: ['Macro','\\mathrel{\\mapstochar\\minuschar\\joinrel\\rightarrow}'], + longleftrightarrow: ['Macro','\\leftarrow\\joinrel\\rightarrow'], + Longleftrightarrow: ['Macro','\\Leftarrow\\joinrel\\Rightarrow'], + iff: ['Macro','\\;\\Longleftrightarrow\\;'], + mathcal: ['Macro','{\\cal #1}',1], + mathrm: ['Macro','{\\rm #1}',1], + mathbf: ['Macro','{\\bf #1}',1], + mathbb: ['Macro','{\\bf #1}',1], + mathit: ['Macro','{\\it #1}',1], + textrm: ['Macro','\\mathord{\\hbox{#1}}',1], + textit: ['Macro','\\mathord{\\class{textit}{\\hbox{#1}}}',1], + textbf: ['Macro','\\mathord{\\class{textbf}{\\hbox{#1}}}',1], + pmb: ['Macro','\\rlap{#1}\\kern1px{#1}',1], + + TeX: ['Macro','T\\kern-.1667em\\lower.5ex{E}\\kern-.125em X'], + + limits: ['Limits',1], + nolimits: ['Limits',0], + + ',': ['Spacer',1/6], + ':': ['Spacer',1/6], // for LaTeX + '>': ['Spacer',2/9], + ';': ['Spacer',5/18], + '!': ['Spacer',-1/6], + enspace: ['Spacer',1/2], + quad: ['Spacer',1], + qquad: ['Spacer',2], + thinspace: ['Spacer',1/6], + negthinspace: ['Spacer',-1/6], + + hskip: 'Hskip', + kern: 'Hskip', + rule: ['Rule','colored'], + space: ['Rule','blank'], + + big: ['MakeBig','ord',0.85], + Big: ['MakeBig','ord',1.15], + bigg: ['MakeBig','ord',1.45], + Bigg: ['MakeBig','ord',1.75], + bigl: ['MakeBig','open',0.85], + Bigl: ['MakeBig','open',1.15], + biggl: ['MakeBig','open',1.45], + Biggl: ['MakeBig','open',1.75], + bigr: ['MakeBig','close',0.85], + Bigr: ['MakeBig','close',1.15], + biggr: ['MakeBig','close',1.45], + Biggr: ['MakeBig','close',1.75], + bigm: ['MakeBig','rel',0.85], + Bigm: ['MakeBig','rel',1.15], + biggm: ['MakeBig','rel',1.45], + Biggm: ['MakeBig','rel',1.75], + + mathord: ['HandleAtom','ord'], + mathop: ['HandleAtom','op'], + mathopen: ['HandleAtom','open'], + mathclose: ['HandleAtom','close'], + mathbin: ['HandleAtom','bin'], + mathrel: ['HandleAtom','rel'], + mathpunct: ['HandleAtom','punct'], + mathinner: ['HandleAtom','inner'], + + mathchoice: ['Extension','mathchoice'], + buildrel: 'BuildRel', + + hbox: 'HBox', + text: 'HBox', + mbox: 'HBox', + fbox: ['Extension','fbox'], + + strut: 'Strut', + mathstrut: ['Macro','\\vphantom{(}'], + phantom: ['Phantom',1,1], + vphantom: ['Phantom',1,0], + hphantom: ['Phantom',0,1], + smash: 'Smash', + + acute: ['MathAccent', [7,0,0x13]], + grave: ['MathAccent', [7,0,0x12]], + ddot: ['MathAccent', [7,0,0x7F]], + tilde: ['MathAccent', [7,0,0x7E]], + bar: ['MathAccent', [7,0,0x16]], + breve: ['MathAccent', [7,0,0x15]], + check: ['MathAccent', [7,0,0x14]], + hat: ['MathAccent', [7,0,0x5E]], + vec: ['MathAccent', [0,1,0x7E]], + dot: ['MathAccent', [7,0,0x5F]], + widetilde: ['MathAccent', [0,3,0x65]], + widehat: ['MathAccent', [0,3,0x62]], + + '_': ['Replace','ord','_','normal',-.4,.1], + ' ': ['Replace','ord',' ','normal'], +// angle: ['Replace','ord','∠','normal'], + angle: ['Macro','\\kern2.5mu\\raise1.54pt{\\rlap{\\scriptstyle \\char{cmsy10}{54}}\\kern1pt\\rule{.45em}{-1.2pt}{1.54pt}\\kern2.5mu}'], + + matrix: 'Matrix', + array: 'Matrix', // ### still need to do alignment options ### + pmatrix: ['Matrix','(',')','c'], + cases: ['Matrix','\\{','.',['l','l'],null,2], + eqalign: ['Matrix',null,null,['r','l'],[5/18],3,'D'], + displaylines: ['Matrix',null,null,['c'],null,3,'D'], + cr: 'HandleRow', + '\\': 'HandleRow', + newline: 'HandleRow', + noalign: 'HandleNoAlign', + eqalignno: ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'], + leqalignno: ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'], + + // LaTeX + begin: 'Begin', + end: 'End', + tiny: ['HandleSize',0], + Tiny: ['HandleSize',1], // non-standard + scriptsize: ['HandleSize',2], + small: ['HandleSize',3], + normalsize: ['HandleSize',4], + large: ['HandleSize',5], + Large: ['HandleSize',6], + LARGE: ['HandleSize',7], + huge: ['HandleSize',8], + Huge: ['HandleSize',9], + dots: ['Macro','\\ldots'], + + newcommand: ['Extension','newcommand'], + newenvironment: ['Extension','newcommand'], + def: ['Extension','newcommand'], + + // Extensions to TeX + color: ['Extension','HTML'], + href: ['Extension','HTML'], + 'class': ['Extension','HTML'], + style: ['Extension','HTML'], + cssId: ['Extension','HTML'], + unicode: ['Extension','HTML'], + bbox: ['Extension','bbox'], + + require: 'Require', + + // debugging and test routines + 'char': 'Char' + }, + + /* + * LaTeX environments + */ + environments: { + array: 'Array', + matrix: ['Array',null,null,'c'], + pmatrix: ['Array','(',')','c'], + bmatrix: ['Array','[',']','c'], + Bmatrix: ['Array','\\{','\\}','c'], + vmatrix: ['Array','\\vert','\\vert','c'], + Vmatrix: ['Array','\\Vert','\\Vert','c'], + cases: ['Array','\\{','.','ll',null,2], + eqnarray: ['Array',null,null,'rcl',[5/18,5/18],3,'D'], + 'eqnarray*': ['Array',null,null,'rcl',[5/18,5/18],3,'D'], + equation: 'Equation', + 'equation*': 'Equation', + + align: ['Extension','AMSmath'], + 'align*': ['Extension','AMSmath'], + aligned: ['Extension','AMSmath'], + multline: ['Extension','AMSmath'], + 'multline*': ['Extension','AMSmath'], + split: ['Extension','AMSmath'], + gather: ['Extension','AMSmath'], + 'gather*': ['Extension','AMSmath'], + gathered: ['Extension','AMSmath'] + }, + + + /***************************************************************************/ + + /* + * Add special characters to list above. (This makes it possible + * to define them in a variable that the user can change.) + */ + AddSpecial: function (obj) { + for (var id in obj) { + jsMath.Parser.prototype.special[jsMath.Parser.prototype[id]] = obj[id]; + } + }, + + /* + * Throw an error + */ + Error: function (s) { + this.i = this.string.length; + if (s.error) {this.error = s.error} else { + if (!this.error) {this.error = s} + } + }, + + /***************************************************************************/ + + /* + * Check if the next character is a space + */ + nextIsSpace: function () { + return this.string.charAt(this.i).match(/[ \n\r\t]/); + }, + + /* + * Trim spaces from a string + */ + trimSpaces: function (text) { + if (typeof(text) != 'string') {return text} + return text.replace(/^\s+|\s+$/g,''); + }, + + /* + * Parse a substring to get its mList, and return it. + * Check that no errors occured + */ + Process: function (arg) { + var data = this.mlist.data; + arg = jsMath.Parse(arg,data.font,data.size,data.style); + if (arg.error) {this.Error(arg); return null} + if (arg.mlist.Length() == 0) {return null} + if (arg.mlist.Length() == 1) { + var atom = arg.mlist.Last(); + if (atom.atom && atom.type == 'ord' && atom.nuc && + !atom.sub && !atom.sup && (atom.nuc.type == 'text' || atom.nuc.type == 'TeX')) + {return atom.nuc} + } + return {type: 'mlist', mlist: arg.mlist}; + }, + + /* + * Get and return a control-sequence name from the TeX string + */ + GetCommand: function () { + var letter = /^([a-z]+|.) ?/i; + var cmd = letter.exec(this.string.slice(this.i)); + if (cmd) {this.i += cmd[1].length; return cmd[1]} + this.i++; return " "; + }, + + /* + * Get and return a TeX argument (either a single character or control sequence, + * or the contents of the next set of braces). + */ + GetArgument: function (name,noneOK) { + while (this.nextIsSpace()) {this.i++} + if (this.i >= this.string.length) {if (!noneOK) this.Error("Missing argument for "+name); return null} + if (this.string.charAt(this.i) == this.close) {if (!noneOK) this.Error("Extra close brace"); return null} + if (this.string.charAt(this.i) == this.cmd) {this.i++; return this.cmd+this.GetCommand()} + if (this.string.charAt(this.i) != this.open) {return this.string.charAt(this.i++)} + var j = ++this.i; var pcount = 1; var c = ''; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == this.cmd) {this.i++} + else if (c == this.open) {pcount++} + else if (c == this.close) { + if (pcount == 0) {this.Error("Extra close brace"); return null} + if (--pcount == 0) {return this.string.slice(j,this.i-1)} + } + } + this.Error("Missing close brace"); + return null; + }, + + /* + * Get an argument and process it into an mList + */ + ProcessArg: function (name) { + var arg = this.GetArgument(name); if (this.error) {return null} + return this.Process(arg); + }, + + /* + * Get and process an argument for a super- or subscript. + * (read extra args for \frac, \sqrt, \mathrm, etc.) + * This handles these macros as special cases, so is really + * rather a hack. A more general method for indicating + * how to handle macros in scripts needs to be developed. + */ + ProcessScriptArg: function (name) { + var arg = this.GetArgument(name); if (this.error) {return null} + if (arg.charAt(0) == this.cmd) { + var csname = arg.substr(1); + if (csname == "frac") { + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } else if (csname == "sqrt") { + arg += '['+this.GetBrackets(csname)+']'; if (this.error) {return null} + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } else if (csname.match(this.scriptargs)) { + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } + } + return this.Process(arg); + }, + + /* + * Get the name of a delimiter (check it in the delimiter list). + */ + GetDelimiter: function (name) { + while (this.nextIsSpace()) {this.i++} + var c = this.string.charAt(this.i); + if (this.i < this.string.length) { + this.i++; + if (c == this.cmd) {c = '\\'+this.GetCommand(name); if (this.error) return null} + if (this.delimiter[c] != null) {return this.delimiter[c]} + } + this.Error("Missing or unrecognized delimiter for "+name); + return null; + }, + + /* + * Get a dimension (including its units). + * Convert the dimen to em's, except for mu's, which must be + * converted when typeset. + */ + GetDimen: function (name,nomu) { + var rest; var advance = 0; + if (this.nextIsSpace()) {this.i++} + if (this.string.charAt(this.i) == '{') { + rest = this.GetArgument(name); + } else { + rest = this.string.slice(this.i); + advance = 1; + } + return this.ParseDimen(rest,name,advance,nomu); + }, + + ParseDimen: function (dimen,name,advance,nomu) { + var match = dimen.match(/^\s*([-+]?(\.\d+|\d+(\.\d*)?))(pt|em|ex|mu|px)/); + if (!match) {this.Error("Missing dimension or its units for "+name); return null} + if (advance) { + this.i += match[0].length; + if (this.nextIsSpace()) {this.i++} + } + var d = match[1]-0; + if (match[4] == 'px') {d /= jsMath.em} + else if (match[4] == 'pt') {d /= 10} + else if (match[4] == 'ex') {d *= jsMath.TeX.x_height} + else if (match[4] == 'mu') {if (nomu) {d = d/18} else {d = [d,'mu']}} + return d; + }, + + /* + * Get the next non-space character + */ + GetNext: function () { + while (this.nextIsSpace()) {this.i++} + return this.string.charAt(this.i); + }, + + /* + * Get an optional LaTeX argument in brackets + */ + GetBrackets: function (name) { + var c = this.GetNext(); if (c != '[') return ''; + var start = ++this.i; var pcount = 0; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == '{') {pcount++} + else if (c == '}') { + if (pcount == 0) + {this.Error("Extra close brace while looking for ']'"); return null} + pcount --; + } else if (c == this.cmd) { + this.i++; + } else if (c == ']') { + if (pcount == 0) {return this.string.slice(start,this.i-1)} + } + } + this.Error("Couldn't find closing ']' for argument to "+this.cmd+name); + return null; + }, + + /* + * Get everything up to the given control sequence name (token) + */ + GetUpto: function (name,token) { + while (this.nextIsSpace()) {this.i++} + var start = this.i; var pcount = 0; + while (this.i < this.string.length) { + var c = this.string.charAt(this.i++); + if (c == '{') {pcount++} + else if (c == '}') { + if (pcount == 0) + {this.Error("Extra close brace while looking for "+this.cmd+token); return null} + pcount --; + } else if (c == this.cmd) { + // really need separate counter for begin/end + // and it should really be a stack (new pcount for each begin) + if (this.string.slice(this.i,this.i+5) == "begin") {pcount++; this.i+=4} + else if (this.string.slice(this.i,this.i+3) == "end") { + if (pcount > 0) {pcount--; this.i += 2} + } + if (pcount == 0) { + if (this.string.slice(this.i,this.i+token.length) == token) { + c = this.string.charAt(this.i+token.length); + if (c.match(/[^a-z]/i) || !token.match(/[a-z]/i)) { + var arg = this.string.slice(start,this.i-1); + this.i += token.length; + return arg; + } + } + } + this.i++; + } + } + this.Error("Couldn't find "+this.cmd+token+" for "+name); + return null; + }, + + /* + * Get a parameter delimited by a control sequence, and + * process it to get its mlist + */ + ProcessUpto: function (name,token) { + var arg = this.GetUpto(name,token); if (this.error) return null; + return this.Process(arg); + }, + + /* + * Get everything up to \end{env} + */ + GetEnd: function (env) { + var body = ''; var name = ''; + while (name != env) { + body += this.GetUpto('begin{'+env+'}','end'); if (this.error) return null; + name = this.GetArgument(this.cmd+'end'); if (this.error) return null; + } + return body; + }, + + + /***************************************************************************/ + + + /* + * Ignore spaces + */ + Space: function () {}, + + /* + * Collect together any primes and convert them to a superscript + */ + Prime: function (c) { + var base = this.mlist.Last(); + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sup) {this.Error("Prime causes double exponent: use braces to clarify"); return} + var sup = ''; + while (c == "'") {sup += this.cmd+'prime'; c = this.GetNext(); if (c == "'") {this.i++}} + base.sup = this.Process(sup); + base.sup.isPrime = 1; + }, + + /* + * Raise or lower its parameter by a given amount + * @@@ Note that this is different from TeX, which requires an \hbox @@@ + * ### make this work with mu's ### + */ + RaiseLower: function (name) { + var h = this.GetDimen(this.cmd+name,1); if (this.error) return; + var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return; + if (name == 'lower') {h = -h} + this.mlist.Add(new jsMath.mItem('raise',{nuc: box, raise: h})); + }, + + /* + * Shift an expression to the right or left + * @@@ Note that this is different from TeX, which requires a \vbox @@@ + * ### make this work with mu's ### + */ + MoveLeftRight: function (name) { + var x = this.GetDimen(this.cmd+name,1); if (this.error) return; + var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return; + if (name == 'moveleft') {x = -x} + this.mlist.Add(jsMath.mItem.Space(x)); + this.mlist.Add(jsMath.mItem.Atom('ord',box)); + this.mlist.Add(jsMath.mItem.Space(-x)); + }, + + /* + * Load an extension if it has not already been loaded + */ + Require: function (name) { + var file = this.GetArgument(this.cmd+name); if (this.error) return; + file = jsMath.Extension.URL(file); + if (jsMath.Setup.loaded[file]) return; + this.Extension(null,[file]); + }, + + /* + * Load an extension file and restart processing the math + */ + Extension: function (name,data) { + jsMath.Translate.restart = 1; + if (name != null) {delete jsMath.Parser.prototype[data[1]||'macros'][name]} + jsMath.Extension.Require(data[0],jsMath.Translate.asynchronous); + throw "restart"; + }, + + /* + * Implements \frac{num}{den} + */ + Frac: function (name) { + var num = this.ProcessArg(this.cmd+name); if (this.error) return; + var den = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Fraction('over',num,den)); + }, + + /* + * Implements \sqrt[n]{...} + */ + Sqrt: function (name) { + var n = this.GetBrackets(this.cmd+name); if (this.error) return; + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + var box = jsMath.mItem.Atom('radical',arg); + if (n != '') {box.root = this.Process(n); if (this.error) return} + this.mlist.Add(box); + }, + + /* + * Implements \root...\of{...} + */ + Root: function (name) { + var n = this.ProcessUpto(this.cmd+name,'of'); if (this.error) return; + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + var box = jsMath.mItem.Atom('radical',arg); + box.root = n; this.mlist.Add(box); + }, + + + /* + * Implements \buildrel...\over{...} + */ + BuildRel: function (name) { + var top = this.ProcessUpto(this.cmd+name,'over'); if (this.error) return; + var bot = this.ProcessArg(this.cmd+name); if (this.error) return; + var op = jsMath.mItem.Atom('op',bot); + op.limits = 1; op.sup = top; + this.mlist.Add(op); + }, + + /* + * Create a delimiter of the type and size specified in the parameters + */ + MakeBig: function (name,data) { + var type = data[0]; var h = data[1] * jsMath.p_height; + var delim = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Atom(type,jsMath.Box.Delimiter(h,delim,'T'))); + }, + + /* + * Insert the specified character in the given font. + * (Try to load the font if it is not already available.) + */ + Char: function (name) { + var font = this.GetArgument(this.cmd+name); if (this.error) return; + var n = this.GetArgument(this.cmd+name); if (this.error) return; + if (!jsMath.TeX[font]) { + jsMath.TeX[font] = []; + this.Extension(null,[jsMath.Font.URL(font)]); + } else { + this.mlist.Add(jsMath.mItem.Typeset(jsMath.Box.TeX(n-0,font,this.mlist.data.style,this.mlist.data.size))); + } + }, + + /* + * Create an array or matrix. + */ + Matrix: function (name,delim) { + var data = this.mlist.data; + var arg = this.GetArgument(this.cmd+name); if (this.error) return; + var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,delim[5] || 'T'); + parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = []; + parse.Parse(); if (parse.error) {this.Error(parse); return} + parse.HandleRow(name,1); // be sure the last row is recorded + var box = jsMath.Box.Layout(data.size,parse.table,delim[2]||null,delim[3]||null,parse.rspacing,delim[4]||null); + // Add parentheses, if needed + if (delim[0] && delim[1]) { + var left = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T'); + var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T'); + box = jsMath.Box.SetList([left,box,right],data.style,data.size); + } + this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box)); + }, + + /* + * When we see an '&', try to add a matrix entry to the row data. + * (Use all the data in the current mList, and then clear it) + */ + HandleEntry: function (name) { + if (!this.matrix) + {this.Error(name+" can only appear in a matrix or array"); return} + if (this.mlist.data.openI != null) { + var open = this.mlist.Get(this.mlist.data.openI); + if (open.left) {this.Error("Missing "+this.cmd+"right")} + else {this.Error("Missing close brace")} + } + if (this.mlist.data.overI != null) {this.mlist.Over()} + var data = this.mlist.data; + this.mlist.Atomize(data.style,data.size); + var box = this.mlist.Typeset(data.style,data.size); + box.entry = data.entry; delete data.entry; if (!box.entry) {box.entry = {}}; + this.row[this.row.length] = box; + this.mlist = new jsMath.mList(null,null,data.size,data.style); + }, + + /* + * When we see a \cr or \\, try to add a row to the table + */ + HandleRow: function (name,last) { + var dimen; + if (!this.matrix) {this.Error(this.cmd+name+" can only appear in a matrix or array"); return} + if (name == "\\") { + dimen = this.GetBrackets(this.cmd+name); if (this.error) return; + if (dimen) {dimen = this.ParseDimen(dimen,this.cmd+name,0,1)} + } + this.HandleEntry(name); + if (!last || this.row.length > 1 || this.row[0].format != 'null') + {this.table[this.table.length] = this.row} + if (dimen) {this.rspacing[this.table.length] = dimen} + this.row = []; + }, + + /* + * Look for \vskip or \vspace in \noalign parameters + */ + HandleNoAlign: function (name) { + var arg = this.GetArgument(this.cmd+name); if (this.error) return; + var skip = arg.replace(/^.*(vskip|vspace)([^a-z])/i,'$2'); + if (skip.length == arg.length) return; + var d = this.ParseDimen(skip,this.cmd+RegExp.$1,0,1); if (this.error) return; + this.rspacing[this.table.length] = (this.rspacing[this.table.length] || 0) + d; + }, + + /* + * LaTeX array environment + */ + Array: function (name,delim) { + var columns = delim[2]; var cspacing = delim[3]; + if (!columns) { + columns = this.GetArgument(this.cmd+'begin{'+name+'}'); + if (this.error) return; + } + columns = columns.replace(/[^clr]/g,''); + columns = columns.split(''); + var data = this.mlist.data; var style = delim[5] || 'T'; + var arg = this.GetEnd(name); if (this.error) return; + var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,style); + parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = []; + parse.Parse(); if (parse.error) {this.Error(parse); return} + parse.HandleRow(name,1); // be sure the last row is recorded + var box = jsMath.Box.Layout(data.size,parse.table,columns,cspacing,parse.rspacing,delim[4],delim[6],delim[7]); + // Add parentheses, if needed + if (delim[0] && delim[1]) { + var left = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T'); + var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T'); + box = jsMath.Box.SetList([left,box,right],data.style,data.size); + } + this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box)); + }, + + /* + * LaTeX \begin{env} + */ + Begin: function (name) { + var env = this.GetArgument(this.cmd+name); if (this.error) return; + if (env.match(/[^a-z*]/i)) {this.Error('Invalid environment name "'+env+'"'); return} + if (!this.environments[env]) {this.Error('Unknown environment "'+env+'"'); return} + var cmd = this.environments[env]; + if (typeof(cmd) == "string") {cmd = [cmd]} + this[cmd[0]](env,cmd.slice(1)); + }, + + /* + * LaTeX \end{env} + */ + End: function (name) { + var env = this.GetArgument(this.cmd+name); if (this.error) return; + this.Error(this.cmd+name+'{'+env+'} without matching '+this.cmd+'begin'); + }, + + /* + * LaTeX equation environment (just remove the environment) + */ + Equation: function (name) { + var arg = this.GetEnd(name); if (this.error) return; + this.string = arg+this.string.slice(this.i); this.i = 0; + }, + + /* + * Add a fixed amount of horizontal space + */ + Spacer: function (name,w) { + this.mlist.Add(jsMath.mItem.Space(w-0)); + }, + + /* + * Add horizontal space given by the argument + */ + Hskip: function (name) { + var w = this.GetDimen(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Space(w)); + }, + + /* + * Typeset the argument as plain text rather than math. + */ + HBox: function (name) { + var text = this.GetArgument(this.cmd+name); if (this.error) return; + var box = jsMath.Box.InternalMath(text,this.mlist.data.size); + this.mlist.Add(jsMath.mItem.Typeset(box)); + }, + + /* + * Insert a rule of a particular width, height and depth + * This replaces \hrule and \vrule + * @@@ not a standard TeX command, and all three parameters must be given @@@ + */ + Rule: function (name,style) { + var w = this.GetDimen(this.cmd+name,1); if (this.error) return; + var h = this.GetDimen(this.cmd+name,1); if (this.error) return; + var d = this.GetDimen(this.cmd+name,1); if (this.error) return; + h += d; var html; + if (h != 0) {h = Math.max(1.05/jsMath.em,h)} + if (h == 0 || w == 0 || style == "blank") + {html = jsMath.HTML.Blank(w,h)} else {html = jsMath.HTML.Rule(w,h)} + if (d) { + html = '' + + html + ''; + } + this.mlist.Add(jsMath.mItem.Typeset(new jsMath.Box('html',html,w,h-d,d))); + }, + + /* + * Inserts an empty box of a specific height and depth + */ + Strut: function () { + var size = this.mlist.data.size; + var box = jsMath.Box.Text('','normal','T',size).Styled(); + box.bh = box.bd = 0; box.h = .8; box.d = .3; box.w = box.Mw = 0; + this.mlist.Add(jsMath.mItem.Typeset(box)); + }, + + /* + * Handles \phantom, \vphantom and \hphantom + */ + Phantom: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(new jsMath.mItem('phantom',{phantom: arg, v: data[0], h: data[1]})); + }, + + /* + * Implements \smash + */ + Smash: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(new jsMath.mItem('smash',{smash: arg})); + }, + + /* + * Puts an accent on the following argument + */ + MathAccent: function (name,accent) { + var c = this.ProcessArg(this.cmd+name); if (this.error) return; + var atom = jsMath.mItem.Atom('accent',c); atom.accent = accent[0]; + this.mlist.Add(atom); + }, + + /* + * Handles functions and operators like sin, cos, sum, etc. + */ + NamedOp: function (name,data) { + var a = (name.match(/[^acegm-su-z]/)) ? 1: 0; + var d = (name.match(/[gjpqy]/)) ? .2: 0; + if (data[1]) {name = data[1]} + var box = jsMath.mItem.TextAtom('op',name,jsMath.TeX.fam[0],a,d); + if (data[0] != null) {box.limits = data[0]} + this.mlist.Add(box); + }, + + /* + * Implements \limits + */ + Limits: function (name,data) { + var atom = this.mlist.Last(); + if (!atom || atom.type != 'op') + {this.Error(this.cmd+name+" is allowed only on operators"); return} + atom.limits = data[0]; + }, + + /* + * Implements macros like those created by \def. The named control + * sequence is replaced by the string given as the first data value. + * If there is a second data value, this specifies how many arguments + * the macro uses, and in this case, those arguments are substituted + * for #1, #2, etc. within the replacement string. + * + * See the jsMath.Macro() command below for more details. + * The "newcommand" extension implements \newcommand and \def + * and are loaded automatically if needed. + */ + Macro: function (name,data) { + var text = data[0]; + if (data[1]) { + var args = []; + for (var i = 0; i < data[1]; i++) + {args[args.length] = this.GetArgument(this.cmd+name); if (this.error) return} + text = this.SubstituteArgs(args,text); + } + this.string = this.AddArgs(text,this.string.slice(this.i)); + this.i = 0; + }, + + /* + * Replace macro paramters with their values + */ + SubstituteArgs: function (args,string) { + var text = ''; var newstring = ''; var c; var i = 0; + while (i < string.length) { + c = string.charAt(i++); + if (c == this.cmd) {text += c + string.charAt(i++)} + else if (c == '#') { + c = string.charAt(i++); + if (c == "#") {text += c} else { + if (!c.match(/[1-9]/) || c > args.length) + {this.Error("Illegal macro parameter reference"); return null} + newstring = this.AddArgs(this.AddArgs(newstring,text),args[c-1]); + text = ''; + } + } else {text += c} + } + return this.AddArgs(newstring,text); + }, + + /* + * Make sure that macros are followed by a space if their names + * could accidentally be continued into the following text. + */ + AddArgs: function (s1,s2) { + if (s2.match(/^[a-z]/i) && s1.match(/(^|[^\\])(\\\\)*\\[a-z]+$/i)) {s1 += ' '} + return s1+s2; + }, + + /* + * Replace the control sequence with the given text + */ + Replace: function (name,data) { + this.mlist.Add(jsMath.mItem.TextAtom(data[0],data[1],data[2],data[3])); + }, + + /* + * Error for # (must use \#) + */ + Hash: function (name) { + this.Error("You can't use 'macro parameter character #' in math mode"); + }, + + /* + * Insert space for ~ + */ + Tilde: function (name) { + this.mlist.Add(jsMath.mItem.TextAtom('ord',' ','normal')); + }, + + /* + * Implements \llap, \rlap, etc. + */ + HandleLap: function (name) { + var box = this.ProcessArg(); if (this.error) return; + box = this.mlist.Add(new jsMath.mItem('lap',{nuc: box, lap: name})); + }, + + /* + * Adds the argument as a specific type of atom (for commands like + * \overline, etc.) + */ + HandleAtom: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Atom(data[0],arg)); + }, + + + /* + * Process the character associated with a specific \mathcharcode + */ + HandleMathCode: function (name,code) { + this.HandleTeXchar(code[0],code[1],code[2]); + }, + + /* + * Add a specific character from a TeX font (use the current + * font if the type is 7 (variable) or the font is not specified) + * Load the font if it is not already loaded. + */ + HandleTeXchar: function (type,font,code) { + if (type == 7 && this.mlist.data.font != null) {font = this.mlist.data.font} + font = jsMath.TeX.fam[font]; + if (!jsMath.TeX[font]) { + jsMath.TeX[font] = []; + this.Extension(null,[jsMath.Font.URL(font)]); + } else { + this.mlist.Add(jsMath.mItem.TeXAtom(jsMath.TeX.atom[type],code,font)); + } + }, + + /* + * Add a TeX variable character or number + */ + HandleVariable: function (c) {this.HandleTeXchar(7,1,c.charCodeAt(0))}, + HandleNumber: function (c) {this.HandleTeXchar(7,0,c.charCodeAt(0))}, + + /* + * For unmapped characters, just add them in as normal + * (non-TeX) characters + */ + HandleOther: function (c) { + this.mlist.Add(jsMath.mItem.TextAtom('ord',c,'normal')); + }, + + /* + * Ignore comments in TeX data + * ### Some browsers remove the newlines, so this might cause + * extra stuff to be ignored; look into this ### + */ + HandleComment: function () { + var c; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == "\r" || c == "\n") return; + } + }, + + /* + * Add a style change (e.g., \displaystyle, etc) + */ + HandleStyle: function (name,style) { + this.mlist.data.style = style[0]; + this.mlist.Add(new jsMath.mItem('style',{style: style[0]})); + }, + + /* + * Implements \small, \large, etc. + */ + HandleSize: function (name,size) { + this.mlist.data.size = size[0]; + this.mlist.Add(new jsMath.mItem('size',{size: size[0]})); + }, + + /* + * Set the current font (e.g., \rm, etc) + */ + HandleFont: function (name,font) { + this.mlist.data.font = font[0]; + }, + + /* + * Look for and process a control sequence + */ + HandleCS: function () { + var cmd = this.GetCommand(); if (this.error) return; + if (this.macros[cmd]) { + var macro = this.macros[cmd]; + if (typeof(macro) == "string") {macro = [macro]} + this[macro[0]](cmd,macro.slice(1)); return; + } + if (this.mathchardef[cmd]) { + this.HandleMathCode(cmd,this.mathchardef[cmd]); + return; + } + if (this.delimiter[this.cmd+cmd]) { + this.HandleMathCode(cmd,this.delimiter[this.cmd+cmd].slice(0,3)) + return; + } + this.Error("Unknown control sequence '"+this.cmd+cmd+"'"); + }, + + /* + * Process open and close braces + */ + HandleOpen: function () {this.mlist.Open()}, + HandleClose: function () { + if (this.mlist.data.openI == null) {this.Error("Extra close brace"); return} + var open = this.mlist.Get(this.mlist.data.openI); + if (!open || open.left == null) {this.mlist.Close()} + else {this.Error("Extra close brace or missing "+this.cmd+"right"); return} + }, + + /* + * Implements \left + */ + HandleLeft: function (name) { + var left = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.Open(left); + }, + + /* + * Implements \right + */ + HandleRight: function (name) { + var right = this.GetDelimiter(this.cmd+name); if (this.error) return; + var open = this.mlist.Get(this.mlist.data.openI); + if (open && open.left != null) {this.mlist.Close(right)} + else {this.Error("Extra open brace or missing "+this.cmd+"left");} + }, + + /* + * Implements generalized fractions (\over, \above, etc.) + */ + HandleOver: function (name,data) { + if (this.mlist.data.overI != null) + {this.Error('Ambiguous use of '+this.cmd+name); return} + this.mlist.data.overI = this.mlist.Length(); + this.mlist.data.overF = {name: name}; + if (data.length > 0) { + this.mlist.data.overF.left = this.delimiter[data[0]]; + this.mlist.data.overF.right = this.delimiter[data[1]]; + } else if (name.match(/withdelims$/)) { + this.mlist.data.overF.left = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.data.overF.right = this.GetDelimiter(this.cmd+name); if (this.error) return; + } else { + this.mlist.data.overF.left = null; + this.mlist.data.overF.right = null; + } + if (name.match(/^above/)) { + this.mlist.data.overF.thickness = this.GetDimen(this.cmd+name,1); + if (this.error) return; + } else { + this.mlist.data.overF.thickness = null; + } + }, + + /* + * Add a superscript to the preceeding atom + */ + HandleSuperscript: function () { + var base = this.mlist.Last(); + if (this.mlist.data.overI == this.mlist.Length()) {base = null} + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sup) { + if (base.sup.isPrime) {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + else {this.Error("Double exponent: use braces to clarify"); return} + } + base.sup = this.ProcessScriptArg('superscript'); if (this.error) return; + }, + + /* + * Add a subscript to the preceeding atom + */ + HandleSubscript: function () { + var base = this.mlist.Last(); + if (this.mlist.data.overI == this.mlist.Length()) {base = null} + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sub) {this.Error("Double subscripts: use braces to clarify"); return} + base.sub = this.ProcessScriptArg('subscript'); if (this.error) return; + }, + + /* + * Parse a TeX math string, handling macros, etc. + */ + Parse: function () { + var c; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (this.mathchar[c]) {this.HandleMathCode(c,this.mathchar[c])} + else if (this.special[c]) {this[this.special[c]](c)} + else if (this.letter.test(c)) {this.HandleVariable(c)} + else if (this.number.test(c)) {this.HandleNumber(c)} + else {this.HandleOther(c)} + } + if (this.mlist.data.openI != null) { + var open = this.mlist.Get(this.mlist.data.openI); + if (open.left) {this.Error("Missing "+this.cmd+"right")} + else {this.Error("Missing close brace")} + } + if (this.mlist.data.overI != null) {this.mlist.Over()} + }, + + /* + * Perform the processing of Appendix G + */ + Atomize: function () { + var data = this.mlist.init; + if (!this.error) this.mlist.Atomize(data.style,data.size) + }, + + /* + * Produce the final HTML. + * + * We have to wrap the HTML it appropriate tags to hide its + * actual dimensions when these don't match the TeX dimensions of the + * results. We also include an image to force the results to take up + * the right amount of space. The results may need to be vertically + * adjusted to make the baseline appear in the correct place. + */ + Typeset: function () { + var data = this.mlist.init; + var box = this.typeset = this.mlist.Typeset(data.style,data.size); + if (this.error) {return ''+this.error+''} + if (box.format == 'null') {return ''}; + + box.Styled().Remeasured(); var isSmall = 0; var isBig = 0; + if (box.bh > box.h && box.bh > jsMath.h+.001) {isSmall = 1} + if (box.bd > box.d && box.bd > jsMath.d+.001) {isSmall = 1} + if (box.h > jsMath.h || box.d > jsMath.d) {isBig = 1} + + var html = box.html; + if (isSmall) {// hide the extra size + if (jsMath.Browser.allowAbsolute) { + var y = (box.bh > jsMath.h+.001 ? jsMath.h - box.bh : 0); + html = jsMath.HTML.Absolute(html,box.w,jsMath.h,0,y); + } else if (jsMath.Browser.valignBug) { + // remove line height + html = '' + + html + ''; + } else if (!jsMath.Browser.operaLineHeightBug) { + // remove line height and try to hide the depth + var dy = jsMath.HTML.Em(Math.max(0,box.bd-jsMath.hd)/3); + html = '' + html + ''; + } + isBig = 1; + } + if (isBig) { + // add height and depth to the line + // (force a little extra to separate lines if needed) + html += jsMath.HTML.Blank(0,box.h+.05,box.d+.05); + } + return ''+html+''; + } + +}); + +/* + * Make these characters special (and call the given routines) + */ +jsMath.Parser.prototype.AddSpecial({ + cmd: 'HandleCS', + open: 'HandleOpen', + close: 'HandleClose' +}); + + +/* + * The web-page author can call jsMath.Macro to create additional + * TeX macros for use within his or her mathematics. See the + * author's documentation for more details. + */ + +jsMath.Add(jsMath,{ + Macro: function (name) { + var macro = jsMath.Parser.prototype.macros; + macro[name] = ['Macro']; + for (var i = 1; i < arguments.length; i++) + {macro[name][macro[name].length] = arguments[i]} + } +}); + +/* + * Use these commands to create macros that load + * JavaScript files and reprocess the mathematics when + * the file is loaded. This lets you to have macros or + * LaTeX environments that autoload their own definitions + * only when they are needed, saving initial download time + * on pages where they are not used. See the author's + * documentation for more details. + * + */ + +jsMath.Extension = { + + safeRequire: 1, // disables access to files outside of jsMath/extensions + + Macro: function (name,file) { + var macro = jsMath.Parser.prototype.macros; + if (file == null) {file = name} + macro[name] = ['Extension',file]; + }, + + LaTeX: function (env,file) { + var latex = jsMath.Parser.prototype.environments; + latex[env] = ['Extension',file,'environments']; + }, + + Font: function (name,font) { + if (font == null) {font = name + "10"} + var macro = jsMath.Parser.prototype.macros; + macro[name] = ['Extension',jsMath.Font.URL(font)]; + }, + + MathChar: function (font,defs) { + var fam = jsMath.TeX.famName[font]; + if (fam == null) { + fam = jsMath.TeX.fam.length; + jsMath.TeX.fam[fam] = font; + jsMath.TeX.famName[font] = fam; + } + var mathchardef = jsMath.Parser.prototype.mathchardef; + for (var c in defs) {mathchardef[c] = [defs[c][0],fam,defs[c][1]]} + }, + + Require: function (file,show) { + if (this.safeRequire && (file.match(/\.\.\/|[^-a-z0-9.\/:_+=%~]/i) || + (file.match(/:/) && file.substr(0,jsMath.root.length) != jsMath.root))) { + jsMath.Setup.loaded[file] = 1; + return; + } + jsMath.Setup.Script(this.URL(file),show); + }, + + URL: function (file) { + file = file.replace(/^\s+|\s+$/g,''); + if (!file.match(/^([a-z]+:|\/|fonts|extensions\/)/i)) {file = 'extensions/'+file} + if (!file.match(/\.js$/)) {file += '.js'} + return file; + } +} + + +/***************************************************************************/ + +/* + * These routines look through the web page for math elements to process. + * There are two main entry points you can call: + * + * + * or + * + * + * The first will process the page asynchronously (so the user can start + * reading the top of the file while jsMath is still processing the bottom) + * while the second does not update until all the mathematics is typeset. + */ + +jsMath.Add(jsMath,{ + /* + * Call this at the bottom of your HTML page to have the + * mathematics typeset asynchronously. This lets the user + * start reading the mathematics while the rest of the page + * is being processed. + */ + Process: function (obj) { + jsMath.Setup.Body(); + jsMath.Script.Push(jsMath.Translate,'Asynchronous',obj); + }, + + /* + * Call this at the bottom of your HTML page to have the + * mathematics typeset before the page is displayed. + * This can take a long time, so the user could cancel the + * page before it is complete; use it with caution, and only + * when there is a relatively small amount of math on the page. + */ + ProcessBeforeShowing: function (obj) { + jsMath.Setup.Body(); + var method = (jsMath.Controls.cookie.asynch ? "Asynchronous": "Synchronous"); + jsMath.Script.Push(jsMath.Translate,method,obj); + }, + + /* + * Process the contents of a single element. It must be of + * class "math". + */ + ProcessElement: function (obj) { + jsMath.Setup.Body(); + jsMath.Script.Push(jsMath.Translate,'ProcessOne',obj); + } + +}); + +jsMath.Translate = { + + element: [], // the list of math elements on the page + cancel: 0, // set to 1 to cancel asynchronous processing + + /* + * Parse a TeX string in Text or Display mode and return + * the HTML for it (taking it from the cache, if available) + */ + Parse: function (style,s,noCache) { + var cache = jsMath.Global.cache[style]; + if (!cache[jsMath.em]) {cache[jsMath.em] = {}} + var HTML = cache[jsMath.em][s]; + if (!HTML || noCache) { + var parse = jsMath.Parse(s,null,null,style); + parse.Atomize(); HTML = parse.Typeset(); + if (!noCache) {cache[jsMath.em][s] = HTML} + } + return HTML; + }, + + TextMode: function (s,noCache) {this.Parse('T',s,noCache)}, + DisplayMode: function (s,noCache) {this.Parse('D',s,noCache)}, + + /* + * Return the text of a given DOM element + */ + GetElementText: function (element) { + if (element.childNodes.length == 1 && element.childNodes[0].nodeName === "#comment") { + var result = element.childNodes[0].nodeValue.match(/^\[CDATA\[(.*)\]\]$/); + if (result != null) {return result[1]}; + } + var text = this.recursiveElementText(element); + element.alt = text; + if (text.search('&') >= 0) { + text = text.replace(/</g,'<'); + text = text.replace(/>/g,'>'); + text = text.replace(/"/g,'"'); + text = text.replace(/&/g,'&'); + } + return text; + }, + recursiveElementText: function (element) { + if (element.nodeValue != null) { + if (element.nodeName !== "#comment") {return element.nodeValue} + return element.nodeValue.replace(/^\[CDATA\[((.|\n)*)\]\]$/,"$1"); + } + if (element.childNodes.length === 0) {return " "} + var text = ''; + for (var i = 0; i < element.childNodes.length; i++) + {text += this.recursiveElementText(element.childNodes[i])} + return text; + }, + + /* + * Move hidden to the location of the math element to be + * processed and reinitialize sizes for that location. + */ + ResetHidden: function (element) { + element.innerHTML = + '' + + jsMath.Browser.operaHiddenFix; // needed by Opera in tables + element.className = ''; + jsMath.hidden = element.firstChild; + if (!jsMath.BBoxFor("x").w) {jsMath.hidden = jsMath.hiddenTop} + jsMath.ReInit(); + }, + + + /* + * Typeset the contents of an element in \textstyle or \displaystyle + */ + ConvertMath: function (style,element,noCache) { + var text = this.GetElementText(element); + this.ResetHidden(element); + if (text.match(/^\s*\\nocache([^a-zA-Z])/)) + {noCache = true; text = text.replace(/\s*\\nocache/,'')} + text = this.Parse(style,text,noCache); + element.className = 'typeset'; + element.innerHTML = text; + }, + + /* + * Process a math element + */ + ProcessElement: function (element) { + this.restart = 0; + if (!element.className.match(/(^| )math( |$)/)) return; // don't reprocess elements + var noCache = (element.className.toLowerCase().match(/(^| )nocache( |$)/) != null); + try { + var style = (element.tagName.toLowerCase() == 'div' ? 'D' : 'T'); + this.ConvertMath(style,element,noCache); + element.onclick = jsMath.Click.CheckClick; + element.ondblclick = jsMath.Click.CheckDblClick; + } catch (err) { + if (element.alt) { + var tex = element.alt; + tex = tex.replace(/&/g,'&') + .replace(//g,'>'); + element.innerHTML = tex; + element.className = 'math'; + if (noCache) {element.className += ' nocache'} + } + jsMath.hidden = jsMath.hiddenTop; + } + }, + + /* + * Asynchronously process all the math elements starting with + * the k-th one. Do them in blocks of 8 (more efficient than one + * at a time, but still allows screen updates periodically). + */ + ProcessElements: function (k) { + jsMath.Script.blocking = 1; + for (var i = 0; i < jsMath.Browser.processAtOnce; i++, k++) { + if (k >= this.element.length || this.cancel) { + this.ProcessComplete(); + if (this.cancel) { + jsMath.Message.Set("Process Math: Canceled"); + jsMath.Message.Clear() + } + jsMath.Script.blocking = 0; + jsMath.Script.Process(); + return; + } else { + var savedQueue = jsMath.Script.SaveQueue(); + this.ProcessElement(this.element[k]); + if (this.restart) { + jsMath.Script.Push(this,'ProcessElements',k); + jsMath.Script.RestoreQueue(savedQueue); + jsMath.Script.blocking = 0; + setTimeout('jsMath.Script.Process()',jsMath.Browser.delay); + return; + } + } + } + jsMath.Script.RestoreQueue(savedQueue); + var p = Math.floor(100 * k / this.element.length); + jsMath.Message.Set('Processing Math: '+p+'%'); + setTimeout('jsMath.Translate.ProcessElements('+k+')',jsMath.Browser.delay); + }, + + /* + * Start the asynchronous processing of mathematics + */ + Asynchronous: function (obj) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = this.GetMathElements(obj); + jsMath.Script.blocking = 1; + this.cancel = 0; this.asynchronous = 1; + jsMath.Message.Set('Processing Math: 0%',1); + setTimeout('jsMath.Translate.ProcessElements(0)',jsMath.Browser.delay); + }, + + /* + * Do synchronous processing of mathematics + */ + Synchronous: function (obj,i) { + if (i == null) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = this.GetMathElements(obj); + i = 0; + } + this.asynchronous = 0; + while (i < this.element.length) { + this.ProcessElement(this.element[i]); + if (this.restart) { + jsMath.Synchronize('jsMath.Translate.Synchronous(null,'+i+')'); + jsMath.Script.Process(); + return; + } + i++; + } + this.ProcessComplete(1); + }, + + /* + * Synchronously process the contents of a single element + */ + ProcessOne: function (obj) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = [obj]; + this.Synchronous(null,0); + }, + + /* + * Look up all the math elements on the page and + * put them in a list sorted from top to bottom of the page + */ + GetMathElements: function (obj) { + var element = []; var k; + if (!obj) {obj = jsMath.document} + if (typeof(obj) == 'string') {obj = jsMath.document.getElementById(obj)} + if (!obj.getElementsByTagName) return null; + var math = obj.getElementsByTagName('div'); + for (k = 0; k < math.length; k++) { + if (math[k].className && math[k].className.match(/(^| )math( |$)/)) { + if (jsMath.Browser.renameOK && obj.getElementsByName) + {math[k].setAttribute('name','_jsMath_')} + else {element[element.length] = math[k]} + } + } + math = obj.getElementsByTagName('span'); + for (k = 0; k < math.length; k++) { + if (math[k].className && math[k].className.match(/(^| )math( |$)/)) { + if (jsMath.Browser.renameOK && obj.getElementsByName) + {math[k].setAttribute('name','_jsMath_')} + else {element[element.length] = math[k]} + } + } + // this gets the SPAN and DIV elements interleaved in order + if (jsMath.Browser.renameOK && obj.getElementsByName) { + element = obj.getElementsByName('_jsMath_'); + } else if (jsMath.hidden.sourceIndex) { + element.sort(function (a,b) {return a.sourceIndex - b.sourceIndex}); + } + return element; + }, + + /* + * Remove the window message about processing math + * and clean up any marked or
tags + */ + ProcessComplete: function (noMessage) { + if (jsMath.Browser.renameOK) { + var element = jsMath.document.getElementsByName('_jsMath_'); + for (var i = element.length-1; i >= 0; i--) { + element[i].removeAttribute('name'); + } + } + jsMath.hidden = jsMath.hiddenTop; + this.element = []; this.restart = null; + if (!noMessage) { + jsMath.Message.Set('Processing Math: Done'); + jsMath.Message.Clear(); + } + jsMath.Message.UnBlank(); + if (jsMath.Browser.safariImgBug && + (jsMath.Controls.cookie.font == 'symbol' || + jsMath.Controls.cookie.font == 'image')) { + // + // For Safari, the images don't always finish + // updating, so nudge the window to cause a + // redraw. (Hack!) + // + if (this.timeout) {clearTimeout(this.timeout)} + this.timeout = setTimeout("jsMath.window.resizeBy(-1,0); " + + "jsMath.window.resizeBy(1,0); " + + "jsMath.Translate.timeout = null",2000); + } + }, + + /* + * Cancel procesing elements + */ + Cancel: function () { + jsMath.Translate.cancel = 1; + if (jsMath.Script.cancelTimer) {jsMath.Script.cancelLoad()} + } + +}; + +jsMath.Add(jsMath,{ + // + // Synchronize these with the loading of the tex2math plugin. + // + ConvertTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX',element)}, + ConvertTeX2: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX2',element)}, + ConvertLaTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertLaTeX',element)}, + ConvertCustom: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertCustom',element)}, + CustomSearch: function (om,cm,od,cd) {jsMath.Script.Push(null,function () {jsMath.tex2math.CustomSearch(om,cm,od,cd)})}, + tex2math: { + ConvertTeX: function () {}, + ConvertTeX2: function () {}, + ConvertLaTeX: function () {}, + ConvertCustom: function () {}, + CustomSearch: function () {} + } +}); +jsMath.Synchronize = jsMath.Script.Synchronize; + +/***************************************************************************/ + + +/* + * Initialize things + */ +try { + if (window.parent != window && window.jsMathAutoload) { + window.parent.jsMath = jsMath; + jsMath.document = window.parent.document; + jsMath.window = window.parent; + } +} catch (err) {} + + +jsMath.Global.Register(); +jsMath.Loaded(); +jsMath.Controls.GetCookie(); +jsMath.Setup.Source(); +jsMath.Global.Init(); +jsMath.Script.Init(); +jsMath.Setup.Fonts(); +if (jsMath.document.body) {jsMath.Setup.Body()} +jsMath.Setup.User("onload"); + +}}