Subversion Repositories wimsdev

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2. ASCIIMathML.js
  3. ==============
  4. This file contains JavaScript functions to convert ASCII math notation
  5. to Presentation MathML. The conversion is done while the (X)HTML page
  6. loads, and should work with Firefox/Mozilla/Netscape 7+ and Internet
  7. Explorer 6+MathPlayer (http://www.dessci.com/en/products/mathplayer/).
  8. Just add the next line to your (X)HTML page with this file in the same folder:
  9. <script type="text/javascript" src="ASCIIMathML.js"></script>
  10. This is a convenient and inexpensive solution for authoring MathML.
  11.  
  12. Version 1.4.7 Dec 15, 2005, (c) Peter Jipsen http://www.chapman.edu/~jipsen
  13. Latest version at http://www.chapman.edu/~jipsen/mathml/ASCIIMathML.js
  14. For changes see http://www.chapman.edu/~jipsen/mathml/asciimathchanges.txt
  15. If you use it on a webpage, please send the URL to jipsen@chapman.edu
  16.  
  17. This program is free software; you can redistribute it and/or modify
  18. it under the terms of the GNU General Public License as published by
  19. the Free Software Foundation; either version 2 of the License, or (at
  20. your option) any later version.
  21.  
  22. This program is distributed in the hope that it will be useful,
  23. but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  25. General Public License (at http://www.gnu.org/copyleft/gpl.html)
  26. for more details.
  27. */
  28.  
  29. var checkForMathML = true;   // check if browser can display MathML
  30. var notifyIfNoMathML = true; // display note if no MathML capability
  31. var alertIfNoMathML = false;  // show alert box if no MathML capability
  32. var mathcolor = "black";       // change it to "" (to inherit) or any other color
  33. var mathfontfamily = "serif"; // change to "" to inherit (works in IE)
  34.                               // or another family (e.g. "arial")
  35. var displaystyle = true;      // puts limits above and below large operators
  36. var showasciiformulaonhover = true; // helps students learn ASCIIMath
  37. var decimalsign = ".";        // change to "," if you like, beware of `(1,2)`!
  38. var AMdelimiter1 = "`", AMescape1 = "\\\\`"; // can use other characters
  39. var AMdelimiter2 = "$", AMescape2 = "\\\\\\$", AMdelimiter2regexp = "\\$";
  40. var doubleblankmathdelimiter = false; // if true,  x+1  is equal to `x+1`
  41.                                       // for IE this works only in <!--   -->
  42. //var separatetokens;// has been removed (email me if this is a problem)
  43. var isIE = document.createElementNS==null;
  44.  
  45. if (document.getElementById==null)
  46.   alert("This webpage requires a recent browser such as\
  47. \nMozilla/Netscape 7+ or Internet Explorer 6+MathPlayer")
  48.  
  49. // all further global variables start with "AM"
  50.  
  51. function AMcreateElementXHTML(t) {
  52.   if (isIE) return document.createElement(t);
  53.   else return document.createElementNS("http://www.w3.org/1999/xhtml",t);
  54. }
  55.  
  56. function AMnoMathMLNote() {
  57.   var nd = AMcreateElementXHTML("h3");
  58.   nd.setAttribute("align","center")
  59.   nd.appendChild(AMcreateElementXHTML("p"));
  60.   nd.appendChild(document.createTextNode("To view the "));
  61.   var an = AMcreateElementXHTML("a");
  62.   an.appendChild(document.createTextNode("ASCIIMathML"));
  63.   an.setAttribute("href","http://www.chapman.edu/~jipsen/asciimath.html");
  64.   nd.appendChild(an);
  65.   nd.appendChild(document.createTextNode(" notation use Internet Explorer 6+"));  
  66.   an = AMcreateElementXHTML("a");
  67.   an.appendChild(document.createTextNode("MathPlayer"));
  68.   an.setAttribute("href","http://www.dessci.com/en/products/mathplayer/download.htm");
  69.   nd.appendChild(an);
  70.   nd.appendChild(document.createTextNode(" or Netscape/Mozilla/Firefox"));
  71.   nd.appendChild(AMcreateElementXHTML("p"));
  72.   return nd;
  73. }
  74.  
  75. function AMisMathMLavailable() {
  76.   if (navigator.appName.slice(0,8)=="Netscape")
  77.     if (navigator.appVersion.slice(0,1)>="5") return null;
  78.     else return AMnoMathMLNote();
  79.   else if (navigator.appName.slice(0,9)=="Microsoft")
  80.     try {
  81.         var ActiveX = new ActiveXObject("MathPlayer.Factory.1");
  82.         return null;
  83.     } catch (e) {
  84.         return AMnoMathMLNote();
  85.     }
  86.   else return AMnoMathMLNote();
  87. }
  88.  
  89. // character lists for Mozilla/Netscape fonts
  90. var AMcal = [0xEF35,0x212C,0xEF36,0xEF37,0x2130,0x2131,0xEF38,0x210B,0x2110,0xEF39,0xEF3A,0x2112,0x2133,0xEF3B,0xEF3C,0xEF3D,0xEF3E,0x211B,0xEF3F,0xEF40,0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xEF46];
  91. var AMfrk = [0xEF5D,0xEF5E,0x212D,0xEF5F,0xEF60,0xEF61,0xEF62,0x210C,0x2111,0xEF63,0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0x211C,0xEF6B,0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0x2128];
  92. var AMbbb = [0xEF8C,0xEF8D,0x2102,0xEF8E,0xEF8F,0xEF90,0xEF91,0x210D,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0x2115,0xEF97,0x2119,0x211A,0x211D,0xEF98,0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0x2124];
  93.  
  94. var CONST = 0, UNARY = 1, BINARY = 2, INFIX = 3, LEFTBRACKET = 4,
  95.     RIGHTBRACKET = 5, SPACE = 6, UNDEROVER = 7, DEFINITION = 8,
  96.     LEFTRIGHT = 9, TEXT = 10; // token types
  97.  
  98. var AMsqrt = {input:"sqrt", tag:"msqrt", output:"sqrt", tex:null, ttype:UNARY},
  99.   AMroot  = {input:"root", tag:"mroot", output:"root", tex:null, ttype:BINARY},
  100.   AMfrac  = {input:"frac", tag:"mfrac", output:"/",    tex:null, ttype:BINARY},
  101.   AMdiv   = {input:"/",    tag:"mfrac", output:"/",    tex:null, ttype:INFIX},
  102.   AMover  = {input:"stackrel", tag:"mover", output:"stackrel", tex:null, ttype:BINARY},
  103.   AMsub   = {input:"_",    tag:"msub",  output:"_",    tex:null, ttype:INFIX},
  104.   AMsup   = {input:"^",    tag:"msup",  output:"^",    tex:null, ttype:INFIX},
  105.   AMtext  = {input:"text", tag:"mtext", output:"text", tex:null, ttype:TEXT},
  106.   AMmbox  = {input:"mbox", tag:"mtext", output:"mbox", tex:null, ttype:TEXT},
  107.   AMquote = {input:"\"",   tag:"mtext", output:"mbox", tex:null, ttype:TEXT};
  108.  
  109. var AMsymbols = [
  110. //some greek symbols
  111. {input:"alpha",  tag:"mi", output:"\u03B1", tex:null, ttype:CONST},
  112. {input:"beta",   tag:"mi", output:"\u03B2", tex:null, ttype:CONST},
  113. {input:"chi",    tag:"mi", output:"\u03C7", tex:null, ttype:CONST},
  114. {input:"delta",  tag:"mi", output:"\u03B4", tex:null, ttype:CONST},
  115. {input:"Delta",  tag:"mo", output:"\u0394", tex:null, ttype:CONST},
  116. {input:"epsi",   tag:"mi", output:"\u03B5", tex:"epsilon", ttype:CONST},
  117. {input:"varepsilon", tag:"mi", output:"\u025B", tex:null, ttype:CONST},
  118. {input:"eta",    tag:"mi", output:"\u03B7", tex:null, ttype:CONST},
  119. {input:"gamma",  tag:"mi", output:"\u03B3", tex:null, ttype:CONST},
  120. {input:"Gamma",  tag:"mo", output:"\u0393", tex:null, ttype:CONST},
  121. {input:"iota",   tag:"mi", output:"\u03B9", tex:null, ttype:CONST},
  122. {input:"kappa",  tag:"mi", output:"\u03BA", tex:null, ttype:CONST},
  123. {input:"lambda", tag:"mi", output:"\u03BB", tex:null, ttype:CONST},
  124. {input:"Lambda", tag:"mo", output:"\u039B", tex:null, ttype:CONST},
  125. {input:"mu",     tag:"mi", output:"\u03BC", tex:null, ttype:CONST},
  126. {input:"nu",     tag:"mi", output:"\u03BD", tex:null, ttype:CONST},
  127. {input:"omega",  tag:"mi", output:"\u03C9", tex:null, ttype:CONST},
  128. {input:"Omega",  tag:"mo", output:"\u03A9", tex:null, ttype:CONST},
  129. {input:"phi",    tag:"mi", output:"\u03C6", tex:null, ttype:CONST},
  130. {input:"varphi", tag:"mi", output:"\u03D5", tex:null, ttype:CONST},
  131. {input:"Phi",    tag:"mo", output:"\u03A6", tex:null, ttype:CONST},
  132. {input:"pi",     tag:"mi", output:"\u03C0", tex:null, ttype:CONST},
  133. {input:"Pi",     tag:"mo", output:"\u03A0", tex:null, ttype:CONST},
  134. {input:"psi",    tag:"mi", output:"\u03C8", tex:null, ttype:CONST},
  135. {input:"Psi",    tag:"mi", output:"\u03A8", tex:null, ttype:CONST},
  136. {input:"rho",    tag:"mi", output:"\u03C1", tex:null, ttype:CONST},
  137. {input:"sigma",  tag:"mi", output:"\u03C3", tex:null, ttype:CONST},
  138. {input:"Sigma",  tag:"mo", output:"\u03A3", tex:null, ttype:CONST},
  139. {input:"tau",    tag:"mi", output:"\u03C4", tex:null, ttype:CONST},
  140. {input:"theta",  tag:"mi", output:"\u03B8", tex:null, ttype:CONST},
  141. {input:"vartheta", tag:"mi", output:"\u03D1", tex:null, ttype:CONST},
  142. {input:"Theta",  tag:"mo", output:"\u0398", tex:null, ttype:CONST},
  143. {input:"upsilon", tag:"mi", output:"\u03C5", tex:null, ttype:CONST},
  144. {input:"xi",     tag:"mi", output:"\u03BE", tex:null, ttype:CONST},
  145. {input:"Xi",     tag:"mo", output:"\u039E", tex:null, ttype:CONST},
  146. {input:"zeta",   tag:"mi", output:"\u03B6", tex:null, ttype:CONST},
  147.  
  148. //binary operation symbols
  149. {input:"*",  tag:"mo", output:"\u22C5", tex:"cdot", ttype:CONST},
  150. {input:"**", tag:"mo", output:"\u22C6", tex:"star", ttype:CONST},
  151. {input:"//", tag:"mo", output:"/",      tex:null, ttype:CONST},
  152. {input:"\\\\", tag:"mo", output:"\\",   tex:"backslash", ttype:CONST},
  153. {input:"setminus", tag:"mo", output:"\\", tex:null, ttype:CONST},
  154. {input:"xx", tag:"mo", output:"\u00D7", tex:"times", ttype:CONST},
  155. {input:"-:", tag:"mo", output:"\u00F7", tex:"divide", ttype:CONST},
  156. {input:"@",  tag:"mo", output:"\u2218", tex:"circ", ttype:CONST},
  157. {input:"o+", tag:"mo", output:"\u2295", tex:"oplus", ttype:CONST},
  158. {input:"ox", tag:"mo", output:"\u2297", tex:"otimes", ttype:CONST},
  159. {input:"o.", tag:"mo", output:"\u2299", tex:"odot", ttype:CONST},
  160. {input:"sum", tag:"mo", output:"\u2211", tex:null, ttype:UNDEROVER},
  161. {input:"prod", tag:"mo", output:"\u220F", tex:null, ttype:UNDEROVER},
  162. {input:"^^",  tag:"mo", output:"\u2227", tex:"wedge", ttype:CONST},
  163. {input:"^^^", tag:"mo", output:"\u22C0", tex:"bigwedge", ttype:UNDEROVER},
  164. {input:"vv",  tag:"mo", output:"\u2228", tex:"vee", ttype:CONST},
  165. {input:"vvv", tag:"mo", output:"\u22C1", tex:"bigvee", ttype:UNDEROVER},
  166. {input:"nn",  tag:"mo", output:"\u2229", tex:"cap", ttype:CONST},
  167. {input:"nnn", tag:"mo", output:"\u22C2", tex:"bigcap", ttype:UNDEROVER},
  168. {input:"uu",  tag:"mo", output:"\u222A", tex:"cup", ttype:CONST},
  169. {input:"uuu", tag:"mo", output:"\u22C3", tex:"bigcup", ttype:UNDEROVER},
  170.  
  171. //binary relation symbols
  172. {input:"!=",  tag:"mo", output:"\u2260", tex:"ne", ttype:CONST},
  173. {input:":=",  tag:"mo", output:":=",     tex:null, ttype:CONST},
  174. {input:"lt",  tag:"mo", output:"<",      tex:null, ttype:CONST},
  175. {input:"<=",  tag:"mo", output:"\u2264", tex:"le", ttype:CONST},
  176. {input:"lt=", tag:"mo", output:"\u2264", tex:"leq", ttype:CONST},
  177. {input:">=",  tag:"mo", output:"\u2265", tex:"ge", ttype:CONST},
  178. {input:"geq", tag:"mo", output:"\u2265", tex:null, ttype:CONST},
  179. {input:"-<",  tag:"mo", output:"\u227A", tex:"prec", ttype:CONST},
  180. {input:"-lt", tag:"mo", output:"\u227A", tex:null, ttype:CONST},
  181. {input:">-",  tag:"mo", output:"\u227B", tex:"succ", ttype:CONST},
  182. {input:"-<=", tag:"mo", output:"\u2AAF", tex:"preceq", ttype:CONST},
  183. {input:">-=", tag:"mo", output:"\u2AB0", tex:"succeq", ttype:CONST},
  184. {input:"in",  tag:"mo", output:"\u2208", tex:null, ttype:CONST},
  185. {input:"!in", tag:"mo", output:"\u2209", tex:"notin", ttype:CONST},
  186. {input:"sub", tag:"mo", output:"\u2282", tex:"subset", ttype:CONST},
  187. {input:"sup", tag:"mo", output:"\u2283", tex:"supset", ttype:CONST},
  188. {input:"sube", tag:"mo", output:"\u2286", tex:"subseteq", ttype:CONST},
  189. {input:"supe", tag:"mo", output:"\u2287", tex:"supseteq", ttype:CONST},
  190. {input:"-=",  tag:"mo", output:"\u2261", tex:"equiv", ttype:CONST},
  191. {input:"~=",  tag:"mo", output:"\u2245", tex:"cong", ttype:CONST},
  192. {input:"~~",  tag:"mo", output:"\u2248", tex:"approx", ttype:CONST},
  193. {input:"prop", tag:"mo", output:"\u221D", tex:"propto", ttype:CONST},
  194.  
  195. //logical symbols
  196. {input:"and", tag:"mtext", output:"and", tex:null, ttype:SPACE},
  197. {input:"or",  tag:"mtext", output:"or",  tex:null, ttype:SPACE},
  198. {input:"not", tag:"mo", output:"\u00AC", tex:"neg", ttype:CONST},
  199. {input:"=>",  tag:"mo", output:"\u21D2", tex:"implies", ttype:CONST},
  200. {input:"if",  tag:"mo", output:"if",     tex:null, ttype:SPACE},
  201. {input:"<=>", tag:"mo", output:"\u21D4", tex:"iff", ttype:CONST},
  202. {input:"AA",  tag:"mo", output:"\u2200", tex:"forall", ttype:CONST},
  203. {input:"EE",  tag:"mo", output:"\u2203", tex:"exists", ttype:CONST},
  204. {input:"_|_", tag:"mo", output:"\u22A5", tex:"bot", ttype:CONST},
  205. {input:"TT",  tag:"mo", output:"\u22A4", tex:"top", ttype:CONST},
  206. {input:"|--",  tag:"mo", output:"\u22A2", tex:"vdash", ttype:CONST},
  207. {input:"|==",  tag:"mo", output:"\u22A8", tex:"models", ttype:CONST},
  208.  
  209. //grouping brackets
  210. {input:"(", tag:"mo", output:"(", tex:null, ttype:LEFTBRACKET},
  211. {input:")", tag:"mo", output:")", tex:null, ttype:RIGHTBRACKET},
  212. {input:"[", tag:"mo", output:"[", tex:null, ttype:LEFTBRACKET},
  213. {input:"]", tag:"mo", output:"]", tex:null, ttype:RIGHTBRACKET},
  214. {input:"{", tag:"mo", output:"{", tex:null, ttype:LEFTBRACKET},
  215. {input:"}", tag:"mo", output:"}", tex:null, ttype:RIGHTBRACKET},
  216. {input:"|", tag:"mo", output:"|", tex:null, ttype:LEFTRIGHT},
  217. //{input:"||", tag:"mo", output:"||", tex:null, ttype:LEFTRIGHT},
  218. {input:"(:", tag:"mo", output:"\u2329", tex:"langle", ttype:LEFTBRACKET},
  219. {input:":)", tag:"mo", output:"\u232A", tex:"rangle", ttype:RIGHTBRACKET},
  220. {input:"<<", tag:"mo", output:"\u2329", tex:null, ttype:LEFTBRACKET},
  221. {input:">>", tag:"mo", output:"\u232A", tex:null, ttype:RIGHTBRACKET},
  222. {input:"{:", tag:"mo", output:"{:", tex:null, ttype:LEFTBRACKET, invisible:true},
  223. {input:":}", tag:"mo", output:":}", tex:null, ttype:RIGHTBRACKET, invisible:true},
  224.  
  225. //miscellaneous symbols
  226. {input:"int",  tag:"mo", output:"\u222B", tex:null, ttype:CONST},
  227. {input:"dx",   tag:"mi", output:"{:d x:}", tex:null, ttype:DEFINITION},
  228. {input:"dy",   tag:"mi", output:"{:d y:}", tex:null, ttype:DEFINITION},
  229. {input:"dz",   tag:"mi", output:"{:d z:}", tex:null, ttype:DEFINITION},
  230. {input:"dt",   tag:"mi", output:"{:d t:}", tex:null, ttype:DEFINITION},
  231. {input:"oint", tag:"mo", output:"\u222E", tex:null, ttype:CONST},
  232. {input:"del",  tag:"mo", output:"\u2202", tex:"partial", ttype:CONST},
  233. {input:"grad", tag:"mo", output:"\u2207", tex:"nabla", ttype:CONST},
  234. {input:"+-",   tag:"mo", output:"\u00B1", tex:"pm", ttype:CONST},
  235. {input:"O/",   tag:"mo", output:"\u2205", tex:"emptyset", ttype:CONST},
  236. {input:"oo",   tag:"mo", output:"\u221E", tex:"infty", ttype:CONST},
  237. {input:"aleph", tag:"mo", output:"\u2135", tex:null, ttype:CONST},
  238. {input:"...",  tag:"mo", output:"...",    tex:"ldots", ttype:CONST},
  239. {input:":.",  tag:"mo", output:"\u2234",  tex:"therefore", ttype:CONST},
  240. {input:"/_",  tag:"mo", output:"\u2220",  tex:"angle", ttype:CONST},
  241. {input:"\\ ",  tag:"mo", output:"\u00A0", tex:null, ttype:CONST},
  242. {input:"quad", tag:"mo", output:"\u00A0\u00A0", tex:null, ttype:CONST},
  243. {input:"qquad", tag:"mo", output:"\u00A0\u00A0\u00A0\u00A0", tex:null, ttype:CONST},
  244. {input:"cdots", tag:"mo", output:"\u22EF", tex:null, ttype:CONST},
  245. {input:"vdots", tag:"mo", output:"\u22EE", tex:null, ttype:CONST},
  246. {input:"ddots", tag:"mo", output:"\u22F1", tex:null, ttype:CONST},
  247. {input:"diamond", tag:"mo", output:"\u22C4", tex:null, ttype:CONST},
  248. {input:"square", tag:"mo", output:"\u25A1", tex:null, ttype:CONST},
  249. {input:"|__", tag:"mo", output:"\u230A",  tex:"lfloor", ttype:CONST},
  250. {input:"__|", tag:"mo", output:"\u230B",  tex:"rfloor", ttype:CONST},
  251. {input:"|~", tag:"mo", output:"\u2308",  tex:"lceiling", ttype:CONST},
  252. {input:"~|", tag:"mo", output:"\u2309",  tex:"rceiling", ttype:CONST},
  253. {input:"CC",  tag:"mo", output:"\u2102", tex:null, ttype:CONST},
  254. {input:"NN",  tag:"mo", output:"\u2115", tex:null, ttype:CONST},
  255. {input:"QQ",  tag:"mo", output:"\u211A", tex:null, ttype:CONST},
  256. {input:"RR",  tag:"mo", output:"\u211D", tex:null, ttype:CONST},
  257. {input:"ZZ",  tag:"mo", output:"\u2124", tex:null, ttype:CONST},
  258. {input:"f",   tag:"mi", output:"f",      tex:null, ttype:UNARY, func:true},
  259. {input:"g",   tag:"mi", output:"g",      tex:null, ttype:UNARY, func:true},
  260.  
  261. //standard functions
  262. {input:"lim",  tag:"mo", output:"lim", tex:null, ttype:UNDEROVER},
  263. {input:"Lim",  tag:"mo", output:"Lim", tex:null, ttype:UNDEROVER},
  264. {input:"sin",  tag:"mo", output:"sin", tex:null, ttype:UNARY, func:true},
  265. {input:"cos",  tag:"mo", output:"cos", tex:null, ttype:UNARY, func:true},
  266. {input:"tan",  tag:"mo", output:"tan", tex:null, ttype:UNARY, func:true},
  267. {input:"sinh", tag:"mo", output:"sinh", tex:null, ttype:UNARY, func:true},
  268. {input:"cosh", tag:"mo", output:"cosh", tex:null, ttype:UNARY, func:true},
  269. {input:"tanh", tag:"mo", output:"tanh", tex:null, ttype:UNARY, func:true},
  270. {input:"cot",  tag:"mo", output:"cot", tex:null, ttype:UNARY, func:true},
  271. {input:"sec",  tag:"mo", output:"sec", tex:null, ttype:UNARY, func:true},
  272. {input:"csc",  tag:"mo", output:"csc", tex:null, ttype:UNARY, func:true},
  273. {input:"log",  tag:"mo", output:"log", tex:null, ttype:UNARY, func:true},
  274. {input:"ln",   tag:"mo", output:"ln",  tex:null, ttype:UNARY, func:true},
  275. {input:"det",  tag:"mo", output:"det", tex:null, ttype:UNARY, func:true},
  276. {input:"dim",  tag:"mo", output:"dim", tex:null, ttype:CONST},
  277. {input:"mod",  tag:"mo", output:"mod", tex:null, ttype:CONST},
  278. {input:"gcd",  tag:"mo", output:"gcd", tex:null, ttype:UNARY, func:true},
  279. {input:"lcm",  tag:"mo", output:"lcm", tex:null, ttype:UNARY, func:true},
  280. {input:"lub",  tag:"mo", output:"lub", tex:null, ttype:CONST},
  281. {input:"glb",  tag:"mo", output:"glb", tex:null, ttype:CONST},
  282. {input:"min",  tag:"mo", output:"min", tex:null, ttype:UNDEROVER},
  283. {input:"max",  tag:"mo", output:"max", tex:null, ttype:UNDEROVER},
  284.  
  285. //arrows
  286. {input:"uarr", tag:"mo", output:"\u2191", tex:"uparrow", ttype:CONST},
  287. {input:"darr", tag:"mo", output:"\u2193", tex:"downarrow", ttype:CONST},
  288. {input:"rarr", tag:"mo", output:"\u2192", tex:"rightarrow", ttype:CONST},
  289. {input:"->",   tag:"mo", output:"\u2192", tex:"to", ttype:CONST},
  290. {input:"|->",  tag:"mo", output:"\u21A6", tex:"mapsto", ttype:CONST},
  291. {input:"larr", tag:"mo", output:"\u2190", tex:"leftarrow", ttype:CONST},
  292. {input:"harr", tag:"mo", output:"\u2194", tex:"leftrightarrow", ttype:CONST},
  293. {input:"rArr", tag:"mo", output:"\u21D2", tex:"Rightarrow", ttype:CONST},
  294. {input:"lArr", tag:"mo", output:"\u21D0", tex:"Leftarrow", ttype:CONST},
  295. {input:"hArr", tag:"mo", output:"\u21D4", tex:"Leftrightarrow", ttype:CONST},
  296.  
  297. //commands with argument
  298. AMsqrt, AMroot, AMfrac, AMdiv, AMover, AMsub, AMsup,
  299. {input:"hat", tag:"mover", output:"\u005E", tex:null, ttype:UNARY, acc:true},
  300. {input:"bar", tag:"mover", output:"\u00AF", tex:"overline", ttype:UNARY, acc:true},
  301. {input:"vec", tag:"mover", output:"\u2192", tex:null, ttype:UNARY, acc:true},
  302. {input:"dot", tag:"mover", output:".",      tex:null, ttype:UNARY, acc:true},
  303. {input:"ddot", tag:"mover", output:"..",    tex:null, ttype:UNARY, acc:true},
  304. {input:"ul", tag:"munder", output:"\u0332", tex:"underline", ttype:UNARY, acc:true},
  305. AMtext, AMmbox, AMquote,
  306. {input:"bb", tag:"mstyle", atname:"fontweight", atval:"bold", output:"bb", tex:null, ttype:UNARY},
  307. {input:"mathbf", tag:"mstyle", atname:"fontweight", atval:"bold", output:"mathbf", tex:null, ttype:UNARY},
  308. {input:"sf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"sf", tex:null, ttype:UNARY},
  309. {input:"mathsf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"mathsf", tex:null, ttype:UNARY},
  310. {input:"bbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"bbb", tex:null, ttype:UNARY, codes:AMbbb},
  311. {input:"mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"mathbb", tex:null, ttype:UNARY, codes:AMbbb},
  312. {input:"cc",  tag:"mstyle", atname:"mathvariant", atval:"script", output:"cc", tex:null, ttype:UNARY, codes:AMcal},
  313. {input:"mathcal", tag:"mstyle", atname:"mathvariant", atval:"script", output:"mathcal", tex:null, ttype:UNARY, codes:AMcal},
  314. {input:"tt",  tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"tt", tex:null, ttype:UNARY},
  315. {input:"mathtt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"mathtt", tex:null, ttype:UNARY},
  316. {input:"fr",  tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"fr", tex:null, ttype:UNARY, codes:AMfrk},
  317. {input:"mathfrak",  tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"mathfrak", tex:null, ttype:UNARY, codes:AMfrk}
  318. ];
  319.  
  320. function compareNames(s1,s2) {
  321.   if (s1.input > s2.input) return 1
  322.   else return -1;
  323. }
  324.  
  325. var AMnames = []; //list of input symbols
  326.  
  327. function AMinitSymbols() {
  328.   var texsymbols = [], i;
  329.   for (i=0; i<AMsymbols.length; i++)
  330.     if (AMsymbols[i].tex)
  331.       texsymbols[texsymbols.length] = {input:AMsymbols[i].tex,
  332.         tag:AMsymbols[i].tag, output:AMsymbols[i].output, ttype:AMsymbols[i].ttype};
  333.   AMsymbols = AMsymbols.concat(texsymbols);
  334.   AMsymbols.sort(compareNames);
  335.   for (i=0; i<AMsymbols.length; i++) AMnames[i] = AMsymbols[i].input;
  336. }
  337.  
  338. var AMmathml = "http://www.w3.org/1998/Math/MathML";
  339.  
  340. function AMcreateElementMathML(t) {
  341.   if (isIE) return document.createElement("m:"+t);
  342.   else return document.createElementNS(AMmathml,t);
  343. }
  344.  
  345. function AMcreateMmlNode(t,frag) {
  346. //  var node = AMcreateElementMathML(name);
  347.   if (isIE) var node = document.createElement("m:"+t);
  348.   else var node = document.createElementNS(AMmathml,t);
  349.   node.appendChild(frag);
  350.   return node;
  351. }
  352.  
  353. function newcommand(oldstr,newstr) {
  354.   AMsymbols = AMsymbols.concat([{input:oldstr, tag:"mo", output:newstr,
  355.                                  tex:null, ttype:DEFINITION}]);
  356. }
  357.  
  358. function AMremoveCharsAndBlanks(str,n) {
  359. //remove n characters and any following blanks
  360.   var st;
  361.   if (str.charAt(n)=="\\" && str.charAt(n+1)!="\\" && str.charAt(n+1)!=" ")
  362.     st = str.slice(n+1);
  363.   else st = str.slice(n);
  364.   for (var i=0; i<st.length && st.charCodeAt(i)<=32; i=i+1);
  365.   return st.slice(i);
  366. }
  367.  
  368. function AMposition(arr, str, n) {
  369. // return position >=n where str appears or would be inserted
  370. // assumes arr is sorted
  371.   if (n==0) {
  372.     var h,m;
  373.     n = -1;
  374.     h = arr.length;
  375.     while (n+1<h) {
  376.       m = (n+h) >> 1;
  377.       if (arr[m]<str) n = m; else h = m;
  378.     }
  379.     return h;
  380.   } else
  381.     for (var i=n; i<arr.length && arr[i]<str; i++);
  382.   return i; // i=arr.length || arr[i]>=str
  383. }
  384.  
  385. function AMgetSymbol(str) {
  386. //return maximal initial substring of str that appears in names
  387. //return null if there is none
  388.   var k = 0; //new pos
  389.   var j = 0; //old pos
  390.   var mk; //match pos
  391.   var st;
  392.   var tagst;
  393.   var match = "";
  394.   var more = true;
  395.   for (var i=1; i<=str.length && more; i++) {
  396.     st = str.slice(0,i); //initial substring of length i
  397.     j = k;
  398.     k = AMposition(AMnames, st, j);
  399.     if (k<AMnames.length && str.slice(0,AMnames[k].length)==AMnames[k]){
  400.       match = AMnames[k];
  401.       mk = k;
  402.       i = match.length;
  403.     }
  404.     more = k<AMnames.length && str.slice(0,AMnames[k].length)>=AMnames[k];
  405.   }
  406.   AMpreviousSymbol=AMcurrentSymbol;
  407.   if (match!=""){
  408.     AMcurrentSymbol=AMsymbols[mk].ttype;
  409.     return AMsymbols[mk];
  410.   }
  411. // if str[0] is a digit or - return maxsubstring of digits.digits
  412.   AMcurrentSymbol=CONST;
  413.   k = 1;
  414.   st = str.slice(0,1);
  415.   var integ = true;
  416.   while ("0"<=st && st<="9" && k<=str.length) {
  417.     st = str.slice(k,k+1);
  418.     k++;
  419.   }
  420.   if (st == decimalsign) {
  421.     st = str.slice(k,k+1);
  422.     if ("0"<=st && st<="9") {
  423.       integ = false;
  424.       k++;
  425.       while ("0"<=st && st<="9" && k<=str.length) {
  426.         st = str.slice(k,k+1);
  427.         k++;
  428.       }
  429.     }
  430.   }
  431.   if ((integ && k>1) || k>2) {
  432.     st = str.slice(0,k-1);
  433.     tagst = "mn";
  434.   } else {
  435.     k = 2;
  436.     st = str.slice(0,1); //take 1 character
  437.     tagst = (("A">st || st>"Z") && ("a">st || st>"z")?"mo":"mi");
  438.   }
  439.   if (st=="-" && AMpreviousSymbol==INFIX) {
  440.     AMcurrentSymbol = INFIX;  //trick "/" into recognizing "-" on second parse
  441.     return {input:st, tag:tagst, output:st, ttype:UNARY, func:true};
  442.   }
  443.   return {input:st, tag:tagst, output:st, ttype:CONST};
  444. }
  445.  
  446. function AMremoveBrackets(node) {
  447.   var st;
  448.   if (node.nodeName=="mrow") {
  449.     st = node.firstChild.firstChild.nodeValue;
  450.     if (st=="(" || st=="[" || st=="{") node.removeChild(node.firstChild);
  451.   }
  452.   if (node.nodeName=="mrow") {
  453.     st = node.lastChild.firstChild.nodeValue;
  454.     if (st==")" || st=="]" || st=="}") node.removeChild(node.lastChild);
  455.   }
  456. }
  457.  
  458. /*Parsing ASCII math expressions with the following grammar
  459. v ::= [A-Za-z] | greek letters | numbers | other constant symbols
  460. u ::= sqrt | text | bb | other unary symbols for font commands
  461. b ::= frac | root | stackrel         binary symbols
  462. l ::= ( | [ | { | (: | {:            left brackets
  463. r ::= ) | ] | } | :) | :}            right brackets
  464. S ::= v | lEr | uS | bSS             Simple expression
  465. I ::= S_S | S^S | S_S^S | S          Intermediate expression
  466. E ::= IE | I/I                       Expression
  467. Each terminal symbol is translated into a corresponding mathml node.*/
  468.  
  469. var AMnestingDepth,AMpreviousSymbol,AMcurrentSymbol;
  470.  
  471. function AMparseSexpr(str) { //parses str and returns [node,tailstr]
  472.   var symbol, node, result, i, st,// rightvert = false,
  473.     newFrag = document.createDocumentFragment();
  474.   str = AMremoveCharsAndBlanks(str,0);
  475.   symbol = AMgetSymbol(str);             //either a token or a bracket or empty
  476.   if (symbol == null || symbol.ttype == RIGHTBRACKET && AMnestingDepth > 0) {
  477.     return [null,str];
  478.   }
  479.   if (symbol.ttype == DEFINITION) {
  480.     str = symbol.output+AMremoveCharsAndBlanks(str,symbol.input.length);
  481.     symbol = AMgetSymbol(str);
  482.   }
  483.   switch (symbol.ttype) {
  484.   case UNDEROVER:
  485.   case CONST:
  486.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  487.     return [AMcreateMmlNode(symbol.tag,        //its a constant
  488.                              document.createTextNode(symbol.output)),str];
  489.   case LEFTBRACKET:   //read (expr+)
  490.     AMnestingDepth++;
  491.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  492.     result = AMparseExpr(str,true);
  493.     AMnestingDepth--;
  494.     if (typeof symbol.invisible == "boolean" && symbol.invisible)
  495.       node = AMcreateMmlNode("mrow",result[0]);
  496.     else {
  497.       node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
  498.       node = AMcreateMmlNode("mrow",node);
  499.       node.appendChild(result[0]);
  500.     }
  501.     return [node,result[1]];
  502.   case TEXT:
  503.       if (symbol!=AMquote) str = AMremoveCharsAndBlanks(str,symbol.input.length);
  504.       if (str.charAt(0)=="{") i=str.indexOf("}");
  505.       else if (str.charAt(0)=="(") i=str.indexOf(")");
  506.       else if (str.charAt(0)=="[") i=str.indexOf("]");
  507.       else if (symbol==AMquote) i=str.slice(1).indexOf("\"")+1;
  508.       else i = 0;
  509.       if (i==-1) i = str.length;
  510.       st = str.slice(1,i);
  511.       if (st.charAt(0) == " ") {
  512.         node = AMcreateElementMathML("mspace");
  513.         node.setAttribute("width","1ex");
  514.         newFrag.appendChild(node);
  515.       }
  516.       newFrag.appendChild(
  517.         AMcreateMmlNode(symbol.tag,document.createTextNode(st)));
  518.       if (st.charAt(st.length-1) == " ") {
  519.         node = AMcreateElementMathML("mspace");
  520.         node.setAttribute("width","1ex");
  521.         newFrag.appendChild(node);
  522.       }
  523.       str = AMremoveCharsAndBlanks(str,i+1);
  524.       return [AMcreateMmlNode("mrow",newFrag),str];
  525.   case UNARY:
  526.       str = AMremoveCharsAndBlanks(str,symbol.input.length);
  527.       result = AMparseSexpr(str);
  528.       if (result[0]==null) return [AMcreateMmlNode(symbol.tag,
  529.                              document.createTextNode(symbol.output)),str];
  530.       if (typeof symbol.func == "boolean" && symbol.func) { // functions hack
  531.         st = str.charAt(0);
  532.         if (st=="^" || st=="_" || st=="/" || st=="|" || st==",") {
  533.           return [AMcreateMmlNode(symbol.tag,
  534.                     document.createTextNode(symbol.output)),str];
  535.         } else {
  536.           node = AMcreateMmlNode("mrow",
  537.            AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)));
  538.           node.appendChild(result[0]);
  539.           return [node,result[1]];
  540.         }
  541.       }
  542.       AMremoveBrackets(result[0]);
  543.       if (symbol.input == "sqrt") {           // sqrt
  544.         return [AMcreateMmlNode(symbol.tag,result[0]),result[1]];
  545.       } else if (typeof symbol.acc == "boolean" && symbol.acc) {   // accent
  546.         node = AMcreateMmlNode(symbol.tag,result[0]);
  547.         node.appendChild(AMcreateMmlNode("mo",document.createTextNode(symbol.output)));
  548.         return [node,result[1]];
  549.       } else {                        // font change command
  550.         if (!isIE && typeof symbol.codes != "undefined") {
  551.           for (i=0; i<result[0].childNodes.length; i++)
  552.             if (result[0].childNodes[i].nodeName=="mi" || result[0].nodeName=="mi") {
  553.               st = (result[0].nodeName=="mi"?result[0].firstChild.nodeValue:
  554.                               result[0].childNodes[i].firstChild.nodeValue);
  555.               var newst = [];
  556.               for (var j=0; j<st.length; j++)
  557.                 if (st.charCodeAt(j)>64 && st.charCodeAt(j)<91) newst = newst +
  558.                   String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]);
  559.                 else newst = newst + st.charAt(j);
  560.               if (result[0].nodeName=="mi")
  561.                 result[0]=AMcreateElementMathML("mo").
  562.                           appendChild(document.createTextNode(newst));
  563.               else result[0].replaceChild(AMcreateElementMathML("mo").
  564.           appendChild(document.createTextNode(newst)),result[0].childNodes[i]);
  565.             }
  566.         }
  567.         node = AMcreateMmlNode(symbol.tag,result[0]);
  568.         node.setAttribute(symbol.atname,symbol.atval);
  569.         return [node,result[1]];
  570.       }
  571.   case BINARY:
  572.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  573.     result = AMparseSexpr(str);
  574.     if (result[0]==null) return [AMcreateMmlNode("mo",
  575.                            document.createTextNode(symbol.input)),str];
  576.     AMremoveBrackets(result[0]);
  577.     var result2 = AMparseSexpr(result[1]);
  578.     if (result2[0]==null) return [AMcreateMmlNode("mo",
  579.                            document.createTextNode(symbol.input)),str];
  580.     AMremoveBrackets(result2[0]);
  581.     if (symbol.input=="root" || symbol.input=="stackrel")
  582.       newFrag.appendChild(result2[0]);
  583.     newFrag.appendChild(result[0]);
  584.     if (symbol.input=="frac") newFrag.appendChild(result2[0]);
  585.     return [AMcreateMmlNode(symbol.tag,newFrag),result2[1]];
  586.   case INFIX:
  587.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  588.     return [AMcreateMmlNode("mo",document.createTextNode(symbol.output)),str];
  589.   case SPACE:
  590.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  591.     node = AMcreateElementMathML("mspace");
  592.     node.setAttribute("width","1ex");
  593.     newFrag.appendChild(node);
  594.     newFrag.appendChild(
  595.       AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)));
  596.     node = AMcreateElementMathML("mspace");
  597.     node.setAttribute("width","1ex");
  598.     newFrag.appendChild(node);
  599.     return [AMcreateMmlNode("mrow",newFrag),str];
  600.   case LEFTRIGHT:
  601. //    if (rightvert) return [null,str]; else rightvert = true;
  602.     AMnestingDepth++;
  603.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  604.     result = AMparseExpr(str,false);
  605.     AMnestingDepth--;
  606.     var st = "";
  607.     if (result[0].lastChild!=null)
  608.       st = result[0].lastChild.firstChild.nodeValue;
  609.     if (st == "|") { // its an absolute value subterm
  610.       node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
  611.       node = AMcreateMmlNode("mrow",node);
  612.       node.appendChild(result[0]);
  613.       return [node,result[1]];
  614.     } else { // the "|" is a \mid
  615.       node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
  616.       node = AMcreateMmlNode("mrow",node);
  617.       return [node,str];
  618.     }
  619.   default:
  620. //alert("default");
  621.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  622.     return [AMcreateMmlNode(symbol.tag,        //its a constant
  623.                              document.createTextNode(symbol.output)),str];
  624.   }
  625. }
  626.  
  627. function AMparseIexpr(str) {
  628.   var symbol, sym1, sym2, node, result, underover;
  629.   str = AMremoveCharsAndBlanks(str,0);
  630.   sym1 = AMgetSymbol(str);
  631.   result = AMparseSexpr(str);
  632.   node = result[0];
  633.   str = result[1];
  634.   symbol = AMgetSymbol(str);
  635.   if (symbol.ttype == INFIX && symbol.input != "/") {
  636.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  637. //    if (symbol.input == "/") result = AMparseIexpr(str); else ...
  638.     result = AMparseSexpr(str);
  639.     if (result[0] == null) // show box in place of missing argument
  640.       result[0] = AMcreateMmlNode("mo",document.createTextNode("\u25A1"));
  641.     else AMremoveBrackets(result[0]);
  642.     str = result[1];
  643. //    if (symbol.input == "/") AMremoveBrackets(node);
  644.     if (symbol.input == "_") {
  645.       sym2 = AMgetSymbol(str);
  646.       underover = (sym1.ttype == UNDEROVER);
  647.       if (sym2.input == "^") {
  648.         str = AMremoveCharsAndBlanks(str,sym2.input.length);
  649.         var res2 = AMparseSexpr(str);
  650.         AMremoveBrackets(res2[0]);
  651.         str = res2[1];
  652.         node = AMcreateMmlNode((underover?"munderover":"msubsup"),node);
  653.         node.appendChild(result[0]);
  654.         node.appendChild(res2[0]);
  655.         node = AMcreateMmlNode("mrow",node); // so sum does not stretch
  656.       } else {
  657.         node = AMcreateMmlNode((underover?"munder":"msub"),node);
  658.         node.appendChild(result[0]);
  659.       }
  660.     } else {
  661.       node = AMcreateMmlNode(symbol.tag,node);
  662.       node.appendChild(result[0]);
  663.     }
  664.   }
  665.   return [node,str];
  666. }
  667.  
  668. function AMparseExpr(str,rightbracket) {
  669.   var symbol, node, result, i, nodeList = [],
  670.   newFrag = document.createDocumentFragment();
  671.   do {
  672.     str = AMremoveCharsAndBlanks(str,0);
  673.     result = AMparseIexpr(str);
  674.     node = result[0];
  675.     str = result[1];
  676.     symbol = AMgetSymbol(str);
  677.     if (symbol.ttype == INFIX && symbol.input == "/") {
  678.       str = AMremoveCharsAndBlanks(str,symbol.input.length);
  679.       result = AMparseIexpr(str);
  680.       if (result[0] == null) // show box in place of missing argument
  681.         result[0] = AMcreateMmlNode("mo",document.createTextNode("\u25A1"));
  682.       else AMremoveBrackets(result[0]);
  683.       str = result[1];
  684.       AMremoveBrackets(node);
  685.       node = AMcreateMmlNode(symbol.tag,node);
  686.       node.appendChild(result[0]);
  687.       newFrag.appendChild(node);
  688.       symbol = AMgetSymbol(str);
  689.     }
  690.     else if (node!=undefined) newFrag.appendChild(node);
  691.   } while ((symbol.ttype != RIGHTBRACKET &&
  692.            (symbol.ttype != LEFTRIGHT || rightbracket)
  693.            || AMnestingDepth == 0) && symbol!=null && symbol.output!="");
  694.   if (symbol.ttype == RIGHTBRACKET || symbol.ttype == LEFTRIGHT) {
  695. //    if (AMnestingDepth > 0) AMnestingDepth--;
  696.     var len = newFrag.childNodes.length;
  697.     if (len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 &&
  698.       newFrag.childNodes[len-2].nodeName == "mo" &&
  699.       newFrag.childNodes[len-2].firstChild.nodeValue == ",") { //matrix
  700.       var right = newFrag.childNodes[len-1].lastChild.firstChild.nodeValue;
  701.       if (right==")" || right=="]") {
  702.         var left = newFrag.childNodes[len-1].firstChild.firstChild.nodeValue;
  703.         if (left=="(" && right==")" && symbol.output != "}" ||
  704.             left=="[" && right=="]") {
  705.         var pos = []; // positions of commas
  706.         var matrix = true;
  707.         var m = newFrag.childNodes.length;
  708.         for (i=0; matrix && i<m; i=i+2) {
  709.           pos[i] = [];
  710.           node = newFrag.childNodes[i];
  711.           if (matrix) matrix = node.nodeName=="mrow" &&
  712.             (i==m-1 || node.nextSibling.nodeName=="mo" &&
  713.             node.nextSibling.firstChild.nodeValue==",")&&
  714.             node.firstChild.firstChild.nodeValue==left &&
  715.             node.lastChild.firstChild.nodeValue==right;
  716.           if (matrix)
  717.             for (var j=0; j<node.childNodes.length; j++)
  718.               if (node.childNodes[j].firstChild.nodeValue==",")
  719.                 pos[i][pos[i].length]=j;
  720.           if (matrix && i>1) matrix = pos[i].length == pos[i-2].length;
  721.         }
  722.         if (matrix) {
  723.           var row, frag, n, k, table = document.createDocumentFragment();
  724.           for (i=0; i<m; i=i+2) {
  725.             row = document.createDocumentFragment();
  726.             frag = document.createDocumentFragment();
  727.             node = newFrag.firstChild; // <mrow>(-,-,...,-,-)</mrow>
  728.             n = node.childNodes.length;
  729.             k = 0;
  730.             node.removeChild(node.firstChild); //remove (
  731.             for (j=1; j<n-1; j++) {
  732.               if (typeof pos[i][k] != "undefined" && j==pos[i][k]){
  733.                 node.removeChild(node.firstChild); //remove ,
  734.                 row.appendChild(AMcreateMmlNode("mtd",frag));
  735.                 k++;
  736.               } else frag.appendChild(node.firstChild);
  737.             }
  738.             row.appendChild(AMcreateMmlNode("mtd",frag));
  739.             if (newFrag.childNodes.length>2) {
  740.               newFrag.removeChild(newFrag.firstChild); //remove <mrow>)</mrow>
  741.               newFrag.removeChild(newFrag.firstChild); //remove <mo>,</mo>
  742.             }
  743.             table.appendChild(AMcreateMmlNode("mtr",row));
  744.           }
  745.           node = AMcreateMmlNode("mtable",table);
  746.           if (typeof symbol.invisible == "boolean" && symbol.invisible) node.setAttribute("columnalign","left");
  747.           newFrag.replaceChild(node,newFrag.firstChild);
  748.         }
  749.        }
  750.       }
  751.     }
  752.     str = AMremoveCharsAndBlanks(str,symbol.input.length);
  753.     if (typeof symbol.invisible != "boolean" || !symbol.invisible) {
  754.       node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
  755.       newFrag.appendChild(node);
  756.     }
  757.   }
  758.   return [newFrag,str];
  759. }
  760.  
  761. function AMparseMath(str) {
  762.   var result, node = AMcreateElementMathML("mstyle");
  763.   if (mathcolor != "") node.setAttribute("mathcolor",mathcolor);
  764.   if (displaystyle) node.setAttribute("displaystyle","true");
  765.   if (mathfontfamily != "") node.setAttribute("fontfamily",mathfontfamily);
  766.   AMnestingDepth = 0;
  767.   node.appendChild(AMparseExpr(str.replace(/^\s+/g,""),false)[0]);
  768.   node = AMcreateMmlNode("math",node);
  769.   if (showasciiformulaonhover)                      //fixed by djhsu so newline
  770.     node.setAttribute("title",str.replace(/\s+/g," "));//does not show in Gecko
  771.   if (mathfontfamily != "" && (isIE || mathfontfamily != "serif")) {
  772.     var fnode = AMcreateElementXHTML("font");
  773.     fnode.setAttribute("face",mathfontfamily);
  774.     fnode.appendChild(node);
  775.     return fnode;
  776.   }
  777.   return node;
  778. }
  779.  
  780. function AMstrarr2docFrag(arr, linebreaks) {
  781.   var newFrag=document.createDocumentFragment();
  782.   var expr = false;
  783.   for (var i=0; i<arr.length; i++) {
  784.     if (expr) newFrag.appendChild(AMparseMath(arr[i]));
  785.     else {
  786.       var arri = (linebreaks ? arr[i].split("\n\n") : [arr[i]]);
  787.       newFrag.appendChild(AMcreateElementXHTML("span").
  788.       appendChild(document.createTextNode(arri[0])));
  789.       for (var j=1; j<arri.length; j++) {
  790.         newFrag.appendChild(AMcreateElementXHTML("p"));
  791.         newFrag.appendChild(AMcreateElementXHTML("span").
  792.         appendChild(document.createTextNode(arri[j])));
  793.       }
  794.     }
  795.     expr = !expr;
  796.   }
  797.   return newFrag;
  798. }
  799.  
  800. function AMprocessNodeR(n, linebreaks) {
  801.   var mtch, str, arr, frg, i;
  802.   if (n.childNodes.length == 0) {
  803.    if ((n.nodeType!=8 || linebreaks) &&
  804.     n.parentNode.nodeName!="form" && n.parentNode.nodeName!="FORM" &&
  805.     n.parentNode.nodeName!="textarea" && n.parentNode.nodeName!="TEXTAREA" &&
  806.     n.parentNode.nodeName!="pre" && n.parentNode.nodeName!="PRE") {
  807.     str = n.nodeValue;
  808.     if (!(str == null)) {
  809.       str = str.replace(/\r\n\r\n/g,"\n\n");
  810.       if (doubleblankmathdelimiter) {
  811.         str = str.replace(/\x20\x20\./g," "+AMdelimiter1+".");
  812.         str = str.replace(/\x20\x20,/g," "+AMdelimiter1+",");
  813.         str = str.replace(/\x20\x20/g," "+AMdelimiter1+" ");
  814.       }
  815.       str = str.replace(/\x20+/g," ");
  816.       str = str.replace(/\s*\r\n/g," ");
  817.       mtch = false;
  818.       str = str.replace(new RegExp(AMescape2, "g"),
  819.               function(st){mtch=true;return "AMescape2"});
  820.       str = str.replace(new RegExp(AMescape1, "g"),
  821.               function(st){mtch=true;return "AMescape1"});
  822.       str = str.replace(new RegExp(AMdelimiter2regexp, "g"),AMdelimiter1);
  823.       arr = str.split(AMdelimiter1);
  824.       for (i=0; i<arr.length; i++)
  825.         arr[i]=arr[i].replace(/AMescape2/g,AMdelimiter2).
  826.                       replace(/AMescape1/g,AMdelimiter1);
  827.       if (arr.length>1 || mtch) {
  828.         if (checkForMathML) {
  829.           checkForMathML = false;
  830.           var nd = AMisMathMLavailable();
  831.           AMnoMathML = nd != null;
  832.           if (AMnoMathML && notifyIfNoMathML)
  833.             if (alertIfNoMathML)
  834.               alert("To view the ASCIIMathML notation use Internet Explorer 6 +\nMathPlayer (free from www.dessci.com)\n\
  835.                or Firefox/Mozilla/Netscape");
  836.             else AMbody.insertBefore(nd,AMbody.childNodes[0]);
  837.         }
  838.         if (!AMnoMathML) {
  839.           frg = AMstrarr2docFrag(arr,n.nodeType==8);
  840.           var len = frg.childNodes.length;
  841.           n.parentNode.replaceChild(frg,n);
  842.           return len-1;
  843.         } else return 0;
  844.       }
  845.     }
  846.    } else return 0;
  847.   } else if (n.nodeName!="math") {
  848.     for (i=0; i<n.childNodes.length; i++)
  849.       i += AMprocessNodeR(n.childNodes[i], linebreaks);
  850.   }
  851.   return 0;
  852. }
  853.  
  854. function AMprocessNode(n, linebreaks, spanclassAM) {
  855.   var frag,st;
  856.   if (spanclassAM!=null) {
  857.     frag = document.getElementsByTagName("span")
  858.     for (var i=0;i<frag.length;i++)
  859.       if (frag[i].className == "AM")
  860.         AMprocessNodeR(frag[i],linebreaks);
  861.   } else {
  862.     try {
  863.       st = n.innerHTML;
  864.     } catch(err) {}
  865.     if (st==null ||
  866.         st.indexOf(AMdelimiter1)!=-1 || st.indexOf(AMdelimiter2)!=-1)
  867.       AMprocessNodeR(n,linebreaks);
  868.   }
  869.   if (isIE) { //needed to match size and font of formula to surrounding text
  870.     frag = document.getElementsByTagName('math');
  871.     for (var i=0;i<frag.length;i++) frag[i].update()
  872.   }
  873. }
  874.  
  875. var AMbody;
  876. var AMnoMathML = false, AMtranslated = false;
  877.  
  878. function translate(spanclassAM) {
  879.   if (!AMtranslated) { // run this only once
  880.     AMtranslated = true;
  881.     AMinitSymbols();
  882.     AMbody = document.getElementsByTagName("body")[0];
  883.     AMprocessNode(AMbody, false, spanclassAM);
  884.   }
  885. }
  886.  
  887. if (isIE) { // avoid adding MathPlayer info explicitly to each webpage
  888.   document.write("<object id=\"mathplayer\"\
  889.  classid=\"clsid:32F66A20-7614-11D4-BD11-00104BD3F987\"></object>");
  890.   document.write("<?import namespace=\"m\" implementation=\"#mathplayer\"?>");
  891. }
  892.  
  893. // GO1.1 Generic onload by Brothercake
  894. // http://www.brothercake.com/
  895. //onload function (replaces the onload="translate()" in the <body> tag)
  896. function generic()
  897. {
  898.   translate();
  899. };
  900. //setup onload function
  901. if(typeof window.addEventListener != 'undefined')
  902. {
  903.   //.. gecko, safari, konqueror and standard
  904.   window.addEventListener('load', generic, false);
  905. }
  906. else if(typeof document.addEventListener != 'undefined')
  907. {
  908.   //.. opera 7
  909.   document.addEventListener('load', generic, false);
  910. }
  911. else if(typeof window.attachEvent != 'undefined')
  912. {
  913.   //.. win/ie
  914.   window.attachEvent('onload', generic);
  915. }
  916. //** remove this condition to degrade older browsers
  917. else
  918. {
  919.   //.. mac/ie5 and anything else that gets this far
  920.   //if there's an existing onload function
  921.   if(typeof window.onload == 'function')
  922.   {
  923.     //store it
  924.     var existing = onload;
  925.     //add new onload handler
  926.     window.onload = function()
  927.     {
  928.       //call existing onload function
  929.       existing();
  930.       //call generic onload function
  931.       generic();
  932.     };
  933.   }
  934.   else
  935.   {
  936.     //setup onload function
  937.     window.onload = generic;
  938.   }
  939. }
  940.