package TDS::Renderer;
# $Id: Renderer.pm,v 1.79 2001/02/06 05:02:09 tom Exp $
################################################################

=head1 NAME

TDS::Renderer - rendering class

=head1 SYNOPSIS

 use TDS::Renderer;
 use TDS::Status;
 use TDS::Collection;

 $col = new TDS::Collection;
 $r = new TDS::Renderer;
 $r->Read;
 print $r->AsHTML($col);


=head1 DESCRIPTION

derived from TDS::Skelton.html.
read skelton.html, expand macros, return html.

=cut

################################################################

use strict qw(vars);
use vars qw(@ISA $SkeltonFilename
	    $LAST_MODIFIED $LAST_MODIFIED_GMT
	    $RECENT $REFRESH $NEXT_MONTH $PREV_MONTH $THIS_MONTH
	    $NEXT_DAY $PREV_DAY $THIS_DAY
	    $FIRST_PART $MIDDLE_PART $LAST_PART
	    $CATEGORY $SUBMIT_JUMP $SUMMARY $TITLE_SUMMARY $POWER
	    $SEARCH_SUBMIT $SEARCH_RESULT $SEARCH_SELECT_SORT $SEARCH_SELECT_OBJECT $SEARCH_CHECKBOX_TITLEONLY $SEARCH_CHECKBOX_IGNORECASE
	    $SEARCH_CHECKBOX_UNUSE_SIGNATURE_METHOD
	    $AUTHOR_MANAGEMENT $TDF_EDITOR $URL_LIST $DICTIONARY
	    $LOG_ANALYSE_ANCHOR
	    $COMMENT_SUBMIT $COMMENT_SHOW_ALL_SUBMIT $COMMENT_COOKIE_OK);


use JConv;
use DateTime::Format;
use CGI::CookieTool;
use CGI::QueryString;
use CGI::Tools;
use Template;

use TDS;
use TDS::System;
use TDS::Mode;
use TDS::Skelton;

use TDS::DirInfo;

@ISA = qw(TDS::Skelton);


=head1 STATIC VARIABLES

テンプレート：

 $LAST_MODIFIED      最新更新日
 $RECENT             ダイジェストへ
 $REFRESH            更新
 $NEXT_MONTH         来月へ
 $PREV_MONTH         先月へ
 $THIS_MONTH         今月へ
 $NEXT_DAY           翌日へ
 $PREV_DAY           先日へ
 $THIS_DAY           今日へ
 $SUBMIT_JUMP        ジャンプサブミットラベル
 $SUMMARY            一覧へ
 $TITLE_SUMMARY      タイトル一覧へ
 $SEARCH_SUBMIT      検索サブミットラベル
 $SEARCH_RESULT      検索結果表示
 $SEARCH_SELECT_SORT 並び順（新しい、古い）
 $SEARCH_SELECT_OBJECT 検索対象
 $SEARCH_CHECKBOX_TITLEONLY   タイトルのみ検索
 $SEARCH_CHECKBOX_IGNORECASE  ignorecase
 $SEARCH_CHECKBOX_UNUSE_SIGNATURE_METHOD
 $AUTHOR_MANAGEMENT  著者管理へ
 $TDF_EDITOR         TdfEditor へ
 $URL_LIST           URL リストへ
 $DICTIONARY         辞書へ
 $LOG_ANALYSE_ANCHOR ログ解析へ
 $COMMENT_SUBMIT     コメントサブミットラベル
 $COMMENT_SHOW_ALL_SUBMIT コメントすべて表示サブミットラベル
 $COMMENT_COOKIE_OK  名前をクッキーで食わせるか   

ファイル名：

 $SkeltonFile             雛形ファイル名

=cut

