Tripletail::Debug - TL デバッグ用クラス


Tripletail documentation Contained in the Tripletail distribution.

Index


Code Index:

NAME

Top

Tripletail::Debug - TL デバッグ用クラス

DESCRIPTION

Top

このクラスは use Tripletail 時に自動的に読み込まれる.

ini"enable_debug" を0以外にセットすると、 以後デバッグ機能が有効になる。 ini グループ名は "Debug" でなければならない。

公開されているメソッドは存在しない。

METHODS

flush

内部メソッド

print

内部メソッド

reset

内部メソッド

Ini パラメータ

enable_debug
  enable_debug = 1

デバッグ機能を使用するか否か。省略可能。

1の場合、有効。 0の場合、無効。 デフォルトは0。

無効にした場合、全てのデバッグオプションの設定は無効となる。

  popup_style = single

デバッグ情報のポップアップ表示の機能選択。可能な値は 'none', 'single', 'multiple' 。省略可能。

none

 ポップアップ表示しない(デフォルト)  noneに設定した場合、xxxxxx_popupでポップアップを指定しても無視される。

single

一つのウインドウに全情報を表示

multiple

表示する情報の個数分のウインドウを表示

warn_logging
  warn_logging = 1

warnメッセージをログに残すか否か。省略可能。

1の場合、残す。 0の場合、残さない。 デフォルトは1。

warn_popup
  warn_popup = 1

warnメッセージをポップアップ表示するように出力を加工するか否か。省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

log_popup
  log_popup = 1

$TL->log されたログをポップアップ表示するように出力を加工するか否か。省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

request_logging
  request_logging = 1

受け取ったリクエストデータの内容と、$CGI への変更履歴をログに残すか否か。省略可能。

1の場合、残す。 0の場合、残さない。 デフォルトは1。

request_logging_max
  request_logging_max = 100K

1回に出力するログの最大サイズの指定。0で無制限。省略可能。

デフォルトは0。

request_popup
  request_popup = 1

受け取ったリクエストデータの内容と、$CGI への変更履歴を別ウィンドウでポップアップするように、出力を加工するか否か。省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

content_logging
  content_logging = 1

応答コンテンツをログに残すか否か。省略可能。

1の場合、残す。 0の場合、残さない。 デフォルトは1。

content_logging_max
  content_logging_max = 100K

1回のログの最大サイズ。0で無制限。省略可能。

デフォルトは0。

content_popup
  content_popup = 1

応答コンテンツを別ウィンドウでポップアップするように出力を加工するか否か。省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

content_popup_max
  content_logging_max = 100K

1回のポップアップで表示する最大サイズ。0で無制限。省略可能。

デフォルトは0

template_logging
  template_logging = 1

使用した テンプレート ファイル名と、展開の内容をログに残すか否か。省略可能。

1の場合、残す。 0の場合、残さない。 デフォルトは1。

template_popup
  template_popup = 1

使用した テンプレートァイル名と、展開の内容を、別ウィンドウでポップアップするように出力を加工するか否か省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

ポップアップで表示するためには、出力するコンテンツに </head> を含む HTML が存在しなければならない。

db_logging
  db_logging = 1

DB へのクエリをログに残すか否か。省略可能。

1の場合、残す。 0の場合、残さない。 デフォルトは1。

db_logging_level及びdb_profileにて残すログの内容を設定可能。

db_popup
  db_popup = 1

DB へのクエリログをポップアップ表示するように出力を加工するか否か。省略可能。

1の場合、加工する。 0の場合、加工しない。 デフォルトは1。

db_logging_level
  db_logging_level = 2

DB へのクエリをログに残す際、応答内容をどの程度残すかを設定する。省略可能。

1の場合、DBへ発行したクエリ文と実行時間を残す。 2の場合、DBへ発行されたクエリ文と実行時間に加え、応答内容を残す。 デフォルトは1。

db_profile
  db_profile = 1

DB へのクエリをログに残す際、実行計画をどこまで残すかを設定する。省略可能。

1の場合、DBへ発行したクエリ文と実行時間を残す。 2の場合、DBへ発行されたクエリ文と実行時間に加え、実行計画を残す。 デフォルトは1。

location_debug
  location_debug = 1

$TL->location でリダイレクトする際に、Locationヘッダの代わりに、HTML画面を表示する。 利用しない場合、すぐにリダイレクトするため、デバッグ情報を確認する時間が取れない。

1の場合、HTML画面を表示する。 0の場合、HTML画面を表示せず、Locationヘッダを出力する。 デフォルトは0。

設定例
  [Debug]
  enable_debug=1
  popup_type=single
  warn_logging=1
  warn_popup=1
  log_popup=1
  request_logging=0
  request_logging_max=0
  request_popup=1
  content_logging=0
  content_logging_max=0
  content_popup=1
  content_popup_max=0
  template_logging=0
  template_popup=0
  db_logging=1
  db_popup=1
  db_logging_level=1
  db_profile=1
  location_debug=1

SEE ALSO

Top

Tripletail
Tripletail::DB
Tripletail::Filter
Tripletail::InputFilter
Tripletail::Template

AUTHOR INFORMATION

Top

Copyright 2006 YMIRLINK Inc.

This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself

このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。

Address bug reports and comments to: tl@tripletail.jp

HP : http://tripletail.jp/


Tripletail documentation Contained in the Tripletail distribution.

# -----------------------------------------------------------------------------
# Tripletail::Debug - TL デバッグ用クラス
# -----------------------------------------------------------------------------
package Tripletail::Debug;
use strict;
use warnings;
use UNIVERSAL qw(isa);
use Tripletail;

sub _INIT_HOOK_PRIORITY() { 1_000_000_000 }; # 順序は問わない。
sub _PRE_REQUEST_HOOK_PRIORITY() { 1_000_000_000 } # 最後でなければならない。 
sub _OUTPUT_FILTER_PRIORITY() { 1_000_000_000 } # 最後でなければならない。

our $_INSTANCE;

1;

sub _getInstance {
	my $class = shift;

	if(!$_INSTANCE) {
		$_INSTANCE = $class->__new(@_);
	}

	$_INSTANCE;
}


