表紙 | 新規 | 一覧 | RSS | 検索 | 閲覧履歴 | 作成履歴 | 更新履歴

text_to_html関数 - YukiWikiベースのPonyWikiのソースを読んでみる その20

差分表示


[[YukiWikiベースのPonyWikiのソースを読んでみる]] その20

YukiWIki書式で保存されているデータをhtml化する関数。

処理の流れは以下の通り。

- 引数のデータを1行毎に配列に取り込む
- 以下1行毎にループをぐるぐる
-- 変換後のhtmlは&verb(@result)へ。

- verbatim終りの処理
- 先頭が&verb(*)で始まれば、見出し処理
-- ここで部分編集用のリンクとTOPへ戻るリンクを付けています。
-- オリジナルの[[WalWiki]]では、[[print_header関数]]で動的に&verb($resource{editthispart})を作成していますが、それは私の趣味ではないので固定値に変えています。
- 先頭が&verb(--)(か&verb(---)(で始まれば、verbatimの処理
- 先頭が&verb(----)で始まれば、水平線
- 先頭が&verb(-)で始まれば、箇条書き処理
- 先頭が&verb(:)で始まれば、用語解説処理
- 先頭が&verb(>)で始まれば、引用処理
- 先頭がスペースで始まれば、そのまま表示
-- ただし、[[escape関数]]で処理されます
- 先頭が&verb(,)で始まれば、テーブル処理
- 先頭が&verb(#)で始まれば、ブロックプラグインの処理
-- インラインプラグインは、[[inline関数]]が[[make_link関数]]を呼んで処理してます
-- つまり、text_to_html関数 → [[inline関数]] → [[make_link関数]]と呼ばれている

- 目次(TOC)の作成
-- 先頭が&verb(*)で始まる行を探して、目次リストをhtmlで作成
-- 先頭行に&verb($resource{table_of_contents})(目次)を挿入
-- cssも先頭と末尾に挿入
-- &verb(@result)と合体させる

---(
sub text_to_html {
    my ($txt, %option) = @_;
	my ($txt, %option) = @_;

    my (@txt) = split(/\r?\n/, $txt);
    my (@toc);
    my $verbatim;
    my $tocnum = 0;
    my (@saved, @result);
    unshift(@saved, "</p>");
    push(@result, "<p>");
    foreach (@txt) {
        chomp;
	my (@toc);
	my $verbatim;
	my $tocnum = 0;
	my (@saved, @result);

        # verbatim.
        if ($verbatim->{func}) {
            if (/^\Q$verbatim->{done}\E$/) {
                undef $verbatim;
                push(@result, splice(@saved));
            } else {
                push(@result, $verbatim->{func}->($_));
            }
            next;
        }
	my @open_tag;
	my ($depth, $nest_1st);

        # non-verbatim follows.
        push(@result, shift(@saved)) if (@saved and $saved[0] eq '</pre>' and /^[^ \t]/);
        if (/^(\*{1,3})(.+)/) {
            # $hn = 'h2', 'h3' or 'h4'
            my $hn = "h" . (length($1) + 1);
	push(@result, "<p>");
	unshift(@saved, "</p>");

            push(@toc, '-' x length($1) . qq( <a href="#i$tocnum">) . &remove_tag(&inline($2)) . qq(</a>\n));
            push(@result, splice(@saved), qq(<$hn><a name="i$tocnum"> </a>) . &inline($2) . qq(</$hn>));
	foreach (split(/\r?\n/, $txt)) {
		chomp;
		# verbatim.
		if ($verbatim->{func}) {
			if (/^\Q$verbatim->{done}\E$/) {
				undef $verbatim;
				push(@result, splice(@saved));
			} else {
				push(@result, $verbatim->{func}->($_));
			}
			next;
		}
		
		# non-verbatim follows.
		push(@result, shift(@saved)) if (@saved and $saved[0] eq '</pre>' and /^[^ \t]/);
		
		if (/^(\*{1,5})(.+)/) {
			# $hn = 'h2', 'h3', 'h4', 'h5' or 'h6'
			my $hn = "h" . (length($1) + 1);
			# Set $nest_1st
			if (!$nest_1st) { $nest_1st = length($1);}
			#
			my $tmp_depth = length($1);
		
			if($depth == $tmp_depth) {
				push(@result, splice(@saved), qq(</div>\n<$hn><a name="i$tocnum"> </a>) . &inline($2) . qq(</$hn>\n<div class="$hn).qq(level">));
			} elsif ($depth < $tmp_depth) { # nest inc →
				push(@result, splice(@saved), qq(<$hn><a name="i$tocnum"> </a>) . &inline($2) . qq(</$hn>\n<div class="$hn).qq(level">));
				$depth = $tmp_depth;
				$open_tag[$depth] = 1;
			} elsif ($depth > $tmp_depth) { # nest dec ←
				$depth = $tmp_depth;
				my $open_tag_cnt;
				my $i;
				
				if($nest_1st > $tmp_depth) {
					$nest_1st = $tmp_depth;
					$i = 1;
				} else {
					$i=$depth;
				}
				for(; $i < 7; $i++){
					$open_tag_cnt = $open_tag_cnt + $open_tag[$i];
					$open_tag[$i] = 0;
				}
				
				if($open_tag_cnt){
					push(@result, splice(@saved));
				}
				while($open_tag_cnt) {
					push(@result,qq(</div>));
					$open_tag_cnt--;
				}
				push(@result, splice(@saved), qq(<$hn><a name="i$tocnum"> </a>) . &inline($2) . qq(</$hn><div class="$hn).qq(level">));
				$open_tag[$depth] = 1;
			}
			
			if($form{keitai}){
				my $index_no = $tocnum +2;
				my $cookedpage = &encode($form{mypage});
				push(@toc, '-' x length($1) . qq( <a href="$url_cgi?mycmd=read&amp;mypage=$cookedpage&amp;mypart=$index_no">) . &remove_tag(&inline($2)) . qq(</a>\n));
			} else {
				push(@toc, '-' x length($1) . qq( <a href="#i$tocnum">) . &remove_tag(&inline($2)) . qq(</a>\n));
			}

# part edit
            my $cookedpage = &encode($form{mypage});
            $cookedpage =~ s/%/%%/g;
            push(@result, sprintf(qq(<div class="partinfo"><a class="partedit" title="$resource{editthispart}" href="$url_cgi?mycmd=edit&amp;mypage=$cookedpage&amp;mypart=%d">$resource{editthispart}</a> <a class="partedit" title="$resource{returntop}" href="$url_cgi?$cookedpage#top">$resource{returntop}</a></div>), $tocnum + 2)) if ($option{'partinfo'});       # Walrus add [part edit]
            $tocnum++;
        } elsif (/^(-{2,3})\($/) {
            if ($& eq '--(') {
                $verbatim = { func => \&inline, done => '--)', class => 'verbatim-soft' };
            } else {
                $verbatim = { func => \&escape, done => '---)', class => 'verbatim-hard' };
            }
            &back_push('pre', 1, \@saved, \@result, " class='$verbatim->{class}'");
        } elsif (/^----/) {
            push(@result, splice(@saved), '<hr>');
        } elsif (/^(-{1,3})(.+)/) {
            &back_push('ul', length($1), \@saved, \@result);
            push(@result, '<li>' . &inline($2) . '</li>');
        } elsif (/^:([^:]+):(.+)/) {
            &back_push('dl', 1, \@saved, \@result);
            push(@result, '<dt>' . &inline($1) . '</dt>', '<dd>' . &inline($2) . '</dd>');
        } elsif (/^(>{1,3})(.+)/) {
            &back_push('blockquote', length($1), \@saved, \@result);
            push(@result, &inline($2));
        } elsif (/^$/) {
            push(@result, splice(@saved));
            unshift(@saved, "</p>");
            push(@result, "<p>");
        } elsif (/^(\s+.*)$/) {
            &back_push('pre', 1, \@saved, \@result);
            push(@result, &escape($1)); # Not &inline, but &escape
        } elsif (/^\,(.*?)[\x0D\x0A]*$/) {
            &back_push('table', 1, \@saved, \@result, ' border="1"');
            #######
            # This part is taken from Mr. Ohzaki's Perl Memo and Makio Tsukamoto's [[WalWiki]].
            # XXXXX
            my $tmp = "$1,";
            my @value = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
            my @align = map {(s/^\s+//) ? ((s/\s+$//) ? ' align="center"' : ' align="right"') : ''} @value;
            my @colspan = map {($_ eq '==') ? 0 : 1} @value;
            for (my $i = 0; $i < @value; $i++) {
                if ($colspan[$i]) {
                    while ($i + $colspan[$i] < @value and $value[$i + $colspan[$i]] eq '==') {
                        $colspan[$i]++;
                    }
                    $colspan[$i] = ($colspan[$i] > 1) ? sprintf(' colspan="%d"', $colspan[$i]) : '';
                    $value[$i] = sprintf('<td%s%s>%s</td>', $align[$i], $colspan[$i], &inline($value[$i]));
                } else {
                    $value[$i] = '';
                }
            }
            push(@result, join('', '<tr>', @value, '</tr>'));
            # XXXXX
            #######
        } elsif (/^\#(\w+)(\((.*)\))?/) {
            # BlockPlugin.
            my $original_line = $_;
            my $plugin_name = $1;
            my $argument = &escape($3);
            my $result = $plugin_manager->call($plugin_name, 'block', $argument);
            if (defined($result)) {
                push(@result, splice(@saved));
            } else {
                $result = $original_line;
            }
            push(@result, $result);
        } else {
            push(@result, &inline($_));
        }
    }
    push(@result, splice(@saved));

    if ($option{toc}) {
        # Convert @toc (table of contents) to HTML.
        # This part is taken from Makio Tsukamoto's [[WalWiki]].
        my (@tocsaved, @tocresult);
        foreach (@toc) {
            if (/^(-{1,3})(.*)/) {
                &back_push('ul', length($1), \@tocsaved, \@tocresult);
                push(@tocresult, '<li>' . $2 . '</li>');
            }
        }
        push(@tocresult, splice(@tocsaved));

        # Insert "table of contents".
        if (@tocresult) {
            unshift(@tocresult, qq(<h2>$resource{table_of_contents}</h2>));
        # Insert CSS <div class="index"> - </div>
            unshift(@tocresult, qq(<div class="index">));
            push(@tocresult, "</div>");
        }


        return join("\n", @tocresult, @result);
    } else {
        return join("\n", @result);
    }
			if(!$form{mypart}){ # (携帯電話向け)部分読みの時は部分編集は表示しない
				my $cookedpage = &encode($form{mypage});
				$cookedpage =~ s/%/%%/g;
				push(@result, sprintf(qq(<div class="partinfo"><a class="partedit" title="$resource{editthispart}" href="$url_cgi?mycmd=edit&amp;mypage=$cookedpage&amp;mypart=%d">$resource{editthispart}</a> <a class="partedit" title="$resource{returntop}" href="$url_cgi?$cookedpage#top">$resource{returntop}</a></div>), $tocnum + 2)) if ($option{'partinfo'});		# Walrus add [part edit]
				push(@result, "<p>");
				unshift(@saved, "</p>");
			}
			$tocnum++;
		} elsif (/^(-{2,3})\($/) {
			if ($& eq '--(') {
				$verbatim = { func => \&inline, done => '--)', class => 'verbatim-soft' };
			} else {
				$verbatim = { func => \&escape, done => '---)', class => 'verbatim-hard' };
			}
			&back_push('pre', 1, \@saved, \@result, " class='$verbatim->{class}'");
		} elsif (/^\/\//) {
			# This is comment out. nop.
		} elsif (/^----/) {
			push(@result, splice(@saved), '<hr>');
		} elsif (/^(-{1,3})(.+)/) {
			&back_push('ul', length($1), \@saved, \@result);
			push(@result, '<li>' . &inline($2) . '</li>');
		} elsif (/^(={1,3})(.+)/) {
			&back_push('ol', length($1), \@saved, \@result);
			push(@result, '<li>' . &inline($2) . '</li>');
		} elsif (/^:([^:]+):(.+)/) {
			&back_push('dl', 1, \@saved, \@result);
			push(@result, '<dt>' . &inline($1) . '</dt>', '<dd>' . &inline($2) . '</dd>');
		} elsif (/^(>{1,3})(.+)/) {
			&back_push('blockquote', length($1), \@saved, \@result);
			push(@result, &inline($2));
		} elsif (/^$/) {
			push(@result, splice(@saved));
			unshift(@saved, "</p>");
			push(@result, "<p>");
		} elsif (/^(\s+.*)$/) {
			&back_push('pre', 1, \@saved, \@result);
			push(@result, &escape($1)); # Not &inline, but &escape
		} elsif (/^\,(.*?)[\x0D\x0A]*$/) {
			&back_push('table', 1, \@saved, \@result, ' border="1"');
			#######
			# This part is taken from Mr. Ohzaki's Perl Memo and Makio Tsukamoto's [[WalWiki]].
			# XXXXX
			my $tmp = "$1,";
			my @value = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
			my @align = map {(s/^\s+//) ? ((s/\s+$//) ? ' align="center"' : ' align="right"') : ''} @value;
			my @colspan = map {($_ eq '==') ? 0 : 1} @value;
			for (my $i = 0; $i < @value; $i++) {
				if ($colspan[$i]) {
					while ($i + $colspan[$i] < @value and $value[$i + $colspan[$i]] eq '==') {
						$colspan[$i]++;
					}
					$colspan[$i] = ($colspan[$i] > 1) ? sprintf(' colspan="%d"', $colspan[$i]) : '';
					$value[$i] = sprintf('<td%s%s>%s</td>', $align[$i], $colspan[$i], &inline($value[$i]));
				} else {
					$value[$i] = '';
				}
			}
			push(@result, join('', '<tr>', @value, '</tr>'));
			# XXXXX
			#######
		} elsif (/^\#(\w+)(\((.*)\))?/) {
			# BlockPlugin.
			my $original_line = $_;
			my $plugin_name = $1;
			my $argument = &escape($3);
			my $result = $plugin_manager->call($plugin_name, 'block', $argument);
			if (defined($result)) {
				push(@result, splice(@saved));
			} else {
				$result = $original_line;
			}
			push(@result, $result);
		} else {
			push(@result, &inline($_));
		}
	}
#
	if ($depth > 0) { # 見出しの<div>を全てクローズ
		my $open_tag_cnt;
		
		for(my $i; $i < 7; $i++){
			$open_tag_cnt = $open_tag_cnt + $open_tag[$i];
			$open_tag[$i] = 0;
		}
		
		if($open_tag_cnt){
			push(@result, splice(@saved));
		}
		while($open_tag_cnt) {
			push(@result,qq(</div>));
			$open_tag_cnt--;
		}
	}
#
	push(@result, splice(@saved));
	
	if ($option{toc} && !$form{mypart}) {
		# Convert @toc (table of contents) to HTML.
		# This part is taken from Makio Tsukamoto's [[WalWiki]].
		my (@tocsaved, @tocresult);
		foreach (@toc) {
			if (/^(-{1,5})(.*)/) {
				&back_push('ul', length($1), \@tocsaved, \@tocresult);
				push(@tocresult, '<li>' . $2 . '</li>');
			}
		}
		push(@tocresult, splice(@tocsaved));
		
		# Insert "table of contents".
		if (@tocresult) {
			unshift(@tocresult, qq(<h2>$resource{table_of_contents}</h2>));
		# Insert CSS <div class="index"> - </div>
			unshift(@tocresult, qq(<div class="index">));
			push(@tocresult, "</div>");
		}
		
		unshift(@result, qq(<div class="h1level">));
		push(@result, qq(</div>));
		my $return_html = join("\n",@tocresult,@result);
		$return_html =~ s/<p>\n<\/p>//mg;
		
		return $return_html;
	} else {
		my $return_html = join("\n",@result);
		$return_html =~ s/<p>\n<\/p>//mg;
		
		return $return_html;
	}
}
---)

- [[remove_tag関数]]
- [[inline関数]]
- encode関数
- [[escape関数]]
- back_push関数

- Yuki::PluginManagerモジュール
-- $plugin_manager->call関数

----
[[#trackback]]

先頭へ