# ラベルテンプレート
# マクロ名と同一
$LAST_MODIFIED = "%a, %d %b %Y %H:%M:%S %Z" unless defined $LAST_MODIFIED;
$LAST_MODIFIED_GMT = "%a, %d %b %Y %H:%M:%S %Z" unless defined $LAST_MODIFIED_GMT;
$RECENT = "[最新版]" unless defined $RECENT;
$REFRESH = "[更新]" unless defined $REFRESH;
$NEXT_MONTH = "&gt;&gt;" unless defined $NEXT_MONTH;
$PREV_MONTH = "&lt;&lt;" unless defined $PREV_MONTH;
$THIS_MONTH = "==" unless defined $THIS_MONTH;
$NEXT_DAY = "&gt;" unless defined $NEXT_DAY;
$PREV_DAY = "&lt;" unless defined $PREV_DAY;
$THIS_DAY = "=" unless defined $THIS_DAY;
$FIRST_PART = "[上旬]" unless defined $FIRST_PART;
$MIDDLE_PART = "[中旬]" unless defined $MIDDLE_PART;
$LAST_PART = "[下旬]" unless defined $LAST_PART;
$SUBMIT_JUMP = "ジャンプ" unless defined $SUBMIT_JUMP;
$SUMMARY = "[一覧]" unless defined $SUMMARY;
$POWER = "[ヲレパワー]" unless defined $POWER;
$TITLE_SUMMARY = "[タイトル]" unless defined $TITLE_SUMMARY;
$CATEGORY = 'ALL' unless defined $CATEGORY;
$SEARCH_SUBMIT =  "検索" unless defined $SEARCH_SUBMIT;
$SEARCH_RESULT = "<p>%diary 日 %num 件の日記にマッチしました</p>" unless defined $SEARCH_RESULT;
$SEARCH_SELECT_SORT =  "新しい順,古い順" unless defined $SEARCH_SELECT_SORT;
$SEARCH_SELECT_OBJECT = "指定した月,最近３ヶ月,最近一年,すべて" unless defined $SEARCH_SELECT_OBJECT;
$SEARCH_CHECKBOX_TITLEONLY = "タイトルのみ" unless defined $SEARCH_CHECKBOX_TITLEONLY;
$SEARCH_CHECKBOX_IGNORECASE = "大小文字無視" unless defined $SEARCH_CHECKBOX_IGNORECASE;
$SEARCH_CHECKBOX_UNUSE_SIGNATURE_METHOD = "シグネチャ法不使用" unless defined $SEARCH_CHECKBOX_UNUSE_SIGNATURE_METHOD;
$AUTHOR_MANAGEMENT =  "[著者管理]" unless defined $AUTHOR_MANAGEMENT;
$TDF_EDITOR = "[日記編集]" unless defined $TDF_EDITOR;
$URL_LIST = "[URL リスト]" unless defined $URL_LIST;
$DICTIONARY = "[辞書]" unless defined $DICTIONARY;
$LOG_ANALYSE_ANCHOR =  "[アクセス解析]" unless defined $LOG_ANALYSE_ANCHOR;
$COMMENT_SUBMIT = "送る" unless defined $COMMENT_SUBMIT;
$COMMENT_SHOW_ALL_SUBMIT = "過去のを表示" unless defined $COMMENT_SHOW_ALL_SUBMIT;
$COMMENT_COOKIE_OK = "名前記録" unless defined $COMMENT_COOKIE_OK;

$SkeltonFilename = "skelton.html";

attributes qw(status);

################################################################

=head1 MEMBER FUNCTIONS

=cut

sub initialize($)
{
    my $self = shift;

    $self->status($TDS::Status);
    $self->SUPER::initialize;
}

=head2 $r->AsHTML($col);

日記ソース集合 $col を元にマクロを展開し HTML 化して返す。
$col は必ず渡さなければならない。

=cut


