OSDN Git Service

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