2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
5 // $Id: convert_html.php,v 1.1 2004/08/01 01:54:35 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 class Inline extends Element
102 function Inline($text)
106 if (substr($text,0,1) == '~') // ¹ÔƬ~¡£¥Ñ¥é¥°¥é¥Õ³«»Ï
108 $this = new Paragraph(' '.substr($text,1));
109 $this->last = &$this;
113 $this->elements[] = trim((substr($text, 0, 1) == "\n") ? $text : make_link($text));
116 function &insert(&$obj)
118 $this->elements[] = $obj->elements[0];
123 function canContain($obj)
125 return is_a($obj,'Inline');
132 return join($line_break ? "<br />\n" : "\n",$this->elements);
135 function &toPara($class = '')
137 $obj = &new Paragraph('', $class);
143 class Paragraph extends Element
147 function Paragraph($text, $param = '')
151 $this->param = $param;
156 if (substr($text,0,1) == '~')
158 $text = ' '.substr($text, 1);
160 $this->insert(new Inline($text));
163 function canContain($obj)
165 return is_a($obj,'Inline');
170 return $this->wrap(parent::toString(), 'p', $this->param);
174 class Heading extends Element
180 function Heading(&$root, $text)
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
190 function &insert(&$obj)
192 parent::insert($obj);
193 return $this->last = &$this;
196 function canContain(&$obj)
203 return $this->msg_top.$this->wrap(parent::toString(), 'h'.$this->level, " id=\"{$this->id}\"");
207 class HRule extends Element
210 function HRule(&$root, $text)
215 function canContain(&$obj)
228 class ListContainer extends Element
237 function ListContainer($tag, $tag2, $head, $text)
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;
251 $this->level = min(3, strspn($text, $head));
252 $text = ltrim(substr($text, $this->level));
254 parent::insert(new ListElement($this->level, $tag2));
257 $this->last = &$this->last->insert(new Inline($text));
261 function canContain(&$obj)
263 return (!is_a($obj, 'ListContainer')
264 or ($this->tag == $obj->tag and $this->level == $obj->level));
267 function setParent(&$parent)
269 global $_list_pad_str;
271 parent::setParent($parent);
273 $step = $this->level;
274 if (isset($parent->parent) and is_a($parent->parent, 'ListContainer'))
276 $step -= $parent->parent->level;
278 $margin = $this->margin * $step;
279 if ($step == $this->level)
281 $margin += $this->left_margin;
283 $this->style = sprintf($_list_pad_str, $this->level, $margin, $margin);
286 function &insert(&$obj)
288 if (!is_a($obj, get_class($this)))
290 return $this->last = &$this->last->insert($obj);
292 // ¹ÔƬʸ»ú¤Î¤ß¤Î»ØÄê»þ¤ÏUL/OL¥Ö¥í¥Ã¥¯¤òæ½Ð
294 if (count($obj->elements) == 1 && count($obj->elements[0]->elements) == 0)
296 return $this->last->parent; // up to ListElement.
299 foreach(array_keys($obj->elements) as $key)
301 parent::insert($obj->elements[$key]);
309 return $this->wrap(parent::toString(), $this->tag, $this->style);
313 class ListElement extends Element
315 function ListElement($level, $head)
318 $this->level = $level;
322 function canContain(&$obj)
324 return (!is_a($obj, 'ListContainer') or ($obj->level > $this->level));
329 return $this->wrap(parent::toString(), $this->head);
333 class UList extends ListContainer
335 function UList(&$root, $text)
337 parent::ListContainer('ul', 'li', '-', $text);
341 class OList extends ListContainer
343 function OList(&$root, $text)
345 parent::ListContainer('ol', 'li', '+', $text);
349 class DList extends ListContainer
351 function DList(&$root, $text)
353 $out = explode('|', $text, 2);
356 $this = new Inline($text);
357 $this->last = &$this;
361 parent::ListContainer('dl', 'dt', ':', $out[0]);
363 $this->last = &Element::insert(new ListElement($this->level, 'dd'));
366 $this->last = &$this->last->insert(new Inline($out[1]));
371 class BQuote extends Element
375 function BQuote(&$root, $text)
379 $head = substr($text, 0, 1);
380 $this->level = min(3, strspn($text, $head));
381 $text = ltrim(substr($text, $this->level));
383 if ($head == '<') //blockquote close
385 $level = $this->level;
387 $this->last = &$this->end($root, $level);
390 $this->last = &$this->last->insert(new Inline($text));
395 $this->insert(new Inline($text));
399 function canContain(&$obj)
401 return (!is_a($obj, get_class($this)) or $obj->level >= $this->level);
404 function &insert(&$obj)
406 // BugTrack/521, BugTrack/545
407 if (is_a($obj, 'inline')) {
408 return parent::insert($obj->toPara(' class="quotation"'));
410 if (is_a($obj, 'BQuote') and $obj->level == $this->level and count($obj->elements))
412 $obj = &$obj->elements[0];
413 if (is_a($this->last,'Paragraph') and count($obj->elements))
415 $obj = &$obj->elements[0];
418 return parent::insert($obj);
423 return $this->wrap(parent::toString(), 'blockquote');
426 function &end(&$root, $level)
428 $parent = &$root->last;
430 while (is_object($parent))
432 if (is_a($parent,'BQuote') and $parent->level == $level)
434 return $parent->parent;
436 $parent = &$parent->parent;
442 class TableCell extends Element
444 var $tag = 'td'; // {td|th}
447 var $style; // is array('width'=>, 'align'=>...);
449 function TableCell($text, $is_template = FALSE)
452 $this->style = $matches = array();
454 while (preg_match('/^(?:(LEFT|CENTER|RIGHT)|(BG)?COLOR\(([#\w]+)\)|SIZE\((\d+)\)):(.*)$/',$text,$matches))
458 $this->style['align'] = 'text-align:'.strtolower($matches[1]).';';
461 else if ($matches[3])
463 $name = $matches[2] ? 'background-color' : 'color';
464 $this->style[$name] = $name.':'.htmlspecialchars($matches[3]).';';
467 else if ($matches[4])
469 $this->style['size'] = 'font-size:'.htmlspecialchars($matches[4]).'px;';
473 if ($is_template and is_numeric($text))
475 $this->style['width'] = "width:{$text}px;";
481 else if ($text == '~')
485 else if (substr($text, 0, 1) == '~')
488 $text = substr($text, 1);
490 if ($text != '' and $text{0} == '#')
492 // ¥»¥ëÆâÍƤ¬'#'¤Ç»Ï¤Þ¤ë¤È¤¤ÏDiv¥¯¥é¥¹¤òÄ̤·¤Æ¤ß¤ë
493 $obj = &new Div($this, $text);
494 if (is_a($obj, 'Paragraph'))
496 $obj = &$obj->elements[0];
501 $obj = &new Inline($text);
506 function setStyle(&$style)
508 foreach ($style as $key=>$value)
510 if (!array_key_exists($key, $this->style))
512 $this->style[$key] = $value;
519 if ($this->rowspan == 0 or $this->colspan == 0)
523 $param = " class=\"style_{$this->tag}\"";
524 if ($this->rowspan > 1)
526 $param .= " rowspan=\"{$this->rowspan}\"";
528 if ($this->colspan > 1)
530 $param .= " colspan=\"{$this->colspan}\"";
531 unset($this->style['width']);
533 if (count($this->style))
535 $param .= ' style="'.join(' ', $this->style).'"';
538 return $this->wrap(parent::toString(), $this->tag, $param, FALSE);
542 class Table extends Element
546 var $col; // number of column
548 function Table(&$root, $text)
553 if (!preg_match("/^\|(.+)\|([hHfFcC]?)$/", $text, $out))
555 $this = new Inline($text);
556 $this->last = &$this;
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');
566 foreach ($cells as $cell)
568 $row[] = &new TableCell($cell, $is_template);
570 $this->elements[] = $row;
573 function canContain(&$obj)
575 return is_a($obj, 'Table') and ($obj->col == $this->col);
578 function &insert(&$obj)
580 $this->elements[] = $obj->elements[0];
581 $this->types[] = $obj->type;
588 static $parts = array('h'=>'thead', 'f'=>'tfoot', ''=>'tbody');
590 // rowspan¤òÀßÄê(²¼¤«¤é¾å¤Ø)
591 for ($ncol = 0; $ncol < $this->col; $ncol++)
594 foreach (array_reverse(array_keys($this->elements)) as $nrow)
596 $row = &$this->elements[$nrow];
597 if ($row[$ncol]->rowspan == 0)
602 $row[$ncol]->rowspan = $rowspan;
603 while (--$rowspan) // ¹Ô¼ïÊ̤ò·Ñ¾µ¤¹¤ë
605 $this->types[$nrow + $rowspan] = $this->types[$nrow];
610 // colspan,style¤òÀßÄê
612 foreach (array_keys($this->elements) as $nrow)
614 $row = &$this->elements[$nrow];
615 if ($this->types[$nrow] == 'c')
620 foreach (array_keys($row) as $ncol)
622 if ($row[$ncol]->colspan == 0)
627 $row[$ncol]->colspan = $colspan;
628 if ($stylerow !== NULL)
630 $row[$ncol]->setStyle($stylerow[$ncol]->style);
631 while (--$colspan) // Îó¥¹¥¿¥¤¥ë¤ò·Ñ¾µ¤¹¤ë
633 $row[$ncol - $colspan]->setStyle($stylerow[$ncol]->style);
641 foreach ($parts as $type => $part)
644 foreach (array_keys($this->elements) as $nrow)
646 if ($this->types[$nrow] != $type)
650 $row = &$this->elements[$nrow];
652 foreach (array_keys($row) as $ncol)
654 $row_string .= $row[$ncol]->toString();
656 $part_string .= $this->wrap($row_string, 'tr');
658 $string .= $this->wrap($part_string, $part);
660 $string = $this->wrap($string, 'table', ' class="style_table" cellspacing="1" border="0"');
661 return $this->wrap($string, 'div', ' class="ie5"');
665 class YTable extends Element
669 function YTable(&$root, $text)
673 $_value = csv_explode(',', substr($text,1));
674 if (count($_value) == 0)
676 $this = new Inline($text);
677 $this->last = &$this;
681 $align = $value = $matches = array();
682 foreach($_value as $val)
684 if (preg_match('/^(\s+)?(.+?)(\s+)?$/', $val, $matches))
686 $align[] =($matches[1] != '') ?
687 ((array_key_exists(3,$matches) and $matches[3] != '') ?
688 ' style="text-align:center"' : ' style="text-align:right"'
690 $value[] = $matches[2];
698 $this->col = count($value);
700 foreach ($value as $val)
702 $colspan[] = ($val == '==') ? 0 : 1;
705 for ($i = 0; $i < count($value); $i++)
709 while ($i + $colspan[$i] < count($value) and $value[$i + $colspan[$i]] == '==')
713 $colspan[$i] = ($colspan[$i] > 1) ? " colspan=\"{$colspan[$i]}\"" : '';
714 $str .= "<td class=\"style_td\"{$align[$i]}{$colspan[$i]}>".make_link($value[$i]).'</td>';
717 $this->elements[] = $str;
720 function canContain(&$obj)
722 return is_a($obj, 'YTable') and ($obj->col == $this->col);
725 function &insert(&$obj)
727 $this->elements[] = $obj->elements[0];
735 foreach ($this->elements as $str)
737 $rows .= "\n<tr class=\"style_tr\">$str</tr>\n";
739 $rows = $this->wrap($rows, 'table', ' class="style_table" cellspacing="1" border="0"');
740 return $this->wrap($rows, 'div', ' class="ie5"');
744 class Pre extends Element
746 function Pre(&$root,$text)
748 global $preformat_ltrim;
751 $this->elements[] = htmlspecialchars(
752 (!$preformat_ltrim or $text == '' or $text{0} != ' ') ? $text : substr($text, 1)
756 function canContain(&$obj)
758 return is_a($obj, 'Pre');
761 function &insert(&$obj)
763 $this->elements[] = $obj->elements[0];
770 return $this->wrap(join("\n", $this->elements), 'pre');
774 class Div extends Element
779 function Div(&$root, $text)
783 if (!preg_match("/^\#([^\(]+)(?:\((.*)\))?/", $text, $out) or !exist_plugin_convert($out[1]))
785 $this = new Paragraph($text);
786 $this->last = &$this;
790 list(, $this->name, $this->param) = array_pad($out,3,'');
793 function canContain(&$obj)
800 return do_plugin_convert($this->name,$this->param);
804 class Align extends Element
805 { // LEFT:/CENTER:/RIGHT:
808 function Align($align)
812 $this->align = $align;
815 function canContain(&$obj)
817 return is_a($obj, 'Inline');
822 return $this->wrap(parent::toString(), 'div', ' style="text-align:'.$this->align.'"');
826 class Body extends Element
832 var $classes = array(
846 $this->contents = &new Element();
847 $this->contents_last = &$this->contents;
851 function parse(&$lines)
853 $this->last = &$this;
855 while (count($lines))
857 $line = array_shift($lines);
859 if (substr($line,0,2) == '//') //¥³¥á¥ó¥È¤Ï½èÍý¤·¤Ê¤¤
864 if (preg_match('/^(LEFT|CENTER|RIGHT):(.*)$/',$line,$matches))
866 $this->last = &$this->last->add(new Align(strtolower($matches[1]))); // <div style="text-align:...">
867 if ($matches[2] == '')
874 $line = preg_replace("/[\r\n]*$/",'',$line);
879 $this->last = &$this;
883 if (substr($line,0,4) == '----')
885 $this->insert(new HRule($this,$line));
894 $this->insert(new Heading($this,$line));
898 if ($head == ' ' or $head == "\t")
900 $this->last = &$this->last->add(new Pre($this,$line));
904 if (substr($line,-1) == '~')
906 $line = substr($line,0,-1)."\r";
909 if (array_key_exists($head, $this->classes))
911 $classname = $this->classes[$head];
912 $this->last = &$this->last->add(new $classname($this,$line));
917 $this->last = &$this->last->add(new Inline($line));
921 function getAnchor($text,$level)
923 global $top,$_symbol_anchor;
925 $anchor = (($id = make_heading($text,FALSE)) == '') ?
926 '' : " &aname($id,super,full)\{$_symbol_anchor};";
928 $id = "content_{$this->id}_{$this->count}";
930 $this->contents_last = &$this->contents_last->add(new Contents_UList($text,$level,$id));
932 return array($text. $anchor, $this->count > 1 ? "\n$top" : '', $id);
935 function &insert(&$obj)
937 if (is_a($obj, 'Inline'))
939 $obj = &$obj->toPara();
941 return parent::insert($obj);
948 $text = parent::toString();
951 $text = preg_replace_callback('/(<p[^>]*>)<del>#contents<\/del>(\s*)(<\/p>)/', array(&$this,'replace_contents'),$text);
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);
960 function replace_contents($arr)
962 $contents = "<div class=\"contents\">\n";
963 $contents .= "<a id=\"contents_{$this->id}\"></a>";
964 $contents .= $this->contents->toString();
965 $contents .= "</div>\n";
968 return ($arr[1] != '') ? $contents.join('',$arr) : $contents;
971 function replace_related($arr)
974 static $related = NULL;
976 if (is_null($related))
978 $related = make_related($vars['page'],'p');
982 return ($arr[1] != '') ? $related.join('',$arr) : $related;
986 class Contents_UList extends ListContainer
988 function Contents_UList($text,$level,$id)
990 // ¥Æ¥¥¹¥È¤Î¥ê¥Õ¥©¡¼¥à
991 // ¹ÔƬ\n¤ÇÀ°·ÁºÑ¤ß¤òɽ¤¹ ... X(
993 $text = "\n<a href=\"#$id\">$text</a>\n";
994 parent::ListContainer('ul', 'li', '-', str_repeat('-',$level));
995 $this->insert(new Inline($text));
998 function setParent(&$parent)
1000 global $_list_pad_str;
1002 parent::setParent($parent);
1003 $step = $this->level;
1004 $margin = $this->left_margin;
1005 if (isset($parent->parent) and is_a($parent->parent,'ListContainer'))
1007 $step -= $parent->parent->level;
1010 $margin += $this->margin * ($step == $this->level ? 1 : $step);
1011 $this->style = sprintf($_list_pad_str,$this->level,$margin,$margin);