2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
5 // $Id: convert_html.php,v 1.27 2003/03/04 06:38:31 panda Exp $
7 function convert_html($lines)
9 global $script,$vars,$digest;
10 static $contents_id = 0;
12 if (!is_array($lines))
14 $lines = explode("\n",$lines);
17 $digest = md5(join('',get_source($vars['page'])));
19 $body = new Body(++$contents_id);
21 $ret = $body->toString();
30 function setParent(&$parent)
32 $this->parent =& $parent;
34 function debug($indent = 0)
36 return str_repeat(' ',$indent).get_class($this)."({$this->text})\n";
40 class Inline extends Element
44 function Inline($text)
46 if (substr($text,0,1) == '~') { // ¹ÔƬ~¡£¥Ñ¥é¥°¥é¥Õ³«»Ï
47 $parent =& $this->parent;
48 $this = new Paragraph(' '.substr($text,1));
49 $this->setParent($parent);
52 $this->text = (substr($text,0,1) == "\n") ? $text : inline2(inline($text));
57 return $this->insert($obj);
59 function &insert(&$obj)
61 return $this->parent->add($obj);
67 function toPara($class = '')
69 $obj = new Paragraph('',$class);
71 $this->setParent($obj);
75 class Block extends Element
77 var $elements; // Í×ÁǤÎÇÛÎó
80 $this->elements = array();
83 function &add(&$obj) // ¥¨¥ì¥á¥ó¥È¤òÄɲÃ
85 if ($this->canContain($obj)) {
86 return $this->insert($obj);
88 return $this->parent->add($obj);
90 function &insert(&$obj)
92 $obj->setParent($this);
93 $this->elements[] =& $obj;
94 if (isset($obj->last) and is_object($obj->last)) {
99 function canContain($obj)
106 if (isset($this->elements) and count($this->elements) > 0) {
107 foreach ($this->elements as $obj) {
108 $ret .= $obj->toString();
113 function wrap($string, $tag, $param = '')
115 return ($string == '') ? '' : "\n<$tag$param>$string</$tag>\n";
117 function debug($indent = 0)
119 $ret = parent::debug($indent);
120 foreach (array_keys($this->elements) as $key) {
121 if (is_object($this->elements[$key]))
122 $ret .= $this->elements[$key]->debug($indent + 2);
124 $ret .= str_repeat(' ',$indent + 2).$this->elements[$key];
129 class Paragraph extends Block
133 function Paragraph($text,$class='')
136 $this->class = $class;
140 if (substr($text,0,1) == '~') {
141 $text = ' '.substr($text,1);
143 $this->elements[] =& new Inline($text);
145 function canContain($obj)
147 return is_a($obj,'Inline');
151 return $this->wrap(parent::toString(), 'p', $this->class);
155 class Heading extends Block
157 var $level,$id,$msg_top;
159 function Heading(&$root,$text)
162 if (($level = strspn($text,'*')) > 3) {
165 $text = ltrim(substr($text,$level));
166 $this->level = ++$level;
167 list($this->msg_top,$this->id) = $root->getAnchor($text,$level);
168 $this->last =& $this->insert(new Inline($text));
170 function canContain(&$obj)
176 return $this->msg_top.
177 $this->wrap(parent::toString(),'h'.$this->level," id=\"{$this->id}\"");
180 class HRule extends Block
182 function HRule(&$root,$text) {
185 function canContain(&$obj)
196 class ListContainer extends Block
198 var $tag,$tag2,$level,$style;
199 var $margin,$left_margin;
201 function ListContainer($tag,$tag2,$level,$text)
205 $var_margin = "_{$tag}_margin";
206 $var_left_margin = "_{$tag}_left_margin";
207 global $$var_margin, $$var_left_margin;
208 $this->margin = $$var_margin;
209 $this->left_margin = $$var_left_margin;
214 $this->level = $level;
217 $this->insert(new Inline($text));
221 function canContain(&$obj)
223 return is_a($obj, 'ListContainer') ? ($this->tag == $obj->tag and $this->level == $obj->level) : TRUE;
225 function setParent(&$parent)
227 global $_list_pad_str;
229 parent::setParent($parent);
230 $step = $this->level;
231 if (isset($parent->parent) and is_a($parent->parent,'ListContainer')) {
232 $step -= $parent->parent->level;
234 $margin = $this->margin * $step;
235 if ($step == $this->level) {
236 $margin += $this->left_margin;
238 $this->style = sprintf($_list_pad_str,$this->level,$margin,$margin);
240 function &insert(&$obj)
242 if (is_a($obj, get_class($this))) {
243 for ($n = 0; $n < count($obj->elements); $n++) {
244 $this->last =& parent::insert($obj->elements[$n]);
248 $obj =& new ListElement($obj, $this->level, $this->tag2); // wrap
250 return parent::insert($obj);
252 function toString($param='')
254 return $this->wrap(parent::toString(),$this->tag,$this->style.$param);
257 class ListElement extends Block
259 function ListElement(&$obj,$level,$head)
262 $this->level = $level;
266 if (isset($obj->last) and is_object($obj->last)) {
267 $this->last =& $obj->last;
270 function canContain(&$obj)
272 return !(is_a($obj, 'ListContainer') and ($obj->level <= $this->level));
276 return $this->wrap(parent::toString(), $this->head);
279 class UList extends ListContainer
281 function UList(&$root,$text)
283 if (($level = strspn($text,'-')) > 3) {
284 $level = 3; // limitation ;(
286 $text = ltrim(substr($text,$level));
287 parent::ListContainer('ul','li',$level,$text);
290 class OList extends ListContainer
292 function OList(&$root,$text)
294 if (($level = strspn($text,'+')) > 3) {
295 $level = 3; // limitation ;(
297 $text = ltrim(substr($text,$level));
298 parent::ListContainer('ol','li',$level,$text);
301 class DList extends ListContainer
303 function DList(&$root,$text)
305 if (($level = strspn($text,':')) > 3) {
306 $level = 3; // limitation ;(
308 $out = explode('|',ltrim(substr($text,$level)),2);
309 if (count($out) < 2) {
310 $this = new Inline($text);
313 parent::ListContainer('dl','dd',$level,$out[1]);
315 array_unshift($this->elements,new Inline("\n<dt>".inline2(inline($out[0]))."</dt>\n"));
319 class BQuote extends Block
323 function BQuote(&$root,$text)
326 $head = substr($text,0,1);
327 if (($level = strspn($text,$head)) > 3) {
328 $level = 3; // limitation ;(
330 $this->level = $level;
331 $text = ltrim(substr($text,$level));
332 if ($head == '<') { //blockquote close
334 $this->last =& $this->end($root,$level,$text);
337 $this->last =& $this->insert(new Paragraph($text, ' class="quotation"'));
340 function canContain(&$obj)
342 if (!is_a($obj, get_class($this))) {
345 return ($obj->level >= $this->level);
347 function &insert(&$obj)
349 if (is_a($obj, 'BQuote') and $obj->level == $this->level) {
350 if (is_a($this->last,'Paragraph')
351 and array_key_exists(0,$obj->elements[0])
352 and is_object($obj->elements[0]->elements[0])) {
353 $this->last->insert($obj->elements[0]->elements[0]);
355 $this->last =& $this->insert($obj->elements[0]);
360 return parent::insert($obj);
364 return $this->wrap(parent::toString(),'blockquote');
366 function &end(&$root,$level,$text)
368 $parent =& $root->last;
369 while (is_object($parent)) {
370 if (is_a($parent,'BQuote') and $parent->level == $level) {
371 return $parent->parent->insert(new Inline($text));
373 $parent =& $parent->parent;
375 return $this->insert(new Inline($text));
378 class TableCell extends Block
380 var $tag = 'td'; // {td|th}
383 var $style; // is array('width'=>, 'align'=>...);
385 function TableCell($text,$is_template=FALSE) {
387 $this->style = array();
389 if (preg_match("/^(LEFT|CENTER|RIGHT):(.*)$/",$text,$out)) {
390 $this->style['align'] = 'text-align:'.strtolower($out[1]).';';
394 if (is_numeric($text)) {
395 $this->style['width'] = "width:{$text}px;";
401 else if ($text == '~') {
404 else if (substr($text,0,1) == '~') {
406 $text = substr($text,1);
408 $this->last =& $this->insert(new Inline($text));
410 function setStyle(&$style) {
411 foreach ($style as $key=>$value) {
412 if (!array_key_exists($key,$this->style)) {
413 $this->style[$key] = $value;
417 function toString() {
418 if ($this->rowspan == 0 or $this->colspan == 0) {
421 $param = " class=\"style_{$this->tag}\"";
422 if ($this->rowspan > 1) {
423 $param .= " rowspan=\"{$this->rowspan}\"";
425 if ($this->colspan > 1) {
426 $param .= " colspan=\"{$this->colspan}\"";
427 unset($this->style['width']);
429 if (count($this->style)) {
430 $param .= ' style="'.join(' ',$this->style).'"';
432 return "\n<{$this->tag}$param>".parent::toString()."</{$this->tag}>\n";
435 class Table extends Block
438 var $col; // number of column
440 function Table(&$root,$text)
442 if (!preg_match("/^\|(.+)\|([hHfFcC]?)$/",$text,$out)) {
443 $this = new Inline($text);
447 $cells = explode('|',$out[1]);
448 $this->col = count($cells);
449 $this->type = strtolower($out[2]);
450 $this->types = array($this->type);
451 $is_template = ($this->type == 'c');
453 foreach ($cells as $cell) {
454 $row[] = new TableCell($cell,$is_template);
456 $this->elements[] = $row;
457 $this->last =& $this;
459 function canContain(&$obj)
461 return is_a($obj, 'Table') and ($obj->col == $this->col);
463 function &insert(&$obj)
465 $this->elements[] = $obj->elements[0];
466 $this->types[] = $obj->type;
471 // rowspan¤òÀßÄê(²¼¤«¤é¾å¤Ø)
472 for ($ncol = 0; $ncol < $this->col; $ncol++) {
474 foreach (array_reverse(array_keys($this->elements)) as $nrow) {
475 $row =& $this->elements[$nrow];
476 if ($row[$ncol]->rowspan == 0) {
480 $row[$ncol]->rowspan = $rowspan;
481 while (--$rowspan) { // ¹Ô¼ïÊ̤ò·Ñ¾µ¤¹¤ë
482 $this->types[$nrow + $rowspan] = $this->types[$nrow];
488 // colspan,style¤òÀßÄê
490 foreach (array_keys($this->elements) as $nrow) {
491 $row =& $this->elements[$nrow];
492 if ($this->types[$nrow] == 'c') {
496 foreach (array_keys($row) as $ncol) {
497 if ($row[$ncol]->colspan == 0) {
501 $row[$ncol]->colspan = $colspan;
502 if ($stylerow !== NULL) {
503 $row[$ncol]->setStyle($stylerow[$ncol]->style);
504 while (--$colspan) { // Îó¥¹¥¿¥¤¥ë¤ò·Ñ¾µ¤¹¤ë
505 $row[$ncol - $colspan]->setStyle($stylerow[$ncol]->style);
514 $parts = array('h'=>'thead',''=>'tbody','f'=>'tfoot');
515 foreach ($parts as $type=>$part) {
517 foreach (array_keys($this->elements) as $nrow) {
518 if ($this->types[$nrow] != $type) {
521 $row =& $this->elements[$nrow];
523 foreach (array_keys($row) as $ncol) {
524 $row_string .= $row[$ncol]->toString();
526 $part_string .= $this->wrap($row_string,'tr');
528 $string .= $this->wrap($part_string,$part);
532 <table class="style_table" cellspacing="1" border="0">
539 class YTable extends Block
543 function YTable(&$root,$text)
546 if (!preg_match_all('/("[^"]*(?:""[^"]*)*"|[^,]*),/',"$text,",$out)) {
547 $this = new Inline($text);
550 array_shift($out[1]);
552 foreach ($out[1] as $val) {
553 $_value[] = preg_match('/^"(.*)"$/',$val,$matches) ? str_replace('""','"',$matches[1]) : $val;
557 foreach($_value as $val) {
558 if (preg_match('/^(\s+)?(.+?)(\s+)?$/',$val,$matches)) {
559 $align[] =($matches[1] != '') ?
560 ((array_key_exists(3,$matches) and $matches[3] != '') ? ' style="text-align:center"' : ' style="text-align:right"') : '';
561 $value[] = $matches[2];
568 $this->col = count($value);
570 foreach ($value as $val) {
571 $colspan[] = ($val == '==') ? 0 : 1;
574 for ($i = 0; $i < count($value); $i++) {
576 while ($i + $colspan[$i] < count($value) and $value[$i + $colspan[$i]] == '==') {
579 $colspan[$i] = ($colspan[$i] > 1) ? " colspan=\"{$colspan[$i]}\"" : '';
580 $str .= "<td class=\"style_td\"{$align[$i]}{$colspan[$i]}>".inline2(inline($value[$i])).'</td>';
583 $this->elements[] = $str;
585 function canContain(&$obj)
587 return is_a($obj, 'YTable') and ($obj->col == $this->col);
589 function &insert(&$obj)
591 $this->elements[] = $obj->elements[0];
597 foreach ($this->elements as $str) {
598 $rows .= "\n<tr class=\"style_tr\">$str</tr>\n";
603 <table class="style_table" cellspacing="1" border="0">
611 class Pre extends Block
614 function Pre(&$root,$text)
617 $this->elements[] = htmlspecialchars($text,ENT_NOQUOTES);
619 function canContain(&$obj)
621 return is_a($obj, 'Pre');
623 function &insert(&$obj)
625 $this->elements[] = $obj->elements[0];
630 return $this->wrap(join("\n",$this->elements),'pre');
633 class Div extends Block
637 function Div(&$root,$text)
639 if (!preg_match("/^\#([^\(]+)(?:\((.*)\))?/",$text,$out) or !exist_plugin_convert($out[1])) {
640 $this = new Paragraph($text);
644 $this->name = $out[1];
645 $this->param = array_key_exists(2,$out) ? $out[2] : '';
647 function canContain(&$obj)
653 return do_plugin_convert($this->name,$this->param);
656 class Align extends Block
657 { // LEFT:/CENTER:/RIGHT:
660 function Align($align)
662 $this->align = $align;
664 function canContain(&$obj)
666 return is_a($obj,'Inline');
670 return $this->wrap(parent::toString(),'div',' style="text-align:'.$this->align.'"');
673 class Body extends Block
679 var $classes = array('HRule','Heading','Pre','UList','OList','DList','Table','YTable','BQuote','BQuoteEnd','Div');
684 $this->contents = new Block();
685 $this->contents_last =& $this->contents;
688 function parse(&$lines)
690 $this->last =& $this;
692 foreach ($lines as $line)
694 if (substr($line,0,2) == '//') //¥³¥á¥ó¥È¤Ï½èÍý¤·¤Ê¤¤
700 if (preg_match('/^(LEFT|CENTER|RIGHT):(.*)$/',$line,$matches))
702 $this->last =& $this->last->add(new Align(strtolower($matches[1]))); // <div style="text-align:...">
703 if ($matches[2] == '')
710 $line = preg_replace("/[\r\n]*$/",'',$line);
713 $head = substr($line,0,1);
715 if ($line == '') { // ¶õ¹Ô
716 $this->last =& $this;
718 else if (substr($line,0,4) == '----') { // HRule
719 $this->last =& $this->insert(new HRule($this,$line));
721 else if ($head == '*') { // Heading
722 $this->last =& $this->insert(new Heading($this,$line));
724 else if ($head == ' ' or $head == "\t") { // Pre
725 $this->last =& $this->last->add(new Pre($this,$line));
728 if (substr($line,-1) == '~') {
729 $line = substr($line,0,-1)."\r";
731 if ($head == '-') { // UList
732 $this->last =& $this->last->add(new UList($this,$line)); // inline
734 else if ($head == '+') { // OList
735 $this->last =& $this->last->add(new OList($this,$line)); // inline
737 else if ($head == ':') { // DList
738 $this->last =& $this->last->add(new DList($this,$line)); // inline
740 else if ($head == '|') { // Table
741 $this->last =& $this->last->add(new Table($this,$line));
743 else if ($head == ',') { // Table(YukiWiki¸ß´¹)
744 $this->last =& $this->last->add(new YTable($this,$line));
746 else if ($head == '>' or $head == '<') { // BrockQuote
747 $this->last =& $this->last->add(new BQuote($this,$line));
749 else if ($head == '#') { // Div
750 $this->last =& $this->last->add(new Div($this,$line));
753 $this->last =& $this->last->add(new Inline($line));
758 function getAnchor($text,$level)
762 $id = "content_{$this->id}_{$this->count}";
764 $this->contents_last =& $this->contents_last->add(new Contents_UList($text,$this->id,$level,$id));
765 return array($this->count > 1 ? $top : '',$id);
767 function getContents()
769 $contents = "<a id=\"contents_{$this->id}\"></a>";
770 $contents .= $this->contents->toString();
773 function &insert(&$obj)
775 if (is_a($obj,'Inline')) {
776 $obj =& $obj->toPara();
778 return parent::insert($obj);
784 $text = parent::toString();
787 $text = preg_replace('/<p[^>]*>#contents<\/p>/',$this->getContents(),$text);
790 // <p>¤Î¤È¤¤Ï¹ÔƬ¤«¤é¡¢<del>¤Î¤È¤¤Ï¾¤ÎÍ×ÁǤλÒÍ×ÁǤȤ·¤Æ¸ºß
791 $text = preg_replace('/<(p|del)>#related<\/\1>/e','make_related($vars[\'page\'],\'$1\')',$text);
795 class Contents_UList extends ListContainer
797 function Contents_UList($text,$id,$level,$id)
799 // ¥Æ¥¥¹¥È¤Î¥ê¥Õ¥©¡¼¥à
800 // ¹ÔƬ\n¤ÇÀ°·ÁºÑ¤ß¤òɽ¤¹ ... X(
801 $text = "\n<a href=\"#$id\">".strip_htmltag(inline2(inline($text,TRUE)))."</a>\n";
802 parent::ListContainer('ul', 'li', --$level, $text);
804 function setParent(&$parent)
806 global $_list_pad_str;
808 parent::setParent($parent);
809 $step = $this->level;
810 $margin = $this->left_margin;
811 if (isset($parent->parent) and is_a($parent->parent,'ListContainer'))
813 $step -= $parent->parent->level;
816 $margin += $this->margin * ($step == $this->level ? 1 : $step);
817 $this->style = sprintf($_list_pad_str,$this->level,$margin,$margin);