1use 5.006;
2use warnings;
3use strict;
4#use Smart::Comments;
5#use Smart::Comments '####';
6
7package Template::Declare::Tags;
8
9our $VERSION = '0.43';
10
11use Template::Declare;
12use base 'Exporter';
13use Carp qw(carp croak);
14use Symbol 'qualify_to_ref';
15
16our $self;
17
18our @EXPORT = qw(
19    template private current_template current_base_path
20    show show_page
21    attr with get_current_attr
22    outs outs_raw
23    xml_decl
24    under setting
25    smart_tag_wrapper create_wrapper
26    $self
27);
28
29our @TAG_SUB_LIST;
30our @TagSubs;
31*TagSubs = \@TAG_SUB_LIST;  # For backward compatibility only
32
33our %ATTRIBUTES       = ();
34our %ELEMENT_ID_CACHE = ();
35our $TAG_NEST_DEPTH   = 0;
36our $TAG_INDENTATION  = 1;
37our $EOL              = "\n";
38our @TEMPLATE_STACK   = ();
39
40our $SKIP_XML_ESCAPING = 0;
41
42sub import {
43    my $self = shift;
44    my @set_modules;
45    if (!@_) {
46        push @_, 'HTML';
47    }
48    ### @_
49    ### caller: caller()
50
51    # XXX We can't reset @TAG_SUB_LIST here since
52    # use statements always run at BEGIN time.
53    # A better approach may be install such lists
54    # directly into the caller's namespace...
55    #undef @TAG_SUB_LIST;
56
57    while (@_) {
58        my $lang = shift;
59        my $opts;
60        if (ref $_[0] and ref $_[0] eq 'HASH') {
61            $opts = shift;
62            $opts->{package} ||= $opts->{namespace};
63            # XXX TODO: carp if the derived package already exists?
64        }
65        $opts->{package} ||= scalar(caller);
66        my $module = $opts->{from} ||
67            "Template::Declare::TagSet::$lang";
68
69        ### Loading tag set: $module
70        if (! $module->can('get_tag_list') ) {
71            eval "use $module";
72            if ($@) {
73                warn $@;
74                croak "Failed to load tagset module $module";
75            }
76        }
77        ### TagSet options: $opts
78        my $tagset = $module->new($opts);
79        my $tag_list = $tagset->get_tag_list;
80        Template::Declare::Tags::install_tag($_, $tagset)
81            for @$tag_list;
82    }
83    __PACKAGE__->export_to_level(1, $self);
84}
85
86sub _install {
87    my ($override, $package, $subname, $coderef) = @_;
88
89    my $name = $package . '::' . $subname;
90    my $slot = qualify_to_ref($name);
91    return if !$override and *$slot{CODE};
92
93    no warnings 'redefine';
94    *$slot = $coderef;
95}
96
97=head1 NAME
98
99Template::Declare::Tags - Build and install XML Tag subroutines for Template::Declare
100
101=head1 SYNOPSIS
102
103    package MyApp::Templates;
104
105    use base 'Template::Declare';
106    use Template::Declare::Tags 'HTML';
107
108    template main => sub {
109        link {}
110        table {
111            row {
112                cell { "Hello, world!" }
113            }
114        }
115        img { attr { src => 'cat.gif' } }
116        img { src is 'dog.gif' }
117    };
118
119Produces:
120
121 <link />
122 <table>
123  <tr>
124   <td>Hello, world!</td>
125  </tr>
126 </table>
127 <img src="cat.gif" />
128 <img src="dog.gif" />
129
130Using XUL templates with a namespace:
131
132    package MyApp::Templates;
133
134    use base 'Template::Declare';
135    use Template::Declare::Tags
136        'XUL', HTML => { namespace => 'html' };
137
138    template main => sub {
139        groupbox {
140            caption { attr { label => 'Colors' } }
141            html::div { html::p { 'howdy!' } }
142            html::br {}
143        }
144    };
145
146Produces:
147
148 <groupbox>
149  <caption label="Colors" />
150  <html:div>
151   <html:p>howdy!</html:p>
152  </html:div>
153  <html:br></html:br>
154 </groupbox>
155
156=head1 DESCRIPTION
157
158C<Template::Declare::Tags> is used to generate templates and install
159subroutines for tag sets into the calling namespace.
160
161You can specify the tag sets to install by providing a list of tag modules in
162the C<use> statement:
163
164    use Template::Declare::Tags qw/ HTML XUL /;
165
166By default, Template::Declare::Tags uses the tag set provided by
167L<Template::Declare::TagSet::HTML>. So
168
169    use Template::Declare::Tags;
170
171is equivalent to
172
173    use Template::Declare::Tags 'HTML';
174
175Currently L<Template::Declare> bundles the following tag sets:
176L<Template::Declare::TagSet::HTML>, L<Template::Declare::TagSet::XUL>,
177L<Template::Declare::TagSet::RDF>, and L<Template::Declare::TagSet::RDF::EM>.
178
179You can specify your own tag set classes, as long as they subclass
180L<Template::Declare::TagSet> and implement the corresponding methods (e.g.
181C<get_tag_list>).
182
183If you implement a custom tag set module named
184C<Template::Declare::TagSet::Foo>, you can load it into a template module like
185so:
186
187    use Template::Declare::Tags 'Foo';
188
189If your tag set module is not under the
190L<Template::Declare::TagSet|Template::Declare::TagSet> namespace, use the
191C<from> option to load it. Fore example, if you created a tag set named
192C<MyTag::Foo>, then you could load it like so:
193
194    use Template::Declare::Tags Foo => { from => 'MyTag::Foo' };
195
196XML namespaces are emulated by Perl packages. For example, to embed HTML tags
197within XUL using the C<html> namespace:
198
199    package MyApp::Templates;
200
201    use base 'Template::Declare';
202    use Template::Declare::Tags 'XUL', HTML => { namespace => 'html' };
203
204    template main => sub {
205        groupbox {
206            caption { attr { label => 'Colors' } }
207            html::div { html::p { 'howdy!' } }
208            html::br {}
209        }
210    };
211
212This will output:
213
214 <groupbox>
215  <caption label="Colors" />
216  <html:div>
217   <html:p>howdy!</html:p>
218  </html:div>
219  <html:br></html:br>
220 </groupbox>
221
222Behind the scenes, C<Template::Declare::Tags> generates a Perl package named
223C<html> and installs the HTML tag subroutines into that package. On the other
224hand, XUL tag subroutines are installed into the current package, namely,
225C<MyApp::Templates> in the previous example.
226
227There may be cases when you want to specify a different Perl package for a
228particular XML namespace. For instance, if the C<html> Perl package has
229already been used for other purposes in your application and you don't want to
230install subs there and mess things up, use the C<package> option to install
231them elsewhere:
232
233    package MyApp::Templates;
234    use base 'Template::Declare';
235    use Template::Declare::Tags 'XUL', HTML => {
236        namespace => 'htm',
237        package   => 'MyHtml'
238    };
239
240    template main => sub {
241        groupbox {
242            caption { attr { label => 'Colors' } }
243            MyHtml::div { MyHtml::p { 'howdy!' } }
244            MyHtml::br {}
245        }
246    };
247
248This code will generate something like the following:
249
250 <groupbox>
251  <caption label="Colors" />
252  <htm:div>
253   <htm:p>howdy!</htm:p>
254  </htm:div>
255  <htm:br></htm:br>
256 </groupbox>
257
258=head1 METHODS AND SUBROUTINES
259
260=head2 Declaring templates
261
262=head3 template TEMPLATENAME => sub { 'Implementation' };
263
264    template select_list => sub {
265        my $self = shift;
266        select {
267            option { $_ } for @_;
268        }
269    };
270
271Declares a template in the current package. The first argument to the template
272subroutine will always be a C<Template::Declare> object. Subsequent arguments
273will be all those passed to C<show()>. For example, to use the above example
274to output a select list of colors, you'd call it like so:
275
276    Template::Declare->show('select_list', qw(red yellow green purple));
277
278You can use any URL-legal characters in the template name;
279C<Template::Declare> will encode the template as a Perl subroutine and stash
280it where C<show()> can find it.
281
282(Did you know that you can have characters like ":" and "/" in your Perl
283subroutine names? The easy way to get at them is with C<can>).
284
285=cut
286
287sub template ($$) {
288    my $template_name  = shift;
289    my $coderef        = shift;
290    my $template_class = ( caller(0) )[0];
291
292    no warnings qw( uninitialized redefine );
293
294    # template "foo" ==> CallerPkg::_jifty_template_foo;
295    # template "foo/bar" ==> CallerPkg::_jifty_template_foo/bar;
296    my $codesub = sub {
297        local $self = shift || $self || $template_class;
298        unshift @_, $self, $coderef;
299        goto $self->can('_dispatch_template');
300    };
301
302    if (wantarray) {
303         # We're being called by something like private that doesn't want us to register ourselves
304        return ( $template_class, $template_name, $codesub );
305    } else {
306       # We've been called in a void context and should register this template
307        Template::Declare::register_template(
308            $template_class,
309            $template_name,
310            $codesub,
311        );
312    }
313}
314
315=head3 private template TEMPLATENAME => sub { 'Implementation' };
316
317    private template select_list => sub {
318        my $self = shift;
319        select {
320            option { $_ } for @_;
321        }
322    };
323
324Declares that a template isn't available to be called directly from client
325code. The resulting template can instead only be called from the package in
326which it's created.
327
328=cut
329
330sub private (@) {
331    my $class   = shift;
332    my $subname = shift;
333    my $code    = shift;
334    Template::Declare::register_private_template( $class, $subname, $code );
335}
336
337=head2 Showing templates
338
339=head3 show [$template_name or $template_coderef], args
340
341    show( main => { user => 'Bob' } );
342
343Displays templates. The first argument is the name of the template to be
344displayed. Any additional arguments will be passed directly to the template.
345
346C<show> can either be called with a template name or a package/object and a
347template. (It's both functional and OO.)
348
349If called from within a Template::Declare subclass, then private templates are
350accessible and visible. If called from something that isn't a
351Template::Declare, only public templates will be visible.
352
353From the outside world, users can either call C<< Template::Declare->show() >>,
354C<< show() >> exported from Template::Declare::Tags or
355C<Template::Declare::Tags::show()> directly to render a publicly visible template.
356
357Private templates may only be called from within the C<Template::Declare>
358package.
359
360=cut
361
362sub show {
363    my $template = shift;
364
365    # if we're inside a template, we should show private templates
366    if ( caller->isa('Template::Declare') ) {
367        _show_template( $template, 1, \@_ );
368        return Template::Declare->buffer->data;
369    } else {
370        show_page( $template, @_);
371    }
372
373}
374
375=head3 show_page
376
377    show_page( main => { user => 'Bob' } );
378
379Like C<show()>, but does not dispatch to private templates. It's used
380internally by C<show()> when when that method is called from outside a
381template class.
382
383=cut
384
385sub show_page {
386    my $template = shift;
387    my $args = \@_;
388
389    Template::Declare->buffer->push(
390        private => defined wantarray,
391        from => "T::D path $template",
392    );
393    _show_template( $template, 0, $args );
394    %ELEMENT_ID_CACHE = ();
395    return Template::Declare->buffer->pop;
396}
397
398=head2 Attributes
399
400=head3 attr HASH
401
402    attr { src => 'logo.png' };
403
404Specifies attributes for the element tag in which it appears. For example, to
405add a class and ID to an HTML paragraph:
406
407    p {
408       attr {
409           class => 'greeting text',
410           id    => 'welcome',
411       };
412       'This is a welcoming paragraph';
413    }
414
415=cut
416
417sub attr (&;@) {
418    my $code = shift;
419    my @rv   = $code->();
420    while ( my ( $field, $val ) = splice( @rv, 0, 2 ) ) {
421
422        # only defined whle in a tag context
423        append_attr( $field, $val );
424    }
425    return @_;
426}
427
428=head3 ATTR is VALUE
429
430Attributes can also be specified by using C<is>, as in
431
432    p {
433       class is 'greeting text';
434       id    is 'welcome';
435       'This is a welcoming paragraph';
436    }
437
438A few tricks work for 'is':
439
440    http_equiv is 'foo'; # => http-equiv="foo"
441    xml__lang is 'foo';  # => xml:lang="foo"
442
443So double underscore replaced with colon and single underscore with dash.
444
445=cut
446
447# 'is' is declared later, when needed, using 'local *is::AUTOLOAD = sub {};'
448
449=head3 with
450
451    with ( id => 'greeting', class => 'foo' ),
452        p { 'Hello, World wide web' };
453
454An alternative way to specify attributes for a tag, just for variation. The
455standard way to do the same as this example using C<attr> is:
456
457    p { attr { id => 'greeting', class => 'foo' }
458        'Hello, World wide web' };
459
460=cut
461
462sub with (@) {
463    %ATTRIBUTES = ();
464    while ( my ( $key, $val ) = splice( @_, 0, 2 ) ) {
465        no warnings 'uninitialized';
466        $ATTRIBUTES{$key} = $val;
467
468        if ( lc($key) eq 'id' ) {
469            if ( $ELEMENT_ID_CACHE{$val}++ ) {
470                my $msg = "HTML appears to contain illegal duplicate element id: $val";
471                die $msg if Template::Declare->strict;
472                warn $msg;
473            }
474        }
475
476    }
477    wantarray ? () : '';
478}
479
480=head2 Displaying text and raw data
481
482=head3 outs STUFF
483
484    p { outs 'Grettings & welcome pyoonie hyoomon.' }
485
486HTML-encodes its arguments and appends them to C<Template::Declare>'s output
487buffer. This is similar to simply returning a string from a tag function call,
488but is occasionally useful when you need to output a mix of things, as in:
489
490    p { outs 'hello'; em { 'world' } }
491
492=head3 outs_raw STUFF
493
494   p { outs_raw "That's what <em>I'm</em> talking about!' }
495
496Appends its arguments to C<Template::Declare>'s output buffer without HTML
497escaping.
498
499=cut
500
501sub outs     { _outs( 0, @_ ); }
502sub outs_raw { _outs( 1, @_ ); }
503
504=head2 Installing tags and wrapping stuff
505
506=head3 install_tag TAGNAME, TAGSET
507
508    install_tag video => 'Template::Declare::TagSet::HTML';
509
510Sets up TAGNAME as a tag that can be used in user templates. TAGSET is an
511instance of a subclass for L<Template::Declare::TagSet>.
512
513=cut
514
515sub install_tag {
516    my $tag  = $_[0]; # we should not do lc($tag) here :)
517    my $name = $tag;
518    my $tagset = $_[1];
519
520    my $alternative = $tagset->get_alternate_spelling($tag);
521    if ( defined $alternative ) {
522        _install(
523            0, # do not override
524            scalar(caller), $tag,
525            sub (&) {
526                die "$tag {...} is invalid; use $alternative {...} instead.\n";
527            }
528        );
529        ### Exporting place-holder sub: $name
530        # XXX TODO: more checking here
531        if ($name !~ /^(?:base|tr|time)$/) {
532            push @EXPORT, $name;
533            push @TAG_SUB_LIST, $name;
534        }
535        $name = $alternative or return;
536    }
537
538    # We don't need this since we directly install
539    # subs into the target package.
540    #push @EXPORT, $name;
541    push @TAG_SUB_LIST, $name;
542
543    no strict 'refs';
544    no warnings 'redefine';
545    #### Installing tag: $name
546    # XXX TODO: use sub _install to insert subs into the caller's package so as to support XML packages
547    my $code  = sub (&;$) {
548        local *__ANON__ = $tag;
549        if ( defined wantarray and not wantarray ) {
550
551            # Scalar context - return a coderef that represents ourselves.
552            my @__    = @_;
553            my $_self = $self;
554            my $sub   = sub {
555                local $self     = $_self;
556                local *__ANON__ = $tag;
557                _tag($tagset, $tag, @__);
558            };
559            bless $sub, 'Template::Declare::Tag';
560            return $sub;
561        } else {
562            _tag($tagset, $tag, @_);
563        }
564    };
565    _install(
566        1, # do override the existing sub with the same name
567        $tagset->package => $name => $code
568    );
569}
570
571=head3 smart_tag_wrapper
572
573    # create a tag that has access to the arguments set with L</with>.
574    sub sample_smart_tag (&) {
575        my $code = shift;
576
577        smart_tag_wrapper {
578            my %args = @_; # set using 'with'
579            outs( 'keys: ' . join( ', ', sort keys %args) . "\n" );
580            $code->();
581        };
582    }
583
584    # use it
585    with ( foo => 'bar', baz => 'bundy' ), sample_smart_tag {
586        outs( "Hello, World!\n" );
587    };
588
589The output would be
590
591    keys: baz, foo
592    Hello, World!
593
594The smart tag wrapper allows you to create code that has access to the
595attribute arguments specified via C<with>. It passes those arguments in to the
596wrapped code in C<@_>. It also takes care of putting the output in the right
597place and tidying up after itself. This might be useful to change the behavior
598of a template based on attributes passed to C<with>.
599
600=cut
601
602sub smart_tag_wrapper (&) {
603    my $coderef = shift;
604
605    Template::Declare->buffer->append($EOL);
606    Template::Declare->buffer->push( from => "T::D tag wrapper", private => 1 );
607
608    my %attr = %ATTRIBUTES;
609    %ATTRIBUTES = ();                              # prevent leakage
610
611    my $last = join '',
612        map { ref($_) ? $_ : _postprocess($_) }
613        $coderef->(%attr);
614
615    my $content = Template::Declare->buffer->pop;
616    $content .= "$last" if not length $content and length $last;
617    Template::Declare->buffer->append( $content );
618
619    return '';
620}
621
622=head3 create_wrapper WRAPPERNAME => sub { 'Implementation' };
623
624    create_wrapper basics => sub {
625        my $code = shift;
626        html {
627            head { title { 'Welcome' } };
628            body { $code->() }
629        }
630    };
631
632C<create_wrapper> declares a wrapper subroutine that can be called like a tag
633sub, but can optionally take arguments to be passed to the wrapper sub. For
634example, if you wanted to wrap all of the output of a template in the usual
635HTML headers and footers, you can do something like this:
636
637    package MyApp::Templates;
638    use Template::Declare::Tags;
639    use base 'Template::Declare';
640
641    BEGIN {
642        create_wrapper wrap => sub {
643            my $code = shift;
644            my %params = @_;
645            html {
646                head { title { outs "Hello, $params{user}!"} };
647                body {
648                    $code->();
649                    div { outs 'This is the end, my friend' };
650                };
651            }
652        };
653    }
654
655    template inner => sub {
656        wrap {
657            h1 { outs "Hello, Jesse, s'up?" };
658        } user => 'Jesse';
659    };
660
661Note how the C<wrap> wrapper function is available for calling after it has
662been declared in a C<BEGIN> block. Also note how you can pass arguments to the
663function after the closing brace (you don't need a comma there!).
664
665The output from the "inner" template will look something like this:
666
667 <html>
668  <head>
669   <title>Hello, Jesse!</title>
670  </head>
671  <body>
672   <h1>Hello, Jesse, s&#39;up?</h1>
673   <div>This is the end, my friend</div>
674  </body>
675 </html>
676
677=cut
678
679sub create_wrapper ($$) {
680    my $wrapper_name   = shift;
681    my $coderef        = shift;
682    my $template_class = caller;
683
684    # Shove the code ref into the calling class.
685    no strict 'refs';
686    *{"$template_class\::$wrapper_name"} = sub (&;@) { goto $coderef };
687}
688
689=head2 Helpers
690
691=head3 xml_decl HASH
692
693    xml_decl { 'xml', version => '1.0' };
694
695Emits an XML declaration. For example:
696
697    xml_decl { 'xml', version => '1.0' };
698    xml_decl { 'xml-stylesheet',  href => "chrome://global/skin/", type => "text/css" };
699
700Produces:
701
702 <?xml version="1.0"?>
703 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
704
705=cut
706
707sub xml_decl (&;$) {
708    my $code = shift;
709    my @rv   = $code->();
710    my $name = shift @rv;
711    outs_raw("<?$name");
712    while ( my ( $field, $val ) = splice( @rv, 0, 2 ) ) {
713        outs_raw(qq/ $field="$val"/);
714    }
715    outs_raw("?>$EOL");
716    return @_;
717}
718
719=head3 current_template
720
721    my $path = current_template();
722
723Returns the absolute path of the current template
724
725=cut
726
727sub current_template {
728    return $TEMPLATE_STACK[-1] || '';
729}
730
731=head3 current_base_path
732
733    my $path = current_base_path();
734
735Returns the absolute base path of the current template
736
737=cut
738
739sub current_base_path {
740    # Rip it apart
741    my @parts = split('/', current_template());
742
743    # Remove the last element
744    pop @parts;
745
746    # Put it back together again
747    my $path = join('/', @parts);
748
749    # And serve
750    return $path;
751}
752
753=head3 under
754
755C<under> is a helper function providing semantic sugar for the C<mix> method
756of L<Template::Declare|Template::Declare/"mix">.
757
758=cut
759
760sub under ($) { return shift }
761
762=head3 setting
763
764C<setting> is a helper function providing semantic sugar for the C<mix> method
765of L<Template::Declare|Template::Declare/"mix">.
766
767=cut
768
769sub setting ($) { return shift }
770
771=begin comment
772
773=head2 get_current_attr
774
775Deprecated.
776
777=end comment
778
779=cut
780
781sub get_current_attr ($) {
782    $ATTRIBUTES{ $_[0] };
783}
784
785sub _tag {
786    my $tagset    = shift;
787    my $tag       = shift;
788    my $code      = shift;
789    my $more_code = shift;
790    $tag = $tagset->namespace . ":$tag" if defined $tagset->namespace;
791
792    Template::Declare->buffer->append(
793              $EOL
794            . ( " " x $TAG_NEST_DEPTH )
795            . "<$tag"
796            . join( '',
797            map { qq{ $_="} . ( $ATTRIBUTES{$_} || '' ) . qq{"} }
798                sort keys %ATTRIBUTES )
799    );
800
801    my $attrs = "";
802    my $last;
803    {
804        no warnings qw( uninitialized redefine once );
805
806        local *is::AUTOLOAD = sub {
807            shift;
808
809            my $field = our $AUTOLOAD;
810            $field =~ s/.*:://;
811
812            $field =~ s/__/:/g;   # xml__lang  is 'foo' ====> xml:lang="foo"
813            $field =~ s/_/-/g;    # http_equiv is 'bar' ====> http-equiv="bar"
814
815            # Squash empty values, but not '0' values
816            my $val = join ' ', grep { defined $_ && $_ ne '' } @_;
817
818            append_attr( $field, $val );
819        };
820
821        local *append_attr = sub {
822            my $field = shift;
823            my $val   = shift;
824
825            $attrs .= ' ' . $field . q{="} . _postprocess($val, 1) . q{"};
826            wantarray ? () : '';
827        };
828
829        local $TAG_NEST_DEPTH = $TAG_NEST_DEPTH + $TAG_INDENTATION;
830        %ATTRIBUTES = ();
831        Template::Declare->buffer->push( private => 1, from => "T::D tag $tag" );
832        $last = join '', map { ref($_) && $_->isa('Template::Declare::Tag') ? $_ : _postprocess($_) } $code->();
833    }
834    my $content = Template::Declare->buffer->pop;
835    $content .= "$last" if not length $content and length $last;
836    Template::Declare->buffer->append($attrs) if length $attrs;
837
838    if (length $content) {
839        Template::Declare->buffer->append(">$content");
840        Template::Declare->buffer->append( $EOL . ( " " x $TAG_NEST_DEPTH )) if $content =~ /\</;
841        Template::Declare->buffer->append("</$tag>");
842    } elsif ( $tagset->can_combine_empty_tags($tag) ) {
843        Template::Declare->buffer->append(" />");
844    } else {
845        # Otherwise we supply a closing tag.
846        Template::Declare->buffer->append("></$tag>");
847    }
848
849    return ( ref($more_code) && $more_code->isa('CODE') )
850        ? $more_code->()
851        : '';
852}
853
854sub _resolve_template_path {
855    my $template = shift;
856
857    my @parts;
858    if ( substr($template, 0, 1) ne '/' ) {
859        # relative
860        @parts = split '/', current_template();
861        # Get rid of the parent's template name
862        pop @parts;
863    }
864
865    foreach ( split '/', $template ) {
866        if ( $_ eq '..' ) {
867            pop @parts;
868        }
869        # Get rid of "." and empty entries by the way
870        elsif ( $_ ne '.' && $_ ne '' ) {
871            push @parts, $_;
872        }
873    }
874
875    return join '/', @parts;
876}
877
878sub _show_template {
879    my $template        = shift;
880    my $inside_template = shift;
881    my $args = shift;
882    $template = _resolve_template_path($template);
883    local @TEMPLATE_STACK  = (@TEMPLATE_STACK, $template);
884
885    my $callable =
886        ( ref($template) && $template->isa('Template::Declare::Tag') )
887        ? $template
888        : Template::Declare->resolve_template( $template, $inside_template );
889
890    # If the template was not found let the user know.
891    unless ($callable) {
892        my $msg = "The template '$template' could not be found";
893        $msg .= " (it might be private)" if !$inside_template;
894        croak $msg if Template::Declare->strict;
895        carp $msg;
896        return '';
897    }
898
899    if (my $instrumentation = Template::Declare->around_template) {
900        $instrumentation->(
901            sub { &$callable($self, @$args) },
902            $template,
903            $args,
904            $callable,
905        );
906    }
907    else {
908        &$callable($self, @$args);
909    }
910
911    return;
912}
913
914sub _outs {
915    my $raw     = shift;
916    my @phrases = (@_);
917
918    Template::Declare->buffer->push(
919        private => (defined wantarray and not wantarray), from => "T::D outs"
920    );
921
922    foreach my $item ( grep {defined} @phrases ) {
923        my $returned = ref($item) eq 'CODE'
924            ? $item->()
925            : $raw
926                ? $item
927                : _postprocess($item);
928        Template::Declare->buffer->append( $returned );
929    }
930    return Template::Declare->buffer->pop;
931}
932
933sub _postprocess {
934    my $val = shift;
935    my $skip_postprocess = shift;
936
937    return $val unless defined $val;
938
939    # stringify in case $val is object with overloaded ""
940    $val = "$val";
941    if ( ! $SKIP_XML_ESCAPING ) {
942        no warnings 'uninitialized';
943        $val =~ s/&/&#38;/g;
944        $val =~ s/</&lt;/g;
945        $val =~ s/>/&gt;/g;
946        $val =~ s/\(/&#40;/g;
947        $val =~ s/\)/&#41;/g;
948        $val =~ s/"/&#34;/g;
949        $val =~ s/'/&#39;/g;
950    }
951    $val = Template::Declare->postprocessor->($val)
952        unless $skip_postprocess;
953
954    return $val;
955}
956
957=begin comment
958
959=head2 append_attr
960
961C<append_attr> is a helper function providing an interface for setting
962attributes from within tags. But it's better to use C<attr> or C<is> to set
963your attributes. Nohting to see here, really. Move along.
964
965=end comment
966
967=cut
968
969sub append_attr {
970    die "Subroutine attr failed: $_[0] => '$_[1]'\n\t".
971        "(Perhaps you're using an unknown tag in the outer container?)";
972}
973
974=head1 VARIABLES
975
976=over 4
977
978=item C<@Template::Declare::Tags::EXPORT>
979
980Holds the names of the static subroutines exported by this class. Tag
981subroutines generated by tag sets, however, are not included here.
982
983=item C<@Template::Declare::Tags::TAG_SUB_LIST>
984
985Contains the names of the tag subroutines generated from a tag set.
986
987Note that this array won't get cleared automatically before another
988C<< use Template::Decalre::Tags >> statement.
989
990C<@Template::Declare::Tags::TagSubs> is aliased to this variable for
991backward-compatibility.
992
993=item C<$Template::Declare::Tags::TAG_NEST_DEPTH>
994
995Controls the indentation of the XML tags in the final outputs. For example,
996you can temporarily disable a tag's indentation by the following lines of
997code:
998
999    body {
1000        pre {
1001          local $Template::Declare::Tags::TAG_NEST_DEPTH = 0;
1002          script { attr { src => 'foo.js' } }
1003        }
1004    }
1005
1006It generates
1007
1008 <body>
1009  <pre>
1010 <script src="foo.js"></script>
1011  </pre>
1012 </body>
1013
1014Note that now the C<script> tag has I<no> indentation and we've got what we
1015want. ;)
1016
1017=item C<$Template::Declare::Tags::SKIP_XML_ESCAPING>
1018
1019Disables XML escape postprocessing entirely. Use at your own risk.
1020
1021=back
1022
1023=head1 SEE ALSO
1024
1025L<Template::Declare::TagSet::HTML>,
1026L<Template::Declare::TagSet::XUL>, L<Template::Declare>.
1027
1028=head1 AUTHORS
1029
1030Jesse Vincent <jesse@bestpractical.com>
1031
1032Agent Zhang <agentzh@yahoo.cn>
1033
1034=head1 COPYRIGHT
1035
1036Copyright 2006-2009 Best Practical Solutions, LLC.
1037
1038=cut
1039
1040package Template::Declare::Tag;
1041
1042use overload '""' => \&stringify;
1043
1044sub stringify {
1045    my $self = shift;
1046
1047    if ( defined wantarray ) {
1048        Template::Declare->buffer->push( private => 1, from => "T::D stringify" );
1049        my $returned = $self->();
1050        return Template::Declare->buffer->pop . $returned;
1051    } else {
1052        return $self->();
1053    }
1054}
1055
10561;
1057