OSDN Git Service

Reform $splitter => define PKWK_SPLITTER
[pukiwiki/pukiwiki.git] / lib / convert_html.php
1 <?php
2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
4 //
5 // $Id: convert_html.php,v 1.3 2004/10/07 14:15:27 henoheno Exp $
6 //
7
8 function convert_html($lines)
9 {
10         global $vars, $digest;
11         static $contents_id = 0;
12
13         // Set digest
14         $digest = md5(join('', get_source($vars['page'])));
15
16         if (! is_array($lines)) $lines = explode("\n", $lines);
17
18         $body = & new Body(++$contents_id);
19         $body->parse($lines);
20
21         return $body->toString();
22 }
23
24 class Element
25 { // ¥Ö¥í¥Ã¥¯Í×ÁÇ
26         var $parent;   // ¿ÆÍ×ÁÇ
27         var $last;     // ¼¡¤ËÍ×ÁǤòÁÞÆþ¤¹¤ëÀè
28         var $elements; // Í×ÁǤÎÇÛÎó
29
30         function Element()
31         {
32                 $this->elements = array();
33                 $this->last     = & $this;
34         }
35
36         function setParent(& $parent)
37         {
38                 $this->parent = & $parent;
39         }
40
41         function & add(& $obj)
42         {
43                 if ($this->canContain($obj)) {
44                         return $this->insert($obj);
45                 } else {
46                         return $this->parent->add($obj);
47                 }
48         }
49
50         function & insert(& $obj)
51         {
52                 $obj->setParent($this);
53                 $this->elements[] = & $obj;
54
55                 return $this->last = & $obj->last;
56         }
57
58         function canContain($obj)
59         {
60                 return TRUE;
61         }
62
63         function wrap($string, $tag, $param = '', $canomit = TRUE)
64         {
65                 return ($canomit && $string == '') ? '' : "<$tag$param>$string</$tag>";
66         }
67
68         function toString()
69         {
70                 $ret = array();
71                 foreach (array_keys($this->elements) as $key) {
72                         $ret[] = $this->elements[$key]->toString();
73                 }
74
75                 return join("\n", $ret);
76         }
77
78         function dump($indent = 0)
79         {
80                 $ret = str_repeat(' ', $indent) . get_class($this) . "\n";
81
82                 $indent += 2;
83
84                 foreach (array_keys($this->elements) as $key) {
85                         $ret .= is_object($this->elements[$key]) ?
86                                 $this->elements[$key]->dump($indent) : '';
87                                 //str_repeat(' ',$indent).$this->elements[$key];
88                 }
89
90                 return $ret;
91         }
92 }
93
94 function & Factory_Inline($text)
95 {
96         if (substr($text, 0, 1) == '~') {
97                 // ¹ÔƬ '~' ¡£¥Ñ¥é¥°¥é¥Õ³«»Ï
98                 return new Paragraph(' ' . substr($text, 1));
99         } else {
100                 return new Inline($text);
101         }
102 }
103
104 function & Factory_DList(& $root, $text)
105 {
106         $out = explode('|', ltrim($text), 2);
107         if (count($out) < 2) {
108                 return Factory_Inline($text);
109         } else {
110                 return new DList($out);
111         }
112 }
113
114 function & Factory_Table(& $root, $text)
115 {
116         if (! preg_match("/^\|(.+)\|([hHfFcC]?)$/", $text, $out)) {
117                 return Factory_Inline($text);
118         } else {
119                 return new Table($out);
120         }
121 }
122
123 function & Factory_YTable(& $root, $text)
124 {
125         $_value = csv_explode(',', substr($text, 1));
126         if (count($_value) == 0) {
127                 return Factory_Inline($text);
128         } else {
129                 return new YTable($_value);
130         }
131 }
132
133 function & Factory_Div(& $root, $text)
134 {
135         if (! preg_match("/^\#([^\(]+)(?:\((.*)\))?/", $text, $out) || ! exist_plugin_convert($out[1])) {
136                 return new Paragraph($text);
137         } else {
138                 return new Div($out);
139         }
140 }
141
142 // ¥¤¥ó¥é¥¤¥óÍ×ÁÇ
143 class Inline extends Element
144 {
145         function Inline($text)
146         {
147                 parent::Element();
148                 $this->elements[] = trim((substr($text, 0, 1) == "\n") ? $text : make_link($text));
149         }
150
151         function & insert(& $obj)
152         {
153                 $this->elements[] = $obj->elements[0];
154                 return $this;
155         }
156
157         function canContain($obj)
158         {
159                 return is_a($obj, 'Inline');
160         }
161
162         function toString()
163         {
164                 global $line_break;
165                 return join($line_break ? "<br />\n" : "\n", $this->elements);
166         }
167
168         function & toPara($class = '')
169         {
170                 $obj = & new Paragraph('', $class);
171                 $obj->insert($this);
172                 return $obj;
173         }
174 }
175
176 class Paragraph extends Element
177 { // ÃÊÍî
178         var $param;
179
180         function Paragraph($text, $param = '')
181         {
182                 parent::Element();
183
184                 $this->param = $param;
185
186                 if ($text == '') return;
187
188                 if (substr($text,0,1) == '~')
189                         $text = ' ' . substr($text, 1);
190
191                 $this->insert(Factory_Inline($text));
192         }
193
194         function canContain($obj)
195         {
196                 return is_a($obj, 'Inline');
197         }
198
199         function toString()
200         {
201                 return $this->wrap(parent::toString(), 'p', $this->param);
202         }
203 }
204
205 // * Heading1
206 // ** Heading2
207 // *** Heading3
208 class Heading extends Element
209 {
210         var $level;
211         var $id;
212         var $msg_top;
213
214         function Heading(& $root, $text)
215         {
216                 parent::Element();
217
218                 $this->level = min(3, strspn($text, '*'));
219                 list($text, $this->msg_top, $this->id) = $root->getAnchor($text, $this->level);
220                 $this->insert(Factory_Inline($text));
221                 $this->level++; // h2,h3,h4
222         }
223
224         function & insert(& $obj)
225         {
226                 parent::insert($obj);
227                 return $this->last = & $this;
228         }
229
230         function canContain(& $obj)
231         {
232                 return FALSE;
233         }
234
235         function toString()
236         {
237                 return $this->msg_top . $this->wrap(parent::toString(), 'h' . $this->level, " id=\"{$this->id}\"");
238         }
239 }
240
241 // ----
242 class HRule extends Element
243 {
244         function HRule(& $root, $text)
245         {
246                 parent::Element();
247         }
248
249         function canContain(& $obj)
250         {
251                 return FALSE;
252         }
253
254         function toString()
255         {
256                 global $hr;
257                 return $hr;
258         }
259 }
260
261 class ListContainer extends Element
262 {
263         var $tag;
264         var $tag2;
265         var $level;
266         var $style;
267         var $margin;
268         var $left_margin;
269
270         function ListContainer($tag, $tag2, $head, $text)
271         {
272                 parent::Element();
273
274                 //¥Þ¡¼¥¸¥ó¤ò¼èÆÀ
275                 $var_margin      = "_{$tag}_margin";
276                 $var_left_margin = "_{$tag}_left_margin";
277
278                 global $$var_margin, $$var_left_margin;
279
280                 $this->margin      = $$var_margin;
281                 $this->left_margin = $$var_left_margin;
282
283                 //½é´ü²½
284                 $this->tag   = $tag;
285                 $this->tag2  = $tag2;
286                 $this->level = min(3, strspn($text, $head));
287                 $text = ltrim(substr($text, $this->level));
288
289                 parent::insert(new ListElement($this->level, $tag2));
290                 if ($text != '')
291                         $this->last = & $this->last->insert(Factory_Inline($text));
292         }
293
294         function canContain(& $obj)
295         {
296                 return (! is_a($obj, 'ListContainer')
297                         || ($this->tag == $obj->tag && $this->level == $obj->level));
298         }
299
300         function setParent(& $parent)
301         {
302                 global $_list_pad_str;
303
304                 parent::setParent($parent);
305
306                 $step = $this->level;
307                 if (isset($parent->parent) && is_a($parent->parent, 'ListContainer')) {
308                         $step -= $parent->parent->level;
309                 }
310                 $margin = $this->margin * $step;
311                 if ($step == $this->level) {
312                         $margin += $this->left_margin;
313                 }
314                 $this->style = sprintf($_list_pad_str, $this->level, $margin, $margin);
315         }
316
317         function & insert(& $obj)
318         {
319                 if (! is_a($obj, get_class($this)))
320                         return $this->last = & $this->last->insert($obj);
321
322                 // ¹ÔƬʸ»ú¤Î¤ß¤Î»ØÄê»þ¤ÏUL/OL¥Ö¥í¥Ã¥¯¤òæ½Ð
323                 // BugTrack/524
324                 if (count($obj->elements) == 1 && empty($obj->elements[0]->elements))
325                         return $this->last->parent; // up to ListElement.
326
327                 // move elements.
328                 foreach(array_keys($obj->elements) as $key)
329                         parent::insert($obj->elements[$key]);
330
331                 return $this->last;
332         }
333
334         function toString()
335         {
336                 return $this->wrap(parent::toString(), $this->tag, $this->style);
337         }
338 }
339
340 class ListElement extends Element
341 {
342         function ListElement($level, $head)
343         {
344                 parent::Element();
345                 $this->level = $level;
346                 $this->head = $head;
347         }
348
349         function canContain(& $obj)
350         {
351                 return (! is_a($obj, 'ListContainer') || ($obj->level > $this->level));
352         }
353
354         function toString()
355         {
356                 return $this->wrap(parent::toString(), $this->head);
357         }
358 }
359
360 // - One
361 // - Two
362 // - Three
363 class UList extends ListContainer
364 {
365         function UList(& $root, $text)
366         {
367                 parent::ListContainer('ul', 'li', '-', $text);
368         }
369 }
370
371 // + One
372 // + Two
373 // + Three
374 class OList extends ListContainer
375 {
376         function OList(& $root, $text)
377         {
378                 parent::ListContainer('ol', 'li', '+', $text);
379         }
380 }
381
382 // : definition1 | description1
383 // : definition2 | description2
384 // : definition3 | description3
385 class DList extends ListContainer
386 {
387         function DList($out)
388         {
389                 parent::ListContainer('dl', 'dt', ':', $out[0]);
390
391                 $this->last = & Element::insert(new ListElement($this->level, 'dd'));
392                 if ($out[1] != '')
393                 {
394                         $this->last = & $this->last->insert(Factory_Inline($out[1]));
395                 }
396         }
397 }
398
399 // > Someting cited
400 // > like E-mail text
401 class BQuote extends Element
402 {
403         var $level;
404
405         function BQuote(& $root, $text)
406         {
407                 parent::Element();
408
409                 $head = substr($text, 0, 1);
410                 $this->level = min(3, strspn($text, $head));
411                 $text = ltrim(substr($text, $this->level));
412
413                 if ($head == '<') { // Blockquote close
414                         $level       = $this->level;
415                         $this->level = 0;
416                         $this->last  = & $this->end($root, $level);
417                         if ($text != '')
418                                 $this->last = & $this->last->insert(Factory_Inline($text));
419                 } else {
420                         $this->insert(Factory_Inline($text));
421                 }
422         }
423
424         function canContain(& $obj)
425         {
426                 return (! is_a($obj, get_class($this)) || $obj->level >= $this->level);
427         }
428
429         function & insert(& $obj)
430         {
431                 // BugTrack/521, BugTrack/545
432                 if (is_a($obj, 'inline'))
433                         return parent::insert($obj->toPara(' class="quotation"'));
434
435                 if (is_a($obj, 'BQuote') && $obj->level == $this->level && count($obj->elements)) {
436                         $obj = & $obj->elements[0];
437                         if (is_a($this->last, 'Paragraph') && count($obj->elements))
438                                 $obj = & $obj->elements[0];
439                 }
440                 return parent::insert($obj);
441         }
442
443         function toString()
444         {
445                 return $this->wrap(parent::toString(), 'blockquote');
446         }
447
448         function & end(& $root, $level)
449         {
450                 $parent = & $root->last;
451
452                 while (is_object($parent)) {
453                         if (is_a($parent, 'BQuote') && $parent->level == $level)
454                                 return $parent->parent;
455                         $parent = & $parent->parent;
456                 }
457                 return $this;
458         }
459 }
460
461 class TableCell extends Element
462 {
463         var $tag = 'td'; // {td|th}
464         var $colspan = 1;
465         var $rowspan = 1;
466         var $style; // is array('width'=>, 'align'=>...);
467
468         function TableCell($text, $is_template = FALSE)
469         {
470                 parent::Element();
471                 $this->style = $matches = array();
472
473                 while (preg_match('/^(?:(LEFT|CENTER|RIGHT)|(BG)?COLOR\(([#\w]+)\)|SIZE\((\d+)\)):(.*)$/',$text,$matches))
474                 {
475                         if ($matches[1])
476                         {
477                                 $this->style['align'] = 'text-align:'.strtolower($matches[1]).';';
478                                 $text = $matches[5];
479                         }
480                         else if ($matches[3])
481                         {
482                                 $name = $matches[2] ? 'background-color' : 'color';
483                                 $this->style[$name] = $name.':'.htmlspecialchars($matches[3]).';';
484                                 $text = $matches[5];
485                         }
486                         else if ($matches[4])
487                         {
488                                 $this->style['size'] = 'font-size:'.htmlspecialchars($matches[4]).'px;';
489                                 $text = $matches[5];
490                         }
491                 }
492                 if ($is_template && is_numeric($text)) {
493                         $this->style['width'] = "width:{$text}px;";
494                 }
495
496                 if ($text == '>') {
497                         $this->colspan = 0;
498                 } else if ($text == '~') {
499                         $this->rowspan = 0;
500                 } else if (substr($text, 0, 1) == '~') {
501                         $this->tag = 'th';
502                         $text      = substr($text, 1);
503                 }
504
505                 if ($text != '' && $text{0} == '#') {
506                         // ¥»¥ëÆâÍƤ¬'#'¤Ç»Ï¤Þ¤ë¤È¤­¤ÏDiv¥¯¥é¥¹¤òÄ̤·¤Æ¤ß¤ë
507                         $obj = & Factory_Div($this, $text);
508                         if (is_a($obj, 'Paragraph')) {
509                                 $obj = & $obj->elements[0];
510                         }
511                 } else {
512                         $obj = & Factory_Inline($text);
513                 }
514
515                 $this->insert($obj);
516         }
517
518         function setStyle(& $style)
519         {
520                 foreach ($style as $key=>$value) {
521                         if (! isset($this->style[$key]))
522                                 $this->style[$key] = $value;
523                 }
524         }
525
526         function toString()
527         {
528                 if ($this->rowspan == 0 || $this->colspan == 0) return '';
529
530                 $param = " class=\"style_{$this->tag}\"";
531                 if ($this->rowspan > 1) {
532                         $param .= " rowspan=\"{$this->rowspan}\"";
533                 }
534                 if ($this->colspan > 1) {
535                         $param .= " colspan=\"{$this->colspan}\"";
536                         unset($this->style['width']);
537                 }
538                 if (! empty($this->style)) {
539                         $param .= ' style="' . join(' ', $this->style) . '"';
540                 }
541
542                 return $this->wrap(parent::toString(), $this->tag, $param, FALSE);
543         }
544 }
545
546 // | title1 | title2 | title3 |
547 // | cell1  | cell2  | cell3  |
548 // | cell4  | cell5  | cell6  |
549 class Table extends Element
550 {
551         var $type;
552         var $types;
553         var $col; // number of column
554
555         function Table($out)
556         {
557                 parent::Element();
558
559                 $cells       = explode('|', $out[1]);
560                 $this->col   = count($cells);
561                 $this->type  = strtolower($out[2]);
562                 $this->types = array($this->type);
563                 $is_template = ($this->type == 'c');
564                 $row = array();
565                 foreach ($cells as $cell) {
566                         $row[] = & new TableCell($cell, $is_template);
567                 }
568                 $this->elements[] = $row;
569         }
570
571         function canContain(& $obj)
572         {
573                 return is_a($obj, 'Table') && ($obj->col == $this->col);
574         }
575
576         function & insert(& $obj)
577         {
578                 $this->elements[] = $obj->elements[0];
579                 $this->types[]    = $obj->type;
580                 return $this;
581         }
582
583         function toString()
584         {
585                 static $parts = array('h'=>'thead', 'f'=>'tfoot', ''=>'tbody');
586
587                 // rowspan¤òÀßÄê(²¼¤«¤é¾å¤Ø)
588                 for ($ncol = 0; $ncol < $this->col; $ncol++) {
589                         $rowspan = 1;
590                         foreach (array_reverse(array_keys($this->elements)) as $nrow) {
591                                 $row = & $this->elements[$nrow];
592                                 if ($row[$ncol]->rowspan == 0) {
593                                         ++$rowspan;
594                                         continue;
595                                 }
596                                 $row[$ncol]->rowspan = $rowspan;
597                                 while (--$rowspan) { // ¹Ô¼ïÊ̤ò·Ñ¾µ¤¹¤ë
598                                         $this->types[$nrow + $rowspan] = $this->types[$nrow];
599                                 }
600                                 $rowspan = 1;
601                         }
602                 }
603
604                 // colspan,style¤òÀßÄê
605                 $stylerow = NULL;
606                 foreach (array_keys($this->elements) as $nrow) {
607                         $row = & $this->elements[$nrow];
608                         if ($this->types[$nrow] == 'c') {
609                                 $stylerow = & $row;
610                         }
611                         $colspan = 1;
612                         foreach (array_keys($row) as $ncol) {
613                                 if ($row[$ncol]->colspan == 0) {
614                                         ++$colspan;
615                                         continue;
616                                 }
617                                 $row[$ncol]->colspan = $colspan;
618                                 if ($stylerow !== NULL) {
619                                         $row[$ncol]->setStyle($stylerow[$ncol]->style);
620                                         while (--$colspan) { // Îó¥¹¥¿¥¤¥ë¤ò·Ñ¾µ¤¹¤ë
621                                                 $row[$ncol - $colspan]->setStyle($stylerow[$ncol]->style);
622                                         }
623                                 }
624                                 $colspan = 1;
625                         }
626                 }
627
628                 // ¥Æ¥­¥¹¥È²½
629                 $string = '';
630                 foreach ($parts as $type => $part)
631                 {
632                         $part_string = '';
633                         foreach (array_keys($this->elements) as $nrow) {
634                                 if ($this->types[$nrow] != $type)
635                                         continue;
636                                 $row        = & $this->elements[$nrow];
637                                 $row_string = '';
638                                 foreach (array_keys($row) as $ncol) {
639                                         $row_string .= $row[$ncol]->toString();
640                                 }
641                                 $part_string .= $this->wrap($row_string, 'tr');
642                         }
643                         $string .= $this->wrap($part_string, $part);
644                 }
645                 $string = $this->wrap($string, 'table', ' class="style_table" cellspacing="1" border="0"');
646
647                 return $this->wrap($string, 'div', ' class="ie5"');
648         }
649 }
650
651 // , title1 , title2 , title3
652 // , cell1  , cell2  , cell3
653 // , cell4  , cell5  , cell6
654 class YTable extends Element
655 {
656         var $col;
657
658         function YTable($_value)
659         {
660                 parent::Element();
661
662                 $align = $value = $matches = array();
663                 foreach($_value as $val) {
664                         if (preg_match('/^(\s+)?(.+?)(\s+)?$/', $val, $matches)) {
665                                 $align[] =($matches[1] != '') ?
666                                         ((isset($matches[3]) && $matches[3] != '') ?
667                                                 ' style="text-align:center"' :
668                                                 ' style="text-align:right"'
669                                         ) : '';
670                                 $value[] = $matches[2];
671                         } else {
672                                 $align[] = '';
673                                 $value[] = $val;
674                         }
675                 }
676                 $this->col = count($value);
677                 $colspan = array();
678                 foreach ($value as $val) {
679                         $colspan[] = ($val == '==') ? 0 : 1;
680                 }
681                 $str = '';
682                 for ($i = 0; $i < count($value); $i++) {
683                         if ($colspan[$i]) {
684                                 while ($i + $colspan[$i] < count($value) && $value[$i + $colspan[$i]] == '==') {
685                                         $colspan[$i]++;
686                                 }
687                                 $colspan[$i] = ($colspan[$i] > 1) ? " colspan=\"{$colspan[$i]}\"" : '';
688                                 $str .= "<td class=\"style_td\"{$align[$i]}{$colspan[$i]}>".make_link($value[$i]).'</td>';
689                         }
690                 }
691                 $this->elements[] = $str;
692         }
693
694         function canContain(& $obj)
695         {
696                 return is_a($obj, 'YTable') && ($obj->col == $this->col);
697         }
698
699         function & insert(& $obj)
700         {
701                 $this->elements[] = $obj->elements[0];
702                 return $this;
703         }
704
705         function toString()
706         {
707                 $rows = '';
708                 foreach ($this->elements as $str) {
709                         $rows .= "\n<tr class=\"style_tr\">$str</tr>\n";
710                 }
711                 $rows = $this->wrap($rows, 'table', ' class="style_table" cellspacing="1" border="0"');
712                 return $this->wrap($rows, 'div', ' class="ie5"');
713         }
714 }
715
716 // ' 'Space-beginning sentence
717 // ' 'Space-beginning sentence
718 // ' 'Space-beginning sentence
719 class Pre extends Element
720 {
721         function Pre(& $root, $text)
722         {
723                 global $preformat_ltrim;
724
725                 parent::Element();
726                 $this->elements[] = htmlspecialchars(
727                         (! $preformat_ltrim || $text == '' || $text{0} != ' ') ? $text : substr($text, 1)
728                 );
729         }
730
731         function canContain(& $obj)
732         {
733                 return is_a($obj, 'Pre');
734         }
735
736         function & insert(& $obj)
737         {
738                 $this->elements[] = $obj->elements[0];
739                 return $this;
740         }
741
742         function toString()
743         {
744                 return $this->wrap(join("\n", $this->elements), 'pre');
745         }
746 }
747
748 // #someting(started with '#')
749 class Div extends Element
750 {
751         var $name;
752         var $param;
753
754         function Div($out)
755         {
756                 parent::Element();
757                 list(, $this->name, $this->param) = array_pad($out, 3, '');
758         }
759
760         function canContain(& $obj)
761         {
762                 return FALSE;
763         }
764
765         function toString()
766         {
767                 return do_plugin_convert($this->name, $this->param);
768         }
769 }
770
771 // LEFT:/CENTER:/RIGHT:
772 class Align extends Element
773 {
774         var $align;
775
776         function Align($align)
777         {
778                 parent::Element();
779                 $this->align = $align;
780         }
781
782         function canContain(& $obj)
783         {
784                 return is_a($obj, 'Inline');
785         }
786
787         function toString()
788         {
789                 return $this->wrap(parent::toString(), 'div', ' style="text-align:' . $this->align . '"');
790         }
791 }
792
793 // Body
794 class Body extends Element
795 {
796         var $id;
797         var $count = 0;
798         var $contents;
799         var $contents_last;
800         var $classes = array(
801                 '-' => 'UList',
802                 '+' => 'OList',
803                 '>' => 'BQuote',
804                 '<' => 'BQuote'
805         );
806         var $factories = array(
807                 ':' => 'DList',
808                 '|' => 'Table',
809                 ',' => 'YTable',
810                 '#' => 'Div'
811         );
812
813         function Body($id)
814         {
815                 $this->id            = $id;
816                 $this->contents      = & new Element();
817                 $this->contents_last = & $this->contents;
818                 parent::Element();
819         }
820
821         function parse(& $lines)
822         {
823                 $this->last = & $this;
824                 $matches = array();
825
826                 while (! empty($lines)) {
827                         $line = array_shift($lines);
828
829                         // Escape comments
830                         if (substr($line, 0, 2) == '//') continue;
831
832                         if (preg_match('/^(LEFT|CENTER|RIGHT):(.*)$/', $line, $matches)) {
833                                 // <div style="text-align:...">
834                                 $this->last = & $this->last->add(new Align(strtolower($matches[1])));
835
836                                 if ($matches[2] == '') continue;
837
838                                 $line = $matches[2];
839                         }
840
841                         $line = preg_replace("/[\r\n]*$/", '', $line);
842
843                         // Empty
844                         if ($line == '') {
845                                 $this->last = & $this;
846                                 continue;
847                         }
848
849                         // Horizontal Rule
850                         if (substr($line, 0, 4) == '----') {
851                                 $this->insert(new HRule($this, $line));
852                                 continue;
853                         }
854
855                         // The first character
856                         $head = $line{0};
857
858                         // Heading
859                         if ($head == '*') {
860                                 $this->insert(new Heading($this, $line));
861                                 continue;
862                         }
863
864                         // Pre
865                         if ($head == ' ' || $head == "\t") {
866                                 $this->last = & $this->last->add(new Pre($this, $line));
867                                 continue;
868                         }
869
870                         // Line Break
871                         if (substr($line, -1) == '~') {
872                                 $line = substr($line,0,-1)."\r";
873                         }
874                         
875                         // Other Character
876                         if (isset($this->classes[$head])) {
877                                 $classname  = $this->classes[$head];
878                                 $this->last = & $this->last->add(new $classname($this, $line));
879                                 continue;
880                         }
881
882                         // Other Character
883                         if (isset($this->factories[$head])) {
884                                 $factoryname = 'Factory_' . $this->factories[$head];
885                                 $this->last  = & $this->last->add($factoryname($this, $line));
886                                 continue;
887                         }
888
889                         // Default
890                         $this->last = & $this->last->add(Factory_Inline($line));
891                 }
892         }
893
894         function getAnchor($text, $level)
895         {
896                 global $top, $_symbol_anchor;
897
898                 $anchor = (($id = make_heading($text, FALSE)) == '') ?
899                         '' : " &aname($id,super,full)\{$_symbol_anchor};";
900                 $text = ' ' . $text;
901                 $id = "content_{$this->id}_{$this->count}";
902                 $this->count++;
903                 $this->contents_last = & $this->contents_last->add(new Contents_UList($text, $level, $id));
904
905                 return array($text. $anchor, $this->count > 1 ? "\n$top" : '', $id);
906         }
907
908         function & insert(& $obj)
909         {
910                 if (is_a($obj, 'Inline')) $obj = & $obj->toPara();
911                 return parent::insert($obj);
912         }
913
914         function toString()
915         {
916                 global $vars;
917
918                 $text = parent::toString();
919
920                 // #contents
921                 $text = preg_replace_callback('/(<p[^>]*>)<del>#contents<\/del>(\s*)(<\/p>)/', array(& $this, 'replace_contents'), $text);
922
923                 // ´ØÏ¢¤¹¤ë¥Ú¡¼¥¸
924                 // <p>¤Î¤È¤­¤Ï¹ÔƬ¤«¤é¡¢<del>¤Î¤È¤­¤Ï¾¤ÎÍ×ÁǤλÒÍ×ÁǤȤ·¤Æ¸ºß
925                 $text = preg_replace_callback('/(<p[^>]*>)<del>#related<\/del>(\s*)(<\/p>)/', array(& $this, 'replace_related'), $text);
926                 $text = preg_replace('/<del>#related<\/del>/', make_related($vars['page'], 'del'), $text);
927                 return "$text\n";
928         }
929
930         function replace_contents($arr)
931         {
932                 $contents  = "<div class=\"contents\">\n";
933                 $contents .= "<a id=\"contents_{$this->id}\"></a>";
934                 $contents .= $this->contents->toString();
935                 $contents .= "</div>\n";
936                 array_shift($arr);
937
938                 return ($arr[1] != '') ? $contents . join('', $arr) : $contents;
939         }
940
941         function replace_related($arr)
942         {
943                 global $vars;
944                 static $related = NULL;
945
946                 if (is_null($related))
947                         $related = make_related($vars['page'],'p');
948
949                 array_shift($arr);
950
951                 return ($arr[1] != '') ? $related . join('', $arr) : $related;
952         }
953 }
954
955 class Contents_UList extends ListContainer
956 {
957         function Contents_UList($text, $level, $id)
958         {
959                 // ¥Æ¥­¥¹¥È¤Î¥ê¥Õ¥©¡¼¥à
960                 // ¹ÔƬ\n¤ÇÀ°·ÁºÑ¤ß¤òɽ¤¹ ... X(
961                 make_heading($text);
962                 $text = "\n<a href=\"#$id\">$text</a>\n";
963                 parent::ListContainer('ul', 'li', '-', str_repeat('-', $level));
964                 $this->insert(Factory_Inline($text));
965         }
966
967         function setParent(& $parent)
968         {
969                 global $_list_pad_str;
970
971                 parent::setParent($parent);
972                 $step   = $this->level;
973                 $margin = $this->left_margin;
974                 if (isset($parent->parent) && is_a($parent->parent, 'ListContainer')) {
975                         $step  -= $parent->parent->level;
976                         $margin = 0;
977                 }
978                 $margin += $this->margin * ($step == $this->level ? 1 : $step);
979                 $this->style = sprintf($_list_pad_str, $this->level, $margin, $margin);
980         }
981 }
982 ?>