sub __new {
	my $class = shift;
	my $group = shift;
	my $this = bless {} => $class;

	$this->{group} = defined $group ? $group : 'Debug';
	$this->{enabled} = $TL->INI->get($this->{group} => 'enable_debug');
	$this->{popup_type} = $TL->INI->get($this->{group} => 'popup_type', 'none');

	$this->{warn_logging} = $TL->INI->get($this->{group} => 'warn_logging', 1);
	$this->{warn_popup} = $TL->INI->get($this->{group} => 'warn_popup', 1);

	$this->{log_popup} = $TL->INI->get($this->{group} => 'log_popup', 1);

	$this->{request_logging} = $TL->INI->get($this->{group} => 'request_logging', 1);
	$this->{request_popup} = $TL->INI->get($this->{group} => 'request_popup', 1);
	$this->{request_logging_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'request_logging_max', 0));

	$this->{content_logging} = $TL->INI->get($this->{group} => 'content_logging', 1);
	$this->{content_popup} = $TL->INI->get($this->{group} => 'content_popup', 1);
	$this->{content_logging_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'content_logging_max', 0));
	$this->{content_popup_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'content_popup_max', 0));

	$this->{template_logging} = $TL->INI->get($this->{group} => 'template_logging', 1);
	$this->{template_popup} = $TL->INI->get($this->{group} => 'template_popup', 1);

	$this->{db_logging} = $TL->INI->get($this->{group} => 'db_logging', 1);
	$this->{db_popup} = $TL->INI->get($this->{group} => 'db_popup', 1);
	$this->{db_logging_level} = $TL->INI->get($this->{group} => 'db_logging_level', 1);
	$this->{db_profile} = $TL->INI->get($this->{group} => 'db_profile', 1);

	$this->{location_debug} = $TL->INI->get($this->{group} => 'location_debug', 0);

	$this->reset;

	if($this->{enabled}) {
		# 最後に呼ばれるpreRequestハンドラを登録。
		$TL->setHook(
			'preRequest',
			_PRE_REQUEST_HOOK_PRIORITY,
			sub {
				if($this->{request_logging}) {
					$this->__log_request;
				}
			},
		);

		# 最初に呼ばれるinitハンドラを登録。
		$TL->setHook(
			'init',
			_INIT_HOOK_PRIORITY,
			sub {
				# 自分自身をContentFilterとして登録。
				$TL->setContentFilter(
					[__PACKAGE__, _OUTPUT_FILTER_PRIORITY], this => $this
				);
			},
		);

		if($this->{warn_logging} or $this->{warn_popup}) {
			$SIG{__WARN__} = sub {
				my $msg = shift;

				isa($msg, 'Tripletail::Error') or $msg = $TL->newError(warn => $msg);

				if($this->{warn_logging}) {
					$TL->_log(__PACKAGE__, "Warn: $msg");
				}

				if($this->{warn_popup}) {
					push @{$this->{warn_log}}, $msg;
				}
			};
		}
	}

	$this;
}

sub reset {
	my $this = shift;

	$this->{header} = undef;
	$this->{header_buf} = '';
	$this->{filter_buf} = '';
	$this->{db_log} = [];
	$this->{db_log_data} = {};
	$this->{dl_log} = [];
	$this->{form_log} = [];
	$this->{tl_log} = [];
	$this->{warn_log} = [];
	$this->{template_log} = [];
	$this->{explain_cache} = {};
	$this->{popup} = [];
}

sub __log_request {
	my $this = shift;

	# $TL->CGIの内容と%ENVの内容をログに吐く。
	my $log = '';

	if($TL->{CGIORIG}) {
		foreach my $key (sort $TL->{CGIORIG}->getKeys) {
			foreach my $value ($TL->{CGIORIG}->getValues($key)) {
				$log .= "[CGI:$key] $value\n";
			}
		}
	}

	foreach my $key (sort keys %ENV) {
		$log .= "[ENV:$key] $ENV{$key}\n";
	}

	my $lim = $this->{request_logging_max};
	if($lim and length($log) > $lim) {
		substr($log, $lim) = '...';
	}
	$TL->_log(__PACKAGE__, "Request Log:\n$log");
}

#--------- Tripletail::Template専用
sub _templateLog {
	my $this = shift;
	# node => Tripletail::Template::Node
	# type => new / expand / add / setForm / extForm/ addHiddenForm / addSessionCheck / toStr / flush
	#
	# ---[new]
	# 追加引数無し
	#
	# ---[expand]
	# args => HASH Ref
	# any  => 真偽値
	#
	# ---[setattr]
	# args => HASH Ref
	#
	# ---[add]
	# 追加引数無し
	#
	# ---[setForm]
	# form => Tripletail::Form
	# name => フォーム名
	#
	# ---[extForm]
	# name => フォーム名
	#
	# ---[addHiddenForm]
	# form => Tripletail::Form
	# name => フォーム名
	#
	# ---[addSessionCheck]
	# name => フォーム名
	#
	# ---[toStr]
	# 追加引数無し
	#
	# ---[flush]
	# 追加引数無し

	if(!$this->{enabled}
	|| (!$this->{template_popup} && !$this->{tamplate_logging})
	|| !$this->{template_log}) {
		return $this;
	}

	my $opts = { @_ };
	my $node = $opts->{node};

	# nodeはTripletail::Templateオブジェクトでなくノードパスとする。(/foo/bar形式)
	$opts->{node} = $node->_nodePath;

	# "fpath"を追加。このテンプレートがファイルから生成されたなら、そのファイル名。
	my $root;
	for($root = $node; $root->{parent}; $root = $root->{parent}) {}
	$opts->{fpath} = defined $root->{fpath} ? $root->{fpath} : '(file path unknown)';

	# formがあれば、それのcloneを保持。
	my $form = $opts->{form};
	if($form) {
		$opts->{form} = $form->clone;

		if(!length($opts->{name})) {
			$opts->{name} = '(anonymous form)';
		}
	}

	if($this->{template_popup}) {
		push @{$this->{template_log}}, $opts;
	}
	if($this->{template_logging}) {
		my $params_dump = '';
		if($opts->{type} eq 'expand') {
			$params_dump .= sprintf("Any: %d\n", $opts->{any});
		}
		if($opts->{type} eq 'setForm' || $opts->{type} eq 'addHiddenForm') {
			$params_dump .= sprintf("Form Name: %s\n", $opts->{name});
			foreach my $key ($opts->{form}->getKeys) {
				$params_dump .= sprintf("  %s = %s\n", $key, $opts->{form}->get($key));
			}
		}
		if($opts->{type} eq 'extForm') {
			$params_dump .= sprintf("Form Name: %s\n", $opts->{name});
		}
		if($opts->{type} eq 'addSessionCheck') {
			$params_dump .= sprintf("Form Name: %s\n", $opts->{name});
		}
		if($opts->{type} eq 'expand' || $opts->{type} eq 'setAttr') {
			foreach my $key (sort keys %{$opts->{args}}) {
				my $val = $opts->{args}{$key};
				defined($val) or $val = '(null)';
				$params_dump .= sprintf("  %s = %s\n", $key, $val);
			}
		}

		$TL->_log(
			__PACKAGE__,
			sprintf(
				"Template Trace [TYPE:$opts->{type}][FPATH:$opts->{fpath}][NODE:$opts->{node}]\n".
				"%s",
				$params_dump,
			)
		);
	}

	$this;
}

