2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
5 // $Id: convert_html.php,v 1.2 2004/09/19 14:05:30 henoheno Exp $
8 function convert_html($lines)
10 global $vars, $digest;
11 static $contents_id = 0;
13 if (!is_array($lines))
15 $lines = explode("\n", $lines);
18 $digest = md5(join('', get_source($vars['page'])));
20 $body = &new Body(++$contents_id);
22 $ret = $body->toString();
29 var $parent; // ¿ÆÍ×ÁÇ
30 var $last; // ¼¡¤ËÍ×ÁǤòÁÞÆþ¤¹¤ëÀè
31 var $elements; // Í×ÁǤÎÇÛÎó
35 $this->elements = array();
39 function setParent(&$parent)
41 $this->parent = &$parent;
46 if ($this->canContain($obj))
48 return $this->insert($obj);
51 return $this->parent->add($obj);
54 function &insert(&$obj)
56 $obj->setParent($this);
57 $this->elements[] = &$obj;
59 return $this->last = &$obj->last;
61 function canContain($obj)
66 function wrap($string, $tag, $param = '', $canomit = TRUE)
68 return ($canomit and $string == '') ? '' : "<$tag$param>$string</$tag>";
74 foreach (array_keys($this->elements) as $key)
76 $ret[] = $this->elements[$key]->toString();
79 return join("\n",$ret);
82 function dump($indent = 0)
84 $ret = str_repeat(' ', $indent).get_class($this)."\n";
88 foreach (array_keys($this->elements) as $key)
90 $ret .= is_object($this->elements[$key]) ?
91 $this->elements[$key]->dump($indent) : '';
92 //str_repeat(' ',$indent).$this->elements[$key];
99 // PHP5 mod: $this cannot be reassigned
100 function &Factory_Inline($text)
102 if (substr($text,0,1) == '~') { // ¹ÔƬ~¡£¥Ñ¥é¥°¥é¥Õ³«»Ï
103 return new Paragraph(' '.substr($text,1));
105 return new Inline($text);
108 // PHP5 mod: $this cannot be reassigned
109 function &Factory_DList(&$root, $text)
111 $out = explode('|', ltrim($text), 2);
112 if (count($out) < 2) {
113 return Factory_Inline($text);
116 return new DList($out);
119 // PHP5 mod: $this cannot be reassigned
120 function &Factory_Table(&$root, $text)
122 if (!preg_match("/^\|(.+)\|([hHfFcC]?)$/",$text,$out)) {
123 return Factory_Inline($text);
125 return new Table($out);
128 // PHP5 mod: $this cannot be reassigned
129 function &Factory_YTable(&$root,$text)
131 $_value = csv_explode(',', substr($text,1));
132 if (count($_value) == 0) {
133 return Factory_Inline($text);
135 return new YTable($_value);
138 // PHP5 mod: $this cannot be reassigned
139 function &Factory_Div(&$root,$text)
141 if (!preg_match("/^\#([^\(]+)(?:\((.*)\))?/", $text, $out) or !exist_plugin_convert($out[1])) {
142 return new Paragraph($text);
144 return new Div($out);
147 class Inline extends Element
150 function Inline($text)
154 $this->elements[] = trim((substr($text, 0, 1) == "\n") ? $text : make_link($text));
157 function &insert(&$obj)
159 $this->elements[] = $obj->elements[0];
164 function canContain($obj)
166 return is_a($obj,'Inline');
173 return join($line_break ? "<br />\n" : "\n",$this->elements);
176 function &toPara($class = '')
178 $obj = &new Paragraph('', $class);
184 class Paragraph extends Element
188 function Paragraph($text, $param = '')
192 $this->param = $param;
197 if (substr($text,0,1) == '~')
199 $text = ' '.substr($text, 1);
201 $this->insert(Factory_Inline($text));
204 function canContain($obj)
206 return is_a($obj,'Inline');
211 return $this->wrap(parent::toString(), 'p', $this->param);
215 class Heading extends Element
221 function Heading(&$root, $text)
225 $this->level = min(3, strspn($text, '*'));
226 list($text, $this->msg_top, $this->id) = $root->getAnchor($text, $this->level);
227 $this->insert(Factory_Inline($text));
228 $this->level++; // h2,h3,h4
231 function &insert(&$obj)
233 parent::insert($obj);
234 return $this->last = &$this;
237 function canContain(&$obj)
244 return $this->msg_top.$this->wrap(parent::toString(), 'h'.$this->level, " id=\"{$this->id}\"");
248 class HRule extends Element
251 function HRule(&$root, $text)
256 function canContain(&$obj)
269 class ListContainer extends Element
278 function ListContainer($tag, $tag2, $head, $text)
283 $var_margin = "_{$tag}_margin";
284 $var_left_margin = "_{$tag}_left_margin";
285 global $$var_margin, $$var_left_margin;
286 $this->margin = $$var_margin;
287 $this->left_margin = $$var_left_margin;
292 $this->level = min(3, strspn($text, $head));
293 $text = ltrim(substr($text, $this->level));
295 parent::insert(new ListElement($this->level, $tag2));
298 $this->last = &$this->last->insert(Factory_Inline($text));
302 function canContain(&$obj)
304 return (!is_a($obj, 'ListContainer')
305 or ($this->tag == $obj->tag and $this->level == $obj->level));
308 function setParent(&$parent)
310 global $_list_pad_str;
312 parent::setParent($parent);
314 $step = $this->level;
315 if (isset($parent->parent) and is_a($parent->parent, 'ListContainer'))
317 $step -= $parent->parent->level;
319 $margin = $this->margin * $step;
320 if ($step == $this->level)
322 $margin += $this->left_margin;
324 $this->style = sprintf($_list_pad_str, $this->level, $margin, $margin);
327 function &insert(&$obj)
329 if (!is_a($obj, get_class($this)))
331 return $this->last = &$this->last->insert($obj);
333 // ¹ÔƬʸ»ú¤Î¤ß¤Î»ØÄê»þ¤ÏUL/OL¥Ö¥í¥Ã¥¯¤òæ½Ð
335 if (count($obj->elements) == 1 && count($obj->elements[0]->elements) == 0)
337 return $this->last->parent; // up to ListElement.
340 foreach(array_keys($obj->elements) as $key)
342 parent::insert($obj->elements[$key]);
350 return $this->wrap(parent::toString(), $this->tag, $this->style);
354 class ListElement extends Element
356 function ListElement($level, $head)
359 $this->level = $level;
363 function canContain(&$obj)
365 return (!is_a($obj, 'ListContainer') or ($obj->level > $this->level));
370 return $this->wrap(parent::toString(), $this->head);
374 class UList extends ListContainer
376 function UList(&$root, $text)
378 parent::ListContainer('ul', 'li', '-', $text);
382 class OList extends ListContainer
384 function OList(&$root, $text)
386 parent::ListContainer('ol', 'li', '+', $text);
390 class DList extends ListContainer
394 parent::ListContainer('dl', 'dt', ':', $out[0]);
396 $this->last = &Element::insert(new ListElement($this->level, 'dd'));
399 $this->last = &$this->last->insert(Factory_Inline($out[1]));
404 class BQuote extends Element
408 function BQuote(&$root, $text)
412 $head = substr($text, 0, 1);
413 $this->level = min(3, strspn($text, $head));
414 $text = ltrim(substr($text, $this->level));
416 if ($head == '<') //blockquote close
418 $level = $this->level;
420 $this->last = &$this->end($root, $level);
423 $this->last = &$this->last->insert(Factory_Inline($text));
428 $this->insert(Factory_Inline($text));
432 function canContain(&$obj)
434 return (!is_a($obj, get_class($this)) or $obj->level >= $this->level);
437 function &insert(&$obj)
439 // BugTrack/521, BugTrack/545
440 if (is_a($obj, 'inline')) {
441 return parent::insert($obj->toPara(' class="quotation"'));
443 if (is_a($obj, 'BQuote') and $obj->level == $this->level and count($obj->elements))
445 $obj = &$obj->elements[0];
446 if (is_a($this->last,'Paragraph') and count($obj->elements))
448 $obj = &$obj->elements[0];
451 return parent::insert($obj);
456 return $this->wrap(parent::toString(), 'blockquote');
459 function &end(&$root, $level)
461 $parent = &$root->last;
463 while (is_object($parent))
465 if (is_a($parent,'BQuote') and $parent->level == $level)
467 return $parent->parent;
469 $parent = &$parent->parent;
475 class TableCell extends Element
477 var $tag = 'td'; // {td|th}
480 var $style; // is array('width'=>, 'align'=>...);
482 function TableCell($text, $is_template = FALSE)
485 $this->style = $matches = array();
487 while (preg_match('/^(?:(LEFT|CENTER|RIGHT)|(BG)?COLOR\(([#\w]+)\)|SIZE\((\d+)\)):(.*)$/',$text,$matches))
491 $this->style['align'] = 'text-align:'.strtolower($matches[1]).';';
494 else if ($matches[3])
496 $name = $matches[2] ? 'background-color' : 'color';
497 $this->style[$name] = $name.':'.htmlspecialchars($matches[3]).';';
500 else if ($matches[4])
502 $this->style['size'] = 'font-size:'.htmlspecialchars($matches[4]).'px;';
506 if ($is_template and is_numeric($text))
508 $this->style['width'] = "width:{$text}px;";
514 else if ($text == '~')
518 else if (substr($text, 0, 1) == '~')
521 $text = substr($text, 1);
523 if ($text != '' and $text{0} == '#')
525 // ¥»¥ëÆâÍƤ¬'#'¤Ç»Ï¤Þ¤ë¤È¤¤ÏDiv¥¯¥é¥¹¤òÄ̤·¤Æ¤ß¤ë
526 $obj = &Factory_Div($this, $text);
527 if (is_a($obj, 'Paragraph'))
529 $obj = &$obj->elements[0];
534 $obj = &Factory_Inline($text);
539 function setStyle(&$style)
541 foreach ($style as $key=>$value)
543 if (!array_key_exists($key, $this->style))
545 $this->style[$key] = $value;
552 if ($this->rowspan == 0 or $this->colspan == 0)
556 $param = " class=\"style_{$this->tag}\"";
557 if ($this->rowspan > 1)
559 $param .= " rowspan=\"{$this->rowspan}\"";
561 if ($this->colspan > 1)
563 $param .= " colspan=\"{$this->colspan}\"";
564 unset($this->style['width']);
566 if (count($this->style))
568 $param .= ' style="'.join(' ', $this->style).'"';
571 return $this->wrap(parent::toString(), $this->tag, $param, FALSE);
575 class Table extends Element
579 var $col; // number of column
585 $cells = explode('|', $out[1]);
586 $this->col = count($cells);
587 $this->type = strtolower($out[2]);
588 $this->types = array($this->type);
589 $is_template = ($this->type == 'c');
591 foreach ($cells as $cell)
593 $row[] = &new TableCell($cell, $is_template);
595 $this->elements[] = $row;
598 function canContain(&$obj)
600 return is_a($obj, 'Table') and ($obj->col == $this->col);
603 function &insert(&$obj)
605 $this->elements[] = $obj->elements[0];
606 $this->types[] = $obj->type;
613 static $parts = array('h'=>'thead', 'f'=>'tfoot', ''=>'tbody');
615 // rowspan¤òÀßÄê(²¼¤«¤é¾å¤Ø)
616 for ($ncol = 0; $ncol < $this->col; $ncol++)
619 foreach (array_reverse(array_keys($this->elements)) as $nrow)
621 $row = &$this->elements[$nrow];
622 if ($row[$ncol]->rowspan == 0)
627 $row[$ncol]->rowspan = $rowspan;
628 while (--$rowspan) // ¹Ô¼ïÊ̤ò·Ñ¾µ¤¹¤ë
630 $this->types[$nrow + $rowspan] = $this->types[$nrow];
635 // colspan,style¤òÀßÄê
637 foreach (array_keys($this->elements) as $nrow)
639 $row = &$this->elements[$nrow];
640 if ($this->types[$nrow] == 'c')
645 foreach (array_keys($row) as $ncol)
647 if ($row[$ncol]->colspan == 0)
652 $row[$ncol]->colspan = $colspan;
653 if ($stylerow !== NULL)
655 $row[$ncol]->setStyle($stylerow[$ncol]->style);
656 while (--$colspan) // Îó¥¹¥¿¥¤¥ë¤ò·Ñ¾µ¤¹¤ë
658 $row[$ncol - $colspan]->setStyle($stylerow[$ncol]->style);
666 foreach ($parts as $type => $part)
669 foreach (array_keys($this->elements) as $nrow)
671 if ($this->types[$nrow] != $type)
675 $row = &$this->elements[$nrow];
677 foreach (array_keys($row) as $ncol)
679 $row_string .= $row[$ncol]->toString();
681 $part_string .= $this->wrap($row_string, 'tr');
683 $string .= $this->wrap($part_string, $part);
685 $string = $this->wrap($string, 'table', ' class="style_table" cellspacing="1" border="0"');
686 return $this->wrap($string, 'div', ' class="ie5"');
690 class YTable extends Element
694 function YTable($_value)
698 $align = $value = $matches = array();
699 foreach($_value as $val)
701 if (preg_match('/^(\s+)?(.+?)(\s+)?$/', $val, $matches))
703 $align[] =($matches[1] != '') ?
704 ((array_key_exists(3,$matches) and $matches[3] != '') ?
705 ' style="text-align:center"' : ' style="text-align:right"'
707 $value[] = $matches[2];
715 $this->col = count($value);
717 foreach ($value as $val)
719 $colspan[] = ($val == '==') ? 0 : 1;
722 for ($i = 0; $i < count($value); $i++)
726 while ($i + $colspan[$i] < count($value) and $value[$i + $colspan[$i]] == '==')
730 $colspan[$i] = ($colspan[$i] > 1) ? " colspan=\"{$colspan[$i]}\"" : '';
731 $str .= "<td class=\"style_td\"{$align[$i]}{$colspan[$i]}>".make_link($value[$i]).'</td>';
734 $this->elements[] = $str;
737 function canContain(&$obj)
739 return is_a($obj, 'YTable') and ($obj->col == $this->col);
742 function &insert(&$obj)
744 $this->elements[] = $obj->elements[0];
752 foreach ($this->elements as $str)
754 $rows .= "\n<tr class=\"style_tr\">$str</tr>\n";
756 $rows = $this->wrap($rows, 'table', ' class="style_table" cellspacing="1" border="0"');
757 return $this->wrap($rows, 'div', ' class="ie5"');
761 class Pre extends Element
763 function Pre(&$root,$text)
765 global $preformat_ltrim;
768 $this->elements[] = htmlspecialchars(
769 (!$preformat_ltrim or $text == '' or $text{0} != ' ') ? $text : substr($text, 1)
773 function canContain(&$obj)
775 return is_a($obj, 'Pre');
778 function &insert(&$obj)
780 $this->elements[] = $obj->elements[0];
787 return $this->wrap(join("\n", $this->elements), 'pre');
791 class Div extends Element
800 list(, $this->name, $this->param) = array_pad($out,3,'');
803 function canContain(&$obj)
810 return do_plugin_convert($this->name,$this->param);
814 class Align extends Element
815 { // LEFT:/CENTER:/RIGHT:
818 function Align($align)
822 $this->align = $align;
825 function canContain(&$obj)
827 return is_a($obj, 'Inline');
832 return $this->wrap(parent::toString(), 'div', ' style="text-align:'.$this->align.'"');
836 class Body extends Element
842 var $classes = array(
848 var $factories = array(
858 $this->contents = &new Element();
859 $this->contents_last = &$this->contents;
863 function parse(&$lines)
865 $this->last = &$this;
867 while (count($lines))
869 $line = array_shift($lines);
871 if (substr($line,0,2) == '//') //¥³¥á¥ó¥È¤Ï½èÍý¤·¤Ê¤¤
876 if (preg_match('/^(LEFT|CENTER|RIGHT):(.*)$/',$line,$matches))
878 $this->last = &$this->last->add(new Align(strtolower($matches[1]))); // <div style="text-align:...">
879 if ($matches[2] == '')
886 $line = preg_replace("/[\r\n]*$/",'',$line);
891 $this->last = &$this;
895 if (substr($line,0,4) == '----')
897 $this->insert(new HRule($this,$line));
906 $this->insert(new Heading($this,$line));
910 if ($head == ' ' or $head == "\t")
912 $this->last = &$this->last->add(new Pre($this,$line));
916 if (substr($line,-1) == '~')
918 $line = substr($line,0,-1)."\r";
921 if (array_key_exists($head, $this->classes))
923 $classname = $this->classes[$head];
924 $this->last = &$this->last->add(new $classname($this,$line));
928 if (array_key_exists($head, $this->factories))
930 $factoryname = 'Factory_'. $this->factories[$head];
931 $this->last = &$this->last->add($factoryname($this,$line));
936 $this->last = &$this->last->add(Factory_Inline($line));
940 function getAnchor($text,$level)
942 global $top,$_symbol_anchor;
944 $anchor = (($id = make_heading($text,FALSE)) == '') ?
945 '' : " &aname($id,super,full)\{$_symbol_anchor};";
947 $id = "content_{$this->id}_{$this->count}";
949 $this->contents_last = &$this->contents_last->add(new Contents_UList($text,$level,$id));
951 return array($text. $anchor, $this->count > 1 ? "\n$top" : '', $id);
954 function &insert(&$obj)
956 if (is_a($obj, 'Inline'))
958 $obj = &$obj->toPara();
960 return parent::insert($obj);
967 $text = parent::toString();
970 $text = preg_replace_callback('/(<p[^>]*>)<del>#contents<\/del>(\s*)(<\/p>)/', array(&$this,'replace_contents'),$text);
973 // <p>¤Î¤È¤¤Ï¹ÔƬ¤«¤é¡¢<del>¤Î¤È¤¤Ï¾¤ÎÍ×ÁǤλÒÍ×ÁǤȤ·¤Æ¸ºß
974 $text = preg_replace_callback('/(<p[^>]*>)<del>#related<\/del>(\s*)(<\/p>)/', array(&$this, 'replace_related'), $text);
975 $text = preg_replace('/<del>#related<\/del>/',make_related($vars['page'],'del'),$text);
979 function replace_contents($arr)
981 $contents = "<div class=\"contents\">\n";
982 $contents .= "<a id=\"contents_{$this->id}\"></a>";
983 $contents .= $this->contents->toString();
984 $contents .= "</div>\n";
987 return ($arr[1] != '') ? $contents.join('',$arr) : $contents;
990 function replace_related($arr)
993 static $related = NULL;
995 if (is_null($related))
997 $related = make_related($vars['page'],'p');
1001 return ($arr[1] != '') ? $related.join('',$arr) : $related;
1005 class Contents_UList extends ListContainer
1007 function Contents_UList($text,$level,$id)
1009 // ¥Æ¥¥¹¥È¤Î¥ê¥Õ¥©¡¼¥à
1010 // ¹ÔƬ\n¤ÇÀ°·ÁºÑ¤ß¤òɽ¤¹ ... X(
1011 make_heading($text);
1012 $text = "\n<a href=\"#$id\">$text</a>\n";
1013 parent::ListContainer('ul', 'li', '-', str_repeat('-',$level));
1014 $this->insert(Factory_Inline($text));
1017 function setParent(&$parent)
1019 global $_list_pad_str;
1021 parent::setParent($parent);
1022 $step = $this->level;
1023 $margin = $this->left_margin;
1024 if (isset($parent->parent) and is_a($parent->parent,'ListContainer'))
1026 $step -= $parent->parent->level;
1029 $margin += $this->margin * ($step == $this->level ? 1 : $step);
1030 $this->style = sprintf($_list_pad_str,$this->level,$margin,$margin);