OSDN Git Service

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