#--------- Tripletail::DB専用
sub _dbLog {
	my $this = shift;

	if (!$this->{enabled}
	|| (!$this->{db_popup} && !$this->{db_logging})
	|| !$this->{db_log}) {
		return $this;
	}
	
	my $opts;
	if( @_ && UNIVERSAL::isa($_[0], 'CODE') )
	{
		my $coderef = shift;
		$opts = { $coderef->(@_) };
	}else
	{
		$opts = { @_ };
	}
	local($@);
	
	eval 'require Data::Dumper';
	$@ and die $@;

	# パラメータはData::Dumperでのダンプを保存しておく。
	my $params_dump = Data::Dumper->new([$opts->{params}])
		->Indent(0)->Terse(1)->Deepcopy(1)->Dump;

	if($this->{db_logging}
	&& ($this->{db_profile} >= 1
	|| $this->{db_logging_level} >= 1)) {
		$TL->_log(
			__PACKAGE__,
			sprintf(
				"DB Trace [Map: %s][Set: %s][DB: %s][ID: %d]\n".
				"Elapsed: %s seconds\n".
				"Query:\n".
				"%s\n".
				"Parameters:\n".
				"%s",
				$opts->{group},
				$opts->{set},
				$opts->{db},
				$opts->{id},
				$opts->{elapsed},
				$opts->{query},
				$params_dump,
			)
		);
	}

    if ($Tripletail::IN_EXTENT_OF_STARTCGI) {
        
        push @{$this->{db_log}}, {
            group   => $opts->{group},
            set     => $opts->{set},
            db      => $opts->{db},
            id      => $opts->{id},
            query   => $opts->{query},
            params  => $params_dump,
            elapsed => $opts->{elapsed},
            error   => $opts->{error},
        };

        push(@{$this->{db_log_data}{$opts->{id}}}, $opts->{names});
    }

	$this;
}

sub _dbLogData {
	my $this = shift;

	if (!$this->{enabled}
	|| (!$this->{db_popup} && !$this->{db_logging})
	|| !$this->{db_log}
	|| ($this->{db_logging_level} <= 1)) {
		return $this;
	}

	my $opts;
	if( @_ && UNIVERSAL::isa($_[0], 'CODE') )
	{
		my $coderef = shift;
		$opts = { $coderef->(@_) };
	}else
	{
		$opts = { @_ };
	}
	
	my $dump;
	my $data;
	if(ref($opts->{data}) eq 'ARRAY') {
		foreach my $data (@{$opts->{data}}) {
			$dump = (defined($data) ? $data : '(undef)') . "\n";
		}
		$data = [ @{$opts->{data}} ];
	} elsif(ref($opts->{data}) eq 'HASH') {
		$this->{db_log_data}{$opts->{id}}[0] = [ sort keys %{$opts->{data}} ];
		$data = [];
		foreach my $key (sort keys %{$opts->{data}}) {
			push(@$data, $opts->{data}{$key});
			$dump .= $key . ': ' . (defined($opts->{data}{$key}) ? $opts->{data}{$key} : '(undef)' ) . "\n";
		}
	}

	if($this->{db_logging} && $this->{db_logging_level} >= 2) {
		$TL->_log(
			__PACKAGE__,
			sprintf("DB Trace [ID: %d]\n%s", $opts->{id}, $dump)
		);
	}

	push(@{$this->{db_log_data}{$opts->{id}}}, $data);
	$this;
}

#--------- Tripletail::Form専用

sub _formLog {
	my $this = shift;
	my $opts = { @_ };

	if (!$this->{enabled}
	|| !$this->{request_logging}
	|| !$this->{request_popup}) {
		return $this;
	}

	# スタックを辿り、最初に現れたTripletail::以外のパッケージが作ったフレームを見て、
	# ファイル名と行番号を得る。
	my $loc = '';
	for(my $i = 0;; $i++) {
		my ($pkg, $fname, $lineno) = caller $i;
		if($pkg !~ m/^Tripletail::/) {
			$fname =~ m!([^/]+)$!;
			$fname = $1;

			$loc = sprintf '%s:%d', $fname, $lineno;

			last;
		}
	}

	my $log = '';
	my $data = '';

	$log .= "Type: $opts->{type}\n";
	$log .= "Loc: $loc\n";

	if(exists($opts->{key})) {
		$data .= "Key: $opts->{key}\n";
	}
	if(exists($opts->{value})) {
		$data .= "Value: $opts->{value}\n";
	}
	if(exists($opts->{data})) {
		my $data_dump = Data::Dumper->new([$opts->{data}])
			->Indent(0)->Terse(1)->Deepcopy(1)->Dump;
		$data .= "Data: $data_dump\n";
	}
	if(exists($opts->{form})) {
		my $form_dump = Data::Dumper->new([$opts->{form}])
			->Indent(0)->Terse(1)->Deepcopy(1)->Dump;
		$data .= "Form: $form_dump\n";
	}

	if($this->{request_logging}) {
		$TL->_log(__PACKAGE__, "Form Log:\n$log$data");
	}

	if($this->{request_popup}) {
		push @{$this->{form_log}}, {
			type  => $opts->{type},
			loc   => $loc,
			data  => $data,
		};
	}

	$this;
}

