2 # -----------------------------------------------------------------------------
3 package POE::Component::IRC::Plugin::NoPaste::Template;
11 our $SETFORM_SUPPORTED = 1;
13 eval q{ use URI; use URI::QueryParam; };
14 $@ and $SETFORM_SUPPORTED = 0;
19 Template - Text Template Class (especially for (x)html)
27 <title>Sample Page - <t-tag:embed> - Sample Page</title>
42 my $t = Template->new(
43 -fpath => './sample.html',
46 embed => 'EMBED TITLE',
60 <title>Sample Page - EMBED TITLE - Sample Page</title>
83 =item $t = Template->new( %args );
87 =item -fpath => file to use as template (one of this and -data is required)
89 =item -data => data to use as template
91 =item -strip => whether delete newlines after <!begin>, <!end> or not
93 =item -lang => language to output
95 Any blocks surrounded by <t-lang:...> and </t-lang:...> are treated as
96 language specific block.
100 <t-lang:default> This is default </t-lang:default>
101 <t-lang:ja> Nihongo(Japanese) </t-lang:ja>
103 my $t = Template->new(
104 -fpath => './foo.html',
106 print $t->str; # this shows "Nihongo(Japanese)"
109 -fpath => './foo.html',
111 print $t->str; # this shows "This is default"
121 # -fpath => テンプレートとして使用するファイル
122 # -data => テンプレートとして使用するデータ
123 # -strip => <!begin>や<!end>の直後の改行を削除するかどうか,
126 my ($class, %args) = @_;
128 original => undef, # リーフを<!mark:foo>に置換した中身。
129 current => undef, # <&foo>を置換した後のもの。
130 leaves => {}, # {名前 => Template}
131 parent => undef, # これがトップレベルでなければ、親(Template)。
132 leafname => undef, # これがトップレベルでなければ、リーフ名。
134 bless $this => $class;
137 if (defined $args{-fpath}) {
138 if (defined $args{-data}) {
139 croak "You can't specify both of -fpath and -data at the same time.\n";
142 open my $fh, '<', $args{-fpath}
143 or croak "Failed to open file $args{-fpath}";
147 elsif (defined $args{-data}) {
148 $source = $args{-data};
151 croak "Missing both of -fpath and -data. Read `perldoc Template'\n";
154 # <t-block:*>や<t-lang:*>の直後が改行コードなら、それを消す。
155 # その改行コードから始まるスペースまたはタブも、インデントと見做して消す。
157 $source =~ s{(</?t-block:.+?>|</?t-lang:.+?>)\x0d?\x0a[ \t]*}{$1}g;
161 my $lang = 'default';
162 if ($args{-lang} and $source =~ m/<t-lang:\Q$args{-lang}\E>/) {
163 $lang = $args{-lang};
165 $source =~ s{<t-lang:(.+?)>(.+?)</t-lang:\1>}{
174 $this->_load($source);
180 $this->{current} = $this->{original};
185 # $t->expand({foo => '---' , bar => '+++'});
187 # $t->expand(foo => '---' , bar => '+++');
189 # このメソッドは、キー内に現われたアンダースコアを
190 # ハイフンにフォールバックする事が出來る。
191 # つまり、<t-tag:foo-bar>というタグを、キー名"foo_bar"で指定する事が出來る。
194 if (@_ == 1 && UNIVERSAL::isa($_[0],'HASH')) {
197 elsif (@_ % 2 == 0) {
202 croak "Illegal argument for Template->expand";
205 while (my ($key,$value) = each %$hash) {
206 # $key,$value共にスカラー値でなければならない。
208 if (!defined $value) {
209 croak "Values must not be undef; key: $key";
211 if (ref($key) ne '') {
212 croak "Keys and values must be scalar values: $key";
214 if (ref($value) ne '') {
215 croak "Keys and values must be scalar values: $value";
218 if ($this->{current} !~ s|<t-tag:\Q$key\E(?:\s*/)?>|$value|g) {
219 # 無い。アンダースコアをハイフンに変えてみる。
220 (my $tred_key = $key) =~ tr/_/-/;
221 if ($this->{current} !~ s|<t-tag:\Q$tred_key\E(?:\s*/)?>|$value|g) {
222 # そのようなキーは存在しなかった。警告。
223 #carp "No <t-tag:$key> are in template, or you have replaced it already";
243 if (!defined $this->{parent}) {
244 croak "This template doesn't have its parent.";
247 # 親の<t-mark:foo>の直前に、このリーフを挿入。
248 my $str = $this->str;
249 $this->{parent}{current} =~ s|(<t-mark:\Q$this->{leafname}\E(?:\s*/)?>)|$str$1|g;
259 my $result = $this->{current};
261 # 未置換の<t-tag:foo>があればそれを消してcarp。
262 while ($result =~ s/(<t-tag:.+?>)//) {
263 carp "Unexpanded tag: $1";
267 $result =~ s/<t-mark:.+?>//g;
273 my ($this,$leafname) = @_;
274 $this->{leaves}{$leafname};
279 (my $leafname = $AUTOLOAD) =~ s/.+?:://g;
281 # 余分な引數が付いてゐれば、恐らくこれは關數名の誤り。
283 croak "Calling autoloaded Template->$leafname with extra arguments must be a mistake";
287 $leafname =~ tr/_/-/;
288 $this->{leaves}{$leafname};
292 my ($class,$parent,$leafname,$source) = @_;
298 leafname => $leafname,
302 $this->_load($source);
306 my ($this,$source) = @_;
308 # <t-block:foo> ... </t-block:foo>を<t-mark:foo>に置換しつつ、そのリーフを保存。
309 while ($source =~ s|<t-block:(.+?)>(.+?)</t-block:\1>|<t-mark:$1>|s) {
310 my ($leafname,$source) = ($1,$2);
312 if (defined $this->{leaves}{$leafname}) {
313 # 既にこのリーフが定義されていたらcroak。
314 croak "duplicated leaves in template: $leafname";
317 $this->{leaves}{$leafname} = __PACKAGE__->_new_leaf($this,$leafname,$source);
320 $this->{original} = $this->{current} = $source;
328 foreach my $str (@args) {
330 $str =~ s/\&/\&/g;
333 $str =~ s/\"/\"/g;
337 wantarray ? @result : $result[0];
343 foreach my $str (@args) {
347 $str =~ s/\"/\"/g;
348 $str =~ s/\&/\&/g;
352 wantarray ? @result : $result[0];
360 s|^(/?\w+)|| and $tag->{__top} = lc $1;
363 s|([\w\-]+)=([\"\'])(.*?)\2|| and do {
367 s|([\w\-]+)=([^\s\>]+)|| and do {
372 $tag->{__end} = lc $1;
379 foreach (keys %$tag) {
380 $new->{unescape_tag($_)} = unescape_tag($tag->{$_});
389 $html .= '<' . $tag->{__top};
390 foreach (keys %$tag) {
392 $html .= sprintf ' %s="%s"', escape_tag($_), escape_tag($tag->{$_});
394 defined $tag->{__end} and
395 $html .= ' ' . $tag->{__end};
403 my ($this, $uri, $name) = @_;
404 defined $name or $name = '';
406 if (!UNIVERSAL::isa($uri, 'URI')) {
407 croak "Usage: Template->setform(URI [, name])";
410 $SETFORM_SUPPORTED or
411 croak "Install URI::QueryParam to enable setform";
413 my $is_xhtml = ($this->{current} =~ m|^\s*<\?xml|);
416 my $in_select; # name
417 my $in_select_option_tag; # tag
418 my $in_textarea; # name
419 my $in_form = ''; # name
420 foreach (split /(<[^\<\>]+?>)/, $this->{current}) {
421 if (defined $in_select and defined $in_select_option_tag) {
423 /^([^ \<\r\n]*)/i and $value = $1;
425 my $tag = $in_select_option_tag;
426 if (grep {$value eq $_} $uri->query_param($in_select)) {
427 $tag->{$is_xhtml ? 'selected' : '__end'} = 'selected';
430 delete $tag->{$is_xhtml ? 'selected' : '__end'};
433 $outhtml .= tag2html($tag);
434 undef $in_select_option_tag;
436 if (defined $in_textarea) {
437 if (!m|^</textarea>|i) {
448 my $tag = html2tag($taghtml);
449 if (!defined $tag->{__top}) {
450 $outhtml .= $taghtml;
452 elsif ($tag->{__top} eq 'form') {
453 if (defined($_ = $tag->{name})) {
454 $in_form = unescape_tag($_);
460 if (!$tag->{action}) {
461 my $act = $uri->clone;
463 $act->fragment(undef);
464 $tag->{action} = $act->canonical->as_string;
467 $outhtml .= tag2html($tag);
469 elsif ($name ne $in_form) {
470 $outhtml .= $taghtml;
472 elsif ($tag->{__top} eq 'input') {
473 if (defined $tag->{name}) {
474 my $type = $tag->{type};
476 grep {$_ eq $type} qw[text password hidden submit]) {
477 if (defined(my $value = $uri->query_param($tag->{name}))) {
478 $tag->{value} = $value;
480 $outhtml .= tag2html($tag);
482 elsif (grep {$_ eq $type} qw[radio checkbox]) {
483 if (defined $tag->{value} and
484 grep {$_ eq $tag->{value}}
485 $uri->query_param($tag->{name})) {
486 $tag->{$is_xhtml ? 'checked' : '__end'} = 'checked';
489 delete $tag->{$is_xhtml ? 'checked' : '__end'};
491 $outhtml .= tag2html($tag);
494 $outhtml .= $taghtml;
498 $outhtml .= $taghtml;
501 elsif ($tag->{__top} eq 'textarea') {
502 if (defined($_ = $tag->{name})) {
504 $outhtml .= tag2html($tag);
505 if (defined(my $value = $uri->query_param($_))) {
506 $outhtml .= escape_tag($value);
510 $outhtml .= tag2html($tag);
513 elsif ($tag->{__top} eq '/textarea') {
514 $outhtml .= tag2html($tag);
517 elsif ($tag->{__top} eq 'select') {
518 if (defined($_ = $tag->{name})) {
521 $outhtml .= tag2html($tag);
523 elsif ($tag->{__top} eq '/select') {
524 $outhtml .= tag2html($tag);
527 elsif (defined $in_select) {
528 if ($tag->{__top} eq 'option') {
529 if (defined(my $val = $tag->{value})) {
530 if (grep {$val eq $_} $uri->query_param($in_select)) {
531 $tag->{$is_xhtml ? 'selected' : '__end'} = 'selected';
534 delete $tag->{$is_xhtml ? 'selected' : '__end'};
536 $outhtml .= tag2html($tag);
539 $in_select_option_tag = $tag;
543 $outhtml .= $taghtml;
547 $outhtml .= $taghtml;
551 $this->{current} = $outhtml;
556 my ($this, $uri, $name) = @_;
557 defined $name or $name = '';
559 $SETFORM_SUPPORTED or
560 croak "Install URI::QueryParam to enable setform";
562 if (!UNIVERSAL::isa($uri, 'URI')) {
563 if (UNIVERSAL::isa($uri, 'HASH')) {
564 my $u = URI->new('', 'http');
565 while (my ($key, $value) = each %$uri) {
567 $key => UNIVERSAL::isa($value, 'ARRAY') ? @$value : $value);
572 croak "\$uri was not URI nor HASH";
576 my $is_xhtml = ($this->{current} =~ m|^\s*<\?xml|);
579 foreach (split /(<[^\<\>]+?>)/, $this->{current}) {
586 my $tag = html2tag($taghtml);
588 if (defined $tag->{__top} and
589 $tag->{__top} eq 'form' and
591 my $act = $uri->clone;
593 $act->fragment(undef);
594 $tag->{action} = $act->canonical->as_string;
596 $outhtml .= tag2html($tag);
599 $outhtml .= $taghtml;
602 if (defined $tag->{__top} and
603 $tag->{__top} eq 'form') {
604 if ((!defined $tag->{name} and $name eq '') or
605 (defined $tag->{name} and $name eq $tag->{name})) {
606 foreach my $key ($uri->query_param) {
607 my $format = $is_xhtml ?
608 q{<input type="hidden" name="%s" value="%s" />} :
609 q{<input type="hidden" name="%s" value="%s">};
610 $outhtml .= sprintf($format, $key, $uri->query_param($key));
616 $this->{current} = $outhtml;
620 my ($this, $keylist, $name) = @_;
621 defined $name or $name = '';
623 my %keyhash = map {$_ => 1} @$keylist;
625 my $is_xhtml = ($this->{current} =~ m|^\s*<\?xml|);
628 my $in_textarea; # name
630 foreach (split /(<[^\<\>]+?>)/, $this->{current}) {
637 if (defined $in_textarea) {
638 if (!m|^</textarea>|i) {
643 my $tag = html2tag($taghtml);
645 if (!defined $tag->{__top}) {
646 $outhtml .= $taghtml;
648 elsif ($tag->{__top} eq 'form') {
649 if (defined($_ = $tag->{name})) {
650 $in_form = unescape_tag($_);
656 $outhtml .= tag2html($tag);
658 elsif ($name ne $in_form) {
659 $outhtml .= $taghtml;
661 elsif ($tag->{__top} eq 'textarea') {
663 $outhtml .= tag2html($tag);
665 elsif ($tag->{__top} eq '/textarea') {
666 $outhtml .= tag2html($tag);
669 elsif ($tag->{__top} eq 'input' or
670 $tag->{__top} eq 'select' or
671 $tag->{__top} eq 'textarea') {
673 my $name = $tag->{name};
674 if ($name and $keyhash{$name}) {
676 $tag->{$is_xhtml ? 'disabled' : '__end'} = 'disabled';
678 # nameを消したいところだが、多分消した時の動作は實裝依存になる。
679 # 代はりにnameを別の名前に變換する。
680 $tag->{name} = sprintf 'DiSaBLeD_%s_DiSaBLeD', $tag->{name};
683 $outhtml .= tag2html($tag);
686 $outhtml .= $taghtml;
690 $this->{current} = $outhtml;