sub AsHTML($$)
{
    my ($self, $col) = @_;

    # $col は必ず渡さなければならない
    die "BUG: Renderer::AsHTML: \$col must be set." unless $col;
    print "<br>html start " .  (time() - $TDS::Status->start_time->time)
	if $TDS::Status->debug;

    ################
    # 設定
    for (keys %{$self->config}){
	require TDS::Tdf::Command;
	my $conf_name = $_;
	if (/(.*)_template$/){        # テンプレート
	    my ($cmd_name, $is_end) = /^(.*)_(end_)?template/;
	    $cmd_name = uc($cmd_name);
	    IllegalConfig("no such command: $cmd_name")
		unless $TDS::Tdf::Command::IsCommand{$cmd_name};
	    my $var = sprintf("TDS::Tdf::Command::%s::%sTemplate",
			      $cmd_name, ($is_end)?'End':'');
	    $$var = $self->config->{$conf_name};
	} elsif (/^date_format$/){    # 日付
	    $TDS::Tdf::Command::DIARY::DateFormat =
		$self->config->{'date_format'};
	} elsif (/^(.*)_mark$/){      # マーク
	    my $conf = $_;
	    my %hash = ('new'=>'NEW',
			'topic'=>'NEW', # 互換性のため
			'sub'=>'SUB');
	    my $cmd_name = uc($hash{$1});
	    unless ($cmd_name){
		IllegalConfig("no such mark: ${cmd_name}_mark");
	    }
	    my $var = "TDS::Tdf::Command::${cmd_name}::Mark";
	    $$var = $self->config->{$conf};
	} elsif (/^(.*)_explain$/){   # 説明文
	    require TDS::Explain;
	    my $conf = $_;
	    my %hash = ('recent'=>'Recent',
			'month'=>'Month',
			'day'=>'Day',
			'part'=>'Part',
			'each_part'=>'EachPart',
			'search'=>'Search');
	    my $name = $hash{$1};
	    unless ($name){
		IllegalConfig("no such explain: $name");
	    }
	    my $var = "TDS::Explain::${name}";
	    $$var = $self->config->{$conf};
	}
    }
    require TDS::Explain;
    my $explain = new TDS::Explain;    # 後で使う
    
    ### 主機能 ###
    # 日記本体
    my $params = {is_author=>$TDS::Status->is_author};
    my $content_html = $col->AsHTML($params);

    $self->SetMacro("(DIARY_)?CONTENT", sub {
	return $content_html;
    });
    # タイトル
    $self->SetMacro("TITLE", $params->{title} || TDS::IdentInfo->Get('title'));
    
    # 日記数
    $self->SetMacro("NUMBER_OF_DIARY", @{$col->diarys}+0);
    # 更新日
    $self->SetMacro("LAST_MODIFIED(_GMT)?", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= $$cmd;
	my $tz = ($1) ? 'GMT' : $TDS::System::TZ;
	my $lm = (TDS::Mode::IsDynamic()) ? $TDS::Status->lmo->GetLastModified :
	    time();
	time2str($var, $lm, $tz);
    });
    
    # explain
    $self->SetMacro("EXPLAIN", $explain->AsString($TDS::Status));
		     
    # info
    $self->SetMacro("INFORMATION", sub {
	require TDS::Information;
	my $self = shift;
#	return unless $TDS::Status->mode eq 'RECENT';
	my $info = new TDS::Information;
	$info->Read;
	return $info->AsPlain;
    });

    # captured antenna
    $self->SetMacro("CAPTURED_ANTENNA", sub {
	require TDS::CapturedAntenna;
	my $datafile = GetDataDir() . "/captured_antenna.dat";
	unless (-f $datafile){
	    #return "no data: $datafile";
	    return undef;
	} else {
	    my $ca = new TDS::CapturedAntenna;
	    $ca->Read;
	    return $ca->AsHTML;
	}
    });

    # mode
    $self->SetMacro("MODE", sub {

	my $self = shift;
	my $str = TDS::Mode::GetMode();
	$str .= "(modperl)"
	    if CGI::Tools::IsModPerl();
	$str .= "<!-- BEGIN @@@ AUTHOR @@@ -->,author<!-- END @@@ AUTHOR @@@ -->"
	    if $TDS::Status->is_author;
	$str .= ",site"
	    if $TDS::System::TDS_Root;
	$str .= ",style:$TDS::Style::Name"
	    if $TDS::Style::Name;
	require TDS::Cache::Base;
	$str .= ",cache:" . ((TDS::Cache::Base->PermittedCaching) ? 'on' : 'off');
	$str .= ",robot" if $TDS::Status->is_robot;
	$str .= ",debug"
	    if $TDS::Status->debug;
	return "[$str]";
    });
    # 環境変数
    my $skelton = $self->skelton;      # 後まで使う
    $skelton =~ s/<!--#echo var="(.*)" *-->/$ENV{$1}/g;

    ################
    # カウンター
    if (TDS::Mode::IsDynamic()){
	$self->SetMacro("COUNTER_?(ALL|TODAY)?", sub {
	    require TDS::AccessLog;
	    return undef unless $TDS::AccessLog::EnableAccessLog;
	    require TDS::AccessLog::Counter;

	    my ($self, $cmd, $var) = @_;
	    my $counter = new TDS::AccessLog::Counter;
	    if ($1){
		if ($1 eq 'ALL'){
		    $var = $counter->All;
		} elsif ($1 eq 'TODAY'){
		    $var = $counter->Today;
		} else {
		    die "Illegal Command: $cmd";
		}
		return "<!-- BEGIN @@@ $cmd @@@ -->$var<!-- END @@@ $cmd @@@ -->";		
	    } else {
		$var ||= $TDS::AccessLog::Counter::Template;
		$var = "<!-- BEGIN @@@ $cmd @@@ -->$var<!-- END @@@ $cmd @@@ -->";
		Expand($var, {'all'=>$counter->All,
			      'today'=>$counter->Today,
			      'times'=>$TDS::Status->id->GetTimes
			      });
	    }
	});
	$self->SetMacro("COOKIE_TIMES", sub {
	    my ($self, $cmd, $var) = @_;
	    return "<!-- BEGIN @@@ $cmd @@@ -->" . $TDS::Status->id->GetTimes .
		"<!-- END @@@ $cmd @@@ -->";
	});
    } else {                                            # static
	$self->SetMacro("COUNTER_?(ALL|TODAY)?", sub {
	    my ($self, $cmd, $var) = @_;
	    require TDS::AccessLog;
	    return undef unless $TDS::AccessLog::EnableAccessLog;
	    if ($1){
		return "<strong>$cmd: depreceted in static mode</strong>";
	    }
	    return qq(<!--\#exec cgi="counter.cgi"-->);
	});
	require TDS::Cookie;
	if ($TDS::Cookie::EnableCookie){
	    $skelton =~ s@(</head>)@<!--#exec cgi="set-cookie.cgi" -->$1@i;
	}
	$self->SetMacro("COOKIE_TIMES", sub {
	    my ($self, $cmd, $var) = @_;
	    return "<strong>$cmd: deprecated in static mode.</strong>";
	});
    }
    ################
    # PIM : 最新版の時のみ表示
    if ($TDS::Status->mode eq 'RECENT' &&
	!$TDS::Status->title_mode){
	$skelton =~ s/<!--#begin +cmd="?PIM"? *-->//;
	$skelton =~ s/<!--#end +cmd="?PIM"? *-->//;
    } else {
	$skelton =~ s/<!--#begin +cmd="?PIM"? *-->.*<!--#end +cmd="?PIM"? *-->//s;
    }
    # TODO
    $self->SetMacro("TODO", sub {
	require TDS::PIM::Todo;
	my $todo = new TDS::PIM::Todo;
	return $todo->AsHTML;
    });
    
    # Schedule
    $self->SetMacro("SCHEDULE", sub {
	require TDS::PIM::Schedule;
	my $sch = new TDS::PIM::Schedule;
	return $sch->AsHTML;
    });
    # CGI が有効なときのみ
    if ($TDS::System::CanUseCGI &&
	!$TDS::Status->title_mode){    # タイトル一覧の時は表示しない
	# SEARCH
	$skelton =~ s/<!--#begin +cmd="?SEARCH"? *-->/<form action="search.cgi" method="get"><div>/;
	$skelton =~ s/<!--#end +cmd="?SEARCH"? *-->/<\/div><\/form>/;
    } else {
	$skelton =~ s/<!--#begin +cmd="?SEARCH"? *-->.*<!--#end +cmd="?SEARCH"? *-->//s;
    }
    # COMMENT
    if ($TDS::System::CanUseCGI &&
	!$TDS::Status->title_mode){
	$skelton =~ s/<!--#begin +cmd="?COMMENT"? *-->/<form action="comment.cgi" method="post">/;	
	$skelton =~ s/<!--#end +cmd="?COMMENT"? *-->/<\/form>/;	
    } else {
	$skelton =~ s/<!--#begin +cmd="?COMMENT"? *-->.*<!--#end +cmd="?COMMENT"? *-->//s;
    }
    $self->skelton($skelton);    # ここまで

    ################
    # ジャンプ
    $self->SetMacro("RECENT", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= $RECENT;
	my $href = (TDS::Mode::IsStatic()) ? 
	    "./$TDS::Static::RecentHtml" : "./TsDiary.cgi";
	return qq(<a href="$href">$var</a>);
    });
    $self->SetMacro("REFRESH", sub {
	return unless $TDS::System::CanUseCGI;
	my ($self, $cmd, $var) = @_;
	$var ||= $REFRESH;
	return qq(<a href="TsDiary.cgi?refresh=1">$var</a>);
    });
    $self->SetMacro("(FIRST|MIDDLE|LAST)_PART", sub {
	my ($self, $cmd, $var) = @_;
	require TDS::Navigator::Partly;
	my $nav = new TDS::Navigator::Partly(year=>$TDS::Status->year,
					     month=>$TDS::Status->month);
	$var ||= "$$cmd";
	if ($1 eq "FIRST"){
	    return $nav->AsFirst($var);
	} elsif ($1 eq "MIDDLE"){
	    return $nav->AsMiddle($var);
	} elsif ($1 eq "LAST"){
	    return $nav->AsLast($var);
	}
    });
	
    $self->SetMacro("(NEXT|PREV|THIS)_MONTH", sub {
	my ($self, $cmd, $var) = @_;
	require TDS::Navigator::Monthly;
	my $nav = new TDS::Navigator::Monthly(year=>$TDS::Status->year,
					      month=>$TDS::Status->month);
	$var ||= "$$cmd";
	if ($1 eq "NEXT"){
	    return $nav->AsNext($var);
	} elsif ($1 eq "THIS"){
	    return $nav->AsThis($var);
	} elsif ($1 eq "PREV"){
	    return $nav->AsPrev($var);
	}
    });
    $self->SetMacro("(NEXT|PREV|THIS)_DAY", sub {
	my ($self, $cmd, $var) = @_;
	require TDS::Navigator::Daily;
	my $nav = new TDS::Navigator::Daily(year=>$TDS::Status->year,
					    month=>$TDS::Status->month,
					    day=>$TDS::Status->day_part);
	$var ||= "$$cmd";
	if ($1 eq "NEXT"){
	    return $nav->AsNext($var);
	} elsif ($1 eq "THIS"){
	    return $nav->AsThis($var);
	} elsif ($1 eq "PREV"){
	    return $nav->AsPrev($var);
	}
    });
    $self->SetMacro("PREV_FEW_DAYS", sub {
	my ($self, $cmd, $var) = @_;
	require TDS::Navigator::FewDays;
	my $nav = new TDS::Navigator::FewDays(year=>$TDS::Status->year,
					      month=>$TDS::Status->month,
					      day=>$TDS::Status->day_part);
	$var ||= "$$cmd";
	return $nav->AsPrev($var, $col->last_day);
    });
    
    # 年月、旬指定
    $self->SetMacro("OTHER_MONTH_FORM", "<strong>deprecated: use SELECT_YEAR/MONTH/PART/CATEGORY,SUBMIT_JUMP</strong>");
    $self->SetMacro("CATEGORY", "<strong>Macro Name Changed: CATEGORY -> SELECT_CATEGORY</strong>");
    $self->SetMacro("YMD_JUMP", sub {
	my ($self, $cmd, $var) = @_;
	my $ymd = sprintf("%04d%02d%02d",
			  $TDS::Status->year,
			  $TDS::Status->month,
			  $TDS::Status->day_part);
	return qq(<form action="title.cgi" method="get">
		  <input type="text" name="keywords" value="$ymd">
		  <input type="submit" value="GO">
		  </form>);
    });

    $self->SetMacro("SELECT_(YEAR_MONTH|JUN|PART|CATEGORY)", sub {
	my ($self, $cmd, $var) = @_;
	
	return undef
	    unless $TDS::System::CanUseCGI;
	my @html;

	if ($1 eq 'YEAR_MONTH'){
	    push(@html, '<select name="year_month">');

	    for ($col->GetMonthDirs){
		my ($year, $month) = @$_;
		my $ym = sprintf("%04d/%02d", $year, $month);
		if ($TDS::Status->year == $year &&
		    $TDS::Status->month == $month){
		    push(@html, qq(<option value="$ym" selected>$ym</option>));
		} else {
		    push(@html, qq(<option value="$ym">$ym</option>));
		}
	    }
	    if (0){
		for (reverse($TDS::Status->start_year..$TDS::Status->year)){
		    my $year = $_;
		    for (reverse(1..12)){
			my $month = $_;
			my $ym = sprintf("%04d/%02d", $year, $month);
			if ($TDS::Status->year == $year &&
			    $TDS::Status->month == $month){
			    push(@html, qq(<option value="$ym" selected>$ym</option>));
			} else {
			    push(@html, qq(<option value="$ym">$ym</option>));
			}
		    }
		}
	    }
	} elsif ($1 eq 'JUN' || $1 eq 'PART'){
	    my @values = split(',', $explain->each_part);
	    push(@html, '<select name="part">');
	    my $start = ($TDS::Collection::AllowAllMonthly) ? 0 : 1;
	    my @table = ('', 'a', 'b', 'c');
	    for ($start..3){
		if (param('part') eq $table[$_]){
		    push(@html, qq(<option value="$table[$_]" selected>$values[$_]</option>));
		} else {
		    push(@html, qq(<option value="$table[$_]">$values[$_]</option>));
		}
	    }
	} elsif ($1 eq 'CATEGORY'){
#	    $var ||= "$$cmd";
	    $var = "ALL";
	    push(@html, $TDS::Status->category->AsHTML_Array($var));
	}
	push(@html, "</select>");
	return join("\n", @html);
    });
    # サブミットジャンプ
    $self->SetMacro("SUBMIT_JUMP", sub {
	my ($self, $cmd, $var) = @_;
	return undef
	    unless $TDS::System::CanUseCGI;

	$var ||= "$$cmd";
	return qq(<input type="submit" value="$var">);
    });
    
    # 一覧
    $self->SetMacro("SUMMARY", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	my $href = (TDS::Mode::IsStatic()) ?
	    "summary.html" : "summary.cgi";
	return qq(<a href="$href">$var</a>);
    });
    # タイトル
    $self->SetMacro("TITLE_SUMMARY", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	unless (TDS::Mode::IsStatic()){
	    my $query = $ENV{'QUERY_STRING'};
	    if ($query){
		$query = "?$query";
	    }
	    return qq(<a href="title.cgi$query">$var</a>);
	} else {
	    undef;
	}
    });
    # ヲレパワー
    $self->SetMacro("POWER", sub {
	my ($self, $cmd, $var) = @_;
	unless ($TDS::System::CanUseCGI){
	    return undef;
	}
	$var ||= "$$cmd";
	my $query;
	if ($TDS::Status->GetMode() !~ /RECENT/){
	    $query = "?" . param('keywords');
	}
	return qq(<a href="power.cgi$query">$var</a>);
    });
    # カレンダー
    $self->SetMacro("CALENDAR", sub {
	my ($self, $cmd, $var) = @_;
	return undef if $TDS::Status->title_mode;
	
	require TDS::Calendar;
	my $calendar = new TDS::Calendar; #::Table;
	$calendar->AsHTML($TDS::Status->year, $TDS::Status->month, $col);
    });
    ################
    # 検索
    $self->SetMacro("SEARCH_([A-Z_]*)", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";

	require TDS::Search;
	
	return undef                # CGI should be enabled
	    unless ($TDS::System::CanUseCGI);
	
	if ($1 eq 'INPUT_KEYWORD'){
	    my $keyword = $TDS::Status->keyword->escaped || "";
	    return qq(<input type="text" name="keyword" value="$keyword" $var>);
	} elsif ($1 eq 'SUBMIT'){
	    return qq(<input type="submit" value="$var">);
	} elsif ($1 eq 'RESULT'){
	    return undef unless param('keyword');
	    return Expand($var, $params->{match});
	} elsif ($1 eq 'SELECT_SORT'){
	    my @list = split(",", $var);
	    my @html = (qq(<select name="search_sort">));
	    for ('last', 'old'){
		my $tmp = shift(@list);
		my $selected = (param('search_sort') eq $_) ? 'selected': '';
		push(@html, qq(<option value="$_" $selected>$tmp</option>));
	    }
	    push(@html, "</select>");
	    return join("\n", @html);
	} elsif ($1 eq 'SELECT_OBJECT'){
	    my @list = split(",", $var);
	    my @html = (qq(<select name="search_object">));

	    my $type;
	    for $type ('this', 'recent', 'year', 'all'){
		my $tmp = shift(@list);
		if ($type eq 'all'){
		    if(&TDS::Mode::GetMode() eq 'dynamic' &&
		       !$TDS::Status->is_author &&
		       !$TDS::Search::AllowAllSearch){
			next;
		    }
		}
		my $selected = (param('search_object') eq $type) ? 'selected': '';
		push(@html, qq(<option value="$type" $selected>$tmp</option>));
	    }
	    push(@html, "</select>");
	    return join("\n", @html);
	} elsif ($1 eq 'CHECKBOX_TITLEONLY'){
	    my $checked = (param('search_titleonly')) ? 'checked' : '';
	    return qq(<input type="checkbox" name="search_titleonly" $checked>$var);
	} elsif ($1 eq 'CHECKBOX_IGNORECASE'){
	    if ($TDS::Search::UseSignatureMethod){
		# シグネチャ法を使うと ignorecase できない
		return undef;
	    } else {
		my $checked = (param('search_ignorecase')) ? 'checked' : '';
		return qq(<input type="checkbox" name="search_ignorecase" $checked>$var);
	    }
	} elsif ($1 eq 'CHECKBOX_UNUSE_SIGNATURE_METHOD'){
	    my $checked = (param('search_unuse_signature_method')) ? 'checked' : '';
	    if ($TDS::Search::UseSignatureMethod){
		return qq(<input type="checkbox" name="search_unuse_signature_method" $checked>$var);
	    } else {
		return undef;
	    }
	}

    });
    ################
    # 付加機能
    # 目次
    $self->SetMacro("TOC|TABLE_OF_CONTENTS", sub {
	my ($self, $cmd, $var) = @_;

	return $params->{_toc}->AsHTML
	    if ref $params->{_toc};
    });
	
    # URL リスト表示
    $self->SetMacro("URL_LIST", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	return sprintf(qq(<a href="%s">$var</a>),
		       (TDS::Mode::IsStatic()) ?
		       "url.html" : "url.cgi");
    });
    # 辞書
    $self->SetMacro("DICTIONARY", sub {
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	return sprintf(qq(<a href="%s">$var</a>),
		       (TDS::Mode::IsStatic()) ?
		       "dictionary.html" : "dictionary.cgi");
    });
    # 著者管理
    $self->SetMacro("AUTHOR_MANAGEMENT", sub {
	return undef unless $TDS::System::CanUseCGI;
	
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	return qq(<a href="admin/">$var</a>);
    });
    # TdfEditor
    $self->SetMacro("TDF_EDITOR", sub {
	return undef unless $TDS::System::CanUseCGI;
	
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";
	return qq(<a href="admin/tdf_editor.cgi">$var</a>);
    });

    # アクセス解析
    $self->SetMacro("LOG_ANALYSE(_ANCHOR)?", sub {
	return undef unless $TDS::AccessLog::EnableAccessLog;
	
	my ($self, $cmd, $var) = @_;
	$var ||= "$$cmd";

	return sprintf(qq(<a href="admin/log.cgi?year=%04d&amp;month=%02d">$var</a>),
		       $TDS::Status->year, $TDS::Status->month);
    });
    # 一言機能
    $self->SetMacro("COMMENT_([A-Z_]+)", sub {
	require TDS::Comment;
	my ($self, $cmd, $var) = @_;
	if ($1 eq "INPUT_NAME"){
	    unless ($TDS::Status->request_uri){    # html
		return qq(<input type="text" name="name" value="<!--\#exec cgi='comment_name.cgi' -->" $var>);
	    } else {
		my $name = GetCookie('name');
		return qq(<!-- BEGIN @@@ $cmd @@@ --><input type="text" name="name" value="$name" $var><!-- END @@@ $cmd @@@ -->);
	    }
	} elsif ($1 eq "INPUT_CONTENT"){
	    return qq(<input type="text" name="comment" value="$TDS::Comment::DefaultComment" $var>);
	} elsif ($1 eq "SUBMIT"){
	    $var ||= "$$cmd";
	    return qq(<input type="submit" name="add" value="$var">);
	} elsif ($1 eq "SHOW_ALL_SUBMIT"){
	    $var ||= "$$cmd";
	    return qq(<input type="submit" name="show_all" value="$var">);
	} elsif ($1 eq "SHOW"){
	    $var ||= 5;
	    if ($TDS::Status->request_uri){   # CGI
		my $cmt = new TDS::Comment;
#		return "<!-- BEGIN @@@ $cmd @@@ -->" . $cmt->AsHTML .
#		    "<!-- END @@@ $cmd @@@ -->";
		return $cmt->AsHTML;
	    } elsif (!$TDS::System::CanUseSSI){
		# SSI is required in static mode
		warn qq(<!--\#macro cmd="COMMENT_SHOW" --> : must be able to use SSI);
	    } else {                           # output to file
		return qq(<!--\#exec cgi="comment_show.cgi" -->);
	    }
	} elsif ($1 eq "COOKIE_OK"){
	    $var ||= "$$cmd";
	    return qq(<input type="checkbox" name="cookie_ok" checked>$var\n);
	}
	    
    });
    ################################################################
    return join("", $self->SUPER::AsHTML);
}

################################################################
sub IllegalConfig($)
{
    my $msg = shift;

    die "<h2>illegal configuration in skelton.html</h2><p>$msg</p>";
}
1;