#--------- $TL->log
sub _tlLog {
	my $this = shift;
	my $opts = { @_ };

	if (!$this->{enabled}
	|| !$this->{log_popup}
	|| !$this->{tl_log}) {
		return $this;
	}

	my @time = localtime;
	$time[5] += 1900;
	$time[4]++;

	push @{$this->{tl_log}}, {
		group => $opts->{group},
		log   => $opts->{log},
		time  => sprintf(
			'%04d-%02d-%02d %02d:%02d:%02d',
			@time[5, 4, 3, 2, 1, 0],
		),
	};
	$this;
}

#--------- Tripletail::startCgi

sub _implant_disperror_popup {
	my $this = shift;

	$this->__db_explain;

	if($this->{enabled}
	&& $this->{popup}
	&& $this->{popup_type} ne 'none') {
		$this->__implant_template_popup;
		$this->__implant_warn_popup;
		$this->__implant_db_popup;
		$this->__implant_log_popup;
		$this->__implant_request_popup;
		$this->__implant_content_popup;
		$TL->dump(POPUP => $this->{popup});
		$TL->dump(POPUPOBJ => $this);

		my $output = $this->__flush_popup;
		$this->reset;
		return $output;
	}

	$this->reset;

	'';
}

#--------- Tripletail::Filter
sub _new {
	my $class = shift;
	my $opts = { @_ };

	$opts->{this};
}

sub print {
	my $this = shift;
	my $data = shift;

	if(!$this->{enabled}) {
		return $data;
	}

	$this->{filter_buf} .= $data;

	if( $this->{popup_type} eq 'none' )
	{
		return $data;
	}else
	{
		# buffering to rewrite after data gathered.
		return '';
	}
}

sub flush {
	my $this = shift;

	$this->__db_explain;

	if($this->{content_logging}) {
		my $content = $this->{filter_buf};
		my $lim = $this->{content_logging_max};
		if($lim && length($content) > $lim) {
			substr($content, $lim) = '...';
		}
		$TL->_log(__PACKAGE__, "Content Log:\n$content");
	}

	my $data = $this->{filter_buf}; # ローカルなコピー

	while($data =~ s/^(.*?(?:\r?\n|\r))//) {
		$this->{header_buf} .= $1;

		if($this->{header_buf} =~ m/(?:\r?\n|\r){2}$/) {
			# 二つ連続した改行コードで終わっている => ヘッダの終わり
			$this->{header} = {};
			foreach(split /\r?\n|\r/, $this->{header_buf}) {
				if(m/^(.+?):\s*(.+)$/) {
					my $array = $this->{header}{lc $1};
					if(!$array) {
						$array = $this->{header}{lc $1} = [];
					}
					push @$array, $2;
				}
			}

			my $ct = $this->{header}{'content-type'}[0];
			if(!$ct || $ct !~ m/html/i) {
				# htmlを出力しているのでなければポップアップは無効に。
				$this->{popup} = undef;
			}

			# charset判定
			my $charset = 'Shift_JIS';
			if($ct && $ct =~ m/charset=([^;]+)/i) {
				$charset = $1;
			}
			$this->{header}{_CHARSET_} = $charset;

			last;
		}
	}

	if($this->{filter_buf} =~ m|</head>|i
	&& $this->{popup}
	&& $this->{popup_type} ne 'none') {
		$this->__implant_template_popup;
		$this->__implant_warn_popup;
		$this->__implant_db_popup;
		$this->__implant_log_popup;
		$this->__implant_request_popup;
		$this->__implant_content_popup;

		my $html = $this->__flush_popup;
		$this->{filter_buf}=~ s|</head>|$html</head>|;
	}

	my $result;
	if( $this->{popup_type} eq 'none' )
	{
		$result = '';
	}else
	{
		# return buffered and rewritten html.
		$result = $this->{filter_buf};
	}

	$this->reset;

	$result;
}

sub __flush_popup {
	my $this = shift;

	if(!@{$this->{popup}}
	|| $this->{popup_type} eq 'none') {
		return '';
	}

	if($this->{popup_type} eq 'multiple') {
		my $script = qq~
<script type="text/javascript"><!--
    var win, doc;
~;
		foreach my $popup (@{$this->{popup}}) {
			# ヘッダとフッタを付ける
			my ($header, $footer) = $this->__tmpl_master_popup;

			$header->node('anchor')->add(
				ID    => $popup->[1],
				TITLE => $popup->[0],
			);

			my $header_popup = $this->__implant_popup(
				funcname => '_tl_debug_HEADER_' . $popup->[1],
				html     => $header->toStr,
				no_push  => 1,
			);
			my $footer_popup = $this->__implant_popup(
				funcname => '_tl_debug_FOOTER_' . $popup->[1],
				html     => $footer->toStr,
				no_push  => 1,
			);

			$script .= qq~
    win = window.open("", "_tl_debug_popup_window_$popup->[1]_", "");
    if (win) {
        doc = win.document;
        doc.open();
~;
			foreach my $parts ($header_popup, $popup, $footer_popup) {
				$script .= $parts->[2]; # 関数定義
				$script .= "  $parts->[1](doc);\n"; # 関数呼出し
			}
			$script .= qq~
        doc.close();
    }
~;
		}
	$script .= qq~
// --></script>
~;
	} elsif($this->{popup_type} eq 'single') {
		# ヘッダとフッタを付ける
		my ($header, $footer) = $this->__tmpl_master_popup;

		foreach my $popup (@{$this->{popup}}) {
			$header->node('anchor')->add(
				ID    => $popup->[1],
				TITLE => $popup->[0],
			);
		}

		unshift @{$this->{popup}},
			$this->__implant_popup(
				funcname => '_tl_debug_HEADER_',
				html     => $header->toStr,
				no_push  => 1,
			);
		push @{$this->{popup}},
			$this->__implant_popup(
				funcname => '_tl_debug_FOOTER_',
				html     => $footer->toStr,
				no_push  => 1,
			);

		my $script = qq~
<script type="text/javascript"><!--
    var win = window.open("", "_tl_debug_popup_window_", "");
    if (win) {
        var doc = win.document;
        doc.open();
~;
		foreach my $popup (@{$this->{popup}}) {
			$script .= $popup->[2]; # 関数定義
			$script .= "  $popup->[1](doc);\n"; # 関数呼出し
		}
		$script .= qq~
        doc.close();
    }
// --></script>
~;

		$script;
	} else {
		die "invalid popup_type: [$this->{popup_tipe}]\n";
	}
}

