]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ASCIIMathPHP/ASCIIMathPHP-2.0.class.php
ASCIIMathPHP-2.0 for PHP 5
[SourceForge/phpwiki.git] / lib / ASCIIMathPHP / ASCIIMathPHP-2.0.class.php
1 <?php\r
2 \r
3 /****\r
4  * ASCIIMathPHP and associated classes:\r
5  * -- XMLNode\r
6  * -- MathMLNode extends XMLNode\r
7  *\r
8  * These classes are a PHP port of ASCIIMath \r
9  * Version 1.3 Feb 19 2004, (c) Peter Jipsen http://www.chapman.edu/~jipsen\r
10  *\r
11  * ASCIIMathPHP Version 1.11, 26 April 2006, (c) Kee-Lin Steven Chan (kc56@cornell.edu)\r
12  *\r
13  * This program is free software; you can redistribute it and/or modify\r
14  * it under the terms of the GNU General Public License as published by\r
15  * the Free Software Foundation; either version 2 of the License, or (at\r
16  * your option) any later version.\r
17  *\r
18  * This program is distributed in the hope that it will be useful, \r
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
21  * General Public License (at http://www.gnu.org/copyleft/gpl.html) \r
22  * for more details.\r
23  *\r
24  * ChangeLog\r
25  *\r
26  * Ver 2.0\r
27  * -- PHP5 only version of ASCIIMathPHP\r
28  *\r
29  * Ver 1.12.1\r
30  * -- Included the missing setCurrExpr() method\r
31  *\r
32  * Ver 1.12\r
33  * -- Added changes that David Lippman <DLippman@pierce.ctc.edu> made to bring ASCIIMathPHP up to \r
34  * ASCIIMath 1.4.7 functionality.\r
35  * -- Added parseIntExpr, for intermediate expression parsing rule, allowing x^2/x^3 to render as (x^2)/(x^3)\r
36  * -- Added quotes as another way of designating text; "hello" is equivalent to text(hello)\r
37  * -- Added FUNC designator to allow sin, cos, etc to act as functions, so sin(x)/x renders as {sin(x)}/x\r
38  *\r
39  * Ver 1.11\r
40  * -- Fixed bug that stopped script execution for incomplete expressions\r
41  * -- Changed the algorithm for parsing expressions so that it matches the longest string possible (greedy)\r
42  *\r
43  * Ver 1.10\r
44  * -- Added definition support\r
45  * -- Added stackrel support\r
46  * -- Added a bunch of different symbols etc. >>, << and definitions like dx, dy, dz etc.\r
47  *\r
48  * Ver 1.02\r
49  * -- Fixed bug with mbox and text\r
50  * -- Fixed spacing bug with mbox and text\r
51  *\r
52  * Ver 1.01\r
53  * -- Fixed Bug that did not parse symbols greater than a single character\r
54  * correctly when appearing at end of expression.\r
55  *\r
56  ***/\r
57 \r
58 class XMLNode\r
59 {\r
60         // Private variables\r
61         var $_id;\r
62         var $_name;\r
63         var $_content;\r
64         var $_mt_elem_flg;\r
65         var $_attr_arr;\r
66         var $_child_arr;\r
67         var $_nmspc;\r
68         var $_nmspc_alias;\r
69         var $_parent_id;\r
70         var $_parent_node;\r
71         \r
72         function XMLNode($id = NULL)\r
73         {\r
74                 $this->_id = isset($id) ? $id : md5(uniqid(rand(),1));\r
75                 $this->_name = '';\r
76                 $this->_content = '';\r
77                 $this->_mt_elem_flg = FALSE;\r
78                 $this->_attr_arr = array();\r
79                 $this->_child_arr = array();\r
80                 $this->_nmspc = '';\r
81                 $this->_nmspc_alias = '';\r
82                 $this->_parent_id = FALSE;\r
83                 $this->_parent_node = NULL;\r
84         }\r
85         \r
86         function addChild(&$node)\r
87         {\r
88                 $this->_child_arr[$node->getId()] = $node;\r
89                 $node->setParentId($this->_id);\r
90                 $node->setParentNode($this);\r
91         }\r
92         \r
93         function addChildArr(&$node_arr)\r
94         {\r
95                 $key_arr = array_keys($node_arr);\r
96                 $num_key = count($key_arr);\r
97                 \r
98                 for ($i = 0; $i < $num_key; $i++) {\r
99                         $node = $node_arr[$key_arr[$i]];\r
100                         $this->addChild($node);\r
101                 }\r
102         }\r
103         \r
104         function insertChildBefore($idx,&$node)\r
105         {\r
106                 $key_arr = array_keys($this->_child_arr);\r
107                 $num_key = count($key_arr);\r
108                 $tmp_arr = arry();\r
109                 \r
110                 for ($i = 0;$i < $num_key;$i++) {\r
111                         if ($i == $idx) {\r
112                                 $tmp_arr[$node->getId()] = $node;\r
113                         }\r
114                         $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]];\r
115                 }\r
116                 $this->_child_arr = $tmp_arr;\r
117         }\r
118         \r
119         function insertChildAfter($idx,&$node)\r
120         {\r
121                 $key_arr = array_keys($this->_child_arr);\r
122                 $num_key = count($key_arr);\r
123                 $tmp_arr = arry();\r
124                 \r
125                 for ($i = 0;$i < $num_key;$i++) {\r
126                         $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]];\r
127                         if ($i == $idx) {\r
128                                 $tmp_arr[$node->getId()] = $node;\r
129                         }\r
130                 }\r
131                 $this->_child_arr = $tmp_arr;\r
132         }\r
133         \r
134         function setId($id)\r
135         {\r
136                 $this->_id = $id;\r
137         }\r
138         \r
139         function setName($name)\r
140         {\r
141                 $this->_name = $name;\r
142         }\r
143         \r
144         function setNamepace($nmspc)\r
145         {\r
146                 $this->_nmspc = $nmspc;\r
147         }\r
148         \r
149         function setNamespaceAlias($nmspc_alias)\r
150         {\r
151                 $this->_nmspc_alias = $nmspc_alias;\r
152         }\r
153         \r
154         function setContent($content)\r
155         {\r
156                 $this->_content = $content;\r
157         }\r
158         \r
159         function setEmptyElem($mt_elem_flg)\r
160         {\r
161                 $this->_mt_elem_flg = $mt_elem_flg;\r
162         }\r
163         \r
164         function setAttr($attr_nm,$attr_val)\r
165         {\r
166                 $this->_attr_arr[$attr_nm] = $attr_val;\r
167         }\r
168         \r
169         function setAttrArr($attr_arr)\r
170         {\r
171                 $this->_attr_arr = $attr_arr;\r
172         }\r
173         \r
174         function setParentId($id)\r
175         {\r
176                 $this->_parent_id = $id;\r
177         }\r
178         \r
179         function setParentNode(&$node)\r
180         {\r
181                 $this->_parent_node = $node;\r
182         }\r
183         \r
184         function getId()\r
185         {\r
186                 return($this->_id);\r
187         }\r
188         \r
189         function getName()\r
190         {\r
191                 return($this->_name);\r
192         }\r
193         \r
194         function getNamespace()\r
195         {\r
196                 return($this->_nmspc);\r
197         }\r
198         \r
199         function getNamespaceAlias()\r
200         {\r
201                 return($this->_nmspc_alias);\r
202         }\r
203         \r
204         function getContent()\r
205         {\r
206                 return($this->_content);\r
207         }\r
208         \r
209         function getAttr($attr_nm)\r
210         {\r
211                 if (isset($this->_attr_arr[$attr_nm])) {\r
212                         return($this->_attr_arr[$attr_nm]);\r
213                 } else {\r
214                         return(NULL);\r
215                 }\r
216         }\r
217         \r
218         function getAttrArr()\r
219         {\r
220                 return($this->_attr_arr);\r
221         }\r
222         \r
223         function getParentId()\r
224         {\r
225                 return($this->parent_id);\r
226         }\r
227         \r
228         function getParentNode()\r
229         {\r
230                 return($this->_parent_node);\r
231         }\r
232         \r
233         function getChild($id)\r
234         {\r
235                 if (isset($this->_child_arr[$id])) {\r
236                         return($this->_child_arr[$id]);\r
237                 } else {\r
238                         return(FALSE);\r
239                 }\r
240         }\r
241         \r
242         function getFirstChild()\r
243         {\r
244                 $id_arr = array_keys($this->_child_arr);\r
245                 $num_child = count($id_arr);\r
246                 \r
247                 if ($num_child > 0) {\r
248                         return($this->_child_arr[$id_arr[0]]);\r
249                 } else {\r
250                         return(FALSE);\r
251                 }\r
252         }\r
253         \r
254         function getLastChild()\r
255         {\r
256                 $id_arr = array_keys($this->_child_arr);\r
257                 $num_child = count($id_arr);\r
258                 \r
259                 if ($num_child > 0) {\r
260                         return($this->_child_arr[$id_arr[$num_child - 1]]);\r
261                 } else {\r
262                         return(FALSE);\r
263                 }\r
264         }\r
265         \r
266         function getChildByIdx($idx)\r
267         {\r
268                 $id_arr = array_keys($this->_child_arr);\r
269                 \r
270                 if (isset($this->_child_arr[$id_arr[$idx]])) {\r
271                         return($this->_child_arr[$id_arr[$idx]]);\r
272                 } else {\r
273                         return(FALSE);\r
274                 }\r
275         }\r
276         \r
277         function getNumChild()\r
278         {\r
279                 return(count($this->_child_arr));\r
280         }\r
281         \r
282         function removeChild($id)\r
283         {\r
284                 unset($this->_child_arr[$id]);\r
285         }\r
286         \r
287         function removeChildByIdx($idx)\r
288         {\r
289                 $key_arr = array_keys($this->_child_arr);\r
290                 unset($this->_child_arr[$key_arr[$idx]]);\r
291         }\r
292         \r
293         function removeFirstChild()\r
294         {\r
295                 $key_arr = array_keys($this->_child_arr);\r
296                 unset($this->_child_arr[$key_arr[0]]);\r
297         }\r
298         \r
299         function removeLastChild()\r
300         {\r
301                 $key_arr = array_keys($this->_child_arr);\r
302                 unset($this->_child_arr[$key_arr[count($key_arr)-1]]);\r
303         }\r
304         \r
305         function dumpXML($indent_str = "\t")\r
306         {\r
307                 $attr_txt = $this->_dumpAttr();\r
308                 $name = $this->_dumpName();\r
309                 $xmlns = $this->_dumpXmlns();\r
310                 $lvl = $this->_getCurrentLevel();\r
311                 $indent = str_pad('',$lvl,$indent_str);\r
312                 \r
313                 if ($this->_mt_elem_flg) {\r
314                         $tag = "$indent<$name$xmlns$attr_txt />";\r
315                         return($tag);\r
316                 } else {\r
317                         $key_arr = array_keys($this->_child_arr);\r
318                         $num_child = count($key_arr);\r
319                         \r
320                         $tag = "$indent<$name$xmlns$attr_txt>$this->_content";\r
321                         \r
322                         for ($i = 0;$i < $num_child;$i++) {\r
323                                 $node = $this->_child_arr[$key_arr[$i]];\r
324                                 \r
325                                 $child_txt = $node->dumpXML($indent_str);\r
326                                 $tag .= "\n$child_txt";\r
327                         }\r
328                         \r
329                         $tag .= ($num_child > 0 ? "\n$indent</$name>" : "</$name>");\r
330                         return($tag);\r
331                 }\r
332         }\r
333         \r
334         function _dumpAttr()\r
335         {\r
336                 $id_arr = array_keys($this->_attr_arr);\r
337                 $id_arr_cnt = count($id_arr);\r
338                 $attr_txt = '';\r
339                 \r
340                 for($i = 0;$i < $id_arr_cnt;$i++) {\r
341                         $key = $id_arr[$i];\r
342                         $attr_txt .= " $key=\"{$this->_attr_arr[$key]}\"";\r
343                 }\r
344                 \r
345                 return($attr_txt);\r
346         }\r
347         \r
348         function _dumpName()\r
349         {\r
350                 $alias = $this->getNamespaceAlias();\r
351                 if ($alias == '') {\r
352                         return($this->getName());\r
353                 } else {\r
354                         return("$alias:" . $this->getName());\r
355                 }\r
356         }\r
357         \r
358         function _dumpXmlns()\r
359         {\r
360                 $nmspc = $this->getNamespace();\r
361                 $alias = $this->getNamespaceAlias();\r
362                 \r
363                 if ($nmspc != '') {\r
364                         if ($alias == '') {\r
365                                 return(" xmlns=\"" . $nmspc . "\"");\r
366                         } else {\r
367                                 return(" xmlns:$alias=\"" . $nmspc . "\"");\r
368                         }\r
369                 } else {\r
370                         return('');\r
371                 }\r
372         }\r
373         \r
374         function _getCurrentLevel()\r
375         {\r
376                 if ($this->_parent_id === FALSE) {\r
377                         return(0);\r
378                 } else {\r
379                         $node = $this->getParentNode();\r
380                         $lvl = $node->_getCurrentLevel();\r
381                         $lvl++;\r
382                         return($lvl);\r
383                 }\r
384         }\r
385 }\r
386 \r
387 class MathMLNode extends XMLNode\r
388 {\r
389         function MathMLNode($id = NULL)\r
390         {\r
391                 parent::XMLNode($id);\r
392         }\r
393         \r
394         function removeBrackets()\r
395         {\r
396                 if ($this->_name == 'mrow') {\r
397                         if ($c_node_0 = $this->getFirstChild()) {\r
398                                 $c_node_0->isLeftBracket() ? $this->removeFirstChild() : 0;\r
399                         }\r
400                         \r
401                         if ($c_node_0 = $this->getLastChild()) {\r
402                                 $c_node_0->isRightBracket() ? $this->removeLastChild() : 0;\r
403                         }\r
404                 }\r
405         }\r
406         \r
407         function isLeftBracket()\r
408         {\r
409                 switch ($this->_content) {\r
410                         case '{':\r
411                         case '[':\r
412                         case '(':\r
413                                 return(TRUE);\r
414                                 break;\r
415                 }\r
416                 return(FALSE);\r
417         }\r
418         \r
419         function isRightBracket()\r
420         {\r
421                 switch ($this->_content) {\r
422                         case '}':\r
423                         case ']':\r
424                         case ')':\r
425                                 return(TRUE);\r
426                                 break;\r
427                 }\r
428                 return(FALSE);\r
429         }\r
430 }\r
431 \r
432 class ASCIIMathPHP\r
433 {\r
434         var $_expr;\r
435         var $_curr_expr;\r
436         var $_prev_expr;\r
437         var $_symbol_arr;\r
438         var $_node_arr;\r
439         var $_node_cntr;\r
440         \r
441         function ASCIIMathPHP($symbol_arr,$expr = NULL)\r
442         {\r
443                 $this->_symbol_arr = $symbol_arr;\r
444                 if (isset($expr)) {\r
445                         $this->setExpr($expr);\r
446                 }\r
447         }\r
448         \r
449         /**\r
450          * Returns an empty node (containing a non-breaking space) 26-Apr-2006\r
451          * \r
452          * Used when an expression is incomplete\r
453          *\r
454          * @return object\r
455          *\r
456          * @access private\r
457          */\r
458         function emptyNode()\r
459         {\r
460                 $tmp_node = $this->createNode();\r
461                 $tmp_node->setName('mn');\r
462                 $tmp_node->setContent('&#' . hexdec('200B') . ';');\r
463                 return $tmp_node;\r
464         }\r
465         \r
466         function pushExpr($prefix) // 2005-06-11 wes\r
467         {\r
468                 $this->_curr_expr = $prefix . $this->_curr_expr;\r
469         }\r
470 \r
471         function setExpr($expr)\r
472         {\r
473                 $this->_expr = $expr;\r
474                 $this->_curr_expr = $expr;\r
475                 $this->_prev_expr = $expr;\r
476                 \r
477                 $this->_node_arr = array();\r
478                 $this->_node_cntr = 0;\r
479         }\r
480         \r
481         function genMathML($attr_arr = NULL)\r
482         {\r
483                 // <math> node\r
484                 $node_0 = $this->createNode();\r
485                 $node_0->setName('math');\r
486                 $node_0->setNamepace('http://www.w3.org/1998/Math/MathML');\r
487                 \r
488                 // <mstyle> node\r
489                 if (isset($attr_arr)) {\r
490                         $node_1 = $this->createNode();\r
491                         $node_1->setName('mstyle');\r
492                         $node_1->setAttrArr($attr_arr);\r
493                         \r
494                         $node_arr = $this->parseExpr();\r
495                         \r
496                         $node_1->addChildArr($node_arr);\r
497                         $node_0->addChild($node_1);\r
498                 } else {\r
499                         $node_arr = $this->parseExpr();\r
500                         $node_0->addChildArr($node_arr);\r
501                 }\r
502                 \r
503                 return TRUE;\r
504         }\r
505         \r
506         /*\r
507         function  mergeNodeArr(&$node_arr_0,&$node_arr_1)\r
508         {\r
509                 $key_arr_0 = array_keys($node_arr_0);\r
510                 $key_arr_1 = array_keys($node_arr_1);\r
511                 \r
512                 $num_key_0 = count($key_arr_0);\r
513                 $num_key_1 = count($key_arr_1);\r
514                 \r
515                 $merge_arr = array();\r
516                 \r
517                 for ($i = 0;$i < $num_key_0;$i++) {\r
518                         $merge_arr[$key_arr_0[$i]] = $node_arr_0[$key_arr_0[$i]];\r
519                 }\r
520                 \r
521                 for ($j = 0;$j < $num_key_1;$i++) {\r
522                         $merge_arr[$key_arr_1[$i]] = $node_arr_1[$key_arr_1[$i]];\r
523                 }\r
524                 \r
525                 return($merge_arr);\r
526         }\r
527         */\r
528         \r
529         //Broken out of parseExpr Sept 7, 2006 David Lippman for\r
530         //ASCIIMathML 1.4.7 compatibility\r
531         function  parseIntExpr()\r
532         {\r
533                 $sym_0 = $this->getSymbol();\r
534                 $node_0 = $this->parseSmplExpr();\r
535                 $sym = $this->getSymbol();\r
536                 \r
537                 if (isset($sym['infix']) && $sym['input'] != '/') {\r
538                         $this->chopExpr($sym['symlen']);\r
539                         $node_1 = $this->parseSmplExpr();\r
540                         \r
541                         if ($node_1 === FALSE) { //show box in place of missing argument\r
542                                 $node_1 = $this->emptyNode();//??\r
543                         } else {\r
544                                 $node_1->removeBrackets();\r
545                         }\r
546                         \r
547                         // If 'sub' -- subscript\r
548                         if ($sym['input'] == '_') {\r
549                                 \r
550                                 $sym_1 = $this->getSymbol();\r
551                                 \r
552                                 // If 'sup' -- superscript\r
553                                 if ($sym_1['input'] == '^') { \r
554                                         $this->chopExpr($sym_1['symlen']);\r
555                                         $node_2 = $this->parseSmplExpr();\r
556                                         $node_2->removeBrackets();\r
557                                         \r
558                                         $node_3 = $this->createNode();\r
559                                         $node_3->setName(isset($sym_0['underover']) ? 'munderover' : 'msubsup');\r
560                                         $node_3->addChild($node_0);\r
561                                         $node_3->addChild($node_1);\r
562                                         $node_3->addChild($node_2);\r
563                                         \r
564                                         $node_4 = $this->createNode();\r
565                                         $node_4->setName('mrow');\r
566                                         $node_4->addChild($node_3);\r
567                                         \r
568                                         return $node_4;\r
569                                 } else {\r
570                                         $node_2 = $this->createNode();\r
571                                         $node_2->setName(isset($sym_0['underover']) ? 'munder' : 'msub');\r
572                                         $node_2->addChild($node_0);\r
573                                         $node_2->addChild($node_1);\r
574                                         \r
575                                         return $node_2;\r
576                                 }\r
577                         } else {\r
578                                 $node_2 = $this->createNode();\r
579                                 $node_2->setName($sym['tag']);\r
580                                 $node_2->addChild($node_0);\r
581                                 $node_2->addChild($node_1);\r
582                                 \r
583                                 return($node_2);\r
584                         }\r
585                 } elseif ($node_0 !== FALSE) {\r
586                         return($node_0);\r
587                 } else {\r
588                         return $this->emptyNode();\r
589                 }\r
590                 \r
591         }\r
592         \r
593         function  parseExpr()\r
594         {\r
595                 // Child/Fragment array\r
596                 $node_arr = array();\r
597                 \r
598                 // Deal whole expressions like 'ax + by + c = 0' etc.\r
599                 do {\r
600                         $sym_0 = $this->getSymbol();\r
601                         $node_0 = $this->parseIntExpr();\r
602                         $sym = $this->getSymbol();\r
603                         // var_dump($sym);\r
604                         \r
605                         if (isset($sym['infix']) && $sym['input'] == '/') {\r
606                                 $this->chopExpr($sym['symlen']);\r
607                                 $node_1 = $this->parseIntExpr();\r
608                                 \r
609                                 if ($node_1 === FALSE) { //should show box in place of missing argument\r
610                                         $node_1 = $this->emptyNode();\r
611                                         continue;\r
612                                 }\r
613 \r
614                                 $node_1->removeBrackets();\r
615                                 \r
616                                 // If 'div' -- divide\r
617                                 $node_0->removeBrackets();\r
618                                 $node_2 = $this->createNode();\r
619                                 $node_2->setName($sym['tag']);\r
620                                 $node_2->addChild($node_0);\r
621                                 $node_2->addChild($node_1);\r
622                                 $node_arr[$node_2->getId()] = $node_2;\r
623                         \r
624                         } elseif ($node_0 !== FALSE) {\r
625                                 $node_arr[$node_0->getId()] = $node_0;\r
626                         }\r
627                 } while (!isset($sym['right_bracket']) && $sym !== FALSE && $sym['output'] != '');\r
628                 \r
629                 //var_dump($sym);\r
630                 // Possibly to deal with matrices\r
631                 if (isset($sym['right_bracket'])) {\r
632                         $node_cnt = count($node_arr);\r
633                         $key_node_arr = array_keys($node_arr);\r
634                         \r
635                         if ($node_cnt > 1) {\r
636                                 $node_5 = $node_arr[$key_node_arr[$node_cnt-1]];\r
637                                 $node_6 = $node_arr[$key_node_arr[$node_cnt-2]];\r
638                         } else {\r
639                                 $node_5 = FALSE;\r
640                                 $node_6 = FALSE;\r
641                         }\r
642                         \r
643                         // Dealing with matrices\r
644                         if ($node_5 !== FALSE && $node_6 !== FALSE &&\r
645                                 $node_cnt > 1 && \r
646                                 $node_5->getName() == 'mrow' && \r
647                                 $node_6->getName() == 'mo' &&\r
648                                 $node_6->getContent() == ',') {\r
649                                 \r
650                                 // Checking if Node 5 has a LastChild\r
651                                 if ($node_7 = $node_5->getLastChild()) {\r
652                                         $node_7_cntnt = $node_7->getContent();\r
653                                 } else {\r
654                                         $node_7_cntnt = FALSE;\r
655                                 }\r
656                                 \r
657                                 // If there is a right bracket\r
658                                 if ($node_7 !== FALSE && ($node_7_cntnt == ']' || $node_7_cntnt == ')')) {\r
659                                         \r
660                                         // Checking if Node 5 has a firstChild\r
661                                         if ($node_8 = $node_5->getFirstChild()) {\r
662                                                 $node_8_cntnt = $node_8->getContent();\r
663                                         } else {\r
664                                                 $node_8_cntnt = FALSE;\r
665                                         }\r
666                                         \r
667                                         // If there is a matching left bracket\r
668                                         if ($node_8 !== FALSE && \r
669                                                 (($node_8_cntnt == '(' && $node_7_cntnt == ')' && $sym['output'] != '}') ||\r
670                                                 ($node_8_cntnt == '[' && $node_7_cntnt == ']'))) {\r
671                                                         \r
672                                                 $is_mtrx_flg = TRUE;\r
673                                                 $comma_pos_arr = array();\r
674                                                 \r
675                                                 $i = 0;\r
676                                                 \r
677                                                 while ($i < $node_cnt && $is_mtrx_flg) {\r
678                                                         $tmp_node = $node_arr[$key_node_arr[$i]];\r
679                                                         \r
680                                                         if($tmp_node_first = $tmp_node->getFirstChild()) {\r
681                                                                 $tnfc = $tmp_node_first->getContent();\r
682                                                         } else {\r
683                                                                 $tnfc = FALSE;\r
684                                                         }\r
685                                                         \r
686                                                         if($tmp_node_last = $tmp_node->getLastChild()) {\r
687                                                                 $tnlc = $tmp_node_last->getContent();\r
688                                                         } else {\r
689                                                                 $tnlc = FALSE;\r
690                                                         }\r
691                                                         \r
692                                                         if (isset($key_node_arr[$i+1])) {\r
693                                                                 $next_tmp_node = $node_arr[$key_node_arr[$i+1]];\r
694                                                                 $ntnn = $next_tmp_node->getName();\r
695                                                                 $ntnc = $next_tmp_node->getContent();\r
696                                                         } else {\r
697                                                                 $ntnn = FALSE;\r
698                                                                 $ntnc = FALSE;\r
699                                                         }\r
700                                                         \r
701                                                         // Checking each node in node array for matrix criteria\r
702                                                         if ($is_mtrx_flg) {\r
703                                                                 $is_mtrx_flg = $tmp_node->getName() == 'mrow' &&\r
704                                                                         ($i == $node_cnt-1 || $ntnn == 'mo' && $ntnc == ',') &&\r
705                                                                         $tnfc == $node_8_cntnt && $tnlc == $node_7_cntnt;\r
706                                                         }\r
707 \r
708                                                         if ($is_mtrx_flg) {\r
709                                                                 for ($j = 0;$j < $tmp_node->getNumChild();$j++) {\r
710                                                                         $tmp_c_node = $tmp_node->getChildByIdx($j);\r
711                                                                         \r
712                                                                         if ($tmp_c_node->getContent() == ',') {\r
713                                                                                 $comma_pos_arr[$i][] = $j;\r
714                                                                         }\r
715                                                                 }\r
716                                                         }       \r
717                                                         \r
718                                                         if ($is_mtrx_flg && $i > 1) {\r
719                                                                 \r
720                                                                 $cnt_cpan = isset($comma_pos_arr[$i]) ? count($comma_pos_arr[$i]) : NULL;\r
721                                                                 $cnt_cpap = isset($comma_pos_arr[$i-2]) ? count($comma_pos_arr[$i-2]) : NULL;\r
722                                                                 $is_mtrx_flg = $cnt_cpan == $cnt_cpap;\r
723                                                         }\r
724                                                         \r
725                                                         $i += 2;\r
726                                                 }\r
727                                                 \r
728                                                 // If the node passes the matrix tests\r
729                                                 if ($is_mtrx_flg) {\r
730                                                         $tab_node_arr = array();\r
731                                                         \r
732                                                         for ($i = 0;$i < $node_cnt;$i += 2) {\r
733                                                                 $tmp_key_node_arr = array_keys($node_arr);\r
734                                                                 if (!($tmp_node = $node_arr[$tmp_key_node_arr[0]])) {\r
735                                                                         break;\r
736                                                                 }\r
737                                                                 $num_child = $tmp_node->getNumChild();\r
738                                                                 $k = 0;\r
739                                                                 \r
740                                                                 $tmp_node->removeFirstChild();\r
741                                                                 \r
742                                                                 $row_node_arr = array();\r
743                                                                 $row_frag_node_arr = array();\r
744                                                                 \r
745                                                                 for ($j = 1;$j < ($num_child-1);$j++) {\r
746                                                                         if (isset($comma_pos_arr[$i][$k]) && \r
747                                                                                 $j == $comma_pos_arr[$i][$k]) {\r
748                                                                                 \r
749                                                                                 $tmp_node->removeFirstChild();\r
750                                                                                 \r
751                                                                                 $tmp_c_node = $this->createNode();\r
752                                                                                 $tmp_c_node->setName('mtd');\r
753                                                                                 $tmp_c_node->addChildArr($row_frag_node_arr);\r
754                                                                                 $row_frag_node_arr = array();\r
755                                                                                 \r
756                                                                                 $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node;\r
757                                                                                 \r
758                                                                                 $k++;\r
759                                                                         } else {\r
760                                                                                 \r
761                                                                                 if ($tmp_c_node = $tmp_node->getFirstChild()) {\r
762                                                                                         $row_frag_node_arr[$tmp_c_node->getId()] = $tmp_c_node;\r
763                                                                                         $tmp_node->removeFirstChild();\r
764                                                                                 }\r
765                                                                         }\r
766                                                                 }\r
767                                                                 \r
768                                                                 $tmp_c_node = $this->createNode();\r
769                                                                 $tmp_c_node->setName('mtd');\r
770                                                                 $tmp_c_node->addChildArr($row_frag_node_arr);\r
771                                                                 \r
772                                                                 $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node;\r
773                                                                 \r
774                                                                 if (count($node_arr) > 2) {\r
775                                                                         $tmp_key_node_arr = array_keys($node_arr);\r
776                                                                         unset($node_arr[$tmp_key_node_arr[0]]);\r
777                                                                         unset($node_arr[$tmp_key_node_arr[1]]);\r
778                                                                 }\r
779                                                                 \r
780                                                                 $tmp_c_node = $this->createNode();\r
781                                                                 $tmp_c_node->setName('mtr');\r
782                                                                 $tmp_c_node->addChildArr($row_node_arr);\r
783                                                                 \r
784                                                                 $tab_node_arr[$tmp_c_node->getId()] = $tmp_c_node;\r
785                                                         }\r
786                                                         \r
787                                                         $tmp_c_node = $this->createNode();\r
788                                                         $tmp_c_node->setName('mtable');\r
789                                                         $tmp_c_node->addChildArr($tab_node_arr);\r
790                                                         \r
791                                                         if (isset($sym['invisible'])) {\r
792                                                                 $tmp_c_node->setAttr('columnalign','left');\r
793                                                         }\r
794                                                         \r
795                                                         $key_node_arr = array_keys($node_arr);\r
796                                                         $tmp_c_node->setId($key_node_arr[0]);\r
797                                                         \r
798                                                         $node_arr[$tmp_c_node->getId()] = $tmp_c_node;\r
799                                                 }\r
800                                         }\r
801                                 }\r
802                         }\r
803                         \r
804                         $this->chopExpr($sym['symlen']);\r
805                         if (!isset($sym['invisible'])) {\r
806                                 $node_7 = $this->createNode();\r
807                                 $node_7->setName('mo');\r
808                                 $node_7->setContent($sym['output']);\r
809                                 $node_arr[$node_7->getId()] = $node_7;\r
810                         }\r
811                 }\r
812                 \r
813                 return($node_arr);\r
814         }\r
815         \r
816         function  parseSmplExpr()\r
817         {\r
818                 $sym = $this->getSymbol();\r
819                 \r
820                 if (!$sym || isset($sym['right_bracket'])) //return FALSE; \r
821                         return $this->emptyNode();\r
822                 \r
823                 $this->chopExpr($sym['symlen']);\r
824                 \r
825                 // 2005-06-11 wes: add definition type support\r
826                 if(isset($sym['definition'])) {\r
827                         $this->pushExpr($sym['output']);\r
828                         $sym = $this->getSymbol();\r
829                         $this->chopExpr($sym['symlen']);\r
830                 }\r
831 \r
832                 if (isset($sym['left_bracket'])) {\r
833                         $node_arr = $this->parseExpr();\r
834                         \r
835                         if (isset($sym['invisible'])) {\r
836                                 $node_0 = $this->createNode();\r
837                                 $node_0->setName('mrow');\r
838                                 $node_0->addChildArr($node_arr);\r
839                                 \r
840                                 return($node_0);\r
841                         } else {\r
842                                 $node_0 = $this->createNode();\r
843                                 $node_0->setName('mo');\r
844                                 $node_0->setContent($sym['output']);\r
845                                 \r
846                                 $node_1 = $this->createNode();\r
847                                 $node_1->setName('mrow');\r
848                                 $node_1->addChild($node_0);\r
849                                 $node_1->addChildArr($node_arr);\r
850                                 \r
851                                 return($node_1);\r
852                         }\r
853                 } elseif (isset($sym['unary'])) {\r
854                         \r
855                         if ($sym['input'] == 'sqrt') {\r
856                                 $node_0 = $this->parseSmplExpr();\r
857                                 $node_0->removeBrackets();\r
858                                 \r
859                                 $node_1 = $this->createNode();\r
860                                 $node_1->setName($sym['tag']);\r
861                                 $node_1->addChild($node_0);\r
862                                 \r
863                                 return($node_1);\r
864                         } elseif (isset($sym['func'])) { //added 2006-9-7 David Lippman\r
865                                 $expr = ltrim($this->getCurrExpr());\r
866                                 $st = $expr{0};\r
867                                 $node_0 = $this->parseSmplExpr();\r
868                                 //$node_0->removeBrackets();\r
869                                 if ($st=='^' || $st == '_' || $st=='/' || $st=='|' || $st==',') {\r
870                                         $node_1 = $this->createNode();\r
871                                         $node_1->setName($sym['tag']);\r
872                                         $node_1->setContent($sym['output']);\r
873                                         $this->setCurrExpr($expr);\r
874                                         return($node_1);\r
875                                 } else {\r
876                                         $node_1 = $this->createNode();\r
877                                         $node_1->setName('mrow');\r
878                                         $node_2 = $this->createNode();\r
879                                         $node_2->setName($sym['tag']);\r
880                                         $node_2->setContent($sym['output']);\r
881                                         $node_1->addChild($node_2);\r
882                                         $node_1->addChild($node_0);\r
883                                         return($node_1);\r
884                                 }\r
885                         } elseif ($sym['input'] == 'text' || $sym['input'] == 'mbox' || $sym['input'] == '"') {\r
886                                 $expr = ltrim($this->getCurrExpr());\r
887                                 if ($sym['input']=='"') {\r
888                                         $end_brckt = '"';\r
889                                         $txt = substr($expr,0,strpos($expr,$end_brckt));\r
890                                 } else {\r
891                                         switch($expr{0}) {\r
892                                                 case '(':\r
893                                                         $end_brckt = ')';\r
894                                                         break;\r
895                                                 case '[':\r
896                                                         $end_brckt = ']';\r
897                                                         break;\r
898                                                 case '{':\r
899                                                         $end_brckt = '}';\r
900                                                         break;\r
901                                                 default:\r
902                                                         $end_brckt = chr(11); // A character that will never be matched.\r
903                                                         break;\r
904                                         }\r
905                                         $txt = substr($expr,1,strpos($expr,$end_brckt)-1);\r
906                                 }\r
907                                 \r
908                                 //$txt = substr($expr,1,strpos($expr,$end_brckt)-1);\r
909                                 $len = strlen($txt);\r
910                                 \r
911                                 $node_0 = $this->createNode();\r
912                                 $node_0->setName('mrow');\r
913                                 \r
914                                 if ($len > 0) {\r
915                                         if ($txt{0} == " ") {\r
916                                                 $node_1 = $this->createNode();\r
917                                                 $node_1->setName('mspace');\r
918                                                 $node_1->setAttr('width','1ex');\r
919                                                 \r
920                                                 $node_0->addChild($node_1);\r
921                                         }\r
922                                         \r
923                                         $node_3 = $this->createNode();\r
924                                         $node_3->setName($sym['tag']);\r
925                                         $node_3->setContent(trim($txt));\r
926                                         \r
927                                         $node_0->addChild($node_3);\r
928                                         \r
929                                         if ($len > 1 && $txt{$len-1} == " ") {\r
930                                                 $node_2 = $this->createNode();\r
931                                                 $node_2->setName('mspace');\r
932                                                 $node_2->setAttr('width','1ex');\r
933                                                 \r
934                                                 $node_0->addChild($node_2);\r
935                                         }\r
936                                         \r
937                                         $this->chopExpr($len+2);\r
938                                 }\r
939                                 return($node_0);\r
940                                 \r
941                         } elseif (isset($sym['acc'])) {\r
942                                 $node_0 = $this->parseSmplExpr();\r
943                                 $node_0->removeBrackets();\r
944                                 \r
945                                 $node_1 = $this->createNode();\r
946                                 $node_1->setName($sym['tag']);\r
947                                 $node_1->addChild($node_0);\r
948                                 \r
949                                 $node_2 = $this->createNode();\r
950                                 $node_2->setName('mo');\r
951                                 $node_2->setContent($sym['output']);\r
952                                 \r
953                                 $node_1->addChild($node_2);\r
954                                 return($node_1);\r
955                         } else {\r
956                                 // Font change commands -- to complete\r
957                         }\r
958                 } elseif (isset($sym['binary'])) {\r
959                         $node_arr = array();\r
960                         \r
961                         $node_0 = $this->parseSmplExpr();\r
962                         $node_0->removeBrackets();\r
963                         \r
964                         $node_1 = $this->parseSmplExpr();\r
965                         $node_1->removeBrackets();\r
966                         \r
967             /* 2005-06-05 wes: added stackrel */\r
968                         if ($sym['input'] == 'root' || $sym['input'] == 'stackrel') {\r
969                                 $node_arr[$node_1->getId()] = $node_1;\r
970                                 $node_arr[$node_0->getId()] = $node_0;\r
971                         } elseif ($sym['input'] == 'frac') {\r
972                                 $node_arr[$node_0->getId()] = $node_0;\r
973                                 $node_arr[$node_1->getId()] = $node_1;\r
974                         }\r
975                         \r
976                         $node_2 = $this->createNode();\r
977                         $node_2->setName($sym['tag']);\r
978                         $node_2->addChildArr($node_arr);\r
979                         \r
980                         return($node_2);\r
981                 } elseif (isset($sym['infix'])) {\r
982                         $node_0 = $this->createNode();\r
983                         $node_0->setName('mo');\r
984                         $node_0->setContent($sym['output']);\r
985                         \r
986                         return($node_0);\r
987                 } elseif (isset($sym['space'])) {\r
988                         $node_0 = $this->createNode();\r
989                         $node_0->setName('mrow');\r
990                         \r
991                         $node_1 = $this->createNode();\r
992                         $node_1->setName('mspace');\r
993                         $node_1->setAttr('width',$sym['space']);\r
994                         \r
995                         $node_2 = $this->createNode();\r
996                         $node_2->setName($sym['tag']);\r
997                         $node_2->setContent($sym['output']);\r
998                         \r
999                         $node_3 = $this->createNode();\r
1000                         $node_3->setName('mspace');\r
1001                         $node_3->setAttr('width',$sym['space']);\r
1002                         \r
1003                         $node_0->addChild($node_1);\r
1004                         $node_0->addChild($node_2);\r
1005                         $node_0->addChild($node_3);\r
1006                         \r
1007                         return($node_0);\r
1008                 } else {\r
1009                         \r
1010                         // A constant\r
1011                         $node_0 = $this->createNode();\r
1012                         $node_0->setName($sym['tag']);\r
1013                         $node_0->setContent($sym['output']);\r
1014                         return($node_0);\r
1015                 }\r
1016                 \r
1017                 // Return an empty node\r
1018                 return $this->emptyNode();\r
1019         }\r
1020         \r
1021         function getMathML()\r
1022         {\r
1023                 $root = $this->_node_arr[0];\r
1024                 return($root->dumpXML());\r
1025         }\r
1026         \r
1027         function getCurrExpr()\r
1028         {\r
1029                 return($this->_curr_expr);\r
1030         }\r
1031         \r
1032         function setCurrExpr($str)\r
1033         {\r
1034                 $this->_curr_expr = $str;\r
1035         }\r
1036         \r
1037         function getExpr()\r
1038         {\r
1039                 return($this->_expr);\r
1040         }\r
1041         \r
1042         function getPrevExpr()\r
1043         {\r
1044                 return($this->_prev_expr);\r
1045         }\r
1046         \r
1047         function  createNode()\r
1048         {\r
1049                 $node = new MathMLNode($this->_node_cntr);\r
1050                 // $node->setNamespaceAlias('m');\r
1051                 $this->_node_arr[$this->_node_cntr] = $node;\r
1052                 $this->_node_cntr++;\r
1053                 return($node);\r
1054         }\r
1055         \r
1056         /**\r
1057          * Gets the largest symbol in the expression (greedy). Changed from non-greedy 26-Apr-2006\r
1058          * \r
1059          * @parameter boolean[optional] Chop original string?\r
1060          *\r
1061          * @return mixed\r
1062          * \r
1063          * @access private\r
1064          */\r
1065         function getSymbol($chop_flg = FALSE)\r
1066         {\r
1067                 // Implemented a reverse symbol matcher. \r
1068                 // Instead of going front to back, it goes back to front. Steven 26-Apr-2006\r
1069                 $chr_cnt = strlen($this->_curr_expr);\r
1070                 \r
1071                 if ($chr_cnt == 0) return FALSE;\r
1072                 \r
1073                 for ($i = $chr_cnt; $i > 0; $i--) {\r
1074                         $sym_0 = substr($this->_curr_expr,0,$i);\r
1075                         \r
1076                         // Reading string for numeric values\r
1077                         if (is_numeric($sym_0)) {\r
1078                                 \r
1079                                 if ($chop_flg) $this->chopExpr($i);\r
1080                                 return array('input'=>$sym_0, 'tag'=>'mn', 'output'=>$sym_0, 'symlen'=>$i);\r
1081                                 \r
1082                         } elseif (isset($this->_symbol_arr[$sym_0])) {\r
1083                                 \r
1084                                 if ($chop_flg) $this->chopExpr($i);\r
1085                                 $sym_arr = $this->_symbol_arr[$sym_0];\r
1086                                 $sym_arr['symlen'] = $i;\r
1087                                 return $sym_arr;\r
1088                         }\r
1089                 }\r
1090                 \r
1091                 // Reading string for alphabetic constants and the minus sign\r
1092                 $char = $this->_curr_expr{0};\r
1093                 $len_left = $chop_flg ? $this->chopExpr(1) : strlen($this->_curr_expr)-1;\r
1094                 \r
1095                 // Deals with expressions of length 1\r
1096                 if ($len_left == 0 && isset($this->_symbol_arr[$char])) {\r
1097                         $sym_arr = $this->_symbol_arr[$char];\r
1098                         $sym_arr['symlen'] = 1;\r
1099                         return $sym_arr;\r
1100                 } else {\r
1101                         $tag = preg_match('/[a-z]/i',$char) ? 'mi' : 'mo';\r
1102                         return array('input'=>$char, 'tag'=>$tag, 'output'=>$char, 'symlen'=>1);\r
1103                 }\r
1104         }\r
1105         \r
1106         function chopExpr($strlen)\r
1107         {\r
1108                 $this->_prev_expr = $this->_curr_expr;\r
1109                 \r
1110                 if ($strlen == strlen($this->_curr_expr)) {\r
1111                         $this->_curr_expr = '';\r
1112                         return(0);\r
1113                 } else {\r
1114                         $this->_curr_expr = ltrim(substr($this->_curr_expr,$strlen));\r
1115                         return(strlen($this->_curr_expr));\r
1116                 }\r
1117         }       \r
1118 }\r
1119 ?>