sub __db_explain {
	my $this = shift;

	if($this->{db_log} && @{$this->{db_log}}) {
		my $explain_id = 0;
		foreach my $entry (@{$this->{db_log}}) {
			next if($entry->{error});
			if($this->__checkIfExplainable($entry->{query})) {
				my $explain = $this->__explain(
					$entry->{group}, $entry->{set}, $entry->{db},
					$entry->{query}, $entry->{params}
				);
				$this->{explain_log}[$explain_id] = $explain;
				$explain_id++;
				my $query = $TL->escapeTag($entry->{query});
				$query =~ s~^\s*|\s*$~~g;

				my $explain_dump = '';
				my @maxcolumn;
				foreach my $row (@$explain) {
					for(my $i = 0; $i < @$row; $i++) {
						$maxcolumn[$i] = length($row->[$i])
							if(!$maxcolumn[$i] || $maxcolumn[$i] < length($row->[$i]));
					}
				}
				foreach my $row (@$explain) {
					$explain_dump .= ' | ';
					for(my $i = 0; $i < @$row; $i++) {
						$explain_dump .= ' ' x ($maxcolumn[$i] - length($row->[$i])) . $row->[$i] . ' | ';
					}
					$explain_dump .= "\n";
				}

				if($this->{db_logging}) {
					$TL->_log(
						__PACKAGE__,
						sprintf(
							"DB Trace [Map: %s][Set: %s][DB: %s][ID: %d]\n".
							"Elapsed: %s seconds\n".
							"Query:\n".
							"%s\n".
							"Parameters:\n".
							"%s\n".
							"Explain:\n".
							"%s",
							$entry->{group},
							$entry->{set},
							$entry->{db},
							$entry->{id},
							$entry->{elapsed},
							$query,
							$entry->{params},
							$explain_dump
						)
					);
				}
			}

		}
	}
}


sub __implant_db_popup {
	my $this = shift;

	if($this->{db_log} and @{$this->{db_log}}
	&& $this->{db_popup}) {
		my $t = $this->__tmpl_db_popup;

		local *expand_table = sub {
			my $node = shift;
			my $fetched = shift;

			foreach my $label (@{$fetched->[0]}) {
				$node->node('label')->add(
					LABEL => $label,
				);
			}

			foreach my $row (@$fetched[1 .. @$fetched-1]) {
				foreach my $column (@$row) {
					$node->node('rows')->node('column')->add(
						COLUMN => (defined $column ? $column : '(NULL)'),
					);
				}
				$node->node('rows')->add;
			}
		};

		my $explain_id = 0;
		my $fetch_id = 0;
		foreach my $entry (@{$this->{db_log}}) {
			if($this->{db_profile} >= 2 && $this->__checkIfExplainable($entry->{query})) {

				next if($entry->{error});

				$t->node('entry')->node('explain-link')->add(
					EXPLAIN_ID => $explain_id,
				);

				my $explain = $this->{explain_log}[$explain_id];

				expand_table(
					$t->node('entry')->node('explain-frame'),
					$explain
				);

				$t->node('entry')->node('explain-frame')->add(
					EXPLAIN_ID => $explain_id,
				);

				$explain_id++;
			}

			if($this->{db_logging_level} >= 2 && $this->__checkIfFetchable($entry->{query})) {
				$t->node('entry')->node('fetch-link')->add(
					FETCH_ID => $fetch_id,
				);

				my $fetched = $this->{db_log_data}{$entry->{id}};

				expand_table(
					$t->node('entry')->node('fetch-frame'),
					$fetched
				);


				$t->node('entry')->node('fetch-frame')->add(
					FETCH_ID => $fetch_id,
				);

				$fetch_id++;
			}

			my $query = $TL->escapeTag($entry->{query});
			$query =~ s~^\s*|\s*$~~g;
			$query =~ s~\n~<br />~g;
			$query =~ s~(--[^\n]+)~<span class="comment">$1</span>~g;
			$query =~ s~(/\*.+?\*/)~<span class="comment">$1</span>~sg;

			my $params = $TL->escapeTag($entry->{params});
			$params =~ s~\s~&nbsp;~g;
			$params =~ s~\n~<br />~g;

			$t->node('entry')->setAttr(
				QUERY       => 'raw',
				PARAMS      => 'raw',
			);
			$t->node('entry')->add(
				GROUP       => $entry->{group},
				SET         => $entry->{set},
				DB          => $entry->{db},
				QUERY       => $query,
				PARAMS      => $params,
				ELAPSED     => "$entry->{elapsed} sec",
			);
		}

		$this->__implant_popup(
			html     => $t->toStr,
			title    => 'DB使用ログ',
			funcname => '_tl_debug_db_popup_',
		);
	}
}

sub __implant_template_popup {
	my $this = shift;

	if($this->{template_log} && @{$this->{template_log}}) {
		my $log = $this->{template_log};
		$this->{template_log} = undef; # これ以降はTempateログを取らない。

		my $t = $this->__tmpl_template_popup;

		foreach my $entry (@$log) {
			my %params = %$entry;

            my $force_defined = sub {
                my $value = shift;

                defined $value ? $value : '(null)';
            };

			if($entry->{type} eq 'expand' || $entry->{type} eq 'setattr') {

                # イテレータをリセットする代わりにコピーを取る。
                my %copy = %{$entry->{args}};
				while (my ($key, $value) = each %copy) {
                    
					defined($value) or $value = '(null)';
					$t->node('entry')->node($entry->{type})->node('arg')->add(
						KEY => $key, VALUE => $force_defined->($value),
					);
				}
				delete $params{args};
			} elsif($entry->{type} eq 'setForm' || $entry->{type} eq 'addHiddenForm') {
				foreach my $key ($entry->{form}->getKeys) {
					foreach my $value ($entry->{form}->getValues($key)) {
						$t->node('entry')->node($entry->{type})->node('pair')->add(
							KEY => $key, VALUE => $force_defined->($value),
						);
					}
				}
				delete $params{form};
			}

			my $type = delete $params{type};
			my $fpath = delete $params{fpath};
			my $node = delete $params{node};

			$t->node('entry')->node($entry->{type})->add(%params);
			$t->node('entry')->add(
				TYPE  => $type,
				FPATH => $fpath,
				NODE  => $node,
			);
		}

		$this->__implant_popup(
			html     => $t->toStr,
			title    => 'テンプレート使用ログ',
			funcname => '_tl_debug_template_popup_',
		);
	}

	$this;
}

sub __implant_warn_popup {
	my $this = shift;

	if($this->{warn_log} && @{$this->{warn_log}}) {
		my $t = $this->__tmpl_warn_popup;

		foreach my $entry (@{$this->{warn_log}}) {
			my $content = $TL->escapeTag($entry);
			$content =~ s~\n~<br />~g;
			$content =~ s~\s~&nbsp;~g;

			$t->node('entry')->setAttr(
				CONTENT     => 'raw',
			);
			$t->node('entry')->add(
				CONTENT     => $content,
			);
		}

		$this->__implant_popup(
			html     => $t->toStr,
			title    => 'warn',
			funcname => '_tl_debug_warn_popup_',
		);
	}

	$this;
}

sub __implant_log_popup {
	my $this = shift;

	if($this->{tl_log} && @{$this->{tl_log}}) {
		# $TL->logのログ
		my $log = $this->{tl_log};
		$this->{tl_log} = undef; # これ以降は$TL->logログを取らない

		my $t = $this->__tmpl_log_popup;

		foreach my $entry (@$log) {
			my $content = $TL->escapeTag($entry->{log});
			$content =~ s~[ \t]~&nbsp;~g;
			$content =~ s~\n~<br />~g;

			$t->node('entry')->setAttr(
				CONTENT     => 'raw',
			);
			$t->node('entry')->add(
				GROUP       => $entry->{group},
				TIME        => $entry->{time},
				CONTENT     => $content,
			);
		}

		$this->__implant_popup(
			html     => $t->toStr,
			title    => '$TL->logログ',
			funcname => '_tl_debug_log_popup_',
		);
	}

	$this;
}

sub __implant_request_popup {
	my $this = shift;

	if($this->{request_popup} && $TL->{CGIORIG}) {
		# $TL->CGIの内容と%ENVの内容
		my $t = $this->__tmpl_request_popup;

		foreach my $key (sort $TL->{CGIORIG}->getKeys) {
			foreach my $value (sort $TL->{CGIORIG}->getValues($key)) {
				$t->node('cgi')->add(
					KEY   => $key,
					VALUE => $value,
				);
			}
		}

		$t->node('form')->setAttr(DATA => 'br');
		foreach my $data (@{$this->{form_log}}) {
			$t->node('form')->add($data);
		}

		foreach my $key (sort keys %ENV) {
			foreach my $value ($ENV{$key}) {
				$t->node('env')->add(
					KEY   => $key,
					VALUE => $value,
				);
			}
		}

		$this->__implant_popup(
			html     => $t->toStr,
			title    => 'リクエスト情報',
			funcname => '_tl_debug_request_popup_',
		);
	}

	$this;
}

sub __implant_content_popup {
	my $this = shift;

	if($this->{content_popup} && length($this->{filter_buf})) {
		my $t = $this->__tmpl_content_popup;

		my $content = $this->{filter_buf};
		my $lim = $this->{content_popup_max};
		if($lim && length($content) > $lim) {
			substr($content, $lim) = '...';
		}
		$content = $TL->charconv($content, 'auto' => 'UTF-8');

		$t->setAttr(VALUE => 'br');
		$t->expand(VALUE => $content);

		$this->__implant_popup(
			html     => $t->toStr,
			title    => 'コンテンツ情報',
			funcname => '_tl_debug_content_popup_',
		);
	}

	$this;
}

sub __implant_popup {
	my $this = shift;
	my $opts = do {
		my %args = @_;
		\%args;
	};
	my $funcname = $opts->{funcname};
	my $html = $opts->{html};
	my $title = $opts->{title};
	my $no_push = $opts->{no_push};

	my $script = "  function $funcname(doc) {\n";
	if(defined($title)) {
		$script .= qq{    doc.writeln("    <h1 id=\\"$funcname\\">$title</h1>");\n};
	}
	my $functions;
	my $line = 0;
	foreach(split /\r?\n|\r/, $html) {
		if( (++$line%1000)==0 )
		{
			if( !$functions )
			{
				$script =~ s/^  function (\w+)/  function $1_001/;
				$functions = 1;
			}
			++$functions;
			$script .= "  }\n";
			my $fname = sprintf('%s_%03d',$funcname,$functions);
			$script .= "  function $fname(doc) {\n";
		}
		# ポップアップウインドウのHTMLの全ての行を、
		# doc.writeln("..."); という形にする。
		s/\\/\\\\/g;
		s/"/\\"/g;
		s/--/"+"-"+"-"+"/g;
		s!//!"+"/"+"/"+"!g;
		s!</script>!"+"<"+"/sc"+"ript>"+"!g;
		$script .= qq{    doc.writeln("$_");\n};
	}
	$script .= "  }\n";
	if( $functions )
	{
		$script .= "  function $funcname(doc) {\n";
		foreach my $i (1..$functions)
		{
			my $fname = sprintf('%s_%03d', $funcname, $i);
			$script .= "    $fname(doc);\n";
		}
		$script .= "  }\n";
	}
	
	$script =~ s/\n/\r\n/g;
	$script = $TL->charconv($script, 'utf8' => $this->{header}{_CHARSET_});
	
	my $popup = [$title, $funcname, $script];
	if(!$no_push) {
		push @{$this->{popup}}, $popup;
	}

	$popup;
}

#--------------- DB関連

sub __checkIfExplainable {
	my $this = shift;
	my $sql = shift;

	$this->__findCommand($sql) eq 'SELECT';
}

sub __checkIfFetchable {
	my $this = shift;
	my $sql = shift;

	my $allowed = {
		SHOW => 1,
		EXPLAIN => 1,
		DESC => 1,
		SELECT => 1,
	};

	$allowed->{$this->__findCommand($sql)};
}

sub __findCommand {
	my $this = shift;
	my $sql = shift;
	while(1) {
		$sql =~ s/^\s*//;

		if($sql =~ s|^/\*.+?\*/||) {
			next;
		} elsif($sql =~ s,--.+?(?:\r?\n|\r),,) {
			next;
		} elsif($sql =~ m|^(\w+)|) {
			return uc $1;
		} else {
			die __PACKAGE__."#__findCommand: found no commands in sql [$sql] (SQLからコマンドを見つけることができませんでした)\n";
		}
	}
}

sub __explain {
	my $this = shift;
	my $group = shift;
	my $dbset = shift;
	my $dbname = shift; # undef可
	my $query = shift;
	my $params = shift;

	if(!$this->{explain_cache}{$group}) {
		$this->{explain_cache}{$group} = {};
	}

	if(my $cached = $this->{explain_cache}{$group}{$query}) {
		return $cached;
	}

	$this->{explain_cache}{$group}{$query} =
	$this->__executeSql($group, $dbset, $dbname, "EXPLAIN $query", $params);
}

sub __executeSql {
	my $this = shift;
	my $group = shift;
	my $dbset = shift;
	my $dbname = shift; # undef可
	my $query = shift;
	my $params = shift;

	# 内部用のクエリはデバッグログに残さない
	local($this->{db_log});
	$this->{db_log} = undef;

	$params = do {
        local($@);
        eval $params; # Dumpしてあったものを戻す
    };
    
	my $DB = $TL->getDB($group);

	my $sth = $DB->execute(\$dbset => $query, @$params);

	my $result = [];
	$result->[0] = [ @{$sth->nameArray} ]; # 0番目がフィールド名

	while(my $row = $sth->fetchArray) {
		foreach my $col (@$row) {
			$col = '(null)' if(!defined($col));
		}
		push @$result, [ @$row ];
	}

	$result;
}

#------------- CSS 及びテンプレート

sub __popup_css {
	my $this = shift;
	my $is_opera = ($ENV{HTTP_USER_AGENT}||'')=~/Opera/i;
	qq{
        <style>
            * {
                margin: 0;
                padding: 0;
            }

            body {
                font-size: 100%;
                color: black;
                background-color: white;
                text-align: left;
                margin-bottom: 50px;
@{[ $is_opera ? '        overflow: scroll;' : '' ]}
            }

            a {
                text-decoration: none;
                color: #880000;
                border-color: #660000;
                border-style: dotted;
                border-width: 0 0 1px 0;
            }

            h1 {
                font-size: 150%;
                text-align: left;

                color: #eecc22;
                border-color: #eecc22;
                border-width: 1px;
                border-style: dashed;
                padding: 3px;

                margin-top: 60px;
                margin-bottom: 0;
                margin-right: 2%;
                margin-left: 2%;
            }

            h2 {
                font-size: 130%;
                color: #888833;
                background-color: #ffeebb;
                border-style: solid;
                border-width: 2px 2px 2px 10px;
                border-color: #eecc22;
                padding: 3px;

                margin-top: 10px;
                margin-bottom: 10px;
                margin-right: 2%;
                margin-left: 2%;
            }

            h1 + table {
                margin-top: 10px;
            }

            table {
                width: 94%;
                margin-left: 2%;
                margin-right: 2%;
            }

            table table {
                width: 100%;
                background-color: #ddddbb;
                margin: 2px;
            }

            td, th {
                background-color: #ffffdd;
                border-style: dashed;
                border-width: 1px;
                border-color: #ddddbb;
                padding: 2px;
                margin: 2px;
            }
            th {
                font-weight: bold;
                background-color: #eeeecc;
            }

            li {
                margin: 3px 0 2px 0;
            }

            dt, dl {
                margin: 1px;
                padding: 2px;
            }
            dt {
                font-weight: bold;
            }
            dd {
                margin-left: 30px;
            }
            dl dl {
                margin-left: 30px;
            }
            dl dl dt {
                background-color: #eeeecc;
            }
            dl dl dd {
                background-color: #ffffee;
                margin-left: 1px;
            }

            ol, ul {
                list-style-type: none;
            }

            .comment {
                font-size: 90%;
                color: #aa0000;
            }

            .initial-hidden {
                display: none;
            }

            .controller {
                position: fixed;
                top: 0;
                width: 100%;
                color: #888833;
                background-color: #ffffbb;
                border-style: solid;
                border-width: 0 0 2px 0;
                border-color: #cccc22;
                padding: 3px;

                font-size: 120%;
                font-weight: bold;
            }
            .controller a {
                font-size: 80%;
            }

            .footer {
                position: fixed;
                bottom: 0;
                font-size: 80%;
                color: #888833;
                background-color: #ffffbb;
                border-style: solid;
                border-width: 2px 0 0 0;
                border-color: #cccc22;
                padding: 3px;
                margin: 5px 0 0 0;
            }
        </style>};
}

sub __iframe_css {
	my $this = shift;
	qq{
        <style>
            * {
                margin: 0;
                padding: 0;
            }

            body {
                font-size: 100%;
                color: black;
                background-color: #ffffdd;
                text-align: left;
            }

            table {
                width: 100%;
            }

            td, th {
                background-color: #ffffdd;
                border-style: dashed;
                border-width: 1px;
                border-color: #ddddbb;
                padding: 2px;
                margin: 2px;
            }
        </style>};
}

sub __tmpl_master_popup {
	my $this = shift;
	my $charset = $this->{header}{_CHARSET_} || 'UTF-8';
	my $css = $this->__popup_css;
	my $group = $this->{group};

	my @result;

	push @result, $TL->newTemplate->setTemplate( # ヘッダ
		qq{<?xml version="1.0" encoding="$charset"?>
<!DOCTYPE html
                    PUBLIC "-//W3C//DTD XHTML 1.1//EN"
                    "http://www.w3.org/TR/xhtml11.dtd">
<html xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=$charset" />
        <meta http-equiv="Content-Style-Type" content="text/css" />
        <title>[Tripletail::Debug] デバッグ情報</title>
        $css
        <script type="text/javascript"><!--
            function scroll_to_anchor(id) {
                var elem = document.getElementById(id);
                window.scroll(0, elem.offsetTop - 60);
            }
        // --></script>
    </head>
    <body>
        <div class="controller">
            <p>[Tripletail::Debug] デバッグ情報</p>
            <p>
                <!begin:anchor>
                <a href="javascript:scroll_to_anchor('<&ID>')"><&TITLE></a>
                <!end:anchor>
            </p>
        </div>
});

	push @result, $TL->newTemplate->setTemplate( # フッタ
		qq{
    <p class="footer">
            このウインドウはTripletail::Debugが表示しています。
            表示をやめるには、 ini ファイル [$group] セクションの enable_debug を無効にして下さい。
        </p>
    </body>
</html>
});

	@result;
}

sub __tmpl_template_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <table>
            <tr>
                <th>操作</th><th>ファイル</th><th>ノード</th><th>パラメータ</th>
            </tr>
            <!begin:entry>
            <tr>
        <td><&TYPE></td><td><&FPATH></td><td><&NODE></td>
        <td>
            <!begin:new><!end:new>
            <!begin:set><!end:set>
            <!begin:load><!end:load>
            <!begin:setattr>
                    <dl>
                        <dt>args</dt>
                            <dl>
                                <!begin:arg>
                                <dt><&KEY></dt><dd><&VALUE></dd>
                                <!end:arg>
                            </dl>
                    </dl>
                    <!end:setattr>
            <!begin:expand>
                    <dl>
                        <dt>any</dt><dd><&ANY></dd>
                        <dt>args</dt>
                            <dl>
                                <!begin:arg>
                                <dt><&KEY></dt><dd><&VALUE></dd>
                                <!end:arg>
                            </dl>
                    </dl>
                    <!end:expand>
                    <!begin:add><!end:add>
                    <!begin:setForm>
            <dl>
                <dt>name of form</dt><dd><&NAME></dd>
                <dt>content</dt>
                            <dl>
                                <!begin:pair>
                                <dt><&KEY></dt><dd><&VALUE></dd>
                                <!end:pair>
                            </dl>
            </dl>
                    <!end:setForm>
                    <!begin:extForm>
            <dl>
                <dt>name of form</dt><dd><&NAME></dd>
            </dl>
                    <!end:extForm>
                    <!begin:addHiddenForm>
            <dl>
                <dt>name of form</dt><dd><&NAME></dd>
                <dt>content</dt>
                            <dl>
                                <!begin:pair>
                                <dt><&KEY></dt><dd><&VALUE></dd>
                                <!end:pair>
                            </dl>
            </dl>
                    <!end:addHiddenForm>
                    <!begin:addSessionCheck>
            <dl>
                    <dt>name of form</dt><dd><&NAME></dd>
            </dl>
                    <!end:addSessionCheck>
                    <!begin:toStr><!end:toStr>
                    <!begin:flush><!end:flush>
                </td>
            </tr>
            <!end:entry>
        </table>
});
}

sub __tmpl_request_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <h2>CGI</h2>
        <table>
            <!begin:cgi>
            <tr><th><&KEY></th><td><&VALUE></td></tr>
            <!end:cgi>
        </table>
        <table>
            <!begin:form>
            <tr><td><&LOC></td><th><&TYPE></th><td><&DATA></td></tr>
            <!end:form>
        </table>

        <h2>ENV</h2>
        <table>
            <!begin:env>
            <tr><th><&KEY></th><td><&VALUE></td></tr>
            <!end:env>
        </table>
});
}

sub __tmpl_content_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <h2>Content</h2>
        <table>
            <tr><td><&VALUE></td></tr>
        </table>
});
}

sub __tmpl_log_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <table>
            <tr><th>グループ</th><th rowspan="2">内容</th></tr>
            <tr><th>時刻</th></tr>
            <!begin:entry>
            <tr><td><&GROUP></td><td rowspan="2"><&CONTENT></td></tr>
            <tr><td><&TIME></td></tr>
            <!end:entry>
        </table>
});
}

sub __tmpl_warn_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <table>
            <tr><th>warnメッセージ</th></tr>
            <!begin:entry>
            <tr><td><&CONTENT></td></tr>
            <!end:entry>
        </table>
});
}

sub __tmpl_db_popup {
	my $this = shift;

	$TL->newTemplate->setTemplate(qq{
        <table>
            <tr>
                <th>グループ</th>
                <th>セット</th>
                <th>コネクション</th>
                <th>クエリ</th>
                <th>パラメータ</th>
                <th>execute実行時間</th>
                <th>詳細</th>
            </tr>
            <!begin:entry>
            <tr>
                <td><&GROUP></td>
                <td><&SET></td>
                <td><&DB></td>
        <td><&QUERY></td>
        <td><&PARAMS></td>
        <td><&ELAPSED></td>
        <td>
            <ul>
                <!begin:explain-link>
                <li>
                    <a href="javascript:tl_debug_toggle_display('TL_EXPLAIN_<&EXPLAIN_ID>')">
                Explain
                    </a>
                </li>
                <!end:explain-link>
                <!begin:fetch-link>
                <li>
                    <a href="javascript:tl_debug_toggle_display('TL_FETCH_<&FETCH_ID>')">
                結果
                    </a>
                </li>
                <!end:fetch-link>
            </ul>
        </td>
            </tr>
            <!begin:explain-frame>
            <tr>
        <td colspan="7">
            <div class="initial-hidden" id="TL_EXPLAIN_<&EXPLAIN_ID>">
                <table>
                    <tr><!begin:label><th><&LABEL></th><!end:label></tr>
                    <!begin:rows>
                    <tr><!begin:column><td><&COLUMN></td><!end:column></tr>
                    <!end:rows>
                </table>
            </div>
        </td>
            </tr>
            <!end:explain-frame>
            <!begin:fetch-frame>
            <tr>
        <td colspan="7">
            <div class="initial-hidden" id="TL_FETCH_<&FETCH_ID>">
                <table>
                    <tr><!begin:label><th><&LABEL></th><!end:label></tr>
                    <!begin:rows>
                    <tr><!begin:column><td><&COLUMN></td><!end:column></tr>
                    <!end:rows>
                </table>
            </div>
        </td>
            </tr>
            <!end:fetch-frame>
            <!end:entry>
        </table>

        <script type="text/javascript"><!--
        function tl_debug_toggle_display(id) {
            var elem = document.getElementById(id);
            if (elem.style.display == "block") {
                elem.style.display = "none";
            }
            else {
                elem.style.display = "block";
            }
        }
        // --></script>
});
}


__END__