summaryrefslogtreecommitdiff
path: root/lib/Template
diff options
context:
space:
mode:
authorAndreas Brachold <vdr07@deltab.de>2007-08-13 18:41:27 +0000
committerAndreas Brachold <vdr07@deltab.de>2007-08-13 18:41:27 +0000
commitbcbf441e09fb502cf64924ff2530fa144bdf52c5 (patch)
treef377707a2dac078db8cd0c7d7abfe69ac1006d71 /lib/Template
downloadxxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.gz
xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.bz2
* Move files to trunk
Diffstat (limited to 'lib/Template')
-rw-r--r--lib/Template/Base.pm314
-rw-r--r--lib/Template/Config.pm467
-rw-r--r--lib/Template/Constants.pm287
-rw-r--r--lib/Template/Context.pm1570
-rw-r--r--lib/Template/Directive.pm1004
-rw-r--r--lib/Template/Document.pm492
-rw-r--r--lib/Template/Exception.pm254
-rw-r--r--lib/Template/FAQ.pod329
-rw-r--r--lib/Template/Filters.pm1448
-rw-r--r--lib/Template/Grammar.pm6179
-rw-r--r--lib/Template/Iterator.pm456
-rw-r--r--lib/Template/Library/HTML.pod316
-rw-r--r--lib/Template/Library/PostScript.pod78
-rw-r--r--lib/Template/Library/Splash.pod1030
-rw-r--r--lib/Template/Manual.pod180
-rw-r--r--lib/Template/Manual/Config.pod2122
-rw-r--r--lib/Template/Manual/Credits.pod188
-rw-r--r--lib/Template/Manual/Directives.pod2179
-rw-r--r--lib/Template/Manual/Filters.pod529
-rw-r--r--lib/Template/Manual/Internals.pod556
-rw-r--r--lib/Template/Manual/Intro.pod295
-rw-r--r--lib/Template/Manual/Plugins.pod552
-rw-r--r--lib/Template/Manual/Refs.pod171
-rw-r--r--lib/Template/Manual/Syntax.pod306
-rw-r--r--lib/Template/Manual/VMethods.pod529
-rw-r--r--lib/Template/Manual/Variables.pod868
-rw-r--r--lib/Template/Manual/Views.pod642
-rw-r--r--lib/Template/Modules.pod448
-rw-r--r--lib/Template/Namespace/Constants.pm205
-rw-r--r--lib/Template/Parser.pm1446
-rw-r--r--lib/Template/Plugin.pm409
-rw-r--r--lib/Template/Plugin/Autoformat.pm242
-rw-r--r--lib/Template/Plugin/CGI.pm168
-rw-r--r--lib/Template/Plugin/DBI.pm947
-rw-r--r--lib/Template/Plugin/Datafile.pm198
-rw-r--r--lib/Template/Plugin/Date.pm361
-rw-r--r--lib/Template/Plugin/Directory.pm410
-rw-r--r--lib/Template/Plugin/Dumper.pm179
-rw-r--r--lib/Template/Plugin/File.pm416
-rw-r--r--lib/Template/Plugin/Filter.pm436
-rw-r--r--lib/Template/Plugin/Format.pm124
-rw-r--r--lib/Template/Plugin/GD/Constants.pm138
-rw-r--r--lib/Template/Plugin/GD/Graph/area.pm148
-rw-r--r--lib/Template/Plugin/GD/Graph/bars.pm191
-rw-r--r--lib/Template/Plugin/GD/Graph/bars3d.pm166
-rw-r--r--lib/Template/Plugin/GD/Graph/lines.pm178
-rw-r--r--lib/Template/Plugin/GD/Graph/lines3d.pm166
-rw-r--r--lib/Template/Plugin/GD/Graph/linespoints.pm158
-rw-r--r--lib/Template/Plugin/GD/Graph/mixed.pm176
-rw-r--r--lib/Template/Plugin/GD/Graph/pie.pm141
-rw-r--r--lib/Template/Plugin/GD/Graph/pie3d.pm145
-rw-r--r--lib/Template/Plugin/GD/Graph/points.pm155
-rw-r--r--lib/Template/Plugin/GD/Image.pm184
-rw-r--r--lib/Template/Plugin/GD/Polygon.pm155
-rw-r--r--lib/Template/Plugin/GD/Text.pm140
-rw-r--r--lib/Template/Plugin/GD/Text/Align.pm147
-rw-r--r--lib/Template/Plugin/GD/Text/Wrap.pm183
-rw-r--r--lib/Template/Plugin/HTML.pm197
-rw-r--r--lib/Template/Plugin/Image.pm425
-rw-r--r--lib/Template/Plugin/Iterator.pm118
-rw-r--r--lib/Template/Plugin/Pod.pm116
-rw-r--r--lib/Template/Plugin/Procedural.pm170
-rw-r--r--lib/Template/Plugin/String.pm796
-rw-r--r--lib/Template/Plugin/Table.pm464
-rw-r--r--lib/Template/Plugin/URL.pm236
-rw-r--r--lib/Template/Plugin/View.pm127
-rw-r--r--lib/Template/Plugin/Wrap.pm162
-rw-r--r--lib/Template/Plugin/XML/DOM.pm841
-rw-r--r--lib/Template/Plugin/XML/RSS.pm194
-rw-r--r--lib/Template/Plugin/XML/Simple.pm124
-rw-r--r--lib/Template/Plugin/XML/Style.pm357
-rw-r--r--lib/Template/Plugin/XML/XPath.pm284
-rw-r--r--lib/Template/Plugins.pm1041
-rw-r--r--lib/Template/Provider.pm1449
-rw-r--r--lib/Template/Service.pm775
-rw-r--r--lib/Template/Stash.pm1040
-rw-r--r--lib/Template/Stash/Context.pm791
-rw-r--r--lib/Template/Stash/XS.pm176
-rw-r--r--lib/Template/Test.pm711
-rw-r--r--lib/Template/Tools/tpage.pod76
-rw-r--r--lib/Template/Tools/ttree.pod332
-rw-r--r--lib/Template/Tutorial.pod109
-rw-r--r--lib/Template/Tutorial/Datafile.pod461
-rw-r--r--lib/Template/Tutorial/Web.pod801
-rw-r--r--lib/Template/View.pm752
85 files changed, 45026 insertions, 0 deletions
diff --git a/lib/Template/Base.pm b/lib/Template/Base.pm
new file mode 100644
index 0000000..5f19d78
--- /dev/null
+++ b/lib/Template/Base.pm
@@ -0,0 +1,314 @@
+#============================================================= -*-perl-*-
+#
+# Template::Base
+#
+# DESCRIPTION
+# Base class module implementing common functionality for various other
+# Template Toolkit modules.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Base.pm,v 2.69 2004/01/13 16:19:09 abw Exp $
+#
+#========================================================================
+
+package Template::Base;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# General purpose constructor method which expects a hash reference of
+# configuration parameters, or a list of name => value pairs which are
+# folded into a hash. Blesses a hash into an object and calls its
+# _init() method, passing the parameter hash reference. Returns a new
+# object derived from Template::Base, or undef on error.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my ($argnames, @args, $arg, $cfg);
+# $class->error(''); # always clear package $ERROR var?
+
+ { no strict qw( refs );
+ $argnames = \@{"$class\::BASEARGS"} || [ ];
+ }
+
+ # shift off all mandatory args, returning error if undefined or null
+ foreach $arg (@$argnames) {
+ return $class->error("no $arg specified")
+ unless ($cfg = shift);
+ push(@args, $cfg);
+ }
+
+ # fold all remaining args into a hash, or use provided hash ref
+# local $" = ', ';
+# print STDERR "args: [@_]\n";
+ $cfg = defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
+
+ my $self = bless {
+ map { ($_ => shift @args) } @$argnames,
+ _ERROR => '',
+ DEBUG => 0,
+ }, $class;
+
+ return $self->_init($cfg) ? $self : $class->error($self->error);
+}
+
+
+#------------------------------------------------------------------------
+# error()
+# error($msg, ...)
+#
+# May be called as a class or object method to set or retrieve the
+# package variable $ERROR (class method) or internal member
+# $self->{ _ERROR } (object method). The presence of parameters indicates
+# that the error value should be set. Undef is then returned. In the
+# abscence of parameters, the current error value is returned.
+#------------------------------------------------------------------------
+
+sub error {
+ my $self = shift;
+ my $errvar;
+
+ {
+ no strict qw( refs );
+ $errvar = ref $self ? \$self->{ _ERROR } : \${"$self\::ERROR"};
+ }
+ if (@_) {
+ $$errvar = ref($_[0]) ? shift : join('', @_);
+ return undef;
+ }
+ else {
+ return $$errvar;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# _init()
+#
+# Initialisation method called by the new() constructor and passing a
+# reference to a hash array containing any configuration items specified
+# as constructor arguments. Should return $self on success or undef on
+# error, via a call to the error() method to set the error message.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+ return $self;
+}
+
+
+sub DEBUG {
+ my $self = shift;
+ print STDERR "DEBUG: ", @_;
+}
+
+sub debug {
+ my $self = shift;
+ my $msg = join('', @_);
+ my ($pkg, $file, $line) = caller();
+
+ unless ($msg =~ /\n$/) {
+ $msg .= ($self->{ DEBUG } & Template::Constants::DEBUG_CALLER)
+ ? " at $file line $line\n"
+ : "\n";
+ }
+
+ print STDERR "[$pkg] $msg";
+}
+
+
+#------------------------------------------------------------------------
+# module_version()
+#
+# Returns the current version number.
+#------------------------------------------------------------------------
+
+sub module_version {
+ my $self = shift;
+ my $class = ref $self || $self;
+ no strict 'refs';
+ return ${"${class}::VERSION"};
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Base - Base class module implementing common functionality
+
+=head1 SYNOPSIS
+
+ package My::Module;
+ use base qw( Template::Base );
+
+ sub _init {
+ my ($self, $config) = @_;
+ $self->{ doodah } = $config->{ doodah }
+ || return $self->error("No 'doodah' specified");
+ return $self;
+ }
+
+ package main;
+
+ my $object = My::Module->new({ doodah => 'foobar' })
+ || die My::Module->error();
+
+=head1 DESCRIPTION
+
+Base class module which implements a constructor and error reporting
+functionality for various Template Toolkit modules.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%config)
+
+Constructor method which accepts a reference to a hash array or a list
+of C<name =E<gt> value> parameters which are folded into a hash. The
+_init() method is then called, passing the configuration hash and should
+return true/false to indicate success or failure. A new object reference
+is returned, or undef on error. Any error message raised can be examined
+via the error() class method or directly via the package variable ERROR
+in the derived class.
+
+ my $module = My::Module->new({ ... })
+ || die My::Module->error(), "\n";
+
+ my $module = My::Module->new({ ... })
+ || die "constructor error: $My::Module::ERROR\n";
+
+=head2 error($msg, ...)
+
+May be called as an object method to get/set the internal _ERROR member
+or as a class method to get/set the $ERROR variable in the derived class's
+package.
+
+ my $module = My::Module->new({ ... })
+ || die My::Module->error(), "\n";
+
+ $module->do_something()
+ || die $module->error(), "\n";
+
+When called with parameters (multiple params are concatenated), this
+method will set the relevant variable and return undef. This is most
+often used within object methods to report errors to the caller.
+
+ package My::Module;
+
+ sub foobar {
+ my $self = shift;
+
+ # some other code...
+
+ return $self->error('some kind of error...')
+ if $some_condition;
+ }
+
+=head2 debug($msg, ...)
+
+Generates a debugging message by concatenating all arguments
+passed into a string and printing it to STDERR. A prefix is
+added to indicate the module of the caller.
+
+ package My::Module;
+
+ sub foobar {
+ my $self = shift;
+
+ $self->debug('called foobar()');
+
+ # some other code...
+ }
+
+When the foobar() method is called, the following message
+is sent to STDERR:
+
+ [My::Module] called foobar()
+
+Objects can set an internal DEBUG value which the debug()
+method will examine. If this value sets the relevant bits
+to indicate DEBUG_CALLER then the file and line number of
+the caller will be appened to the message.
+
+ use Template::Constants qw( :debug );
+
+ my $module = My::Module->new({
+ DEBUG => DEBUG_SERVICE | DEBUG_CONTEXT | DEBUG_CALLER,
+ });
+
+ $module->foobar();
+
+This generates an error message such as:
+
+ [My::Module] called foobar() at My/Module.pm line 6
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Config.pm b/lib/Template/Config.pm
new file mode 100644
index 0000000..9a3f378
--- /dev/null
+++ b/lib/Template/Config.pm
@@ -0,0 +1,467 @@
+#============================================================= -*-perl-*-
+#
+# Template::Config
+#
+# DESCRIPTION
+# Template Toolkit configuration module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Config.pm,v 2.67 2004/01/13 16:19:10 abw Exp $
+#
+#========================================================================
+
+package Template::Config;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $ERROR $INSTDIR
+ $PARSER $PROVIDER $PLUGINS $FILTERS $ITERATOR
+ $LATEX_PATH $PDFLATEX_PATH $DVIPS_PATH
+ $STASH $SERVICE $CONTEXT $CONSTANTS @PRELOAD );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.67 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+$ERROR = '';
+$CONTEXT = 'Template::Context';
+$FILTERS = 'Template::Filters';
+$ITERATOR = 'Template::Iterator';
+$PARSER = 'Template::Parser';
+$PLUGINS = 'Template::Plugins';
+$PROVIDER = 'Template::Provider';
+$SERVICE = 'Template::Service';
+$STASH = 'Template::Stash';
+$CONSTANTS = 'Template::Namespace::Constants';
+
+@PRELOAD = ( $CONTEXT, $FILTERS, $ITERATOR, $PARSER,
+ $PLUGINS, $PROVIDER, $SERVICE, $STASH );
+
+# the following is set at installation time by the Makefile.PL
+$INSTDIR = '';
+
+# LaTeX executable paths set at installation time by the Makefile.PL
+# Empty strings cause the latex(pdf|dvi|ps) filters to throw an error.
+$LATEX_PATH = '';
+$PDFLATEX_PATH = '';
+$DVIPS_PATH = '';
+
+#========================================================================
+# --- CLASS METHODS ---
+#========================================================================
+
+#------------------------------------------------------------------------
+# preload($module, $module, ...)
+#
+# Preloads all the standard TT modules that are likely to be used, along
+# with any other passed as arguments.
+#------------------------------------------------------------------------
+
+sub preload {
+ my $class = shift;
+
+ foreach my $module (@PRELOAD, @_) {
+ $class->load($module) || return;
+ };
+ return 1;
+}
+
+
+#------------------------------------------------------------------------
+# load($module)
+#
+# Load a module via require(). Any occurences of '::' in the module name
+# are be converted to '/' and '.pm' is appended. Returns 1 on success
+# or undef on error. Use $class->error() to examine the error string.
+#------------------------------------------------------------------------
+
+sub load {
+ my ($class, $module) = @_;
+ $module =~ s[::][/]g;
+ $module .= '.pm';
+# print STDERR "loading $module\n"
+# if $DEBUG;
+ eval {
+ require $module;
+ };
+ return $@ ? $class->error("failed to load $module: $@") : 1;
+}
+
+
+#------------------------------------------------------------------------
+# parser(\%params)
+#
+# Instantiate a new parser object of the class whose name is denoted by
+# the package variable $PARSER (default: Template::Parser). Returns
+# a reference to a newly instantiated parser object or undef on error.
+# The class error() method can be called without arguments to examine
+# the error message generated by this failure.
+#------------------------------------------------------------------------
+
+sub parser {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PARSER);
+ return $PARSER->new($params)
+ || $class->error("failed to create parser: ", $PARSER->error);
+}
+
+
+#------------------------------------------------------------------------
+# provider(\%params)
+#
+# Instantiate a new template provider object (default: Template::Provider).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub provider {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PROVIDER);
+ return $PROVIDER->new($params)
+ || $class->error("failed to create template provider: ",
+ $PROVIDER->error);
+}
+
+
+#------------------------------------------------------------------------
+# plugins(\%params)
+#
+# Instantiate a new plugins provider object (default: Template::Plugins).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub plugins {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PLUGINS);
+ return $PLUGINS->new($params)
+ || $class->error("failed to create plugin provider: ",
+ $PLUGINS->error);
+}
+
+
+#------------------------------------------------------------------------
+# filters(\%params)
+#
+# Instantiate a new filters provider object (default: Template::Filters).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub filters {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($FILTERS);
+ return $FILTERS->new($params)
+ || $class->error("failed to create filter provider: ",
+ $FILTERS->error);
+}
+
+
+#------------------------------------------------------------------------
+# iterator(\@list)
+#
+# Instantiate a new Template::Iterator object (default: Template::Iterator).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub iterator {
+ my $class = shift;
+ my $list = shift;
+
+ return undef unless $class->load($ITERATOR);
+ return $ITERATOR->new($list, @_)
+ || $class->error("failed to create iterator: ", $ITERATOR->error);
+}
+
+
+#------------------------------------------------------------------------
+# stash(\%vars)
+#
+# Instantiate a new template variable stash object (default:
+# Template::Stash). Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub stash {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($STASH);
+ return $STASH->new($params)
+ || $class->error("failed to create stash: ", $STASH->error);
+}
+
+
+#------------------------------------------------------------------------
+# context(\%params)
+#
+# Instantiate a new template context object (default: Template::Context).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub context {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($CONTEXT);
+ return $CONTEXT->new($params)
+ || $class->error("failed to create context: ", $CONTEXT->error);
+}
+
+
+#------------------------------------------------------------------------
+# service(\%params)
+#
+# Instantiate a new template context object (default: Template::Service).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub service {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($SERVICE);
+ return $SERVICE->new($params)
+ || $class->error("failed to create context: ", $SERVICE->error);
+}
+
+
+#------------------------------------------------------------------------
+# constants(\%params)
+#
+# Instantiate a new namespace handler for compile time constant folding
+# (default: Template::Namespace::Constants).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub constants {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($CONSTANTS);
+ return $CONSTANTS->new($params)
+ || $class->error("failed to create constants namespace: ",
+ $CONSTANTS->error);
+}
+
+
+#------------------------------------------------------------------------
+# instdir($dir)
+#
+# Returns the root installation directory appended with any local
+# component directory passed as an argument.
+#------------------------------------------------------------------------
+
+sub instdir {
+ my ($class, $dir) = @_;
+ my $inst = $INSTDIR
+ || return $class->error("no installation directory");
+ $inst =~ s[/$][]g;
+ $inst .= "/$dir" if $dir;
+ return $inst;
+}
+
+#------------------------------------------------------------------------
+# latexpaths()
+#
+# Returns a reference to a three element array:
+# [latex_path, pdf2latex_path, dvips_path]
+# These values are determined by Makefile.PL at installation time
+# and are used by the latex(pdf|dvi|ps) filters.
+#------------------------------------------------------------------------
+
+sub latexpaths {
+ return [$LATEX_PATH, $PDFLATEX_PATH, $DVIPS_PATH];
+}
+
+#========================================================================
+# This should probably be moved somewhere else in the long term, but for
+# now it ensures that Template::TieString is available even if the
+# Template::Directive module hasn't been loaded, as is the case when
+# using compiled templates and Template::Parser hasn't yet been loaded
+# on demand.
+#========================================================================
+
+#------------------------------------------------------------------------
+# simple package for tying $output variable to STDOUT, used by perl()
+#------------------------------------------------------------------------
+
+package Template::TieString;
+
+sub TIEHANDLE {
+ my ($class, $textref) = @_;
+ bless $textref, $class;
+}
+sub PRINT {
+ my $self = shift;
+ $$self .= join('', @_);
+}
+
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Config - Factory module for instantiating other TT2 modules
+
+=head1 SYNOPSIS
+
+ use Template::Config;
+
+=head1 DESCRIPTION
+
+This module implements various methods for loading and instantiating
+other modules that comprise the Template Toolkit. It provides a consistent
+way to create toolkit components and allows custom modules to be used in
+place of the regular ones.
+
+Package variables such as $STASH, $SERVICE, $CONTEXT, etc., contain
+the default module/package name for each component (Template::Stash,
+Template::Service and Template::Context, respectively) and are used by
+the various factory methods (stash(), service() and context()) to load
+the appropriate module. Changing these package variables will cause
+subsequent calls to the relevant factory method to load and instantiate
+an object from the new class.
+
+=head1 PUBLIC METHODS
+
+=head2 load($module)
+
+Load a module via require(). Any occurences of '::' in the module name
+are be converted to '/' and '.pm' is appended. Returns 1 on success
+or undef on error. Use $class-E<gt>error() to examine the error string.
+
+=head2 preload()
+
+This method preloads all the other Template::* modules that are likely
+to be used. It is called by the Template module when running under
+mod_perl ($ENV{MOD_PERL} is set).
+
+=head2 parser(\%config)
+
+Instantiate a new parser object of the class whose name is denoted by
+the package variable $PARSER (default: Template::Parser). Returns
+a reference to a newly instantiated parser object or undef on error.
+
+=head2 provider(\%config)
+
+Instantiate a new template provider object (default: Template::Provider).
+Returns an object reference or undef on error, as above.
+
+=head2 plugins(\%config)
+
+Instantiate a new plugins provider object (default: Template::Plugins).
+Returns an object reference or undef on error, as above.
+
+=head2 filters(\%config)
+
+Instantiate a new filter provider object (default: Template::Filters).
+Returns an object reference or undef on error, as above.
+
+=head2 stash(\%vars)
+
+Instantiate a new stash object (Template::Stash or Template::Stash::XS
+depending on the default set at installation time) using the contents
+of the optional hash array passed by parameter as initial variable
+definitions. Returns an object reference or undef on error, as above.
+
+=head2 context(\%config)
+
+Instantiate a new template context object (default: Template::Context).
+Returns an object reference or undef on error, as above.
+
+=head2 service(\%config)
+
+Instantiate a new template service object (default: Template::Service).
+Returns an object reference or undef on error, as above.
+
+=head2 instdir($dir)
+
+Returns the root directory of the Template Toolkit installation under
+which optional components are installed. Any relative directory specified
+as an argument will be appended to the returned directory.
+
+ # e.g. returns '/usr/local/tt2'
+ my $ttroot = Template::Config->instdir()
+ || die "$Template::Config::ERROR\n";
+
+ # e.g. returns '/usr/local/tt2/templates'
+ my $template = Template::Config->instdir('templates')
+ || die "$Template::Config::ERROR\n";
+
+Returns undef and sets $Template::Config::ERROR appropriately if the
+optional components of the Template Toolkit have not been installed.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.67, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Constants.pm b/lib/Template/Constants.pm
new file mode 100644
index 0000000..b227467
--- /dev/null
+++ b/lib/Template/Constants.pm
@@ -0,0 +1,287 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Constants.pm
+#
+# DESCRIPTION
+# Definition of constants for the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Constants.pm,v 2.67 2004/01/13 16:19:10 abw Exp $
+#
+#============================================================================
+
+package Template::Constants;
+
+require 5.004;
+require Exporter;
+
+use strict;
+use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS );
+use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG);
+
+@ISA = qw( Exporter );
+$VERSION = sprintf("%d.%02d", q$Revision: 2.67 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# ----- EXPORTER -----
+#========================================================================
+
+# STATUS constants returned by directives
+use constant STATUS_OK => 0; # ok
+use constant STATUS_RETURN => 1; # ok, block ended by RETURN
+use constant STATUS_STOP => 2; # ok, stoppped by STOP
+use constant STATUS_DONE => 3; # ok, iterator done
+use constant STATUS_DECLINED => 4; # ok, declined to service request
+use constant STATUS_ERROR => 255; # error condition
+
+# ERROR constants for indicating exception types
+use constant ERROR_RETURN => 'return'; # return a status code
+use constant ERROR_FILE => 'file'; # file error: I/O, parse, recursion
+use constant ERROR_VIEW => 'view'; # view error
+use constant ERROR_UNDEF => 'undef'; # undefined variable value used
+use constant ERROR_PERL => 'perl'; # error in [% PERL %] block
+use constant ERROR_FILTER => 'filter'; # filter error
+use constant ERROR_PLUGIN => 'plugin'; # plugin error
+
+# CHOMP constants for PRE_CHOMP and POST_CHOMP
+use constant CHOMP_NONE => 0; # do not remove whitespace
+use constant CHOMP_ALL => 1; # remove whitespace
+use constant CHOMP_COLLAPSE => 2; # collapse whitespace to a single space
+
+# DEBUG constants to enable various debugging options
+use constant DEBUG_OFF => 0; # do nothing
+use constant DEBUG_ON => 1; # basic debugging flag
+use constant DEBUG_UNDEF => 2; # throw undef on undefined variables
+use constant DEBUG_VARS => 4; # general variable debugging
+use constant DEBUG_DIRS => 8; # directive debugging
+use constant DEBUG_STASH => 16; # general stash debugging
+use constant DEBUG_CONTEXT => 32; # context debugging
+use constant DEBUG_PARSER => 64; # parser debugging
+use constant DEBUG_PROVIDER => 128; # provider debugging
+use constant DEBUG_PLUGINS => 256; # plugins debugging
+use constant DEBUG_FILTERS => 512; # filters debugging
+use constant DEBUG_SERVICE => 1024; # context debugging
+use constant DEBUG_ALL => 2047; # everything
+
+# extra debugging flags
+use constant DEBUG_CALLER => 4096; # add caller file/line
+use constant DEBUG_FLAGS => 4096; # bitmask to extraxt flags
+
+$DEBUG_OPTIONS = {
+ &DEBUG_OFF => off => off => &DEBUG_OFF,
+ &DEBUG_ON => on => on => &DEBUG_ON,
+ &DEBUG_UNDEF => undef => undef => &DEBUG_UNDEF,
+ &DEBUG_VARS => vars => vars => &DEBUG_VARS,
+ &DEBUG_DIRS => dirs => dirs => &DEBUG_DIRS,
+ &DEBUG_STASH => stash => stash => &DEBUG_STASH,
+ &DEBUG_CONTEXT => context => context => &DEBUG_CONTEXT,
+ &DEBUG_PARSER => parser => parser => &DEBUG_PARSER,
+ &DEBUG_PROVIDER => provider => provider => &DEBUG_PROVIDER,
+ &DEBUG_PLUGINS => plugins => plugins => &DEBUG_PLUGINS,
+ &DEBUG_FILTERS => filters => filters => &DEBUG_FILTERS,
+ &DEBUG_SERVICE => service => service => &DEBUG_SERVICE,
+ &DEBUG_ALL => all => all => &DEBUG_ALL,
+ &DEBUG_CALLER => caller => caller => &DEBUG_CALLER,
+};
+
+@STATUS = qw( STATUS_OK STATUS_RETURN STATUS_STOP STATUS_DONE
+ STATUS_DECLINED STATUS_ERROR );
+@ERROR = qw( ERROR_FILE ERROR_VIEW ERROR_UNDEF ERROR_PERL
+ ERROR_RETURN ERROR_FILTER ERROR_PLUGIN );
+@CHOMP = qw( CHOMP_NONE CHOMP_ALL CHOMP_COLLAPSE );
+@DEBUG = qw( DEBUG_OFF DEBUG_ON DEBUG_UNDEF DEBUG_VARS
+ DEBUG_DIRS DEBUG_STASH DEBUG_CONTEXT DEBUG_PARSER
+ DEBUG_PROVIDER DEBUG_PLUGINS DEBUG_FILTERS DEBUG_SERVICE
+ DEBUG_ALL DEBUG_CALLER DEBUG_FLAGS );
+
+@EXPORT_OK = ( @STATUS, @ERROR, @CHOMP, @DEBUG );
+%EXPORT_TAGS = (
+ 'all' => [ @EXPORT_OK ],
+ 'status' => [ @STATUS ],
+ 'error' => [ @ERROR ],
+ 'chomp' => [ @CHOMP ],
+ 'debug' => [ @DEBUG ],
+);
+
+
+sub debug_flags {
+ my ($self, $debug) = @_;
+ my (@flags, $flag, $value);
+ $debug = $self unless defined($debug) || ref($self);
+
+ if ($debug =~ /^\d+$/) {
+ foreach $flag (@DEBUG) {
+ next if $flag =~ /^DEBUG_(OFF|ALL|FLAGS)$/;
+
+ # don't trash the original
+ my $copy = $flag;
+ $flag =~ s/^DEBUG_//;
+ $flag = lc $flag;
+ return $self->error("no value for flag: $flag")
+ unless defined($value = $DEBUG_OPTIONS->{ $flag });
+ $flag = $value;
+
+ if ($debug & $flag) {
+ $value = $DEBUG_OPTIONS->{ $flag };
+ return $self->error("no value for flag: $flag") unless defined $value;
+ push(@flags, $value);
+ }
+ }
+ return wantarray ? @flags : join(', ', @flags);
+ }
+ else {
+ @flags = split(/\W+/, $debug);
+ $debug = 0;
+ foreach $flag (@flags) {
+ $value = $DEBUG_OPTIONS->{ $flag };
+ return $self->error("unknown debug flag: $flag") unless defined $value;
+ $debug |= $value;
+ }
+ return $debug;
+ }
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Constants - Defines constants for the Template Toolkit
+
+=head1 SYNOPSIS
+
+ use Template::Constants qw( :status :error :all );
+
+=head1 DESCRIPTION
+
+The Template::Constants modules defines, and optionally exports into the
+caller's namespace, a number of constants used by the Template package.
+
+Constants may be used by specifying the Template::Constants package
+explicitly:
+
+ use Template::Constants;
+
+ print Template::Constants::STATUS_DECLINED;
+
+Constants may be imported into the caller's namespace by naming them as
+options to the C<use Template::Constants> statement:
+
+ use Template::Constants qw( STATUS_DECLINED );
+
+ print STATUS_DECLINED;
+
+Alternatively, one of the following tagset identifiers may be specified
+to import sets of constants; :status, :error, :all.
+
+ use Template::Constants qw( :status );
+
+ print STATUS_DECLINED;
+
+See L<Exporter> for more information on exporting variables.
+
+=head1 EXPORTABLE TAG SETS
+
+The following tag sets and associated constants are defined:
+
+ :status
+ STATUS_OK # no problem, continue
+ STATUS_RETURN # ended current block then continue (ok)
+ STATUS_STOP # controlled stop (ok)
+ STATUS_DONE # iterator is all done (ok)
+ STATUS_DECLINED # provider declined to service request (ok)
+ STATUS_ERROR # general error condition (not ok)
+
+ :error
+ ERROR_RETURN # return a status code (e.g. 'stop')
+ ERROR_FILE # file error: I/O, parse, recursion
+ ERROR_UNDEF # undefined variable value used
+ ERROR_PERL # error in [% PERL %] block
+ ERROR_FILTER # filter error
+ ERROR_PLUGIN # plugin error
+
+ :chomp # for PRE_CHOMP and POST_CHOMP
+ CHOMP_NONE # do not remove whitespace
+ CHOMP_ALL # remove whitespace
+ CHOMP_COLLAPSE # collapse whitespace to a single space
+
+ :debug
+ DEBUG_OFF # do nothing
+ DEBUG_ON # basic debugging flag
+ DEBUG_UNDEF # throw undef on undefined variables
+ DEBUG_VARS # general variable debugging
+ DEBUG_DIRS # directive debugging
+ DEBUG_STASH # general stash debugging
+ DEBUG_CONTEXT # context debugging
+ DEBUG_PARSER # parser debugging
+ DEBUG_PROVIDER # provider debugging
+ DEBUG_PLUGINS # plugins debugging
+ DEBUG_FILTERS # filters debugging
+ DEBUG_SERVICE # context debugging
+ DEBUG_ALL # everything
+ DEBUG_CALLER # add caller file/line info
+ DEBUG_FLAGS # bitmap used internally
+
+ :all All the above constants.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.67, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Exporter|Exporter>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Context.pm b/lib/Template/Context.pm
new file mode 100644
index 0000000..cdc699e
--- /dev/null
+++ b/lib/Template/Context.pm
@@ -0,0 +1,1570 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Context
+#
+# DESCRIPTION
+# Module defining a context in which a template document is processed.
+# This is the runtime processing interface through which templates
+# can access the functionality of the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Context.pm,v 2.89 2004/01/30 18:37:47 abw Exp $
+#
+#============================================================================
+
+package Template::Context;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD $DEBUG_FORMAT );
+use base qw( Template::Base );
+
+use Template::Base;
+use Template::Config;
+use Template::Constants;
+use Template::Exception;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.89 $ =~ /(\d+)\.(\d+)/);
+$DEBUG_FORMAT = "\n## \$file line \$line : [% \$text %] ##\n";
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# template($name)
+#
+# General purpose method to fetch a template and return it in compiled
+# form. In the usual case, the $name parameter will be a simple string
+# containing the name of a template (e.g. 'header'). It may also be
+# a reference to Template::Document object (or sub-class) or a Perl
+# sub-routine. These are considered to be compiled templates and are
+# returned intact. Finally, it may be a reference to any other kind
+# of valid input source accepted by Template::Provider (e.g. scalar
+# ref, glob, IO handle, etc).
+#
+# Templates may be cached at one of 3 different levels. The internal
+# BLOCKS member is a local cache which holds references to all
+# template blocks used or imported via PROCESS since the context's
+# reset() method was last called. This is checked first and if the
+# template is not found, the method then walks down the BLOCKSTACK
+# list. This contains references to the block definition tables in
+# any enclosing Template::Documents that we're visiting (e.g. we've
+# been called via an INCLUDE and we want to access a BLOCK defined in
+# the template that INCLUDE'd us). If nothing is defined, then we
+# iterate through the LOAD_TEMPLATES providers list as a 'chain of
+# responsibility' (see Design Patterns) asking each object to fetch()
+# the template if it can.
+#
+# Returns the compiled template. On error, undef is returned and
+# the internal ERROR value (read via error()) is set to contain an
+# error message of the form "$name: $error".
+#------------------------------------------------------------------------
+
+sub template {
+ my ($self, $name) = @_;
+ my ($prefix, $blocks, $defblocks, $provider, $template, $error);
+ my ($shortname, $blockname, $providers);
+
+ $self->debug("template($name)") if $self->{ DEBUG };
+
+ # references to Template::Document (or sub-class) objects objects, or
+ # CODE references are assumed to be pre-compiled templates and are
+ # returned intact
+ return $name
+ if UNIVERSAL::isa($name, 'Template::Document')
+ || ref($name) eq 'CODE';
+
+ $shortname = $name;
+
+ unless (ref $name) {
+
+ $self->debug("looking for block [$name]") if $self->{ DEBUG };
+
+ # we first look in the BLOCKS hash for a BLOCK that may have
+ # been imported from a template (via PROCESS)
+ return $template
+ if ($template = $self->{ BLOCKS }->{ $name });
+
+ # then we iterate through the BLKSTACK list to see if any of the
+ # Template::Documents we're visiting define this BLOCK
+ foreach $blocks (@{ $self->{ BLKSTACK } }) {
+ return $template
+ if $blocks && ($template = $blocks->{ $name });
+ }
+
+ # now it's time to ask the providers, so we look to see if any
+ # prefix is specified to indicate the desired provider set.
+ if ($^O eq 'MSWin32') {
+ # let C:/foo through
+ $prefix = $1 if $shortname =~ s/^(\w{2,})://o;
+ }
+ else {
+ $prefix = $1 if $shortname =~ s/^(\w+)://;
+ }
+
+ if (defined $prefix) {
+ $providers = $self->{ PREFIX_MAP }->{ $prefix }
+ || return $self->throw( Template::Constants::ERROR_FILE,
+ "no providers for template prefix '$prefix'");
+ }
+ }
+ $providers = $self->{ PREFIX_MAP }->{ default }
+ || $self->{ LOAD_TEMPLATES }
+ unless $providers;
+
+
+ # Finally we try the regular template providers which will
+ # handle references to files, text, etc., as well as templates
+ # reference by name. If
+
+ $blockname = '';
+ while ($shortname) {
+ $self->debug("asking providers for [$shortname] [$blockname]")
+ if $self->{ DEBUG };
+
+ foreach my $provider (@$providers) {
+ ($template, $error) = $provider->fetch($shortname, $prefix);
+ if ($error) {
+ if ($error == Template::Constants::STATUS_ERROR) {
+ # $template contains exception object
+ if (UNIVERSAL::isa($template, 'Template::Exception')
+ && $template->type() eq Template::Constants::ERROR_FILE) {
+ $self->throw($template);
+ }
+ else {
+ $self->throw( Template::Constants::ERROR_FILE, $template );
+ }
+ }
+ # DECLINE is ok, carry on
+ }
+ elsif (length $blockname) {
+ return $template
+ if $template = $template->blocks->{ $blockname };
+ }
+ else {
+ return $template;
+ }
+ }
+
+ last if ref $shortname || ! $self->{ EXPOSE_BLOCKS };
+ $shortname =~ s{/([^/]+)$}{} || last;
+ $blockname = length $blockname ? "$1/$blockname" : $1;
+ }
+
+ $self->throw(Template::Constants::ERROR_FILE, "$name: not found");
+}
+
+
+#------------------------------------------------------------------------
+# plugin($name, \@args)
+#
+# Calls on each of the LOAD_PLUGINS providers in turn to fetch() (i.e. load
+# and instantiate) a plugin of the specified name. Additional parameters
+# passed are propagated to the new() constructor for the plugin.
+# Returns a reference to a new plugin object or other reference. On
+# error, undef is returned and the appropriate error message is set for
+# subsequent retrieval via error().
+#------------------------------------------------------------------------
+
+sub plugin {
+ my ($self, $name, $args) = @_;
+ my ($provider, $plugin, $error);
+
+ $self->debug("plugin($name, ", defined $args ? @$args : '[ ]', ')')
+ if $self->{ DEBUG };
+
+ # request the named plugin from each of the LOAD_PLUGINS providers in turn
+ foreach my $provider (@{ $self->{ LOAD_PLUGINS } }) {
+ ($plugin, $error) = $provider->fetch($name, $args, $self);
+ return $plugin unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($plugin) if ref $plugin;
+ $self->throw(Template::Constants::ERROR_PLUGIN, $plugin);
+ }
+ }
+
+ $self->throw(Template::Constants::ERROR_PLUGIN, "$name: plugin not found");
+}
+
+
+#------------------------------------------------------------------------
+# filter($name, \@args, $alias)
+#
+# Similar to plugin() above, but querying the LOAD_FILTERS providers to
+# return filter instances. An alias may be provided which is used to
+# save the returned filter in a local cache.
+#------------------------------------------------------------------------
+
+sub filter {
+ my ($self, $name, $args, $alias) = @_;
+ my ($provider, $filter, $error);
+
+ $self->debug("filter($name, ",
+ defined $args ? @$args : '[ ]',
+ defined $alias ? $alias : '<no alias>', ')')
+ if $self->{ DEBUG };
+
+ # use any cached version of the filter if no params provided
+ return $filter
+ if ! $args && ! ref $name
+ && ($filter = $self->{ FILTER_CACHE }->{ $name });
+
+ # request the named filter from each of the FILTERS providers in turn
+ foreach my $provider (@{ $self->{ LOAD_FILTERS } }) {
+ ($filter, $error) = $provider->fetch($name, $args, $self);
+ last unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($filter) if ref $filter;
+ $self->throw(Template::Constants::ERROR_FILTER, $filter);
+ }
+ # return $self->error($filter)
+ # if $error == &Template::Constants::STATUS_ERROR;
+ }
+
+ return $self->error("$name: filter not found")
+ unless $filter;
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # commented out by abw on 19 Nov 2001 to fix problem with xmlstyle
+ # plugin which may re-define a filter by calling define_filter()
+ # multiple times. With the automatic aliasing/caching below, any
+ # new filter definition isn't seen. Don't think this will cause
+ # any problems as filters explicitly supplied with aliases will
+ # still work as expected.
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # alias defaults to name if undefined
+ # $alias = $name
+ # unless defined($alias) or ref($name) or $args;
+
+ # cache FILTER if alias is valid
+ $self->{ FILTER_CACHE }->{ $alias } = $filter
+ if $alias;
+
+ return $filter;
+}
+
+
+#------------------------------------------------------------------------
+# view(\%config)
+#
+# Create a new Template::View bound to this context.
+#------------------------------------------------------------------------
+
+sub view {
+ my $self = shift;
+ require Template::View;
+ return Template::View->new($self, @_)
+ || $self->throw(&Template::Constants::ERROR_VIEW,
+ $Template::View::ERROR);
+}
+
+
+#------------------------------------------------------------------------
+# process($template, \%params) [% PROCESS template var=val ... %]
+# process($template, \%params, $local) [% INCLUDE template var=val ... %]
+#
+# Processes the template named or referenced by the first parameter.
+# The optional second parameter may reference a hash array of variable
+# definitions. These are set before the template is processed by
+# calling update() on the stash. Note that, unless the third parameter
+# is true, the context is not localised and these, and any other
+# variables set in the template will retain their new values after this
+# method returns. The third parameter is in place so that this method
+# can handle INCLUDE calls: the stash will be localized.
+#
+# Returns the output of processing the template. Errors are thrown
+# as Template::Exception objects via die().
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $template, $params, $localize) = @_;
+ my ($trim, $blocks) = @$self{ qw( TRIM BLOCKS ) };
+ my (@compiled, $name, $compiled);
+ my ($stash, $component, $tblocks, $error, $tmpout);
+ my $output = '';
+
+ $template = [ $template ] unless ref $template eq 'ARRAY';
+
+ $self->debug("process([ ", join(', '), @$template, ' ], ',
+ defined $params ? $params : '<no params>', ', ',
+ $localize ? '<localized>' : '<unlocalized>', ')')
+ if $self->{ DEBUG };
+
+ # fetch compiled template for each name specified
+ foreach $name (@$template) {
+ push(@compiled, $self->template($name));
+ }
+
+ if ($localize) {
+ # localise the variable stash with any parameters passed
+ $stash = $self->{ STASH } = $self->{ STASH }->clone($params);
+ } else {
+ # update stash with any new parameters passed
+ $self->{ STASH }->update($params);
+ $stash = $self->{ STASH };
+ }
+
+ eval {
+ # save current component
+ eval { $component = $stash->get('component') };
+
+ foreach $name (@$template) {
+ $compiled = shift @compiled;
+ my $element = ref $compiled eq 'CODE'
+ ? { (name => (ref $name ? '' : $name), modtime => time()) }
+ : $compiled;
+
+ if (UNIVERSAL::isa($component, 'Template::Document')) {
+ $element->{ caller } = $component->{ name };
+ $element->{ callers } = $component->{ callers } || [];
+ push(@{$element->{ callers }}, $element->{ caller });
+ }
+
+ $stash->set('component', $element);
+
+ unless ($localize) {
+ # merge any local blocks defined in the Template::Document
+ # into our local BLOCKS cache
+ @$blocks{ keys %$tblocks } = values %$tblocks
+ if UNIVERSAL::isa($compiled, 'Template::Document')
+ && ($tblocks = $compiled->blocks());
+ }
+
+ if (ref $compiled eq 'CODE') {
+ $tmpout = &$compiled($self);
+ }
+ elsif (ref $compiled) {
+ $tmpout = $compiled->process($self);
+ }
+ else {
+ $self->throw('file',
+ "invalid template reference: $compiled");
+ }
+
+ if ($trim) {
+ for ($tmpout) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ }
+ $output .= $tmpout;
+ }
+ $stash->set('component', $component);
+ };
+ $error = $@;
+
+ if ($localize) {
+ # ensure stash is delocalised before dying
+ $self->{ STASH } = $self->{ STASH }->declone();
+ }
+
+ $self->throw(ref $error
+ ? $error : (Template::Constants::ERROR_FILE, $error))
+ if $error;
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# include($template, \%params) [% INCLUDE template var = val, ... %]
+#
+# Similar to process() above but processing the template in a local
+# context. Any variables passed by reference to a hash as the second
+# parameter will be set before the template is processed and then
+# revert to their original values before the method returns. Similarly,
+# any changes made to non-global variables within the template will
+# persist only until the template is processed.
+#
+# Returns the output of processing the template. Errors are thrown
+# as Template::Exception objects via die().
+#------------------------------------------------------------------------
+
+sub include {
+ my ($self, $template, $params) = @_;
+ return $self->process($template, $params, 'localize me!');
+}
+
+#------------------------------------------------------------------------
+# insert($file)
+#
+# Insert the contents of a file without parsing.
+#------------------------------------------------------------------------
+
+sub insert {
+ my ($self, $file) = @_;
+ my ($prefix, $providers, $text, $error);
+ my $output = '';
+
+ my $files = ref $file eq 'ARRAY' ? $file : [ $file ];
+
+ $self->debug("insert([ ", join(', '), @$files, " ])")
+ if $self->{ DEBUG };
+
+
+ FILE: foreach $file (@$files) {
+ my $name = $file;
+
+ if ($^O eq 'MSWin32') {
+ # let C:/foo through
+ $prefix = $1 if $name =~ s/^(\w{2,})://o;
+ }
+ else {
+ $prefix = $1 if $name =~ s/^(\w+)://;
+ }
+
+ if (defined $prefix) {
+ $providers = $self->{ PREFIX_MAP }->{ $prefix }
+ || return $self->throw(Template::Constants::ERROR_FILE,
+ "no providers for file prefix '$prefix'");
+ }
+ else {
+ $providers = $self->{ PREFIX_MAP }->{ default }
+ || $self->{ LOAD_TEMPLATES };
+ }
+
+ foreach my $provider (@$providers) {
+ ($text, $error) = $provider->load($name, $prefix);
+ next FILE unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($text) if ref $text;
+ $self->throw(Template::Constants::ERROR_FILE, $text);
+ }
+ }
+ $self->throw(Template::Constants::ERROR_FILE, "$file: not found");
+ }
+ continue {
+ $output .= $text;
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# throw($type, $info, \$output) [% THROW errtype "Error info" %]
+#
+# Throws a Template::Exception object by calling die(). This method
+# may be passed a reference to an existing Template::Exception object;
+# a single value containing an error message which is used to
+# instantiate a Template::Exception of type 'undef'; or a pair of
+# values representing the exception type and info from which a
+# Template::Exception object is instantiated. e.g.
+#
+# $context->throw($exception);
+# $context->throw("I'm sorry Dave, I can't do that");
+# $context->throw('denied', "I'm sorry Dave, I can't do that");
+#
+# An optional third parameter can be supplied in the last case which
+# is a reference to the current output buffer containing the results
+# of processing the template up to the point at which the exception
+# was thrown. The RETURN and STOP directives, for example, use this
+# to propagate output back to the user, but it can safely be ignored
+# in most cases.
+#
+# This method rides on a one-way ticket to die() oblivion. It does not
+# return in any real sense of the word, but should get caught by a
+# surrounding eval { } block (e.g. a BLOCK or TRY) and handled
+# accordingly, or returned to the caller as an uncaught exception.
+#------------------------------------------------------------------------
+
+sub throw {
+ my ($self, $error, $info, $output) = @_;
+ local $" = ', ';
+
+ # die! die! die!
+ if (UNIVERSAL::isa($error, 'Template::Exception')) {
+ die $error;
+ }
+ elsif (defined $info) {
+ die (Template::Exception->new($error, $info, $output));
+ }
+ else {
+ $error ||= '';
+ die (Template::Exception->new('undef', $error, $output));
+ }
+
+ # not reached
+}
+
+
+#------------------------------------------------------------------------
+# catch($error, \$output)
+#
+# Called by various directives after catching an error thrown via die()
+# from within an eval { } block. The first parameter contains the errror
+# which may be a sanitized reference to a Template::Exception object
+# (such as that raised by the throw() method above, a plugin object,
+# and so on) or an error message thrown via die from somewhere in user
+# code. The latter are coerced into 'undef' Template::Exception objects.
+# Like throw() above, a reference to a scalar may be passed as an
+# additional parameter to represent the current output buffer
+# localised within the eval block. As exceptions are thrown upwards
+# and outwards from nested blocks, the catch() method reconstructs the
+# correct output buffer from these fragments, storing it in the
+# exception object for passing further onwards and upwards.
+#
+# Returns a reference to a Template::Exception object..
+#------------------------------------------------------------------------
+
+sub catch {
+ my ($self, $error, $output) = @_;
+
+ if (UNIVERSAL::isa($error, 'Template::Exception')) {
+ $error->text($output) if $output;
+ return $error;
+ }
+ else {
+ return Template::Exception->new('undef', $error, $output);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# localise(\%params)
+# delocalise()
+#
+# The localise() method creates a local copy of the current stash,
+# allowing the existing state of variables to be saved and later
+# restored via delocalise().
+#
+# A reference to a hash array may be passed containing local variable
+# definitions which should be added to the cloned namespace. These
+# values persist until delocalisation.
+#------------------------------------------------------------------------
+
+sub localise {
+ my $self = shift;
+ $self->{ STASH } = $self->{ STASH }->clone(@_);
+}
+
+sub delocalise {
+ my $self = shift;
+ $self->{ STASH } = $self->{ STASH }->declone();
+}
+
+
+#------------------------------------------------------------------------
+# visit($document, $blocks)
+#
+# Each Template::Document calls the visit() method on the context
+# before processing itself. It passes a reference to the hash array
+# of named BLOCKs defined within the document, allowing them to be
+# added to the internal BLKSTACK list which is subsequently used by
+# template() to resolve templates.
+# from a provider.
+#------------------------------------------------------------------------
+
+sub visit {
+ my ($self, $document, $blocks) = @_;
+ unshift(@{ $self->{ BLKSTACK } }, $blocks)
+}
+
+
+#------------------------------------------------------------------------
+# leave()
+#
+# The leave() method is called when the document has finished
+# processing itself. This removes the entry from the BLKSTACK list
+# that was added visit() above. For persistance of BLOCK definitions,
+# the process() method (i.e. the PROCESS directive) does some extra
+# magic to copy BLOCKs into a shared hash.
+#------------------------------------------------------------------------
+
+sub leave {
+ my $self = shift;
+ shift(@{ $self->{ BLKSTACK } });
+}
+
+
+#------------------------------------------------------------------------
+# define_block($name, $block)
+#
+# Adds a new BLOCK definition to the local BLOCKS cache. $block may
+# be specified as a reference to a sub-routine or Template::Document
+# object or as text which is compiled into a template. Returns a true
+# value (the $block reference or compiled block reference) if
+# succesful or undef on failure. Call error() to retrieve the
+# relevent error message (i.e. compilation failure).
+#------------------------------------------------------------------------
+
+sub define_block {
+ my ($self, $name, $block) = @_;
+ $block = $self->template(\$block)
+ || return undef
+ unless ref $block;
+ $self->{ BLOCKS }->{ $name } = $block;
+}
+
+
+#------------------------------------------------------------------------
+# define_filter($name, $filter, $is_dynamic)
+#
+# Adds a new FILTER definition to the local FILTER_CACHE.
+#------------------------------------------------------------------------
+
+sub define_filter {
+ my ($self, $name, $filter, $is_dynamic) = @_;
+ my ($result, $error);
+ $filter = [ $filter, 1 ] if $is_dynamic;
+
+ foreach my $provider (@{ $self->{ LOAD_FILTERS } }) {
+ ($result, $error) = $provider->store($name, $filter);
+ return 1 unless $error;
+ $self->throw(&Template::Constants::ERROR_FILTER, $result)
+ if $error == &Template::Constants::STATUS_ERROR;
+ }
+ $self->throw(&Template::Constants::ERROR_FILTER,
+ "FILTER providers declined to store filter $name");
+}
+
+
+#------------------------------------------------------------------------
+# reset()
+#
+# Reset the state of the internal BLOCKS hash to clear any BLOCK
+# definitions imported via the PROCESS directive. Any original
+# BLOCKS definitions passed to the constructor will be restored.
+#------------------------------------------------------------------------
+
+sub reset {
+ my ($self, $blocks) = @_;
+ $self->{ BLKSTACK } = [ ];
+ $self->{ BLOCKS } = { %{ $self->{ INIT_BLOCKS } } };
+}
+
+
+#------------------------------------------------------------------------
+# stash()
+#
+# Simple accessor methods to return the STASH values. This is likely
+# to be called quite often so we provide a direct method rather than
+# relying on the slower AUTOLOAD.
+#------------------------------------------------------------------------
+
+sub stash {
+ return $_[0]->{ STASH };
+}
+
+
+#------------------------------------------------------------------------
+# define_vmethod($type, $name, \&sub)
+#
+# Passes $type, $name, and &sub on to stash->define_vmethod().
+#------------------------------------------------------------------------
+sub define_vmethod {
+ my $self = shift;
+ $self->stash->define_vmethod(@_);
+}
+
+
+#------------------------------------------------------------------------
+# debugging($command, @args, \%params)
+#
+# Method for controlling the debugging status of the context. The first
+# argument can be 'on' or 'off' to enable/disable debugging, 'format'
+# to define the format of the debug message, or 'msg' to generate a
+# debugging message reporting the file, line, message text, etc.,
+# according to the current debug format.
+#------------------------------------------------------------------------
+
+sub debugging {
+ my $self = shift;
+ my $hash = ref $_[-1] eq 'HASH' ? pop : { };
+ my @args = @_;
+
+# print "*** debug(@args)\n";
+ if (@args) {
+ if ($args[0] =~ /^on|1$/i) {
+ $self->{ DEBUG_DIRS } = 1;
+ shift(@args);
+ }
+ elsif ($args[0] =~ /^off|0$/i) {
+ $self->{ DEBUG_DIRS } = 0;
+ shift(@args);
+ }
+ }
+
+ if (@args) {
+ if ($args[0] =~ /^msg$/i) {
+ return unless $self->{ DEBUG_DIRS };
+ my $format = $self->{ DEBUG_FORMAT };
+ $format = $DEBUG_FORMAT unless defined $format;
+ $format =~ s/\$(\w+)/$hash->{ $1 }/ge;
+ return $format;
+ }
+ elsif ($args[0] =~ /^format$/i) {
+ $self->{ DEBUG_FORMAT } = $args[1];
+ }
+ # else ignore
+ }
+
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides pseudo-methods for read-only access to various internal
+# members. For example, templates(), plugins(), filters(),
+# eval_perl(), load_perl(), etc. These aren't called very often, or
+# may never be called at all.
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+ my $result;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ warn "no such context method/member: $method\n"
+ unless defined ($result = $self->{ uc $method });
+
+ return $result;
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# Stash may contain references back to the Context via macro closures,
+# etc. This breaks the circular references.
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+ undef $self->{ STASH };
+}
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Initialisation method called by Template::Base::new()
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+ my ($name, $item, $method, $block, $blocks);
+ my @itemlut = (
+ LOAD_TEMPLATES => 'provider',
+ LOAD_PLUGINS => 'plugins',
+ LOAD_FILTERS => 'filters'
+ );
+
+ # LOAD_TEMPLATE, LOAD_PLUGINS, LOAD_FILTERS - lists of providers
+ while (($name, $method) = splice(@itemlut, 0, 2)) {
+ $item = $config->{ $name }
+ || Template::Config->$method($config)
+ || return $self->error($Template::Config::ERROR);
+ $self->{ $name } = ref $item eq 'ARRAY' ? $item : [ $item ];
+ }
+
+ my $providers = $self->{ LOAD_TEMPLATES };
+ my $prefix_map = $self->{ PREFIX_MAP } = $config->{ PREFIX_MAP } || { };
+ while (my ($key, $val) = each %$prefix_map) {
+ $prefix_map->{ $key } = [ ref $val ? $val :
+ map { $providers->[$_] }
+ split(/\D+/, $val) ]
+ unless ref $val eq 'ARRAY';
+# print(STDERR "prefix $key => $val => [",
+# join(', ', @{ $prefix_map->{ $key } }), "]\n");
+ }
+
+ # STASH
+ $self->{ STASH } = $config->{ STASH } || do {
+ my $predefs = $config->{ VARIABLES }
+ || $config->{ PRE_DEFINE }
+ || { };
+
+ # hack to get stash to know about debug mode
+ $predefs->{ _DEBUG } = ( ($config->{ DEBUG } || 0)
+ & &Template::Constants::DEBUG_UNDEF ) ? 1 : 0
+ unless defined $predefs->{ _DEBUG };
+
+ Template::Config->stash($predefs)
+ || return $self->error($Template::Config::ERROR);
+ };
+
+ # compile any template BLOCKS specified as text
+ $blocks = $config->{ BLOCKS } || { };
+ $self->{ INIT_BLOCKS } = $self->{ BLOCKS } = {
+ map {
+ $block = $blocks->{ $_ };
+ $block = $self->template(\$block)
+ || return undef
+ unless ref $block;
+ ($_ => $block);
+ }
+ keys %$blocks
+ };
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # RECURSION - flag indicating is recursion into templates is supported
+ # EVAL_PERL - flag indicating if PERL blocks should be processed
+ # TRIM - flag to remove leading and trailing whitespace from output
+ # BLKSTACK - list of hashes of BLOCKs defined in current template(s)
+ # CONFIG - original configuration hash
+ # EXPOSE_BLOCKS - make blocks visible as pseudo-files
+ # DEBUG_FORMAT - format for generating template runtime debugging messages
+ # DEBUG - format for generating template runtime debugging messages
+
+ $self->{ RECURSION } = $config->{ RECURSION } || 0;
+ $self->{ EVAL_PERL } = $config->{ EVAL_PERL } || 0;
+ $self->{ TRIM } = $config->{ TRIM } || 0;
+ $self->{ BLKSTACK } = [ ];
+ $self->{ CONFIG } = $config;
+ $self->{ EXPOSE_BLOCKS } = defined $config->{ EXPOSE_BLOCKS }
+ ? $config->{ EXPOSE_BLOCKS }
+ : 0;
+
+ $self->{ DEBUG_FORMAT } = $config->{ DEBUG_FORMAT };
+ $self->{ DEBUG_DIRS } = ($config->{ DEBUG } || 0)
+ & Template::Constants::DEBUG_DIRS;
+ $self->{ DEBUG } = defined $config->{ DEBUG }
+ ? $config->{ DEBUG } & ( Template::Constants::DEBUG_CONTEXT
+ | Template::Constants::DEBUG_FLAGS )
+ : $DEBUG;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the context object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Context] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( RECURSION EVAL_PERL TRIM )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+ foreach my $pname (qw( LOAD_TEMPLATES LOAD_PLUGINS LOAD_FILTERS )) {
+ my $provtext = "[\n";
+ foreach my $prov (@{ $self->{ $pname } }) {
+ $provtext .= $prov->_dump();
+# $provtext .= ",\n";
+ }
+ $provtext =~ s/\n/\n /g;
+ $provtext =~ s/\s+$//;
+ $provtext .= ",\n ]";
+ $output .= sprintf($format, $pname, $provtext);
+ }
+ $output .= sprintf($format, STASH => $self->{ STASH }->_dump());
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Context - Runtime context in which templates are processed
+
+=head1 SYNOPSIS
+
+ use Template::Context;
+
+ # constructor
+ $context = Template::Context->new(\%config)
+ || die $Template::Context::ERROR;
+
+ # fetch (load and compile) a template
+ $template = $context->template($template_name);
+
+ # fetch (load and instantiate) a plugin object
+ $plugin = $context->plugin($name, \@args);
+
+ # fetch (return or create) a filter subroutine
+ $filter = $context->filter($name, \@args, $alias);
+
+ # process/include a template, errors are thrown via die()
+ $output = $context->process($template, \%vars);
+ $output = $context->include($template, \%vars);
+
+ # raise an exception via die()
+ $context->throw($error_type, $error_message, \$output_buffer);
+
+ # catch an exception, clean it up and fix output buffer
+ $exception = $context->catch($exception, \$output_buffer);
+
+ # save/restore the stash to effect variable localisation
+ $new_stash = $context->localise(\%vars);
+ $old_stash = $context->delocalise();
+
+ # add new BLOCK or FILTER definitions
+ $context->define_block($name, $block);
+ $context->define_filter($name, \&filtersub, $is_dynamic);
+
+ # reset context, clearing any imported BLOCK definitions
+ $context->reset();
+
+ # methods for accessing internal items
+ $stash = $context->stash();
+ $tflag = $context->trim();
+ $epflag = $context->eval_perl();
+ $providers = $context->templates();
+ $providers = $context->plugins();
+ $providers = $context->filters();
+ ...
+
+=head1 DESCRIPTION
+
+The Template::Context module defines an object class for representing
+a runtime context in which templates are processed. It provides an
+interface to the fundamental operations of the Template Toolkit
+processing engine through which compiled templates (i.e. Perl code
+constructed from the template source) can process templates, load
+plugins and filters, raise exceptions and so on.
+
+A default Template::Context object is created by the Template module.
+Any Template::Context options may be passed to the Template new()
+constructor method and will be forwarded to the Template::Context
+constructor.
+
+ use Template;
+
+ my $template = Template->new({
+ TRIM => 1,
+ EVAL_PERL => 1,
+ BLOCKS => {
+ header => 'This is the header',
+ footer => 'This is the footer',
+ },
+ });
+
+Similarly, the Template::Context constructor will forward all configuration
+parameters onto other default objects (e.g. Template::Provider, Template::Plugins,
+Template::Filters, etc.) that it may need to instantiate.
+
+ $context = Template::Context->new({
+ INCLUDE_PATH => '/home/abw/templates', # provider option
+ TAG_STYLE => 'html', # parser option
+ });
+
+A Template::Context object (or subclass/derivative) can be explicitly
+instantiated and passed to the Template new() constructor method as
+the CONTEXT item.
+
+ use Template;
+ use Template::Context;
+
+ my $context = Template::Context->new({ TRIM => 1 });
+ my $template = Template->new({ CONTEXT => $context });
+
+The Template module uses the Template::Config context() factory method
+to create a default context object when required. The
+$Template::Config::CONTEXT package variable may be set to specify an
+alternate context module. This will be loaded automatically and its
+new() constructor method called by the context() factory method when
+a default context object is required.
+
+ use Template;
+
+ $Template::Config::CONTEXT = 'MyOrg::Template::Context';
+
+ my $template = Template->new({
+ EVAL_PERL => 1,
+ EXTRA_MAGIC => 'red hot', # your extra config items
+ ...
+ });
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+The new() constructor method is called to instantiate a Template::Context
+object. Configuration parameters may be specified as a HASH reference or
+as a list of (name =E<gt> value) pairs.
+
+ my $context = Template::Context->new({
+ INCLUDE_PATH => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $context = Template::Context->new( EVAL_PERL => 1 );
+
+The new() method returns a Template::Context object (or sub-class) or
+undef on error. In the latter case, a relevant error message can be
+retrieved by the error() class method or directly from the
+$Template::Context::ERROR package variable.
+
+ my $context = Template::Context->new(\%config)
+ || die Template::Context->error();
+
+ my $context = Template::Context->new(\%config)
+ || die $Template::Context::ERROR;
+
+The following configuration items may be specified.
+
+=over 4
+
+
+=item VARIABLES, PRE_DEFINE
+
+The VARIABLES option (or PRE_DEFINE - they're equivalent) can be used
+to specify a hash array of template variables that should be used to
+pre-initialise the stash when it is created. These items are ignored
+if the STASH item is defined.
+
+ my $context = Template::Context->new({
+ VARIABLES => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+or
+
+ my $context = Template::Context->new({
+ PRE_DEFINE => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+
+
+
+
+=item BLOCKS
+
+The BLOCKS option can be used to pre-define a default set of template
+blocks. These should be specified as a reference to a hash array
+mapping template names to template text, subroutines or Template::Document
+objects.
+
+ my $context = Template::Context->new({
+ BLOCKS => {
+ header => 'The Header. [% title %]',
+ footer => sub { return $some_output_text },
+ another => Template::Document->new({ ... }),
+ },
+ });
+
+
+
+
+
+=item TRIM
+
+The TRIM option can be set to have any leading and trailing whitespace
+automatically removed from the output of all template files and BLOCKs.
+
+By example, the following BLOCK definition
+
+ [% BLOCK foo %]
+ Line 1 of foo
+ [% END %]
+
+will be processed is as "\nLine 1 of foo\n". When INCLUDEd, the surrounding
+newlines will also be introduced.
+
+ before
+ [% INCLUDE foo %]
+ after
+
+output:
+ before
+
+ Line 1 of foo
+
+ after
+
+With the TRIM option set to any true value, the leading and trailing
+newlines (which count as whitespace) will be removed from the output
+of the BLOCK.
+
+ before
+ Line 1 of foo
+ after
+
+The TRIM option is disabled (0) by default.
+
+
+
+
+
+
+=item EVAL_PERL
+
+This flag is used to indicate if PERL and/or RAWPERL blocks should be
+evaluated. By default, it is disabled and any PERL or RAWPERL blocks
+encountered will raise exceptions of type 'perl' with the message
+'EVAL_PERL not set'. Note however that any RAWPERL blocks should
+always contain valid Perl code, regardless of the EVAL_PERL flag. The
+parser will fail to compile templates that contain invalid Perl code
+in RAWPERL blocks and will throw a 'file' exception.
+
+When using compiled templates (see
+L<COMPILE_EXT|Template::Manual::Config/Caching_and_Compiling_Options> and
+L<COMPILE_DIR|Template::Manual::Config/Caching_and_Compiling_Options>),
+the EVAL_PERL has an affect when the template is compiled, and again
+when the templates is subsequently processed, possibly in a different
+context to the one that compiled it.
+
+If the EVAL_PERL is set when a template is compiled, then all PERL and
+RAWPERL blocks will be included in the compiled template. If the
+EVAL_PERL option isn't set, then Perl code will be generated which
+B<always> throws a 'perl' exception with the message 'EVAL_PERL not
+set' B<whenever> the compiled template code is run.
+
+Thus, you must have EVAL_PERL set if you want your compiled templates
+to include PERL and RAWPERL blocks.
+
+At some point in the future, using a different invocation of the
+Template Toolkit, you may come to process such a pre-compiled
+template. Assuming the EVAL_PERL option was set at the time the
+template was compiled, then the output of any RAWPERL blocks will be
+included in the compiled template and will get executed when the
+template is processed. This will happen regardless of the runtime
+EVAL_PERL status.
+
+Regular PERL blocks are a little more cautious, however. If the
+EVAL_PERL flag isn't set for the I<current> context, that is, the
+one which is trying to process it, then it will throw the familiar 'perl'
+exception with the message, 'EVAL_PERL not set'.
+
+Thus you can compile templates to include PERL blocks, but optionally
+disable them when you process them later. Note however that it is
+possible for a PERL block to contain a Perl "BEGIN { # some code }"
+block which will always get run regardless of the runtime EVAL_PERL
+status. Thus, if you set EVAL_PERL when compiling templates, it is
+assumed that you trust the templates to Do The Right Thing. Otherwise
+you must accept the fact that there's no bulletproof way to prevent
+any included code from trampling around in the living room of the
+runtime environment, making a real nuisance of itself if it really
+wants to. If you don't like the idea of such uninvited guests causing
+a bother, then you can accept the default and keep EVAL_PERL disabled.
+
+
+
+
+
+
+
+=item RECURSION
+
+The template processor will raise a file exception if it detects
+direct or indirect recursion into a template. Setting this option to
+any true value will allow templates to include each other recursively.
+
+
+
+=item LOAD_TEMPLATES
+
+The LOAD_TEMPLATE option can be used to provide a reference to a list
+of Template::Provider objects or sub-classes thereof which will take
+responsibility for loading and compiling templates.
+
+ my $context = Template::Context->new({
+ LOAD_TEMPLATES => [
+ MyOrg::Template::Provider->new({ ... }),
+ Template::Provider->new({ ... }),
+ ],
+ });
+
+When a PROCESS, INCLUDE or WRAPPER directive is encountered, the named
+template may refer to a locally defined BLOCK or a file relative to
+the INCLUDE_PATH (or an absolute or relative path if the appropriate
+ABSOLUTE or RELATIVE options are set). If a BLOCK definition can't be
+found (see the Template::Context template() method for a discussion of
+BLOCK locality) then each of the LOAD_TEMPLATES provider objects is
+queried in turn via the fetch() method to see if it can supply the
+required template. Each provider can return a compiled template, an
+error, or decline to service the request in which case the
+responsibility is passed to the next provider. If none of the
+providers can service the request then a 'not found' error is
+returned. The same basic provider mechanism is also used for the
+INSERT directive but it bypasses any BLOCK definitions and doesn't
+attempt is to parse or process the contents of the template file.
+
+This is an implementation of the 'Chain of Responsibility'
+design pattern as described in
+"Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John
+Vlissides), Addision-Wesley, ISBN 0-201-63361-2, page 223
+.
+
+If LOAD_TEMPLATES is undefined, a single default provider will be
+instantiated using the current configuration parameters. For example,
+the Template::Provider INCLUDE_PATH option can be specified in the Template::Context configuration and will be correctly passed to the provider's
+constructor method.
+
+ my $context = Template::Context->new({
+ INCLUDE_PATH => '/here:/there',
+ });
+
+
+
+
+
+=item LOAD_PLUGINS
+
+The LOAD_PLUGINS options can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for loading and instantiating template plugin objects. The
+Template::Content plugin() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and filter() methods.
+
+ my $context = Template::Context->new({
+ LOAD_PLUGINS => [
+ MyOrg::Template::Plugins->new({ ... }),
+ Template::Plugins->new({ ... }),
+ ],
+ });
+
+By default, a single Template::Plugins object is created using the
+current configuration hash. Configuration items destined for the
+Template::Plugins constructor may be added to the Template::Context
+constructor.
+
+ my $context = Template::Context->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugins',
+ LOAD_PERL => 1,
+ });
+
+
+
+
+
+=item LOAD_FILTERS
+
+The LOAD_FILTERS option can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for returning and/or creating filter subroutines. The
+Template::Context filter() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and plugin() methods.
+
+ my $context = Template::Context->new({
+ LOAD_FILTERS => [
+ MyTemplate::Filters->new(),
+ Template::Filters->new(),
+ ],
+ });
+
+By default, a single Template::Filters object is created for the
+LOAD_FILTERS list.
+
+
+
+=item STASH
+
+A reference to a Template::Stash object or sub-class which will take
+responsibility for managing template variables.
+
+ my $stash = MyOrg::Template::Stash->new({ ... });
+ my $context = Template::Context->new({
+ STASH => $stash,
+ });
+
+If unspecified, a default stash object is created using the VARIABLES
+configuration item to initialise the stash variables. These may also
+be specified as the PRE_DEFINE option for backwards compatibility with
+version 1.
+
+ my $context = Template::Context->new({
+ VARIABLES => {
+ id => 'abw',
+ name => 'Andy Wardley',
+ },
+ };
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable various debugging features
+of the Template::Context module.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_CONTEXT | DEBUG_DIRS,
+ });
+
+The DEBUG value can include any of the following. Multiple values
+should be combined using the logical OR operator, '|'.
+
+=over 4
+
+=item DEBUG_CONTEXT
+
+Enables general debugging messages for the
+L<Template::Context|Template::Context> module.
+
+=item DEBUG_DIRS
+
+This option causes the Template Toolkit to generate comments
+indicating the source file, line and original text of each directive
+in the template. These comments are embedded in the template output
+using the format defined in the DEBUG_FORMAT configuration item, or a
+simple default format if unspecified.
+
+For example, the following template fragment:
+
+
+ Hello World
+
+would generate this output:
+
+ ## input text line 1 : ##
+ Hello
+ ## input text line 2 : World ##
+ World
+
+
+=back
+
+
+
+
+
+=back
+
+=head2 template($name)
+
+Returns a compiled template by querying each of the LOAD_TEMPLATES providers
+(instances of Template::Provider, or sub-class) in turn.
+
+ $template = $context->template('header');
+
+On error, a Template::Exception object of type 'file' is thrown via
+die(). This can be caught by enclosing the call to template() in an
+eval block and examining $@.
+
+ eval {
+ $template = $context->template('header');
+ };
+ if ($@) {
+ print "failed to fetch template: $@\n";
+ }
+
+=head2 plugin($name, \@args)
+
+Instantiates a plugin object by querying each of the LOAD_PLUGINS
+providers. The default LOAD_PLUGINS provider is a Template::Plugins
+object which attempts to load plugin modules, according the various
+configuration items such as PLUGIN_BASE, LOAD_PERL, etc., and then
+instantiate an object via new(). A reference to a list of constructor
+arguments may be passed as the second parameter. These are forwarded
+to the plugin constructor.
+
+Returns a reference to a plugin (which is generally an object, but
+doesn't have to be). Errors are thrown as Template::Exception objects
+of type 'plugin'.
+
+ $plugin = $context->plugin('DBI', 'dbi:msql:mydbname');
+
+=head2 filter($name, \@args, $alias)
+
+Instantiates a filter subroutine by querying the LOAD_FILTERS providers.
+The default LOAD_FILTERS providers is a Template::Filters object.
+Additional arguments may be passed by list reference along with an
+optional alias under which the filter will be cached for subsequent
+use. The filter is cached under its own $name if $alias is undefined.
+Subsequent calls to filter($name) will return the cached entry, if
+defined. Specifying arguments bypasses the caching mechanism and
+always creates a new filter. Errors are thrown as Template::Exception
+objects of typre 'filter'.
+
+ # static filter (no args)
+ $filter = $context->filter('html');
+
+ # dynamic filter (args) aliased to 'padright'
+ $filter = $context->filter('format', '%60s', 'padright');
+
+ # retrieve previous filter via 'padright' alias
+ $filter = $context->filter('padright');
+
+=head2 process($template, \%vars)
+
+Processes a template named or referenced by the first parameter and returns
+the output generated. An optional reference to a hash array may be passed
+as the second parameter, containing variable definitions which will be set
+before the template is processed. The template is processed in the current
+context, with no localisation of variables performed. Errors are thrown
+as Template::Exception objects via die().
+
+ $output = $context->process('header', { title => 'Hello World' });
+
+=head2 include($template, \%vars)
+
+Similar to process() above, but using localised variables. Changes made to
+any variables will only persist until the include() method completes.
+
+ $output = $context->include('header', { title => 'Hello World' });
+
+=head2 throw($error_type, $error_message, \$output)
+
+Raises an exception in the form of a Template::Exception object by
+calling die(). This method may be passed a reference to an existing
+Template::Exception object; a single value containing an error message
+which is used to instantiate a Template::Exception of type 'undef'; or
+a pair of values representing the exception type and info from which a
+Template::Exception object is instantiated. e.g.
+
+ $context->throw($exception);
+ $context->throw("I'm sorry Dave, I can't do that");
+ $context->throw('denied', "I'm sorry Dave, I can't do that");
+
+The optional third parameter may be a reference to the current output
+buffer. This is then stored in the exception object when created,
+allowing the catcher to examine and use the output up to the point at
+which the exception was raised.
+
+ $output .= 'blah blah blah';
+ $output .= 'more rhubarb';
+ $context->throw('yack', 'Too much yacking', \$output);
+
+=head2 catch($exception, \$output)
+
+Catches an exception thrown, either as a reference to a
+Template::Exception object or some other value. In the latter case,
+the error string is promoted to a Template::Exception object of
+'undef' type. This method also accepts a reference to the current
+output buffer which is passed to the Template::Exception constructor,
+or is appended to the output buffer stored in an existing
+Template::Exception object, if unique (i.e. not the same reference).
+By this process, the correct state of the output buffer can be
+reconstructed for simple or nested throws.
+
+=head2 define_block($name, $block)
+
+Adds a new block definition to the internal BLOCKS cache. The first
+argument should contain the name of the block and the second a reference
+to a Template::Document object or template sub-routine, or template text
+which is automatically compiled into a template sub-routine. Returns
+a true value (the sub-routine or Template::Document reference) on
+success or undef on failure. The relevant error message can be
+retrieved by calling the error() method.
+
+=head2 define_filter($name, \&filter, $is_dynamic)
+
+Adds a new filter definition by calling the store() method on each of
+the LOAD_FILTERS providers until accepted (in the usual case, this is
+accepted straight away by the one and only Template::Filters
+provider). The first argument should contain the name of the filter
+and the second a reference to a filter subroutine. The optional
+third argument can be set to any true value to indicate that the
+subroutine is a dynamic filter factory. Returns a true value or
+throws a 'filter' exception on error.
+
+=head2 localise(\%vars)
+
+Clones the stash to create a context with localised variables. Returns a
+reference to the newly cloned stash object which is also stored
+internally.
+
+ $stash = $context->localise();
+
+=head2 delocalise()
+
+Restore the stash to its state prior to localisation.
+
+ $stash = $context->delocalise();
+
+=head2 visit(\%blocks)
+
+This method is called by Template::Document objects immediately before
+they process their content. It is called to register any local BLOCK
+definitions with the context object so that they may be subsequently
+delivered on request.
+
+=head2 leave()
+
+Compliment to visit(), above. Called by Template::Document objects
+immediately after they process their content.
+
+=head2 reset()
+
+Clears the local BLOCKS cache of any BLOCK definitions. Any initial set of
+BLOCKS specified as a configuration item to the constructor will be reinstated.
+
+=head2 AUTOLOAD
+
+An AUTOLOAD method provides access to context configuration items.
+
+ $stash = $context->stash();
+ $tflag = $context->trim();
+ $epflag = $context->eval_perl();
+ ...
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.89, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Document|Template::Document>, L<Template::Exception|Template::Exception>, L<Template::Filters|Template::Filters>, L<Template::Plugins|Template::Plugins>, L<Template::Provider|Template::Provider>, L<Template::Service|Template::Service>, L<Template::Stash|Template::Stash>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Directive.pm b/lib/Template/Directive.pm
new file mode 100644
index 0000000..c3f86a9
--- /dev/null
+++ b/lib/Template/Directive.pm
@@ -0,0 +1,1004 @@
+#================================================================= -*-Perl-*-
+#
+# Template::Directive
+#
+# DESCRIPTION
+# Factory module for constructing templates from Perl code.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# WARNING
+# Much of this module is hairy, even furry in places. It needs
+# a lot of tidying up and may even be moved into a different place
+# altogether. The generator code is often inefficient, particulary in
+# being very anal about pretty-printing the Perl code all neatly, but
+# at the moment, that's still high priority for the sake of easier
+# debugging.
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Directive.pm,v 2.18 2003/10/08 09:34:41 abw Exp $
+#
+#============================================================================
+
+package Template::Directive;
+
+require 5.004;
+
+use strict;
+use Template::Base;
+use Template::Constants;
+use Template::Exception;
+
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $PRETTY $WHILE_MAX $OUTPUT );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.18 $ =~ /(\d+)\.(\d+)/);
+
+$WHILE_MAX = 1000 unless defined $WHILE_MAX;
+$PRETTY = 0 unless defined $PRETTY;
+$OUTPUT = '$output .= ';
+
+
+sub _init {
+ my ($self, $config) = @_;
+ $self->{ NAMESPACE } = $config->{ NAMESPACE };
+ return $self;
+}
+
+
+sub pad {
+ my ($text, $pad) = @_;
+ $pad = ' ' x ($pad * 4);
+ $text =~ s/^(?!#line)/$pad/gm;
+ $text;
+}
+
+#========================================================================
+# FACTORY METHODS
+#
+# These methods are called by the parser to construct directive instances.
+#========================================================================
+
+#------------------------------------------------------------------------
+# template($block)
+#------------------------------------------------------------------------
+
+sub template {
+ my ($class, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return "sub { return '' }" unless $block =~ /\S/;
+
+ return <<EOF;
+sub {
+ my \$context = shift || die "template sub called without context\\n";
+ my \$stash = \$context->stash;
+ my \$output = '';
+ my \$error;
+
+ eval { BLOCK: {
+$block
+ } };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error unless \$error->type eq 'return';
+ }
+
+ return \$output;
+}
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# anon_block($block) [% BLOCK %] ... [% END %]
+#------------------------------------------------------------------------
+
+sub anon_block {
+ my ($class, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return <<EOF;
+
+# BLOCK
+$OUTPUT do {
+ my \$output = '';
+ my \$error;
+
+ eval { BLOCK: {
+$block
+ } };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error unless \$error->type eq 'return';
+ }
+
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# block($blocktext)
+#------------------------------------------------------------------------
+
+sub block {
+ my ($class, $block) = @_;
+ return join("\n", @{ $block || [] });
+}
+
+
+#------------------------------------------------------------------------
+# textblock($text)
+#------------------------------------------------------------------------
+
+sub textblock {
+ my ($class, $text) = @_;
+ return "$OUTPUT " . &text($class, $text) . ';';
+}
+
+
+#------------------------------------------------------------------------
+# text($text)
+#------------------------------------------------------------------------
+
+sub text {
+ my ($class, $text) = @_;
+ for ($text) {
+ s/(["\$\@\\])/\\$1/g;
+ s/\n/\\n/g;
+ }
+ return '"' . $text . '"';
+}
+
+
+#------------------------------------------------------------------------
+# quoted(\@items) "foo$bar"
+#------------------------------------------------------------------------
+
+sub quoted {
+ my ($class, $items) = @_;
+ return '' unless @$items;
+ return ("('' . " . $items->[0] . ')') if scalar @$items == 1;
+ return '(' . join(' . ', @$items) . ')';
+# my $r = '(' . join(' . ', @$items) . ' . "")';
+# print STDERR "[$r]\n";
+# return $r;
+}
+
+
+#------------------------------------------------------------------------
+# ident(\@ident) foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub ident {
+ my ($class, $ident) = @_;
+ return "''" unless @$ident;
+ my $ns;
+
+ # does the first element of the identifier have a NAMESPACE
+ # handler defined?
+ if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) {
+ my $key = $ident->[0];
+ $key =~ s/^'(.+)'$/$1/s;
+ if ($ns = $ns->{ $key }) {
+ return $ns->ident($ident);
+ }
+ }
+
+ if (scalar @$ident <= 2 && ! $ident->[1]) {
+ $ident = $ident->[0];
+ }
+ else {
+ $ident = '[' . join(', ', @$ident) . ']';
+ }
+ return "\$stash->get($ident)";
+}
+
+#------------------------------------------------------------------------
+# identref(\@ident) \foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub identref {
+ my ($class, $ident) = @_;
+ return "''" unless @$ident;
+ if (scalar @$ident <= 2 && ! $ident->[1]) {
+ $ident = $ident->[0];
+ }
+ else {
+ $ident = '[' . join(', ', @$ident) . ']';
+ }
+ return "\$stash->getref($ident)";
+}
+
+
+#------------------------------------------------------------------------
+# assign(\@ident, $value, $default) foo = bar
+#------------------------------------------------------------------------
+
+sub assign {
+ my ($class, $var, $val, $default) = @_;
+
+ if (ref $var) {
+ if (scalar @$var == 2 && ! $var->[1]) {
+ $var = $var->[0];
+ }
+ else {
+ $var = '[' . join(', ', @$var) . ']';
+ }
+ }
+ $val .= ', 1' if $default;
+ return "\$stash->set($var, $val)";
+}
+
+
+#------------------------------------------------------------------------
+# args(\@args) foo, bar, baz = qux
+#------------------------------------------------------------------------
+
+sub args {
+ my ($class, $args) = @_;
+ my $hash = shift @$args;
+ push(@$args, '{ ' . join(', ', @$hash) . ' }')
+ if @$hash;
+
+ return '0' unless @$args;
+ return '[ ' . join(', ', @$args) . ' ]';
+}
+
+#------------------------------------------------------------------------
+# filenames(\@names)
+#------------------------------------------------------------------------
+
+sub filenames {
+ my ($class, $names) = @_;
+ if (@$names > 1) {
+ $names = '[ ' . join(', ', @$names) . ' ]';
+ }
+ else {
+ $names = shift @$names;
+ }
+ return $names;
+}
+
+
+#------------------------------------------------------------------------
+# get($expr) [% foo %]
+#------------------------------------------------------------------------
+
+sub get {
+ my ($class, $expr) = @_;
+ return "$OUTPUT $expr;";
+}
+
+
+#------------------------------------------------------------------------
+# call($expr) [% CALL bar %]
+#------------------------------------------------------------------------
+
+sub call {
+ my ($class, $expr) = @_;
+ $expr .= ';';
+ return $expr;
+}
+
+
+#------------------------------------------------------------------------
+# set(\@setlist) [% foo = bar, baz = qux %]
+#------------------------------------------------------------------------
+
+sub set {
+ my ($class, $setlist) = @_;
+ my $output;
+ while (my ($var, $val) = splice(@$setlist, 0, 2)) {
+ $output .= &assign($class, $var, $val) . ";\n";
+ }
+ chomp $output;
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# default(\@setlist) [% DEFAULT foo = bar, baz = qux %]
+#------------------------------------------------------------------------
+
+sub default {
+ my ($class, $setlist) = @_;
+ my $output;
+ while (my ($var, $val) = splice(@$setlist, 0, 2)) {
+ $output .= &assign($class, $var, $val, 1) . ";\n";
+ }
+ chomp $output;
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# insert(\@nameargs) [% INSERT file %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub insert {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ $file = $class->filenames($file);
+ return "$OUTPUT \$context->insert($file);";
+}
+
+
+#------------------------------------------------------------------------
+# include(\@nameargs) [% INCLUDE template foo = bar %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub include {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $file = $class->filenames($file);
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->include($file);";
+}
+
+
+#------------------------------------------------------------------------
+# process(\@nameargs) [% PROCESS template foo = bar %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub process {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $file = $class->filenames($file);
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->process($file);";
+}
+
+
+#------------------------------------------------------------------------
+# if($expr, $block, $else) [% IF foo < bar %]
+# ...
+# [% ELSE %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub if {
+ my ($class, $expr, $block, $else) = @_;
+ my @else = $else ? @$else : ();
+ $else = pop @else;
+ $block = pad($block, 1) if $PRETTY;
+
+ my $output = "if ($expr) {\n$block\n}\n";
+
+ foreach my $elsif (@else) {
+ ($expr, $block) = @$elsif;
+ $block = pad($block, 1) if $PRETTY;
+ $output .= "elsif ($expr) {\n$block\n}\n";
+ }
+ if (defined $else) {
+ $else = pad($else, 1) if $PRETTY;
+ $output .= "else {\n$else\n}\n";
+ }
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# foreach($target, $list, $args, $block) [% FOREACH x = [ foo bar ] %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub foreach {
+ my ($class, $target, $list, $args, $block) = @_;
+ $args = shift @$args;
+ $args = @$args ? ', { ' . join(', ', @$args) . ' }' : '';
+
+ my ($loop_save, $loop_set, $loop_restore, $setiter);
+ if ($target) {
+ $loop_save = 'eval { $oldloop = ' . &ident($class, ["'loop'"]) . ' }';
+ $loop_set = "\$stash->{'$target'} = \$value";
+ $loop_restore = "\$stash->set('loop', \$oldloop)";
+ }
+ else {
+ $loop_save = '$stash = $context->localise()';
+# $loop_set = "\$stash->set('import', \$value) "
+# . "if ref \$value eq 'HASH'";
+ $loop_set = "\$stash->get(['import', [\$value]]) "
+ . "if ref \$value eq 'HASH'";
+ $loop_restore = '$stash = $context->delocalise()';
+ }
+ $block = pad($block, 3) if $PRETTY;
+
+ return <<EOF;
+
+# FOREACH
+do {
+ my (\$value, \$error, \$oldloop);
+ my \$list = $list;
+
+ unless (UNIVERSAL::isa(\$list, 'Template::Iterator')) {
+ \$list = Template::Config->iterator(\$list)
+ || die \$Template::Config::ERROR, "\\n";
+ }
+
+ (\$value, \$error) = \$list->get_first();
+ $loop_save;
+ \$stash->set('loop', \$list);
+ eval {
+LOOP: while (! \$error) {
+ $loop_set;
+$block;
+ (\$value, \$error) = \$list->get_next();
+ }
+ };
+ $loop_restore;
+ die \$@ if \$@;
+ \$error = 0 if \$error && \$error eq Template::Constants::STATUS_DONE;
+ die \$error if \$error;
+};
+EOF
+}
+
+#------------------------------------------------------------------------
+# next() [% NEXT %]
+#
+# Next iteration of a FOREACH loop (experimental)
+#------------------------------------------------------------------------
+
+sub next {
+ return <<EOF;
+(\$value, \$error) = \$list->get_next();
+next LOOP;
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# wrapper(\@nameargs, $block) [% WRAPPER template foo = bar %]
+# # => [ [$file,...], \@args ]
+#------------------------------------------------------------------------
+
+sub wrapper {
+ my ($class, $nameargs, $block) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+
+ local $" = ', ';
+# print STDERR "wrapper([@$file], { @$hash })\n";
+
+ return $class->multi_wrapper($file, $hash, $block)
+ if @$file > 1;
+ $file = shift @$file;
+
+ $block = pad($block, 1) if $PRETTY;
+ push(@$hash, "'content'", '$output');
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+
+ return <<EOF;
+
+# WRAPPER
+$OUTPUT do {
+ my \$output = '';
+$block
+ \$context->include($file);
+};
+EOF
+}
+
+
+sub multi_wrapper {
+ my ($class, $file, $hash, $block) = @_;
+ $block = pad($block, 1) if $PRETTY;
+
+ push(@$hash, "'content'", '$output');
+ $hash = @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+
+ $file = join(', ', reverse @$file);
+# print STDERR "multi wrapper: $file\n";
+
+ return <<EOF;
+
+# WRAPPER
+$OUTPUT do {
+ my \$output = '';
+$block
+ foreach ($file) {
+ \$output = \$context->include(\$_$hash);
+ }
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# while($expr, $block) [% WHILE x < 10 %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub while {
+ my ($class, $expr, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return <<EOF;
+
+# WHILE
+do {
+ my \$failsafe = $WHILE_MAX;
+LOOP:
+ while (--\$failsafe && ($expr)) {
+$block
+ }
+ die "WHILE loop terminated (> $WHILE_MAX iterations)\\n"
+ unless \$failsafe;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# switch($expr, \@case) [% SWITCH %]
+# [% CASE foo %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub switch {
+ my ($class, $expr, $case) = @_;
+ my @case = @$case;
+ my ($match, $block, $default);
+ my $caseblock = '';
+
+ $default = pop @case;
+
+ foreach $case (@case) {
+ $match = $case->[0];
+ $block = $case->[1];
+ $block = pad($block, 1) if $PRETTY;
+ $caseblock .= <<EOF;
+\$match = $match;
+\$match = [ \$match ] unless ref \$match eq 'ARRAY';
+if (grep(/^\$result\$/, \@\$match)) {
+$block
+ last SWITCH;
+}
+EOF
+ }
+
+ $caseblock .= $default
+ if defined $default;
+ $caseblock = pad($caseblock, 2) if $PRETTY;
+
+return <<EOF;
+
+# SWITCH
+do {
+ my \$result = $expr;
+ my \$match;
+ SWITCH: {
+$caseblock
+ }
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# try($block, \@catch) [% TRY %]
+# ...
+# [% CATCH %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub try {
+ my ($class, $block, $catch) = @_;
+ my @catch = @$catch;
+ my ($match, $mblock, $default, $final, $n);
+ my $catchblock = '';
+ my $handlers = [];
+
+ $block = pad($block, 2) if $PRETTY;
+ $final = pop @catch;
+ $final = "# FINAL\n" . ($final ? "$final\n" : '')
+ . 'die $error if $error;' . "\n" . '$output;';
+ $final = pad($final, 1) if $PRETTY;
+
+ $n = 0;
+ foreach $catch (@catch) {
+ $match = $catch->[0] || do {
+ $default ||= $catch->[1];
+ next;
+ };
+ $mblock = $catch->[1];
+ $mblock = pad($mblock, 1) if $PRETTY;
+ push(@$handlers, "'$match'");
+ $catchblock .= $n++
+ ? "elsif (\$handler eq '$match') {\n$mblock\n}\n"
+ : "if (\$handler eq '$match') {\n$mblock\n}\n";
+ }
+ $catchblock .= "\$error = 0;";
+ $catchblock = pad($catchblock, 3) if $PRETTY;
+ if ($default) {
+ $default = pad($default, 1) if $PRETTY;
+ $default = "else {\n # DEFAULT\n$default\n \$error = '';\n}";
+ }
+ else {
+ $default = '# NO DEFAULT';
+ }
+ $default = pad($default, 2) if $PRETTY;
+
+ $handlers = join(', ', @$handlers);
+return <<EOF;
+
+# TRY
+$OUTPUT do {
+ my \$output = '';
+ my (\$error, \$handler);
+ eval {
+$block
+ };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error if \$error->type =~ /^return|stop\$/;
+ \$stash->set('error', \$error);
+ \$stash->set('e', \$error);
+ if (defined (\$handler = \$error->select_handler($handlers))) {
+$catchblock
+ }
+$default
+ }
+$final
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# throw(\@nameargs) [% THROW foo "bar error" %]
+# # => [ [$type], \@args ]
+#------------------------------------------------------------------------
+
+sub throw {
+ my ($class, $nameargs) = @_;
+ my ($type, $args) = @$nameargs;
+ my $hash = shift(@$args);
+ my $info = shift(@$args);
+ $type = shift @$type; # uses same parser production as INCLUDE
+ # etc., which allow multiple names
+ # e.g. INCLUDE foo+bar+baz
+
+ if (! $info) {
+ $args = "$type, undef";
+ }
+ elsif (@$hash || @$args) {
+ local $" = ', ';
+ my $i = 0;
+ $args = "$type, { args => [ "
+ . join(', ', $info, @$args)
+ . ' ], '
+ . join(', ',
+ (map { "'" . $i++ . "' => $_" } ($info, @$args)),
+ @$hash)
+ . ' }';
+ }
+ else {
+ $args = "$type, $info";
+ }
+
+ return "\$context->throw($args, \\\$output);";
+}
+
+
+#------------------------------------------------------------------------
+# clear() [% CLEAR %]
+#
+# NOTE: this is redundant, being hard-coded (for now) into Parser.yp
+#------------------------------------------------------------------------
+
+sub clear {
+ return "\$output = '';";
+}
+
+#------------------------------------------------------------------------
+# break() [% BREAK %]
+#
+# NOTE: this is redundant, being hard-coded (for now) into Parser.yp
+#------------------------------------------------------------------------
+
+sub break {
+ return 'last LOOP;';
+}
+
+#------------------------------------------------------------------------
+# return() [% RETURN %]
+#------------------------------------------------------------------------
+
+sub return {
+ return "\$context->throw('return', '', \\\$output);";
+}
+
+#------------------------------------------------------------------------
+# stop() [% STOP %]
+#------------------------------------------------------------------------
+
+sub stop {
+ return "\$context->throw('stop', '', \\\$output);";
+}
+
+
+#------------------------------------------------------------------------
+# use(\@lnameargs) [% USE alias = plugin(args) %]
+# # => [ [$file, ...], \@args, $alias ]
+#------------------------------------------------------------------------
+
+sub use {
+ my ($class, $lnameargs) = @_;
+ my ($file, $args, $alias) = @$lnameargs;
+ $file = shift @$file; # same production rule as INCLUDE
+ $alias ||= $file;
+ $args = &args($class, $args);
+ $file .= ", $args" if $args;
+# my $set = &assign($class, $alias, '$plugin');
+ return "# USE\n"
+ . "\$stash->set($alias,\n"
+ . " \$context->plugin($file));";
+}
+
+#------------------------------------------------------------------------
+# view(\@nameargs, $block) [% VIEW name args %]
+# # => [ [$file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub view {
+ my ($class, $nameargs, $block, $defblocks) = @_;
+ my ($name, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $name = shift @$name; # same production rule as INCLUDE
+ $block = pad($block, 1) if $PRETTY;
+
+ if (%$defblocks) {
+ $defblocks = join(",\n", map { "'$_' => $defblocks->{ $_ }" }
+ keys %$defblocks);
+ $defblocks = pad($defblocks, 1) if $PRETTY;
+ $defblocks = "{\n$defblocks\n}";
+ push(@$hash, "'blocks'", $defblocks);
+ }
+ $hash = @$hash ? '{ ' . join(', ', @$hash) . ' }' : '';
+
+ return <<EOF;
+# VIEW
+do {
+ my \$output = '';
+ my \$oldv = \$stash->get('view');
+ my \$view = \$context->view($hash);
+ \$stash->set($name, \$view);
+ \$stash->set('view', \$view);
+
+$block
+
+ \$stash->set('view', \$oldv);
+ \$view->seal();
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# perl($block)
+#------------------------------------------------------------------------
+
+sub perl {
+ my ($class, $block) = @_;
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# PERL
+\$context->throw('perl', 'EVAL_PERL not set')
+ unless \$context->eval_perl();
+
+$OUTPUT do {
+ my \$output = "package Template::Perl;\\n";
+
+$block
+
+ local(\$Template::Perl::context) = \$context;
+ local(\$Template::Perl::stash) = \$stash;
+
+ my \$result = '';
+ tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$result;
+ my \$save_stdout = select *Template::Perl::PERLOUT;
+
+ eval \$output;
+ select \$save_stdout;
+ \$context->throw(\$@) if \$@;
+ \$result;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# no_perl()
+#------------------------------------------------------------------------
+
+sub no_perl {
+ my $class = shift;
+ return "\$context->throw('perl', 'EVAL_PERL not set');";
+}
+
+
+#------------------------------------------------------------------------
+# rawperl($block)
+#
+# NOTE: perhaps test context EVAL_PERL switch at compile time rather than
+# runtime?
+#------------------------------------------------------------------------
+
+sub rawperl {
+ my ($class, $block, $line) = @_;
+ for ($block) {
+ s/^\n+//;
+ s/\n+$//;
+ }
+ $block = pad($block, 1) if $PRETTY;
+ $line = $line ? " (starting line $line)" : '';
+
+ return <<EOF;
+# RAWPERL
+#line 1 "RAWPERL block$line"
+$block
+EOF
+}
+
+
+
+#------------------------------------------------------------------------
+# filter()
+#------------------------------------------------------------------------
+
+sub filter {
+ my ($class, $lnameargs, $block) = @_;
+ my ($name, $args, $alias) = @$lnameargs;
+ $name = shift @$name;
+ $args = &args($class, $args);
+ $args = $args ? "$args, $alias" : ", undef, $alias"
+ if $alias;
+ $name .= ", $args" if $args;
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# FILTER
+$OUTPUT do {
+ my \$output = '';
+ my \$filter = \$context->filter($name)
+ || \$context->throw(\$context->error);
+
+$block
+
+ &\$filter(\$output);
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# capture($name, $block)
+#------------------------------------------------------------------------
+
+sub capture {
+ my ($class, $name, $block) = @_;
+
+ if (ref $name) {
+ if (scalar @$name == 2 && ! $name->[1]) {
+ $name = $name->[0];
+ }
+ else {
+ $name = '[' . join(', ', @$name) . ']';
+ }
+ }
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# CAPTURE
+\$stash->set($name, do {
+ my \$output = '';
+$block
+ \$output;
+});
+EOF
+
+}
+
+
+#------------------------------------------------------------------------
+# macro($name, $block, \@args)
+#------------------------------------------------------------------------
+
+sub macro {
+ my ($class, $ident, $block, $args) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ if ($args) {
+ my $nargs = scalar @$args;
+ $args = join(', ', map { "'$_'" } @$args);
+ $args = $nargs > 1
+ ? "\@args{ $args } = splice(\@_, 0, $nargs)"
+ : "\$args{ $args } = shift";
+
+ return <<EOF;
+
+# MACRO
+\$stash->set('$ident', sub {
+ my \$output = '';
+ my (%args, \$params);
+ $args;
+ \$params = shift;
+ \$params = { } unless ref(\$params) eq 'HASH';
+ \$params = { \%args, %\$params };
+
+ my \$stash = \$context->localise(\$params);
+ eval {
+$block
+ };
+ \$stash = \$context->delocalise();
+ die \$@ if \$@;
+ return \$output;
+});
+EOF
+
+ }
+ else {
+ return <<EOF;
+
+# MACRO
+\$stash->set('$ident', sub {
+ my \$params = \$_[0] if ref(\$_[0]) eq 'HASH';
+ my \$output = '';
+
+ my \$stash = \$context->localise(\$params);
+ eval {
+$block
+ };
+ \$stash = \$context->delocalise();
+ die \$@ if \$@;
+ return \$output;
+});
+EOF
+ }
+}
+
+
+sub debug {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $args = join(', ', @$file, @$args);
+ $args .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->debugging($args); ## DEBUG ##";
+}
+
+
+1;
+
+__END__
+
diff --git a/lib/Template/Document.pm b/lib/Template/Document.pm
new file mode 100644
index 0000000..ce3beb2
--- /dev/null
+++ b/lib/Template/Document.pm
@@ -0,0 +1,492 @@
+##============================================================= -*-Perl-*-
+#
+# Template::Document
+#
+# DESCRIPTION
+# Module defining a class of objects which encapsulate compiled
+# templates, storing additional block definitions and metadata
+# as well as the compiled Perl sub-routine representing the main
+# template content.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Document.pm,v 2.71 2004/01/13 16:19:10 abw Exp $
+#
+#============================================================================
+
+package Template::Document;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $ERROR $COMPERR $DEBUG $AUTOLOAD );
+use base qw( Template::Base );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.71 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%document)
+#
+# Creates a new self-contained Template::Document object which
+# encapsulates a compiled Perl sub-routine, $block, any additional
+# BLOCKs defined within the document ($defblocks, also Perl sub-routines)
+# and additional $metadata about the document.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $doc) = @_;
+ my ($block, $defblocks, $metadata) = @$doc{ qw( BLOCK DEFBLOCKS METADATA ) };
+ $defblocks ||= { };
+ $metadata ||= { };
+
+ # evaluate Perl code in $block to create sub-routine reference if necessary
+ unless (ref $block) {
+ local $SIG{__WARN__} = \&catch_warnings;
+ $COMPERR = '';
+
+ # DON'T LOOK NOW! - blindly untainting can make you go blind!
+ $block =~ /(.*)/s;
+ $block = $1;
+
+ $block = eval $block;
+ return $class->error($@)
+ unless defined $block;
+ }
+
+ # same for any additional BLOCK definitions
+ @$defblocks{ keys %$defblocks } =
+ # MORE BLIND UNTAINTING - turn away if you're squeamish
+ map {
+ ref($_)
+ ? $_
+ : ( /(.*)/s && eval($1) or return $class->error($@) )
+ } values %$defblocks;
+
+ bless {
+ %$metadata,
+ _BLOCK => $block,
+ _DEFBLOCKS => $defblocks,
+ _HOT => 0,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# block()
+#
+# Returns a reference to the internal sub-routine reference, _BLOCK,
+# that constitutes the main document template.
+#------------------------------------------------------------------------
+
+sub block {
+ return $_[0]->{ _BLOCK };
+}
+
+
+#------------------------------------------------------------------------
+# blocks()
+#
+# Returns a reference to a hash array containing any BLOCK definitions
+# from the template. The hash keys are the BLOCK nameand the values
+# are references to Template::Document objects. Returns 0 (# an empty hash)
+# if no blocks are defined.
+#------------------------------------------------------------------------
+
+sub blocks {
+ return $_[0]->{ _DEFBLOCKS };
+}
+
+
+#------------------------------------------------------------------------
+# process($context)
+#
+# Process the document in a particular context. Checks for recursion,
+# registers the document with the context via visit(), processes itself,
+# and then unwinds with a large gin and tonic.
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $context) = @_;
+ my $defblocks = $self->{ _DEFBLOCKS };
+ my $output;
+
+
+ # check we're not already visiting this template
+ return $context->throw(Template::Constants::ERROR_FILE,
+ "recursion into '$self->{ name }'")
+ if $self->{ _HOT } && ! $context->{ RECURSION }; ## RETURN ##
+
+ $context->visit($self, $defblocks);
+
+ $self->{ _HOT } = 1;
+ eval {
+ my $block = $self->{ _BLOCK };
+ $output = &$block($context);
+ };
+ $self->{ _HOT } = 0;
+
+ $context->leave();
+
+ die $context->catch($@)
+ if $@;
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides pseudo-methods for read-only access to various internal
+# members.
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+# my ($pkg, $file, $line) = caller();
+# print STDERR "called $self->AUTOLOAD($method) from $file line $line\n";
+ return $self->{ $method };
+}
+
+
+#========================================================================
+# ----- PRIVATE METHODS -----
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $dblks;
+ my $output = "$self : $self->{ name }\n";
+
+ $output .= "BLOCK: $self->{ _BLOCK }\nDEFBLOCKS:\n";
+
+ if ($dblks = $self->{ _DEFBLOCKS }) {
+ foreach my $b (keys %$dblks) {
+ $output .= " $b: $dblks->{ $b }\n";
+ }
+ }
+
+ return $output;
+}
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# as_perl($content)
+#
+# This method expects a reference to a hash passed as the first argument
+# containing 3 items:
+# METADATA # a hash of template metadata
+# BLOCK # string containing Perl sub definition for main block
+# DEFBLOCKS # hash containing further subs for addional BLOCK defs
+# It returns a string containing Perl code which, when evaluated and
+# executed, will instantiate a new Template::Document object with the
+# above data. On error, it returns undef with an appropriate error
+# message set in $ERROR.
+#------------------------------------------------------------------------
+
+sub as_perl {
+ my ($class, $content) = @_;
+ my ($block, $defblocks, $metadata) = @$content{ qw( BLOCK DEFBLOCKS METADATA ) };
+
+ $block =~ s/\n/\n /g;
+ $block =~ s/\s+$//;
+
+ $defblocks = join('', map {
+ my $code = $defblocks->{ $_ };
+ $code =~ s/\n/\n /g;
+ $code =~ s/\s*$//;
+ " '$_' => $code,\n";
+ } keys %$defblocks);
+ $defblocks =~ s/\s+$//;
+
+ $metadata = join('', map {
+ my $x = $metadata->{ $_ };
+ $x =~ s/(['\\])/\\$1/g;
+ " '$_' => '$x',\n";
+ } keys %$metadata);
+ $metadata =~ s/\s+$//;
+
+ return <<EOF
+#------------------------------------------------------------------------
+# Compiled template generated by the Template Toolkit version $Template::VERSION
+#------------------------------------------------------------------------
+
+$class->new({
+ METADATA => {
+$metadata
+ },
+ BLOCK => $block,
+ DEFBLOCKS => {
+$defblocks
+ },
+});
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# write_perl_file($filename, \%content)
+#
+# This method calls as_perl() to generate the Perl code to represent a
+# compiled template with the content passed as the second argument.
+# It then writes this to the file denoted by the first argument.
+#
+# Returns 1 on success. On error, sets the $ERROR package variable
+# to contain an error message and returns undef.
+#------------------------------------------------------------------------
+
+sub write_perl_file {
+ my ($class, $file, $content) = @_;
+ my ($fh, $tmpfile);
+
+ return $class->error("invalid filename: $file")
+ unless $file =~ /^(.+)$/s;
+
+ eval {
+ require File::Temp;
+ require File::Basename;
+ ($fh, $tmpfile) = File::Temp::tempfile(
+ DIR => File::Basename::dirname($file)
+ );
+ print $fh $class->as_perl($content) || die $!;
+ close($fh);
+ };
+ return $class->error($@) if $@;
+ return rename($tmpfile, $file)
+ || $class->error($!);
+}
+
+
+#------------------------------------------------------------------------
+# catch_warnings($msg)
+#
+# Installed as
+#------------------------------------------------------------------------
+
+sub catch_warnings {
+ $COMPERR .= join('', @_);
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Document - Compiled template document object
+
+=head1 SYNOPSIS
+
+ use Template::Document;
+
+ $doc = Template::Document->new({
+ BLOCK => sub { # some perl code; return $some_text },
+ DEFBLOCKS => {
+ header => sub { # more perl code; return $some_text },
+ footer => sub { # blah blah blah; return $some_text },
+ },
+ METADATA => {
+ author => 'Andy Wardley',
+ version => 3.14,
+ }
+ }) || die $Template::Document::ERROR;
+
+ print $doc->process($context);
+
+=head1 DESCRIPTION
+
+This module defines an object class whose instances represent compiled
+template documents. The Template::Parser module creates a
+Template::Document instance to encapsulate a template as it is compiled
+into Perl code.
+
+The constructor method, new(), expects a reference to a hash array
+containing the BLOCK, DEFBLOCKS and METADATA items. The BLOCK item
+should contain a reference to a Perl subroutine or a textual
+representation of Perl code, as generated by the Template::Parser
+module, which is then evaluated into a subroutine reference using
+eval(). The DEFLOCKS item should reference a hash array containing
+further named BLOCKs which may be defined in the template. The keys
+represent BLOCK names and the values should be subroutine references
+or text strings of Perl code as per the main BLOCK item. The METADATA
+item should reference a hash array of metadata items relevant to the
+document.
+
+The process() method can then be called on the instantiated
+Template::Document object, passing a reference to a Template::Content
+object as the first parameter. This will install any locally defined
+blocks (DEFBLOCKS) in the the contexts() BLOCKS cache (via a call to
+visit()) so that they may be subsequently resolved by the context. The
+main BLOCK subroutine is then executed, passing the context reference
+on as a parameter. The text returned from the template subroutine is
+then returned by the process() method, after calling the context leave()
+method to permit cleanup and de-registration of named BLOCKS previously
+installed.
+
+An AUTOLOAD method provides access to the METADATA items for the document.
+The Template::Service module installs a reference to the main
+Template::Document object in the stash as the 'template' variable.
+This allows metadata items to be accessed from within templates,
+including PRE_PROCESS templates.
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]
+ </head>
+ ...
+
+Template::Document objects are usually created by the Template::Parser
+but can be manually instantiated or sub-classed to provide custom
+template components.
+
+=head1 METHODS
+
+=head2 new(\%config)
+
+Constructor method which accept a reference to a hash array containing the
+structure as shown in this example:
+
+ $doc = Template::Document->new({
+ BLOCK => sub { # some perl code; return $some_text },
+ DEFBLOCKS => {
+ header => sub { # more perl code; return $some_text },
+ footer => sub { # blah blah blah; return $some_text },
+ },
+ METADATA => {
+ author => 'Andy Wardley',
+ version => 3.14,
+ }
+ }) || die $Template::Document::ERROR;
+
+BLOCK and DEFBLOCKS items may be expressed as references to Perl subroutines
+or as text strings containing Perl subroutine definitions, as is generated
+by the Template::Parser module. These are evaluated into subroutine references
+using eval().
+
+Returns a new Template::Document object or undef on error. The error() class
+method can be called, or the $ERROR package variable inspected to retrieve
+the relevant error message.
+
+=head2 process($context)
+
+Main processing routine for the compiled template document. A reference to
+a Template::Context object should be passed as the first parameter. The
+method installs any locally defined blocks via a call to the context
+visit() method, processes it's own template, passing the context reference
+by parameter and then calls leave() in the context to allow cleanup.
+
+ print $doc->process($context);
+
+Returns a text string representing the generated output for the template.
+Errors are thrown via die().
+
+=head2 block()
+
+Returns a reference to the main BLOCK subroutine.
+
+=head2 blocks()
+
+Returns a reference to the hash array of named DEFBLOCKS subroutines.
+
+=head2 AUTOLOAD
+
+An autoload method returns METADATA items.
+
+ print $doc->author();
+
+=head1 PACKAGE SUB-ROUTINES
+
+=head2 write_perl_file(\%config)
+
+This package subroutine is provided to effect persistance of compiled
+templates. If the COMPILE_EXT option (to indicate a file extension
+for saving compiled templates) then the Template::Parser module calls
+this subroutine before calling the new() constructor. At this stage,
+the parser has a representation of the template as text strings
+containing Perl code. We can write that to a file, enclosed in a
+small wrapper which will allow us to susequently require() the file
+and have Perl parse and compile it into a Template::Document. Thus we
+have persistance of compiled templates.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.71, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Parser|Template::Parser>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Exception.pm b/lib/Template/Exception.pm
new file mode 100644
index 0000000..9a95af7
--- /dev/null
+++ b/lib/Template/Exception.pm
@@ -0,0 +1,254 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Exception
+#
+# DESCRIPTION
+# Module implementing a generic exception class used for error handling
+# in the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Exception.pm,v 2.64 2004/01/13 16:19:10 abw Exp $
+#
+#========================================================================
+
+
+package Template::Exception;
+
+require 5.005;
+
+use strict;
+use vars qw( $VERSION );
+
+use constant TYPE => 0;
+use constant INFO => 1;
+use constant TEXT => 2;
+use overload q|""| => "as_string", fallback => 1;
+
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($type, $info, \$text)
+#
+# Constructor method used to instantiate a new Template::Exception
+# object. The first parameter should contain the exception type. This
+# can be any arbitrary string of the caller's choice to represent a
+# specific exception. The second parameter should contain any
+# information (i.e. error message or data reference) relevant to the
+# specific exception event. The third optional parameter may be a
+# reference to a scalar containing output text from the template
+# block up to the point where the exception was thrown.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $type, $info, $textref) = @_;
+ bless [ $type, $info, $textref ], $class;
+}
+
+
+#------------------------------------------------------------------------
+# type()
+# info()
+# type_info()
+#
+# Accessor methods to return the internal TYPE and INFO fields.
+#------------------------------------------------------------------------
+
+sub type {
+ $_[0]->[ TYPE ];
+}
+
+sub info {
+ $_[0]->[ INFO ];
+}
+
+sub type_info {
+ my $self = shift;
+ @$self[ TYPE, INFO ];
+}
+
+#------------------------------------------------------------------------
+# text()
+# text(\$pretext)
+#
+# Method to return the text referenced by the TEXT member. A text
+# reference may be passed as a parameter to supercede the existing
+# member. The existing text is added to the *end* of the new text
+# before being stored. This facility is provided for template blocks
+# to gracefully de-nest when an exception occurs and allows them to
+# reconstruct their output in the correct order.
+#------------------------------------------------------------------------
+
+sub text {
+ my ($self, $newtextref) = @_;
+ my $textref = $self->[ TEXT ];
+
+ if ($newtextref) {
+ $$newtextref .= $$textref if $textref && $textref ne $newtextref;
+ $self->[ TEXT ] = $newtextref;
+ return '';
+
+ }
+ elsif ($textref) {
+ return $$textref;
+ }
+ else {
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# as_string()
+#
+# Accessor method to return a string indicating the exception type and
+# information.
+#------------------------------------------------------------------------
+
+sub as_string {
+ my $self = shift;
+ return $self->[ TYPE ] . ' error - ' . $self->[ INFO ];
+}
+
+
+#------------------------------------------------------------------------
+# select_handler(@types)
+#
+# Selects the most appropriate handler for the exception TYPE, from
+# the list of types passed in as parameters. The method returns the
+# item which is an exact match for TYPE or the closest, more
+# generic handler (e.g. foo being more generic than foo.bar, etc.)
+#------------------------------------------------------------------------
+
+sub select_handler {
+ my ($self, @options) = @_;
+ my $type = $self->[ TYPE ];
+ my %hlut;
+ @hlut{ @options } = (1) x @options;
+
+ while ($type) {
+ return $type if $hlut{ $type };
+
+ # strip .element from the end of the exception type to find a
+ # more generic handler
+ $type =~ s/\.?[^\.]*$//;
+ }
+ return undef;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Exception - Exception handling class module
+
+=head1 SYNOPSIS
+
+ use Template::Exception;
+
+ my $exception = Template::Exception->new($type, $info);
+ $type = $exception->type;
+ $info = $exception->info;
+ ($type, $info) = $exception->type_info;
+
+ print $exception->as_string();
+
+ $handler = $exception->select_handler(\@candidates);
+
+=head1 DESCRIPTION
+
+The Template::Exception module defines an object class for
+representing exceptions within the template processing life cycle.
+Exceptions can be raised by modules within the Template Toolkit, or
+can be generated and returned by user code bound to template
+variables.
+
+
+Exceptions can be raised in a template using the THROW directive,
+
+ [% THROW user.login 'no user id: please login' %]
+
+or by calling the throw() method on the current Template::Context object,
+
+ $context->throw('user.passwd', 'Incorrect Password');
+ $context->throw('Incorrect Password'); # type 'undef'
+
+or from Perl code by calling die() with a Template::Exception object,
+
+ die (Template::Exception->new('user.denied', 'Invalid User ID'));
+
+or by simply calling die() with an error string. This is
+automagically caught and converted to an exception of 'undef'
+type which can then be handled in the usual way.
+
+ die "I'm sorry Dave, I can't do that";
+
+
+
+Each exception is defined by its type and a information component
+(e.g. error message). The type can be any identifying string and may
+contain dotted components (e.g. 'foo', 'foo.bar', 'foo.bar.baz').
+Exception types are considered to be hierarchical such that 'foo.bar'
+would be a specific type of the more general 'foo' type.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/FAQ.pod b/lib/Template/FAQ.pod
new file mode 100644
index 0000000..0807ace
--- /dev/null
+++ b/lib/Template/FAQ.pod
@@ -0,0 +1,329 @@
+#============================================================= -*-perl-*-
+#
+# Template::FAQ
+#
+# DESCRIPTION
+# This is the Frequently Asked Questions list for the Template
+# Toolkit. More accurately, it's a very thin placeholder for where
+# the FAQ will soon be.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# 2.69
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::FAQ - Frequently Asked Questions about the Template Toolkit
+
+=head1 DESCRIPTION
+
+This is the Frequently Asked Questions list for the Template Toolkit.
+More accurately, it's a very thin placeholder for where the FAQ will
+soon be.
+
+
+=head1 Template Toolkit Language
+
+=head2 Why doesn't [% a = b IF c %] work as expected?
+
+Because the parser interprets it as
+
+ [% a = (b IF c) %]
+
+Do this instead:
+
+ [% SET a = b IF c %]
+
+=head2 If I'm using TT to write out a TT template, is there a good way to escape [% and %]?
+
+You can do this:
+
+ [% stag = "[\%"
+ etag = "%\]"
+ %]
+
+and then:
+
+ [% stag; 'hello'; etag %]
+
+Or something like:
+
+ [% TAGS [- -] %]
+ [- INCLUDE foo -] # is a directive
+ [% INCLUDE foo %] # not a directive, just plain text, passed through
+
+=head2 How do I iterate over a hash?
+
+This is covered in the L<Template::Manual::VMethods|VMethods> section
+of the manual page. A list of all the keys that are in the hash can
+be obtained with the 'keys' virtual method. You can then iterate
+over that list and by looking up each key in turn get the value.
+
+ [% FOREACH key = product.keys %]
+ [% key %] => [% product.$key %]
+ [% END %]
+
+=head1 Plugins
+
+=head2 How do I get the Table plugin to order data across rather than down?
+
+Order the data into rows:
+
+ Steve Karen Jeff
+ Brooklyn Nantucket Fairfax
+ NY MA VA
+
+ [% USE table(data, rows=3) %]
+
+Then ask for each column
+
+ [% FOREACH column = table.cols %]
+
+And then print each item in the column going across the output rows
+
+ [% FOREACH item = column %]
+ <td>[% item %]</td>
+ [% END %]
+
+=head2 Accessing Cookies
+
+Jeff Boes E<lt>jboes@nexcerpt.comE<gt> asks:
+
+ Does anyone have a quick-n-dirty approach to accessing
+ cookies from templates?
+
+Jonas Liljegren answers:
+
+ [% USE CGI %]
+
+ <p>The value is [% CGI.cookie('cookie_name') | html %]
+
+
+=head1 Extending the Template Toolkit
+
+=head2 Can I serve templates from a database?
+
+Short answer: yes, Chris Nandor has done this for Slash. You need to
+subclass Template::Provider. See the mailing list archives for further
+info.
+
+=head2 Can I fetch templates via http?
+
+To do the job properly, you should sublcass Template::Provider to
+Template::Provider::HTTP and use a PREFIX_MAP option to bind the
+'http' template prefix to that particular provider (you may want to
+go digging around in the F<Changes> file around version 2.01 for
+more info on PREFIX_MAP - it may not be properly documented anywhere
+else...yet!). e.g. (untested due to lack of existing HTTP Provider
+- patches welcome!).
+
+ use Template::Provider::HTTP;
+
+ my $file = Template::Provider( INCLUDE_PATH => [...] );
+ my $http = Template::Provider::HTTP->new(...);
+ my $tt2 = Template->new({
+ LOAD_TEMPLATES => [ $file, $http ],
+ PREFIX_MAP => {
+ file => '0', # file:foo.html
+ http => '1', # http:foo.html
+ default => '0', # foo.html => file:foo.html
+ }
+ });
+
+Now a template specified as:
+
+ [% INCLUDE foo %]
+
+will be served by the 'file' provider (the default). Otherwise you
+can explicitly add a prefix:
+
+ [% INCLUDE file:foo.html %]
+ [% INCLUDE http:foo.html %]
+ [% INCLUDE http://www.xyz.com/tt2/header.tt2 %]
+
+This same principal can be used to create a DBI template provider. e.g.
+
+ [% INCLUDE dbi:foo.html %]
+
+But similarly, alas, we don't yet have a DBI provider as part of the
+Template Toolkit. There has been some talk on the mailing list about
+efforts to develop DBI and/or HTTP providers but as yet no-one has
+stepped forward to take up the challenge...
+
+In the mean time, Craig's post from the mailing list has some useful
+pointers on how to acheive this using existing modules:
+
+ To: Adam Theo <adamtheo@theoretic.com>
+ From: Craig Barratt <craig@arraycomm.com>
+ Date: Fri, 18 May 2001 17:06:59 -0700
+
+ > i was wondering if there is anyway to fetch a file using http:// or
+ > ftp:// and include that?
+
+ Here's one way. Set the LOAD_PERL option:
+
+ use Template;
+
+ my $template = Template->new({
+ LOAD_PERL => 1
+ });
+ $template->process("example.tt", { stdout => *STDOUT })
+ || die $template->error();
+
+ and then use LWP::UserAgent and HTTP::Request:
+
+ [%
+ USE ua = LWP.UserAgent;
+ ua.proxy("http", "http://your_proxy/");
+ USE req = HTTP.Request("GET", "http://www.cpan.org");
+ ua.request(req).content;
+ -%]
+
+ For FTP use Net::FTP:
+
+ [%
+ USE ftp = Net.FTP("ftp.cpan.org");
+ x = ftp.login("anonymous", "me@here.there");
+ x = ftp.cwd("/");
+ x = ftp.get("welcome.msg", stdout);
+ x = ftp.quit;
+ -%]
+
+ Normally ftp.get would write the file into the current directory.
+ Instead we pass stdout as a second argument so that it is written
+ to stdout. We set stdout to STDOUT in the variables we pass to
+ process.
+
+ Craig
+
+=head1 Miscellaneous
+
+=head2 How can I configure variables on a per-request basis?
+
+One easy way to acheive this is to define a single PRE_PROCESS template which
+loads in other configuration files based on variables defined or other
+conditions.
+
+For example, my setup usually looks something like this:
+
+ PRE_PROCESS => 'config/main'
+
+config/main:
+
+ [% DEFAULT style = 'text'
+ section = template.section or 'home';
+
+ PROCESS config/site
+ + config/urls
+ + config/macros
+ + "config/style/$style"
+ + "config/section/$section"
+ + ...
+ %]
+
+This allows me to set a single 'style' variable to control which config
+file gets pre-processed to set my various style options (colours, img paths,
+etc). For example:
+
+config/style/basic:
+
+ [% style = {
+ name = style # save existing 'style' var as 'style.name'
+
+ # define various other style variables....
+ col = {
+ back => '#ffffff'
+ text => '#000000'
+ # ...etc...
+ }
+
+ logo = {
+ # ...etc...
+ }
+
+ # ...etc...
+ }
+ %]
+
+Each source template can declare which section it's in via a META
+directive:
+
+ [% META
+ title = 'General Information'
+ section = 'info'
+ %]
+
+ ...
+
+This controls which section configuration file gets loaded to set various
+other variables for defining the section title, menu, etc.
+
+config/section/info:
+
+ [% section = {
+ name = section # save 'section' var as 'section.name'
+ title = 'Information'
+ menu = [ ... ]
+ # ...etc...
+ }
+ %]
+
+This illustrates the basic principal but you can extend it to perform
+pretty much any kind of per-document initialisation that you require.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Filters.pm b/lib/Template/Filters.pm
new file mode 100644
index 0000000..a9c4846
--- /dev/null
+++ b/lib/Template/Filters.pm
@@ -0,0 +1,1448 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Filters
+#
+# DESCRIPTION
+# Defines filter plugins as used by the FILTER directive.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>, with a number of filters contributed
+# by Leslie Michael Orchard <deus_x@nijacode.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Filters.pm,v 2.77 2004/01/13 16:19:10 abw Exp $
+#
+#============================================================================
+
+package Template::Filters;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $FILTERS $URI_ESCAPES $PLUGIN_FILTER );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.77 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# standard filters, defined in one of the following forms:
+# name => \&static_filter
+# name => [ \&subref, $is_dynamic ]
+# If the $is_dynamic flag is set then the sub-routine reference
+# is called to create a new filter each time it is requested; if
+# not set, then it is a single, static sub-routine which is returned
+# for every filter request for that name.
+#------------------------------------------------------------------------
+
+$FILTERS = {
+ # static filters
+ 'html' => \&html_filter,
+ 'html_para' => \&html_paragraph,
+ 'html_break' => \&html_para_break,
+ 'html_para_break' => \&html_para_break,
+ 'html_line_break' => \&html_line_break,
+ 'uri' => \&uri_filter,
+ 'upper' => sub { uc $_[0] },
+ 'lower' => sub { lc $_[0] },
+ 'ucfirst' => sub { ucfirst $_[0] },
+ 'lcfirst' => sub { lcfirst $_[0] },
+ 'stderr' => sub { print STDERR @_; return '' },
+ 'trim' => sub { for ($_[0]) { s/^\s+//; s/\s+$// }; $_[0] },
+ 'null' => sub { return '' },
+ 'collapse' => sub { for ($_[0]) { s/^\s+//; s/\s+$//; s/\s+/ /g };
+ $_[0] },
+
+ # dynamic filters
+ 'html_entity' => [ \&html_entity_filter_factory, 1 ],
+ 'indent' => [ \&indent_filter_factory, 1 ],
+ 'format' => [ \&format_filter_factory, 1 ],
+ 'truncate' => [ \&truncate_filter_factory, 1 ],
+ 'repeat' => [ \&repeat_filter_factory, 1 ],
+ 'replace' => [ \&replace_filter_factory, 1 ],
+ 'remove' => [ \&remove_filter_factory, 1 ],
+ 'eval' => [ \&eval_filter_factory, 1 ],
+ 'evaltt' => [ \&eval_filter_factory, 1 ], # alias
+ 'perl' => [ \&perl_filter_factory, 1 ],
+ 'evalperl' => [ \&perl_filter_factory, 1 ], # alias
+ 'redirect' => [ \&redirect_filter_factory, 1 ],
+ 'file' => [ \&redirect_filter_factory, 1 ], # alias
+ 'stdout' => [ \&stdout_filter_factory, 1 ],
+ 'latex' => [ \&latex_filter_factory, 1 ],
+};
+
+# name of module implementing plugin filters
+$PLUGIN_FILTER = 'Template::Plugin::Filter';
+
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name, \@args, $context)
+#
+# Attempts to instantiate or return a reference to a filter sub-routine
+# named by the first parameter, $name, with additional constructor
+# arguments passed by reference to a list as the second parameter,
+# $args. A reference to the calling Template::Context object is
+# passed as the third paramter.
+#
+# Returns a reference to a filter sub-routine or a pair of values
+# (undef, STATUS_DECLINED) or ($error, STATUS_ERROR) to decline to
+# deliver the filter or to indicate an error.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name, $args, $context) = @_;
+ my ($factory, $is_dynamic, $filter, $error);
+
+ $self->debug("fetch($name, ",
+ defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ',
+ defined $context ? $context : '<no context>',
+ ')') if $self->{ DEBUG };
+
+ # allow $name to be specified as a reference to
+ # a plugin filter object; any other ref is
+ # assumed to be a coderef and hence already a filter;
+ # non-refs are assumed to be regular name lookups
+
+ if (ref $name) {
+ if (UNIVERSAL::isa($name, $PLUGIN_FILTER)) {
+ $factory = $name->factory()
+ || return $self->error($name->error());
+ }
+ else {
+ return $name;
+ }
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DECLINED)
+ unless ($factory = $self->{ FILTERS }->{ $name }
+ || $FILTERS->{ $name });
+ }
+
+ # factory can be an [ $code, $dynamic ] or just $code
+ if (ref $factory eq 'ARRAY') {
+ ($factory, $is_dynamic) = @$factory;
+ }
+ else {
+ $is_dynamic = 0;
+ }
+
+ if (ref $factory eq 'CODE') {
+ if ($is_dynamic) {
+ # if the dynamic flag is set then the sub-routine is a
+ # factory which should be called to create the actual
+ # filter...
+ eval {
+ ($filter, $error) = &$factory($context, $args ? @$args : ());
+ };
+ $error ||= $@;
+ $error = "invalid FILTER for '$name' (not a CODE ref)"
+ unless $error || ref($filter) eq 'CODE';
+ }
+ else {
+ # ...otherwise, it's a static filter sub-routine
+ $filter = $factory;
+ }
+ }
+ else {
+ $error = "invalid FILTER entry for '$name' (not a CODE ref)";
+ }
+
+ if ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR) ;
+ }
+ else {
+ return $filter;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# store($name, \&filter)
+#
+# Stores a new filter in the internal FILTERS hash. The first parameter
+# is the filter name, the second a reference to a subroutine or
+# array, as per the standard $FILTERS entries.
+#------------------------------------------------------------------------
+
+sub store {
+ my ($self, $name, $filter) = @_;
+
+ $self->debug("store($name, $filter)") if $self->{ DEBUG };
+
+ $self->{ FILTERS }->{ $name } = $filter;
+ return 1;
+}
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Private initialisation method.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+
+ $self->{ FILTERS } = $params->{ FILTERS } || { };
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ DEBUG } = ( $params->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_FILTERS;
+
+
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Filters] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( TOLERANT )) {
+ my $val = $self->{ $key };
+ $val = '<undef>' unless defined $val;
+ $output .= sprintf($format, $key, $val);
+ }
+
+ my $filters = $self->{ FILTERS };
+ $filters = join('', map {
+ sprintf(" $format", $_, $filters->{ $_ });
+ } keys %$filters);
+ $filters = "{\n$filters }";
+
+ $output .= sprintf($format, 'FILTERS (local)' => $filters);
+
+ $filters = $FILTERS;
+ $filters = join('', map {
+ my $f = $filters->{ $_ };
+ my ($ref, $dynamic) = ref $f eq 'ARRAY' ? @$f : ($f, 0);
+ sprintf(" $format", $_, $dynamic ? 'dynamic' : 'static');
+ } sort keys %$filters);
+ $filters = "{\n$filters }";
+
+ $output .= sprintf($format, 'FILTERS (global)' => $filters);
+
+ $output .= '}';
+ return $output;
+}
+
+
+#========================================================================
+# -- STATIC FILTER SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# uri_filter() [% FILTER uri %]
+#
+# URI escape a string. This code is borrowed from Gisle Aas' URI::Escape
+# module. For something so simple, I can't see any validation in making
+# the user install the URI modules just for this, so we cut and paste.
+#
+# URI::Escape is Copyright 1995-2000 Gisle Aas.
+#------------------------------------------------------------------------
+
+sub uri_filter {
+ my $text = shift;
+
+ # construct and cache a lookup table for escapes (faster than
+ # doing a sprintf() for every character in every string each
+ # time)
+ $URI_ESCAPES ||= {
+ map { ( chr($_), sprintf("%%%02X", $_) ) } (0..255),
+ };
+
+ $text =~ s/([^;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()])/$URI_ESCAPES->{$1}/g;
+ $text;
+}
+
+
+#------------------------------------------------------------------------
+# html_filter() [% FILTER html %]
+#
+# Convert any '<', '>' or '&' characters to the HTML equivalents, '&lt;',
+# '&gt;' and '&amp;', respectively.
+#------------------------------------------------------------------------
+
+sub html_filter {
+ my $text = shift;
+ for ($text) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/"/&quot;/g;
+ }
+ return $text;
+}
+
+
+#------------------------------------------------------------------------
+# html_paragraph() [% FILTER html_para %]
+#
+# Wrap each paragraph of text (delimited by two or more newlines) in the
+# <p>...</p> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_paragraph {
+ my $text = shift;
+ return "<p>\n"
+ . join("\n</p>\n\n<p>\n", split(/(?:\r?\n){2,}/, $text))
+ . "</p>\n";
+}
+
+
+#------------------------------------------------------------------------
+# html_para_break() [% FILTER html_para_break %]
+#
+# Join each paragraph of text (delimited by two or more newlines) with
+# <br><br> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_para_break {
+ my $text = shift;
+ $text =~ s|(\r?\n){2,}|$1<br />$1<br />$1|g;
+ return $text;
+}
+
+#------------------------------------------------------------------------
+# html_line_break() [% FILTER html_line_break %]
+#
+# replaces any newlines with <br> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_line_break {
+ my $text = shift;
+ $text =~ s|(\r?\n)|<br />$1|g;
+ return $text;
+}
+
+#========================================================================
+# -- DYNAMIC FILTER FACTORIES --
+#========================================================================
+
+#------------------------------------------------------------------------
+# html_entity_filter_factory(\%options) [% FILTER html %]
+#
+# Dynamic version of the static html filter which attempts to locate the
+# Apache::Util or HTML::Entities modules to perform full entity encoding
+# of the text passed. Returns an exception if one or other of the
+# modules can't be located.
+#------------------------------------------------------------------------
+
+sub html_entity_filter_factory {
+ my $context = shift;
+
+ # if Apache::Util is installed then we use it
+ eval {
+ require Apache::Util;
+ Apache::Util::escape_html('');
+ };
+ return \&Apache::Util::escape_html
+ unless $@;
+
+ # otherwise if HTML::Entities is installed then we use that
+ eval {
+ require HTML::Entities;
+ };
+ return \&HTML::Entities::encode_entities
+ unless $@;
+
+ return (undef, Template::Exception->new( html_entity =>
+ 'cannot locate Apache::Util or HTML::Entities' ));
+
+}
+
+
+#------------------------------------------------------------------------
+# indent_filter_factory($pad) [% FILTER indent(pad) %]
+#
+# Create a filter to indent text by a fixed pad string or when $pad is
+# numerical, a number of space.
+#------------------------------------------------------------------------
+
+sub indent_filter_factory {
+ my ($context, $pad) = @_;
+ $pad = 4 unless defined $pad;
+ $pad = ' ' x $pad if $pad =~ /^\d+$/;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/^/$pad/mg;
+ return $text;
+ }
+}
+
+#------------------------------------------------------------------------
+# format_filter_factory() [% FILTER format(format) %]
+#
+# Create a filter to format text according to a printf()-like format
+# string.
+#------------------------------------------------------------------------
+
+sub format_filter_factory {
+ my ($context, $format) = @_;
+ $format = '%s' unless defined $format;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ return join("\n", map{ sprintf($format, $_) } split(/\n/, $text));
+ }
+}
+
+
+#------------------------------------------------------------------------
+# repeat_filter_factory($n) [% FILTER repeat(n) %]
+#
+# Create a filter to repeat text n times.
+#------------------------------------------------------------------------
+
+sub repeat_filter_factory {
+ my ($context, $iter) = @_;
+ $iter = 1 unless defined $iter and length $iter;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ return join('\n', $text) x $iter;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# replace_filter_factory($s, $r) [% FILTER replace(search, replace) %]
+#
+# Create a filter to replace 'search' text with 'replace'
+#------------------------------------------------------------------------
+
+sub replace_filter_factory {
+ my ($context, $search, $replace) = @_;
+ $search = '' unless defined $search;
+ $replace = '' unless defined $replace;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/$search/$replace/g;
+ return $text;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# remove_filter_factory($text) [% FILTER remove(text) %]
+#
+# Create a filter to remove 'search' string from the input text.
+#------------------------------------------------------------------------
+
+sub remove_filter_factory {
+ my ($context, $search) = @_;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/$search//g;
+ return $text;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# truncate_filter_factory($n) [% FILTER truncate(n) %]
+#
+# Create a filter to truncate text after n characters.
+#------------------------------------------------------------------------
+
+sub truncate_filter_factory {
+ my ($context, $len) = @_;
+ $len = 32 unless defined $len;
+
+ return sub {
+ my $text = shift;
+ return $text if length $text < $len;
+ return substr($text, 0, $len - 3) . "...";
+ }
+}
+
+
+#------------------------------------------------------------------------
+# eval_filter_factory [% FILTER eval %]
+#
+# Create a filter to evaluate template text.
+#------------------------------------------------------------------------
+
+sub eval_filter_factory {
+ my $context = shift;
+
+ return sub {
+ my $text = shift;
+ $context->process(\$text);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# perl_filter_factory [% FILTER perl %]
+#
+# Create a filter to process Perl text iff the context EVAL_PERL flag
+# is set.
+#------------------------------------------------------------------------
+
+sub perl_filter_factory {
+ my $context = shift;
+ my $stash = $context->stash;
+
+ return (undef, Template::Exception->new('perl', 'EVAL_PERL is not set'))
+ unless $context->eval_perl();
+
+ return sub {
+ my $text = shift;
+ local($Template::Perl::context) = $context;
+ local($Template::Perl::stash) = $stash;
+ my $out = eval <<EOF;
+package Template::Perl;
+\$stash = \$context->stash();
+$text
+EOF
+ $context->throw($@) if $@;
+ return $out;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# redirect_filter_factory($context, $file) [% FILTER redirect(file) %]
+#
+# Create a filter to redirect the block text to a file.
+#------------------------------------------------------------------------
+
+sub redirect_filter_factory {
+ my ($context, $file, $options) = @_;
+ my $outpath = $context->config->{ OUTPUT_PATH };
+
+ return (undef, Template::Exception->new('redirect',
+ 'OUTPUT_PATH is not set'))
+ unless $outpath;
+
+ $options = { binmode => $options } unless ref $options;
+
+ sub {
+ my $text = shift;
+ my $outpath = $context->config->{ OUTPUT_PATH }
+ || return '';
+ $outpath .= "/$file";
+ my $error = Template::_output($outpath, \$text, $options);
+ die Template::Exception->new('redirect', $error)
+ if $error;
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# stdout_filter_factory($context, $binmode) [% FILTER stdout(binmode) %]
+#
+# Create a filter to print a block to stdout, with an optional binmode.
+#------------------------------------------------------------------------
+
+sub stdout_filter_factory {
+ my ($context, $options) = @_;
+
+ $options = { binmode => $options } unless ref $options;
+
+ sub {
+ my $text = shift;
+ binmode(STDOUT) if $options->{ binmode };
+ print STDOUT $text;
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# latex_filter_factory($context, $outputType) [% FILTER latex(outputType) %]
+#
+# Return a filter sub that converts a (hopefully) complete LaTeX source
+# file to either "ps", "dvi", or "pdf". Output type should be "ps", "dvi"
+# or "pdf" (pdf is default).
+#
+# Creates a temporary directory below File::Spec->tmpdir() (often /tmp)
+# and writes the text into doc.tex. It then runs either pdflatex or
+# latex and optionally dvips. Based on the exit status either returns
+# the entire doc.(pdf|ps|dvi) output or throws an error with a summary
+# of the error messages from doc.log.
+#
+# Written by Craig Barratt, Apr 28 2001.
+# Win32 additions by Richard Tietjen.
+#------------------------------------------------------------------------
+use File::Path;
+use File::Spec;
+use Cwd;
+
+sub latex_filter_factory
+{
+ my($context, $output) = @_;
+
+ $output = lc($output);
+ my $fName = "latex";
+ my($LaTeXPath, $PdfLaTeXPath, $DviPSPath)
+ = @{Template::Config->latexpaths()};
+ if ( $output eq "ps" || $output eq "dvi" ) {
+ $context->throw($fName,
+ "latex not installed (see Template::Config::LATEX_PATH)")
+ if ( $LaTeXPath eq "" );
+ } else {
+ $output = "pdf";
+ $LaTeXPath = $PdfLaTeXPath;
+ $context->throw($fName,
+ "pdflatex not installed (see Template::Config::PDFLATEX_PATH)")
+ if ( $LaTeXPath eq "" );
+ }
+ if ( $output eq "ps" && $DviPSPath eq "" ) {
+ $context->throw($fName,
+ "dvips not installed (see Template::Config::DVIPS_PATH)");
+ }
+ if ( $^O !~ /^(MacOS|os2|VMS)$/i ) {
+ return sub {
+ local(*FH);
+ my $text = shift;
+ my $tmpRootDir = File::Spec->tmpdir();
+ my $cnt = 0;
+ my($tmpDir, $fileName, $devnull);
+ my $texDoc = 'doc';
+
+ do {
+ $tmpDir = File::Spec->catdir($tmpRootDir,
+ "tt2latex$$" . "_$cnt");
+ $cnt++;
+ } while ( -e $tmpDir );
+ mkpath($tmpDir, 0, 0700);
+ $context->throw($fName, "can't create temp dir $tmpDir")
+ if ( !-d $tmpDir );
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.tex");
+ $devnull = File::Spec->devnull();
+ if ( !open(FH, ">$fileName") ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't open $fileName for output");
+ }
+ print(FH $text);
+ close(FH);
+
+ # latex must run in tmpDir directory
+ my $currDir = cwd();
+ if ( !chdir($tmpDir) ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $tmpDir");
+ }
+ #
+ # We don't need to quote the backslashes on windows, but we
+ # do on other OSs
+ #
+ my $LaTeX_arg = "\\nonstopmode\\input{$texDoc}";
+ $LaTeX_arg = "'$LaTeX_arg'" if ( $^O ne 'MSWin32' );
+ if ( system("$LaTeXPath $LaTeX_arg"
+ . " 1>$devnull 2>$devnull 0<$devnull") ) {
+ my $texErrs = "";
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.log");
+ if ( open(FH, "<$fileName") ) {
+ my $state = 0;
+ #
+ # Try to extract just the interesting errors from
+ # the verbose log file
+ #
+ while ( <FH> ) {
+ #
+ # TeX errors seems to start with a "!" at the
+ # start of the line, and are followed several
+ # lines later by a line designator of the
+ # form "l.nnn" where nnn is the line number.
+ # We make sure we pick up every /^!/ line, and
+ # the first /^l.\d/ line after each /^!/ line.
+ #
+ if ( /^(!.*)/ ) {
+ $texErrs .= $1 . "\n";
+ $state = 1;
+ }
+ if ( $state == 1 && /^(l\.\d.*)/ ) {
+ $texErrs .= $1 . "\n";
+ $state = 0;
+ }
+ }
+ close(FH);
+ } else {
+ $texErrs = "Unable to open $fileName\n";
+ }
+ my $ok = chdir($currDir);
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir") if ( !$ok );
+ $context->throw($fName, "latex exited with errors:\n$texErrs");
+ }
+ if ( $output eq "ps" ) {
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.dvi");
+ if ( system("$DviPSPath $texDoc -o"
+ . " 1>$devnull 2>$devnull 0<$devnull") ) {
+ my $ok = chdir($currDir);
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir") if ( !$ok );
+ $context->throw($fName, "can't run $DviPSPath $fileName");
+ }
+ }
+ if ( !chdir($currDir) ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir");
+ }
+
+ my $retStr;
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.$output");
+ if ( open(FH, $fileName) ) {
+ local $/ = undef; # slurp file in one go
+ binmode(FH);
+ $retStr = <FH>;
+ close(FH);
+ } else {
+ rmtree($tmpDir);
+ $context->throw($fName, "Can't open output file $fileName");
+ }
+ rmtree($tmpDir);
+ return $retStr;
+ }
+ } else {
+ $context->throw("$fName not yet supported on $^O OS."
+ . " Please contribute code!!");
+ }
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Filters - Post-processing filters for template blocks
+
+=head1 SYNOPSIS
+
+ use Template::Filters;
+
+ $filters = Template::Filters->new(\%config);
+
+ ($filter, $error) = $filters->fetch($name, \@args, $context);
+
+=head1 DESCRIPTION
+
+The Template::Filters module implements a provider for creating and/or
+returning subroutines that implement the standard filters. Additional
+custom filters may be provided via the FILTERS options.
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+Constructor method which instantiates and returns a reference to a
+Template::Filters object. A reference to a hash array of configuration
+items may be passed as a parameter. These are described below.
+
+ my $filters = Template::Filters->new({
+ FILTERS => { ... },
+ });
+
+ my $template = Template->new({
+ LOAD_FILTERS => [ $filters ],
+ });
+
+A default Template::Filters module is created by the Template.pm module
+if the LOAD_FILTERS option isn't specified. All configuration parameters
+are forwarded to the constructor.
+
+ $template = Template->new({
+ FILTERS => { ... },
+ });
+
+=head2 fetch($name, \@args, $context)
+
+Called to request that a filter of a given name be provided. The name
+of the filter should be specified as the first parameter. This should
+be one of the standard filters or one specified in the FILTERS
+configuration hash. The second argument should be a reference to an
+array containing configuration parameters for the filter. This may be
+specified as 0, or undef where no parameters are provided. The third
+argument should be a reference to the current Template::Context
+object.
+
+The method returns a reference to a filter sub-routine on success. It
+may also return (undef, STATUS_DECLINE) to decline the request, to allow
+delegation onto other filter providers in the LOAD_FILTERS chain of
+responsibility. On error, ($error, STATUS_ERROR) is returned where $error
+is an error message or Template::Exception object indicating the error
+that occurred.
+
+When the TOLERANT option is set, errors are automatically downgraded to
+a STATUS_DECLINE response.
+
+
+=head1 CONFIGURATION OPTIONS
+
+The following list details the configuration options that can be provided
+to the Template::Filters new() constructor.
+
+=over 4
+
+
+
+
+=item FILTERS
+
+The FILTERS option can be used to specify custom filters which can
+then be used with the FILTER directive like any other. These are
+added to the standard filters which are available by default. Filters
+specified via this option will mask any standard filters of the same
+name.
+
+The FILTERS option should be specified as a reference to a hash array
+in which each key represents the name of a filter. The corresponding
+value should contain a reference to an array containing a subroutine
+reference and a flag which indicates if the filter is static (0) or
+dynamic (1). A filter may also be specified as a solitary subroutine
+reference and is assumed to be static.
+
+ $filters = Template::Filters->new({
+ FILTERS => {
+ 'sfilt1' => \&static_filter, # static
+ 'sfilt2' => [ \&static_filter, 0 ], # same as above
+ 'dfilt1' => [ \&dyanamic_filter_factory, 1 ],
+ },
+ });
+
+Additional filters can be specified at any time by calling the
+define_filter() method on the current Template::Context object.
+The method accepts a filter name, a reference to a filter
+subroutine and an optional flag to indicate if the filter is
+dynamic.
+
+ my $context = $template->context();
+ $context->define_filter('new_html', \&new_html);
+ $context->define_filter('new_repeat', \&new_repeat, 1);
+
+Static filters are those where a single subroutine reference is used
+for all invocations of a particular filter. Filters that don't accept
+any configuration parameters (e.g. 'html') can be implemented
+statically. The subroutine reference is simply returned when that
+particular filter is requested. The subroutine is called to filter
+the output of a template block which is passed as the only argument.
+The subroutine should return the modified text.
+
+ sub static_filter {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+
+The following template fragment:
+
+ [% FILTER sfilt1 %]
+ Blah blah blah.
+ [% END %]
+
+is approximately equivalent to:
+
+ &static_filter("\nBlah blah blah.\n");
+
+Filters that can accept parameters (e.g. 'truncate') should be
+implemented dynamically. In this case, the subroutine is taken to be
+a filter 'factory' that is called to create a unique filter subroutine
+each time one is requested. A reference to the current
+Template::Context object is passed as the first parameter, followed by
+any additional parameters specified. The subroutine should return
+another subroutine reference (usually a closure) which implements the
+filter.
+
+ sub dynamic_filter_factory {
+ my ($context, @args) = @_;
+
+ return sub {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+ }
+
+The following template fragment:
+
+ [% FILTER dfilt1(123, 456) %]
+ Blah blah blah
+ [% END %]
+
+is approximately equivalent to:
+
+ my $filter = &dynamic_filter_factory($context, 123, 456);
+ &$filter("\nBlah blah blah.\n");
+
+See the FILTER directive for further examples.
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Filters module by setting it to include the DEBUG_FILTERS
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS,
+ });
+
+
+
+
+=back
+
+=head1 TEMPLATE TOOLKIT FILTERS
+
+The following standard filters are distributed with the Template Toolkit.
+
+
+
+=head2 format(format)
+
+The 'format' filter takes a format string as a parameter (as per
+printf()) and formats each line of text accordingly.
+
+ [% FILTER format('<!-- %-40s -->') %]
+ This is a block of text filtered
+ through the above format.
+ [% END %]
+
+output:
+
+ <!-- This is a block of text filtered -->
+ <!-- through the above format. -->
+
+=head2 upper
+
+Folds the input to UPPER CASE.
+
+ [% "hello world" FILTER upper %]
+
+output:
+
+ HELLO WORLD
+
+=head2 lower
+
+Folds the input to lower case.
+
+ [% "Hello World" FILTER lower %]
+
+output:
+
+ hello world
+
+=head2 ucfirst
+
+Folds the first character of the input to UPPER CASE.
+
+ [% "hello" FILTER ucfirst %]
+
+output:
+
+ Hello
+
+=head2 lcfirst
+
+Folds the first character of the input to lower case.
+
+ [% "HELLO" FILTER lcfirst %]
+
+output:
+
+ hELLO
+
+=head2 trim
+
+Trims any leading or trailing whitespace from the input text. Particularly
+useful in conjunction with INCLUDE, PROCESS, etc., having the same effect
+as the TRIM configuration option.
+
+ [% INCLUDE myfile | trim %]
+
+=head2 collapse
+
+Collapse any whitespace sequences in the input text into a single space.
+Leading and trailing whitespace (which would be reduced to a single space)
+is removed, as per trim.
+
+ [% FILTER collapse %]
+
+ The cat
+
+ sat on
+
+ the mat
+
+ [% END %]
+
+output:
+
+ The cat sat on the mat
+
+=head2 html
+
+Converts the characters 'E<lt>', 'E<gt>' and '&' to '&lt;', '&gt;' and
+'&amp;', respectively, protecting them from being interpreted as
+representing HTML tags or entities.
+
+ [% FILTER html %]
+ Binary "<=>" returns -1, 0, or 1 depending on...
+ [% END %]
+
+output:
+
+ Binary "&lt;=&gt;" returns -1, 0, or 1 depending on...
+
+=head2 html_entity
+
+The html filter is fast and simple but it doesn't encode the full
+range of HTML entities that your text may contain. The html_entity
+filter uses either the Apache::Util module (which is written in C and
+is therefore faster) or the HTML::Entities module (written in Perl but
+equally as comprehensive) to perform the encoding. If one or other of
+these modules are installed on your system then the text will be
+encoded (via the escape_html() or encode_entities() subroutines
+respectively) to convert all extended characters into their
+appropriate HTML entities (e.g. converting 'é' to '&eacute;'). If
+neither module is available on your system then an 'html_entity' exception
+will be thrown reporting an appropriate message.
+
+For further information on HTML entity encoding, see
+http://www.w3.org/TR/REC-html40/sgml/entities.html.
+
+=head2 html_para
+
+This filter formats a block of text into HTML paragraphs. A sequence of
+two or more newlines is used as the delimiter for paragraphs which are
+then wrapped in HTML E<lt>pE<gt>...E<lt>/pE<gt> tags.
+
+ [% FILTER html_para %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ <p>
+ The cat sat on the mat.
+ </p>
+
+ <p>
+ Mary had a little lamb.
+ </p>
+
+=head2 html_break / html_para_break
+
+Similar to the html_para filter described above, but uses the HTML tag
+sequence E<lt>brE<gt>E<lt>brE<gt> to join paragraphs.
+
+ [% FILTER html_break %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.
+ <br>
+ <br>
+ Mary had a little lamb.
+
+=head2 html_line_break
+
+This filter replaces any newlines with E<lt>brE<gt> HTML tags,
+thus preserving the line breaks of the original text in the
+HTML output.
+
+ [% FILTER html_line_break %]
+ The cat sat on the mat.
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.<br>
+ Mary had a little lamb.<br>
+
+=head2 uri
+
+This filter URI escapes the input text, converting any characters
+outside of the permitted URI character set (as defined by RFC 2396)
+into a C<%nn> hex escape.
+
+ [% 'my file.html' | uri %]
+
+output:
+
+ my%20file.html
+
+Note that URI escaping isn't always enough when generating hyperlinks in
+an HTML document. The C<&> character, for example, is valid in a URI and
+will not be escaped by the URI filter. In this case you should also filter
+the text through the 'html' filter.
+
+ <a href="[% filename | uri | html %]">click here</a>
+
+=head2 indent(pad)
+
+Indents the text block by a fixed pad string or width. The 'pad' argument
+can be specified as a string, or as a numerical value to indicate a pad
+width (spaces). Defaults to 4 spaces if unspecified.
+
+ [% FILTER indent('ME> ') %]
+ blah blah blah
+ cabbages, rhubard, onions
+ [% END %]
+
+output:
+
+ ME> blah blah blah
+ ME> cabbages, rhubard, onions
+
+=head2 truncate(length)
+
+Truncates the text block to the length specified, or a default length of
+32. Truncated text will be terminated with '...' (i.e. the '...' falls
+inside the required length, rather than appending to it).
+
+ [% FILTER truncate(21) %]
+ I have much to say on this matter that has previously
+ been said on more than one occasion.
+ [% END %]
+
+output:
+
+ I have much to say...
+
+=head2 repeat(iterations)
+
+Repeats the text block for as many iterations as are specified (default: 1).
+
+ [% FILTER repeat(3) %]
+ We want more beer and we want more beer,
+ [% END %]
+ We are the more beer wanters!
+
+output:
+
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We are the more beer wanters!
+
+=head2 remove(string)
+
+Searches the input text for any occurrences of the specified string and
+removes them. A Perl regular expression may be specified as the search
+string.
+
+ [% "The cat sat on the mat" FILTER remove('\s+') %]
+
+output:
+
+ Thecatsatonthemat
+
+=head2 replace(search, replace)
+
+Similar to the remove filter described above, but taking a second parameter
+which is used as a replacement string for instances of the search string.
+
+ [% "The cat sat on the mat" | replace('\s+', '_') %]
+
+output:
+
+ The_cat_sat_on_the_mat
+
+=head2 redirect(file, options)
+
+The 'redirect' filter redirects the output of the block into a separate
+file, specified relative to the OUTPUT_PATH configuration item.
+
+ [% FOREACH user = myorg.userlist %]
+ [% FILTER redirect("users/${user.id}.html") %]
+ [% INCLUDE userinfo %]
+ [% END %]
+ [% END %]
+
+or more succinctly, using side-effect notation:
+
+ [% INCLUDE userinfo
+ FILTER redirect("users/${user.id}.html")
+ FOREACH user = myorg.userlist
+ %]
+
+A 'file' exception will be thrown if the OUTPUT_PATH option is undefined.
+
+An optional 'binmode' argument can follow the filename to explicitly set
+the output file to binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", binmode=1) %]
+
+For backwards compatibility with earlier versions, a single true/false
+value can be used to set binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", 1) %]
+
+For the sake of future compatibility and clarity, if nothing else, we
+would strongly recommend you explicitly use the named 'binmode' option
+as shown in the first example.
+
+=head2 eval / evaltt
+
+The 'eval' filter evaluates the block as template text, processing
+any directives embedded within it. This allows template variables to
+contain template fragments, or for some method to be provided for
+returning template fragments from an external source such as a
+database, which can then be processed in the template as required.
+
+ my $vars = {
+ fragment => "The cat sat on the [% place %]",
+ };
+ $template->process($file, $vars);
+
+The following example:
+
+ [% fragment | eval %]
+
+is therefore equivalent to
+
+ The cat sat on the [% place %]
+
+The 'evaltt' filter is provided as an alias for 'eval'.
+
+=head2 perl / evalperl
+
+The 'perl' filter evaluates the block as Perl code. The EVAL_PERL
+option must be set to a true value or a 'perl' exception will be
+thrown.
+
+ [% my_perl_code | perl %]
+
+In most cases, the [% PERL %] ... [% END %] block should suffice for
+evaluating Perl code, given that template directives are processed
+before being evaluate as Perl. Thus, the previous example could have
+been written in the more verbose form:
+
+ [% PERL %]
+ [% my_perl_code %]
+ [% END %]
+
+as well as
+
+ [% FILTER perl %]
+ [% my_perl_code %]
+ [% END %]
+
+The 'evalperl' filter is provided as an alias for 'perl' for backwards
+compatibility.
+
+=head2 stdout(options)
+
+The stdout filter prints the output generated by the enclosing block to
+STDOUT. The 'binmode' option can be passed as either a named parameter
+or a single argument to set STDOUT to binary mode (see the
+binmode perl function).
+
+ [% PROCESS something/cool
+ FILTER stdout(binmode=1) # recommended %]
+
+ [% PROCESS something/cool
+ FILTER stdout(1) # alternate %]
+
+The stdout filter can be used to force binmode on STDOUT, or also inside
+redirect, null or stderr blocks to make sure that particular output goes
+to stdout. See the null filter below for an example.
+
+=head2 stderr
+
+The stderr filter prints the output generated by the enclosing block to
+STDERR.
+
+=head2 null
+
+The null filter prints nothing. This is useful for plugins whose
+methods return values that you don't want to appear in the output.
+Rather than assigning every plugin method call to a dummy variable
+to silence it, you can wrap the block in a null filter:
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ im.arc(50,50,95,75,0,360,blue);
+ im.fill(50,50,red);
+ im.png | stdout(1);
+ END;
+ -%]
+
+Notice the use of the stdout filter to ensure that a particular expression
+generates output to stdout (in this case in binary mode).
+
+=head2 latex(outputType)
+
+Passes the text block to LaTeX and produces either PDF, DVI or
+PostScript output. The 'outputType' argument determines the output
+format and it should be set to one of the strings: "pdf" (default),
+"dvi", or "ps".
+
+The text block should be a complete LaTeX source file.
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+
+ \begin{document}
+
+ \title{A Sample TT2 \LaTeX\ Source File}
+ \author{Craig Barratt}
+ \maketitle
+
+ \section{Introduction}
+ This is some text.
+
+ \end{document}
+ [% END -%]
+
+The output will be a PDF file. You should be careful not to prepend or
+append any extraneous characters or text outside the FILTER block,
+since this text will wrap the (binary) output of the latex filter.
+Notice the END directive uses '-%]' for the END_TAG to remove the
+trailing new line.
+
+One example where you might prepend text is in a CGI script where
+you might include the Content-Type before the latex output, eg:
+
+ Content-Type: application/pdf
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END -%]
+
+In other cases you might use the redirect filter to put the output
+into a file, rather than delivering it to stdout. This might be
+suitable for batch scripts:
+
+ [% output = FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END; output | redirect("document.pdf", 1) -%]
+
+(Notice the second argument to redirect to force binary mode.)
+
+Note that the latex filter runs one or two external programs, so it
+isn't very fast. But for modest documents the performance is adequate,
+even for interactive applications.
+
+A error of type 'latex' will be thrown if there is an error reported
+by latex, pdflatex or dvips.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.77, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>, L<Template::Manual::Filters|Template::Manual::Filters>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Grammar.pm b/lib/Template/Grammar.pm
new file mode 100644
index 0000000..8635426
--- /dev/null
+++ b/lib/Template/Grammar.pm
@@ -0,0 +1,6179 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Grammar
+#
+# DESCRIPTION
+# Grammar file for the Template Toolkit language containing token
+# definitions and parser state/rules tables generated by Parse::Yapp.
+#
+# AUTHOR
+# Andy Wardley <abw@wardley.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# NOTE: this module is constructed from the parser/Grammar.pm.skel
+# file by running the parser/yc script. You only need to do this if
+# you have modified the grammar in the parser/Parser.yp file and need
+# to-recompile it. See the README in the 'parser' directory for more
+# information (sub-directory of the Template distribution).
+#
+#------------------------------------------------------------------------
+#
+# $Id: Grammar.pm,v 2.22 2004/01/13 16:19:10 abw Exp $
+#
+#========================================================================
+
+package Template::Grammar;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.22 $ =~ /(\d+)\.(\d+)/);
+
+my (@RESERVED, %CMPOP, $LEXTABLE, $RULES, $STATES);
+my ($factory, $rawstart);
+
+
+#========================================================================
+
+# Reserved words, comparison and binary operators
+#========================================================================
+
+@RESERVED = qw(
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER BLOCK END
+ USE PLUGIN FILTER MACRO PERL RAWPERL TO STEP AND OR NOT DIV MOD
+ IF UNLESS ELSE ELSIF FOR NEXT WHILE SWITCH CASE META IN
+ TRY THROW CATCH FINAL LAST RETURN STOP CLEAR VIEW DEBUG
+ );
+
+# for historical reasons, != and == are converted to ne and eq to perform
+# stringwise comparison (mainly because it doesn't generate "non-numerical
+# comparison" warnings which != and == can) but the others (e.g. < > <= >=)
+# are not converted to their stringwise equivalents. I added 'gt' et al,
+# briefly for v2.04d and then took them out again in 2.04e.
+
+%CMPOP = qw(
+ != ne
+ == eq
+ < <
+ > >
+ >= >=
+ <= <=
+);
+
+
+#========================================================================
+# Lexer Token Table
+#========================================================================
+
+# lookup table used by lexer is initialised with special-cases
+$LEXTABLE = {
+ 'FOREACH' => 'FOR',
+ 'BREAK' => 'LAST',
+ '&&' => 'AND',
+ '||' => 'OR',
+ '!' => 'NOT',
+ '|' => 'FILTER',
+ '.' => 'DOT',
+ '_' => 'CAT',
+ '..' => 'TO',
+# ':' => 'MACRO',
+ '=' => 'ASSIGN',
+ '=>' => 'ASSIGN',
+# '->' => 'ARROW',
+ ',' => 'COMMA',
+ '\\' => 'REF',
+ 'and' => 'AND', # explicitly specified so that qw( and or
+ 'or' => 'OR', # not ) can always be used in lower case,
+ 'not' => 'NOT', # regardless of ANYCASE flag
+ 'mod' => 'MOD',
+ 'div' => 'DIV',
+};
+
+# localise the temporary variables needed to complete lexer table
+{
+# my @tokens = qw< ( ) [ ] { } ${ $ / ; : ? >;
+ my @tokens = qw< ( ) [ ] { } ${ $ + / ; : ? >;
+ my @cmpop = keys %CMPOP;
+# my @binop = qw( + - * % ); # '/' above, in @tokens
+ my @binop = qw( - * % ); # '+' and '/' above, in @tokens
+
+ # fill lexer table, slice by slice, with reserved words and operators
+ @$LEXTABLE{ @RESERVED, @cmpop, @binop, @tokens }
+ = ( @RESERVED, ('CMPOP') x @cmpop, ('BINOP') x @binop, @tokens );
+}
+
+
+#========================================================================
+# CLASS METHODS
+#========================================================================
+
+sub new {
+ my $class = shift;
+ bless {
+ LEXTABLE => $LEXTABLE,
+ STATES => $STATES,
+ RULES => $RULES,
+ }, $class;
+}
+
+# update method to set package-scoped $factory lexical
+sub install_factory {
+ my ($self, $new_factory) = @_;
+ $factory = $new_factory;
+}
+
+
+#========================================================================
+# States
+#========================================================================
+
+$STATES = [
+ {#State 0
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'template' => 52,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'block' => 72,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 1
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'setlist' => 76,
+ 'item' => 39,
+ 'assign' => 19,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 2
+ DEFAULT => -130
+ },
+ {#State 3
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 79,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 4
+ DEFAULT => -23
+ },
+ {#State 5
+ ACTIONS => {
+ ";" => 80
+ }
+ },
+ {#State 6
+ DEFAULT => -37
+ },
+ {#State 7
+ DEFAULT => -14
+ },
+ {#State 8
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 90,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 9
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "]" => 94,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 96,
+ 'item' => 39,
+ 'range' => 93,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 95,
+ 'list' => 92,
+ 'lterm' => 56
+ }
+ },
+ {#State 10
+ ACTIONS => {
+ ";" => 97
+ }
+ },
+ {#State 11
+ DEFAULT => -5
+ },
+ {#State 12
+ ACTIONS => {
+ ";" => -20
+ },
+ DEFAULT => -27
+ },
+ {#State 13
+ DEFAULT => -78,
+ GOTOS => {
+ '@5-1' => 98
+ }
+ },
+ {#State 14
+ ACTIONS => {
+ 'IDENT' => 99
+ },
+ DEFAULT => -87,
+ GOTOS => {
+ 'blockargs' => 102,
+ 'metadata' => 101,
+ 'meta' => 100
+ }
+ },
+ {#State 15
+ ACTIONS => {
+ 'IDENT' => 99
+ },
+ GOTOS => {
+ 'metadata' => 103,
+ 'meta' => 100
+ }
+ },
+ {#State 16
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 105
+ },
+ DEFAULT => -109
+ },
+ {#State 17
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 106,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 18
+ ACTIONS => {
+ 'IDENT' => 107
+ }
+ },
+ {#State 19
+ DEFAULT => -149
+ },
+ {#State 20
+ DEFAULT => -12
+ },
+ {#State 21
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 108,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'loopvar' => 110,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 109,
+ 'lterm' => 56
+ }
+ },
+ {#State 22
+ DEFAULT => -40
+ },
+ {#State 23
+ DEFAULT => -127
+ },
+ {#State 24
+ DEFAULT => -6
+ },
+ {#State 25
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 115,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 26
+ DEFAULT => -113
+ },
+ {#State 27
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 119
+ }
+ },
+ {#State 28
+ ACTIONS => {
+ 'LITERAL' => 124,
+ 'FILENAME' => 83,
+ 'IDENT' => 120,
+ 'NUMBER' => 84
+ },
+ DEFAULT => -87,
+ GOTOS => {
+ 'blockargs' => 123,
+ 'filepart' => 87,
+ 'filename' => 122,
+ 'blockname' => 121,
+ 'metadata' => 101,
+ 'meta' => 100
+ }
+ },
+ {#State 29
+ DEFAULT => -43
+ },
+ {#State 30
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 129,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -119,
+ GOTOS => {
+ 'params' => 128,
+ 'hash' => 125,
+ 'item' => 126,
+ 'param' => 127
+ }
+ },
+ {#State 31
+ DEFAULT => -25
+ },
+ {#State 32
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 130,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 33
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -2,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 131,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 34
+ DEFAULT => -22
+ },
+ {#State 35
+ DEFAULT => -24
+ },
+ {#State 36
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 132,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 37
+ ACTIONS => {
+ "\"" => 60,
+ "\$" => 43,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ 'REF' => 27,
+ 'NUMBER' => 26,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 133,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77
+ }
+ },
+ {#State 38
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 134,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 39
+ ACTIONS => {
+ "(" => 135
+ },
+ DEFAULT => -128
+ },
+ {#State 40
+ ACTIONS => {
+ ";" => 136
+ }
+ },
+ {#State 41
+ DEFAULT => -38
+ },
+ {#State 42
+ DEFAULT => -11
+ },
+ {#State 43
+ ACTIONS => {
+ 'IDENT' => 137
+ }
+ },
+ {#State 44
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 138,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 45
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 139,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 46
+ DEFAULT => -42
+ },
+ {#State 47
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 140,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 48
+ ACTIONS => {
+ 'IF' => 144,
+ 'FILTER' => 143,
+ 'FOR' => 142,
+ 'WHILE' => 146,
+ 'WRAPPER' => 145,
+ 'UNLESS' => 141
+ }
+ },
+ {#State 49
+ DEFAULT => -39
+ },
+ {#State 50
+ DEFAULT => -10
+ },
+ {#State 51
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 147,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 52
+ ACTIONS => {
+ '' => 148
+ }
+ },
+ {#State 53
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 57,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 149,
+ 'term' => 58,
+ 'expr' => 151,
+ 'assign' => 150,
+ 'lterm' => 56
+ }
+ },
+ {#State 54
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 152,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 55
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 153,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 56
+ DEFAULT => -103
+ },
+ {#State 57
+ ACTIONS => {
+ 'ASSIGN' => 154
+ },
+ DEFAULT => -112
+ },
+ {#State 58
+ DEFAULT => -146
+ },
+ {#State 59
+ DEFAULT => -15
+ },
+ {#State 60
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 155
+ }
+ },
+ {#State 61
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 156,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 62
+ ACTIONS => {
+ ";" => -16,
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -26
+ },
+ {#State 63
+ DEFAULT => -13
+ },
+ {#State 64
+ DEFAULT => -36
+ },
+ {#State 65
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 167,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 66
+ DEFAULT => -9
+ },
+ {#State 67
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 168,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 68
+ DEFAULT => -104
+ },
+ {#State 69
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'setlist' => 169,
+ 'item' => 39,
+ 'assign' => 19,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 70
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -19,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 71
+ DEFAULT => -8
+ },
+ {#State 72
+ DEFAULT => -1
+ },
+ {#State 73
+ DEFAULT => -21
+ },
+ {#State 74
+ ACTIONS => {
+ 'ASSIGN' => 172,
+ 'DOT' => 104
+ }
+ },
+ {#State 75
+ ACTIONS => {
+ 'ASSIGN' => 154
+ }
+ },
+ {#State 76
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -30,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 77
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -109
+ },
+ {#State 78
+ DEFAULT => -112
+ },
+ {#State 79
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 173,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 80
+ DEFAULT => -7
+ },
+ {#State 81
+ DEFAULT => -173
+ },
+ {#State 82
+ DEFAULT => -166
+ },
+ {#State 83
+ DEFAULT => -172
+ },
+ {#State 84
+ DEFAULT => -174
+ },
+ {#State 85
+ ACTIONS => {
+ 'DOT' => 174
+ },
+ DEFAULT => -168
+ },
+ {#State 86
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 175
+ }
+ },
+ {#State 87
+ DEFAULT => -171
+ },
+ {#State 88
+ DEFAULT => -169
+ },
+ {#State 89
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 176
+ }
+ },
+ {#State 90
+ DEFAULT => -35
+ },
+ {#State 91
+ ACTIONS => {
+ "+" => 177,
+ "(" => 178
+ },
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 179
+ }
+ },
+ {#State 92
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 182,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "]" => 180,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 181,
+ 'lterm' => 56
+ }
+ },
+ {#State 93
+ ACTIONS => {
+ "]" => 183
+ }
+ },
+ {#State 94
+ DEFAULT => -107
+ },
+ {#State 95
+ DEFAULT => -116
+ },
+ {#State 96
+ ACTIONS => {
+ 'TO' => 184
+ },
+ DEFAULT => -104
+ },
+ {#State 97
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 185,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 98
+ ACTIONS => {
+ ";" => 186
+ }
+ },
+ {#State 99
+ ACTIONS => {
+ 'ASSIGN' => 187
+ }
+ },
+ {#State 100
+ DEFAULT => -99
+ },
+ {#State 101
+ ACTIONS => {
+ 'COMMA' => 189,
+ 'IDENT' => 99
+ },
+ DEFAULT => -86,
+ GOTOS => {
+ 'meta' => 188
+ }
+ },
+ {#State 102
+ ACTIONS => {
+ ";" => 190
+ }
+ },
+ {#State 103
+ ACTIONS => {
+ 'COMMA' => 189,
+ 'IDENT' => 99
+ },
+ DEFAULT => -17,
+ GOTOS => {
+ 'meta' => 188
+ }
+ },
+ {#State 104
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ 'NUMBER' => 192,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 191
+ }
+ },
+ {#State 105
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 195,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 194,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 106
+ DEFAULT => -33
+ },
+ {#State 107
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 198,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 199,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 197,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 108
+ ACTIONS => {
+ 'IN' => 201,
+ 'ASSIGN' => 200
+ },
+ DEFAULT => -130
+ },
+ {#State 109
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 202
+ }
+ },
+ {#State 110
+ ACTIONS => {
+ ";" => 203
+ }
+ },
+ {#State 111
+ ACTIONS => {
+ 'ASSIGN' => -130
+ },
+ DEFAULT => -173
+ },
+ {#State 112
+ ACTIONS => {
+ 'ASSIGN' => 204
+ }
+ },
+ {#State 113
+ DEFAULT => -159
+ },
+ {#State 114
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 205,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 175
+ }
+ },
+ {#State 115
+ ACTIONS => {
+ ";" => 206
+ }
+ },
+ {#State 116
+ ACTIONS => {
+ 'ASSIGN' => -161
+ },
+ DEFAULT => -169
+ },
+ {#State 117
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 207
+ }
+ },
+ {#State 118
+ DEFAULT => -158
+ },
+ {#State 119
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -110
+ },
+ {#State 120
+ ACTIONS => {
+ 'ASSIGN' => 187
+ },
+ DEFAULT => -173
+ },
+ {#State 121
+ DEFAULT => -83
+ },
+ {#State 122
+ ACTIONS => {
+ 'DOT' => 174
+ },
+ DEFAULT => -84
+ },
+ {#State 123
+ ACTIONS => {
+ ";" => 208
+ }
+ },
+ {#State 124
+ DEFAULT => -85
+ },
+ {#State 125
+ ACTIONS => {
+ "}" => 209
+ }
+ },
+ {#State 126
+ ACTIONS => {
+ 'ASSIGN' => 210
+ }
+ },
+ {#State 127
+ DEFAULT => -122
+ },
+ {#State 128
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 212,
+ 'LITERAL' => 129,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -118,
+ GOTOS => {
+ 'item' => 126,
+ 'param' => 211
+ }
+ },
+ {#State 129
+ ACTIONS => {
+ 'ASSIGN' => 213
+ }
+ },
+ {#State 130
+ DEFAULT => -73
+ },
+ {#State 131
+ DEFAULT => -4
+ },
+ {#State 132
+ ACTIONS => {
+ ";" => 214
+ }
+ },
+ {#State 133
+ ACTIONS => {
+ "}" => 215
+ }
+ },
+ {#State 134
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -142
+ },
+ {#State 135
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 216
+ }
+ },
+ {#State 136
+ DEFAULT => -76,
+ GOTOS => {
+ '@4-2' => 217
+ }
+ },
+ {#State 137
+ DEFAULT => -132
+ },
+ {#State 138
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 218,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 139
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -29
+ },
+ {#State 140
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -28
+ },
+ {#State 141
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 219,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 142
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 108,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'loopvar' => 220,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 109,
+ 'lterm' => 56
+ }
+ },
+ {#State 143
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 221,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 144
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 222,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 145
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 223,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 146
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 224,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 147
+ DEFAULT => -41
+ },
+ {#State 148
+ DEFAULT => 0
+ },
+ {#State 149
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 172
+ },
+ DEFAULT => -109
+ },
+ {#State 150
+ ACTIONS => {
+ ")" => 225
+ }
+ },
+ {#State 151
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ ")" => 226,
+ 'OR' => 162
+ }
+ },
+ {#State 152
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 227,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 153
+ ACTIONS => {
+ ";" => 228
+ }
+ },
+ {#State 154
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 229,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 155
+ ACTIONS => {
+ "\"" => 234,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 156
+ DEFAULT => -34
+ },
+ {#State 157
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 235,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 158
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 236,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 159
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 237,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 160
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 238,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 161
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 239,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 162
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 240,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 163
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 241,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 164
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 242,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 165
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 243,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 166
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 244,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 167
+ DEFAULT => -32
+ },
+ {#State 168
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 245,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 169
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -31,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 170
+ DEFAULT => -147
+ },
+ {#State 171
+ DEFAULT => -148
+ },
+ {#State 172
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 246,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 173
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 247,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 174
+ ACTIONS => {
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 248
+ }
+ },
+ {#State 175
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 249
+ }
+ },
+ {#State 176
+ ACTIONS => {
+ "\"" => 250,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 177
+ ACTIONS => {
+ "\"" => 89,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'filename' => 85,
+ 'name' => 251
+ }
+ },
+ {#State 178
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 252
+ }
+ },
+ {#State 179
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -163,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 180
+ DEFAULT => -105
+ },
+ {#State 181
+ DEFAULT => -114
+ },
+ {#State 182
+ DEFAULT => -115
+ },
+ {#State 183
+ DEFAULT => -106
+ },
+ {#State 184
+ ACTIONS => {
+ "\"" => 60,
+ "\$" => 43,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ 'REF' => 27,
+ 'NUMBER' => 26,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 259,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77
+ }
+ },
+ {#State 185
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 261
+ }
+ },
+ {#State 186
+ ACTIONS => {
+ 'TEXT' => 263
+ }
+ },
+ {#State 187
+ ACTIONS => {
+ "\"" => 266,
+ 'LITERAL' => 265,
+ 'NUMBER' => 264
+ }
+ },
+ {#State 188
+ DEFAULT => -97
+ },
+ {#State 189
+ DEFAULT => -98
+ },
+ {#State 190
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'template' => 267,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 72,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 191
+ DEFAULT => -125
+ },
+ {#State 192
+ DEFAULT => -126
+ },
+ {#State 193
+ ACTIONS => {
+ ";" => 268
+ }
+ },
+ {#State 194
+ DEFAULT => -89
+ },
+ {#State 195
+ ACTIONS => {
+ ";" => -150,
+ "+" => 157,
+ 'LITERAL' => -150,
+ 'IDENT' => -150,
+ 'CAT' => 163,
+ "\$" => -150,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ 'COMMA' => -150,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162,
+ "\${" => -150
+ },
+ DEFAULT => -26
+ },
+ {#State 196
+ DEFAULT => -92
+ },
+ {#State 197
+ DEFAULT => -91
+ },
+ {#State 198
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 57,
+ 'IDENT' => 269,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'margs' => 270,
+ 'node' => 23,
+ 'ident' => 149,
+ 'term' => 58,
+ 'expr' => 151,
+ 'assign' => 150,
+ 'lterm' => 56
+ }
+ },
+ {#State 199
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -26
+ },
+ {#State 200
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 271,
+ 'lterm' => 56
+ }
+ },
+ {#State 201
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 272,
+ 'lterm' => 56
+ }
+ },
+ {#State 202
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -64,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 203
+ DEFAULT => -56,
+ GOTOS => {
+ '@1-3' => 273
+ }
+ },
+ {#State 204
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 274,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 205
+ ACTIONS => {
+ 'ASSIGN' => -132
+ },
+ DEFAULT => -130
+ },
+ {#State 206
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 275,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 207
+ ACTIONS => {
+ "\"" => 276,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 208
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 277,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 209
+ DEFAULT => -108
+ },
+ {#State 210
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 278,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 211
+ DEFAULT => -120
+ },
+ {#State 212
+ DEFAULT => -121
+ },
+ {#State 213
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 279,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 214
+ DEFAULT => -74,
+ GOTOS => {
+ '@3-3' => 280
+ }
+ },
+ {#State 215
+ DEFAULT => -131
+ },
+ {#State 216
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ ")" => 281,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 217
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 282,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 218
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 283,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 219
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -47
+ },
+ {#State 220
+ DEFAULT => -58
+ },
+ {#State 221
+ DEFAULT => -81
+ },
+ {#State 222
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -45
+ },
+ {#State 223
+ DEFAULT => -66
+ },
+ {#State 224
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -61
+ },
+ {#State 225
+ DEFAULT => -144
+ },
+ {#State 226
+ DEFAULT => -145
+ },
+ {#State 227
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 284,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 228
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 285,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 229
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -151
+ },
+ {#State 230
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -177
+ },
+ {#State 231
+ DEFAULT => -178
+ },
+ {#State 232
+ DEFAULT => -175
+ },
+ {#State 233
+ DEFAULT => -179
+ },
+ {#State 234
+ DEFAULT => -111
+ },
+ {#State 235
+ ACTIONS => {
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166
+ },
+ DEFAULT => -135
+ },
+ {#State 236
+ ACTIONS => {
+ ":" => 286,
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 237
+ ACTIONS => {
+ 'MOD' => 165
+ },
+ DEFAULT => -136
+ },
+ {#State 238
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -140
+ },
+ {#State 239
+ ACTIONS => {
+ "+" => 157,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166
+ },
+ DEFAULT => -133
+ },
+ {#State 240
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -141
+ },
+ {#State 241
+ ACTIONS => {
+ "+" => 157,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -139
+ },
+ {#State 242
+ ACTIONS => {
+ "+" => 157,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -138
+ },
+ {#State 243
+ DEFAULT => -137
+ },
+ {#State 244
+ ACTIONS => {
+ 'DIV' => 159,
+ 'MOD' => 165
+ },
+ DEFAULT => -134
+ },
+ {#State 245
+ DEFAULT => -59,
+ GOTOS => {
+ '@2-3' => 287
+ }
+ },
+ {#State 246
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -150
+ },
+ {#State 247
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 289
+ }
+ },
+ {#State 248
+ DEFAULT => -170
+ },
+ {#State 249
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -162,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 250
+ DEFAULT => -167
+ },
+ {#State 251
+ DEFAULT => -165
+ },
+ {#State 252
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ ")" => 291,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 253
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 292
+ },
+ DEFAULT => -109
+ },
+ {#State 254
+ ACTIONS => {
+ "(" => 135,
+ 'ASSIGN' => 210
+ },
+ DEFAULT => -128
+ },
+ {#State 255
+ DEFAULT => -153
+ },
+ {#State 256
+ ACTIONS => {
+ 'ASSIGN' => 213
+ },
+ DEFAULT => -112
+ },
+ {#State 257
+ DEFAULT => -152
+ },
+ {#State 258
+ DEFAULT => -155
+ },
+ {#State 259
+ DEFAULT => -117
+ },
+ {#State 260
+ ACTIONS => {
+ ";" => 293
+ }
+ },
+ {#State 261
+ ACTIONS => {
+ 'END' => 294
+ }
+ },
+ {#State 262
+ ACTIONS => {
+ ";" => 296,
+ 'DEFAULT' => 297,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'filename' => 295
+ }
+ },
+ {#State 263
+ ACTIONS => {
+ 'END' => 298
+ }
+ },
+ {#State 264
+ DEFAULT => -102
+ },
+ {#State 265
+ DEFAULT => -100
+ },
+ {#State 266
+ ACTIONS => {
+ 'TEXT' => 299
+ }
+ },
+ {#State 267
+ ACTIONS => {
+ 'END' => 300
+ }
+ },
+ {#State 268
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 301,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 269
+ ACTIONS => {
+ 'COMMA' => -96,
+ 'IDENT' => -96,
+ ")" => -96
+ },
+ DEFAULT => -130
+ },
+ {#State 270
+ ACTIONS => {
+ 'COMMA' => 304,
+ 'IDENT' => 302,
+ ")" => 303
+ }
+ },
+ {#State 271
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 305
+ }
+ },
+ {#State 272
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 306
+ }
+ },
+ {#State 273
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 307,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 274
+ DEFAULT => -157
+ },
+ {#State 275
+ ACTIONS => {
+ 'END' => 308
+ }
+ },
+ {#State 276
+ ACTIONS => {
+ 'ASSIGN' => -160
+ },
+ DEFAULT => -167
+ },
+ {#State 277
+ ACTIONS => {
+ 'END' => 309
+ }
+ },
+ {#State 278
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -124
+ },
+ {#State 279
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -123
+ },
+ {#State 280
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 310,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 281
+ DEFAULT => -129
+ },
+ {#State 282
+ ACTIONS => {
+ 'END' => 311
+ }
+ },
+ {#State 283
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 312
+ }
+ },
+ {#State 284
+ ACTIONS => {
+ 'CASE' => 313
+ },
+ DEFAULT => -55,
+ GOTOS => {
+ 'case' => 314
+ }
+ },
+ {#State 285
+ ACTIONS => {
+ 'END' => 315
+ }
+ },
+ {#State 286
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 316,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 287
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 317,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 288
+ ACTIONS => {
+ ";" => 318
+ }
+ },
+ {#State 289
+ ACTIONS => {
+ 'END' => 319
+ }
+ },
+ {#State 290
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 320,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 291
+ DEFAULT => -164
+ },
+ {#State 292
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 321,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 293
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 322,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 294
+ DEFAULT => -67
+ },
+ {#State 295
+ ACTIONS => {
+ 'DOT' => 174,
+ ";" => 323
+ }
+ },
+ {#State 296
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 324,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 297
+ ACTIONS => {
+ ";" => 325
+ }
+ },
+ {#State 298
+ DEFAULT => -79
+ },
+ {#State 299
+ ACTIONS => {
+ "\"" => 326
+ }
+ },
+ {#State 300
+ DEFAULT => -82
+ },
+ {#State 301
+ ACTIONS => {
+ 'END' => 327
+ }
+ },
+ {#State 302
+ DEFAULT => -94
+ },
+ {#State 303
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 199,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 328,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 304
+ DEFAULT => -95
+ },
+ {#State 305
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -62,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 306
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -63,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 307
+ ACTIONS => {
+ 'END' => 329
+ }
+ },
+ {#State 308
+ DEFAULT => -80
+ },
+ {#State 309
+ DEFAULT => -88
+ },
+ {#State 310
+ ACTIONS => {
+ 'END' => 330
+ }
+ },
+ {#State 311
+ DEFAULT => -77
+ },
+ {#State 312
+ ACTIONS => {
+ 'END' => 331
+ }
+ },
+ {#State 313
+ ACTIONS => {
+ ";" => 332,
+ 'DEFAULT' => 334,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 333,
+ 'lterm' => 56
+ }
+ },
+ {#State 314
+ ACTIONS => {
+ 'END' => 335
+ }
+ },
+ {#State 315
+ DEFAULT => -65
+ },
+ {#State 316
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -143
+ },
+ {#State 317
+ ACTIONS => {
+ 'END' => 336
+ }
+ },
+ {#State 318
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 337,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 319
+ DEFAULT => -46
+ },
+ {#State 320
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 338,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 321
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -154
+ },
+ {#State 322
+ DEFAULT => -71
+ },
+ {#State 323
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 339,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 324
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 340
+ }
+ },
+ {#State 325
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 341,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 326
+ DEFAULT => -101
+ },
+ {#State 327
+ DEFAULT => -93
+ },
+ {#State 328
+ DEFAULT => -90
+ },
+ {#State 329
+ DEFAULT => -57
+ },
+ {#State 330
+ DEFAULT => -75
+ },
+ {#State 331
+ DEFAULT => -44
+ },
+ {#State 332
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 342,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 333
+ ACTIONS => {
+ ";" => 343
+ }
+ },
+ {#State 334
+ ACTIONS => {
+ ";" => 344
+ }
+ },
+ {#State 335
+ DEFAULT => -51
+ },
+ {#State 336
+ DEFAULT => -60
+ },
+ {#State 337
+ DEFAULT => -49
+ },
+ {#State 338
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 345,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 339
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 346
+ }
+ },
+ {#State 340
+ DEFAULT => -70
+ },
+ {#State 341
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 347
+ }
+ },
+ {#State 342
+ DEFAULT => -54
+ },
+ {#State 343
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 348,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 344
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 349,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 345
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 350
+ }
+ },
+ {#State 346
+ DEFAULT => -68
+ },
+ {#State 347
+ DEFAULT => -69
+ },
+ {#State 348
+ ACTIONS => {
+ 'CASE' => 313
+ },
+ DEFAULT => -55,
+ GOTOS => {
+ 'case' => 351
+ }
+ },
+ {#State 349
+ DEFAULT => -53
+ },
+ {#State 350
+ DEFAULT => -48
+ },
+ {#State 351
+ DEFAULT => -52
+ }
+];
+
+
+#========================================================================
+# Rules
+#========================================================================
+
+$RULES = [
+ [#Rule 0
+ '$start', 2, undef
+ ],
+ [#Rule 1
+ 'template', 1,
+sub
+#line 64 "Parser.yp"
+{ $factory->template($_[1]) }
+ ],
+ [#Rule 2
+ 'block', 1,
+sub
+#line 67 "Parser.yp"
+{ $factory->block($_[1]) }
+ ],
+ [#Rule 3
+ 'block', 0,
+sub
+#line 68 "Parser.yp"
+{ $factory->block() }
+ ],
+ [#Rule 4
+ 'chunks', 2,
+sub
+#line 71 "Parser.yp"
+{ push(@{$_[1]}, $_[2])
+ if defined $_[2]; $_[1] }
+ ],
+ [#Rule 5
+ 'chunks', 1,
+sub
+#line 73 "Parser.yp"
+{ defined $_[1] ? [ $_[1] ] : [ ] }
+ ],
+ [#Rule 6
+ 'chunk', 1,
+sub
+#line 76 "Parser.yp"
+{ $factory->textblock($_[1]) }
+ ],
+ [#Rule 7
+ 'chunk', 2,
+sub
+#line 77 "Parser.yp"
+{ return '' unless $_[1];
+ $_[0]->location() . $_[1];
+ }
+ ],
+ [#Rule 8
+ 'statement', 1, undef
+ ],
+ [#Rule 9
+ 'statement', 1, undef
+ ],
+ [#Rule 10
+ 'statement', 1, undef
+ ],
+ [#Rule 11
+ 'statement', 1, undef
+ ],
+ [#Rule 12
+ 'statement', 1, undef
+ ],
+ [#Rule 13
+ 'statement', 1, undef
+ ],
+ [#Rule 14
+ 'statement', 1, undef
+ ],
+ [#Rule 15
+ 'statement', 1, undef
+ ],
+ [#Rule 16
+ 'statement', 1,
+sub
+#line 90 "Parser.yp"
+{ $factory->get($_[1]) }
+ ],
+ [#Rule 17
+ 'statement', 2,
+sub
+#line 91 "Parser.yp"
+{ $_[0]->add_metadata($_[2]); }
+ ],
+ [#Rule 18
+ 'statement', 0, undef
+ ],
+ [#Rule 19
+ 'directive', 1,
+sub
+#line 95 "Parser.yp"
+{ $factory->set($_[1]) }
+ ],
+ [#Rule 20
+ 'directive', 1, undef
+ ],
+ [#Rule 21
+ 'directive', 1, undef
+ ],
+ [#Rule 22
+ 'directive', 1, undef
+ ],
+ [#Rule 23
+ 'directive', 1, undef
+ ],
+ [#Rule 24
+ 'directive', 1, undef
+ ],
+ [#Rule 25
+ 'directive', 1, undef
+ ],
+ [#Rule 26
+ 'atomexpr', 1,
+sub
+#line 109 "Parser.yp"
+{ $factory->get($_[1]) }
+ ],
+ [#Rule 27
+ 'atomexpr', 1, undef
+ ],
+ [#Rule 28
+ 'atomdir', 2,
+sub
+#line 113 "Parser.yp"
+{ $factory->get($_[2]) }
+ ],
+ [#Rule 29
+ 'atomdir', 2,
+sub
+#line 114 "Parser.yp"
+{ $factory->call($_[2]) }
+ ],
+ [#Rule 30
+ 'atomdir', 2,
+sub
+#line 115 "Parser.yp"
+{ $factory->set($_[2]) }
+ ],
+ [#Rule 31
+ 'atomdir', 2,
+sub
+#line 116 "Parser.yp"
+{ $factory->default($_[2]) }
+ ],
+ [#Rule 32
+ 'atomdir', 2,
+sub
+#line 117 "Parser.yp"
+{ $factory->insert($_[2]) }
+ ],
+ [#Rule 33
+ 'atomdir', 2,
+sub
+#line 118 "Parser.yp"
+{ $factory->include($_[2]) }
+ ],
+ [#Rule 34
+ 'atomdir', 2,
+sub
+#line 119 "Parser.yp"
+{ $factory->process($_[2]) }
+ ],
+ [#Rule 35
+ 'atomdir', 2,
+sub
+#line 120 "Parser.yp"
+{ $factory->throw($_[2]) }
+ ],
+ [#Rule 36
+ 'atomdir', 1,
+sub
+#line 121 "Parser.yp"
+{ $factory->return() }
+ ],
+ [#Rule 37
+ 'atomdir', 1,
+sub
+#line 122 "Parser.yp"
+{ $factory->stop() }
+ ],
+ [#Rule 38
+ 'atomdir', 1,
+sub
+#line 123 "Parser.yp"
+{ "\$output = '';"; }
+ ],
+ [#Rule 39
+ 'atomdir', 1,
+sub
+#line 124 "Parser.yp"
+{ $_[0]->{ INFOR } || $_[0]->{ INWHILE }
+ ? 'last LOOP;'
+ : 'last;' }
+ ],
+ [#Rule 40
+ 'atomdir', 1,
+sub
+#line 127 "Parser.yp"
+{ $_[0]->{ INFOR }
+ ? $factory->next()
+ : ($_[0]->{ INWHILE }
+ ? 'next LOOP;'
+ : 'next;') }
+ ],
+ [#Rule 41
+ 'atomdir', 2,
+sub
+#line 132 "Parser.yp"
+{ if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) {
+ $_[0]->{ DEBUG_DIRS } = ($1 eq 'on');
+ $factory->debug($_[2]);
+ }
+ else {
+ $_[0]->{ DEBUG_DIRS } ? $factory->debug($_[2]) : '';
+ }
+ }
+ ],
+ [#Rule 42
+ 'atomdir', 1, undef
+ ],
+ [#Rule 43
+ 'atomdir', 1, undef
+ ],
+ [#Rule 44
+ 'condition', 6,
+sub
+#line 145 "Parser.yp"
+{ $factory->if(@_[2, 4, 5]) }
+ ],
+ [#Rule 45
+ 'condition', 3,
+sub
+#line 146 "Parser.yp"
+{ $factory->if(@_[3, 1]) }
+ ],
+ [#Rule 46
+ 'condition', 6,
+sub
+#line 148 "Parser.yp"
+{ $factory->if("!($_[2])", @_[4, 5]) }
+ ],
+ [#Rule 47
+ 'condition', 3,
+sub
+#line 149 "Parser.yp"
+{ $factory->if("!($_[3])", $_[1]) }
+ ],
+ [#Rule 48
+ 'else', 5,
+sub
+#line 153 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2, 4] ]);
+ $_[5]; }
+ ],
+ [#Rule 49
+ 'else', 3,
+sub
+#line 155 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 50
+ 'else', 0,
+sub
+#line 156 "Parser.yp"
+{ [ undef ] }
+ ],
+ [#Rule 51
+ 'switch', 6,
+sub
+#line 160 "Parser.yp"
+{ $factory->switch(@_[2, 5]) }
+ ],
+ [#Rule 52
+ 'case', 5,
+sub
+#line 164 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2, 4] ]);
+ $_[5]; }
+ ],
+ [#Rule 53
+ 'case', 4,
+sub
+#line 166 "Parser.yp"
+{ [ $_[4] ] }
+ ],
+ [#Rule 54
+ 'case', 3,
+sub
+#line 167 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 55
+ 'case', 0,
+sub
+#line 168 "Parser.yp"
+{ [ undef ] }
+ ],
+ [#Rule 56
+ '@1-3', 0,
+sub
+#line 171 "Parser.yp"
+{ $_[0]->{ INFOR }++ }
+ ],
+ [#Rule 57
+ 'loop', 6,
+sub
+#line 172 "Parser.yp"
+{ $_[0]->{ INFOR }--;
+ $factory->foreach(@{$_[2]}, $_[5]) }
+ ],
+ [#Rule 58
+ 'loop', 3,
+sub
+#line 176 "Parser.yp"
+{ $factory->foreach(@{$_[3]}, $_[1]) }
+ ],
+ [#Rule 59
+ '@2-3', 0,
+sub
+#line 177 "Parser.yp"
+{ $_[0]->{ INWHILE }++ }
+ ],
+ [#Rule 60
+ 'loop', 6,
+sub
+#line 178 "Parser.yp"
+{ $_[0]->{ INWHILE }--;
+ $factory->while(@_[2, 5]) }
+ ],
+ [#Rule 61
+ 'loop', 3,
+sub
+#line 180 "Parser.yp"
+{ $factory->while(@_[3, 1]) }
+ ],
+ [#Rule 62
+ 'loopvar', 4,
+sub
+#line 183 "Parser.yp"
+{ [ @_[1, 3, 4] ] }
+ ],
+ [#Rule 63
+ 'loopvar', 4,
+sub
+#line 184 "Parser.yp"
+{ [ @_[1, 3, 4] ] }
+ ],
+ [#Rule 64
+ 'loopvar', 2,
+sub
+#line 185 "Parser.yp"
+{ [ 0, @_[1, 2] ] }
+ ],
+ [#Rule 65
+ 'wrapper', 5,
+sub
+#line 189 "Parser.yp"
+{ $factory->wrapper(@_[2, 4]) }
+ ],
+ [#Rule 66
+ 'wrapper', 3,
+sub
+#line 191 "Parser.yp"
+{ $factory->wrapper(@_[3, 1]) }
+ ],
+ [#Rule 67
+ 'try', 5,
+sub
+#line 195 "Parser.yp"
+{ $factory->try(@_[3, 4]) }
+ ],
+ [#Rule 68
+ 'final', 5,
+sub
+#line 199 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2,4] ]);
+ $_[5]; }
+ ],
+ [#Rule 69
+ 'final', 5,
+sub
+#line 202 "Parser.yp"
+{ unshift(@{$_[5]}, [ undef, $_[4] ]);
+ $_[5]; }
+ ],
+ [#Rule 70
+ 'final', 4,
+sub
+#line 205 "Parser.yp"
+{ unshift(@{$_[4]}, [ undef, $_[3] ]);
+ $_[4]; }
+ ],
+ [#Rule 71
+ 'final', 3,
+sub
+#line 207 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 72
+ 'final', 0,
+sub
+#line 208 "Parser.yp"
+{ [ 0 ] }
+ ],
+ [#Rule 73
+ 'use', 2,
+sub
+#line 211 "Parser.yp"
+{ $factory->use($_[2]) }
+ ],
+ [#Rule 74
+ '@3-3', 0,
+sub
+#line 214 "Parser.yp"
+{ $_[0]->push_defblock(); }
+ ],
+ [#Rule 75
+ 'view', 6,
+sub
+#line 215 "Parser.yp"
+{ $factory->view(@_[2,5],
+ $_[0]->pop_defblock) }
+ ],
+ [#Rule 76
+ '@4-2', 0,
+sub
+#line 219 "Parser.yp"
+{ ${$_[0]->{ INPERL }}++; }
+ ],
+ [#Rule 77
+ 'perl', 5,
+sub
+#line 220 "Parser.yp"
+{ ${$_[0]->{ INPERL }}--;
+ $_[0]->{ EVAL_PERL }
+ ? $factory->perl($_[4])
+ : $factory->no_perl(); }
+ ],
+ [#Rule 78
+ '@5-1', 0,
+sub
+#line 226 "Parser.yp"
+{ ${$_[0]->{ INPERL }}++;
+ $rawstart = ${$_[0]->{'LINE'}}; }
+ ],
+ [#Rule 79
+ 'rawperl', 5,
+sub
+#line 228 "Parser.yp"
+{ ${$_[0]->{ INPERL }}--;
+ $_[0]->{ EVAL_PERL }
+ ? $factory->rawperl($_[4], $rawstart)
+ : $factory->no_perl(); }
+ ],
+ [#Rule 80
+ 'filter', 5,
+sub
+#line 235 "Parser.yp"
+{ $factory->filter(@_[2,4]) }
+ ],
+ [#Rule 81
+ 'filter', 3,
+sub
+#line 237 "Parser.yp"
+{ $factory->filter(@_[3,1]) }
+ ],
+ [#Rule 82
+ 'defblock', 5,
+sub
+#line 242 "Parser.yp"
+{ my $name = join('/', @{ $_[0]->{ DEFBLOCKS } });
+ pop(@{ $_[0]->{ DEFBLOCKS } });
+ $_[0]->define_block($name, $_[4]);
+ undef
+ }
+ ],
+ [#Rule 83
+ 'defblockname', 2,
+sub
+#line 249 "Parser.yp"
+{ push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]);
+ $_[2];
+ }
+ ],
+ [#Rule 84
+ 'blockname', 1, undef
+ ],
+ [#Rule 85
+ 'blockname', 1,
+sub
+#line 255 "Parser.yp"
+{ $_[1] =~ s/^'(.*)'$/$1/; $_[1] }
+ ],
+ [#Rule 86
+ 'blockargs', 1, undef
+ ],
+ [#Rule 87
+ 'blockargs', 0, undef
+ ],
+ [#Rule 88
+ 'anonblock', 5,
+sub
+#line 263 "Parser.yp"
+{ local $" = ', ';
+ print STDERR "experimental block args: [@{ $_[2] }]\n"
+ if $_[2];
+ $factory->anon_block($_[4]) }
+ ],
+ [#Rule 89
+ 'capture', 3,
+sub
+#line 269 "Parser.yp"
+{ $factory->capture(@_[1, 3]) }
+ ],
+ [#Rule 90
+ 'macro', 6,
+sub
+#line 273 "Parser.yp"
+{ $factory->macro(@_[2, 6, 4]) }
+ ],
+ [#Rule 91
+ 'macro', 3,
+sub
+#line 274 "Parser.yp"
+{ $factory->macro(@_[2, 3]) }
+ ],
+ [#Rule 92
+ 'mdir', 1, undef
+ ],
+ [#Rule 93
+ 'mdir', 4,
+sub
+#line 278 "Parser.yp"
+{ $_[3] }
+ ],
+ [#Rule 94
+ 'margs', 2,
+sub
+#line 281 "Parser.yp"
+{ push(@{$_[1]}, $_[2]); $_[1] }
+ ],
+ [#Rule 95
+ 'margs', 2,
+sub
+#line 282 "Parser.yp"
+{ $_[1] }
+ ],
+ [#Rule 96
+ 'margs', 1,
+sub
+#line 283 "Parser.yp"
+{ [ $_[1] ] }
+ ],
+ [#Rule 97
+ 'metadata', 2,
+sub
+#line 286 "Parser.yp"
+{ push(@{$_[1]}, @{$_[2]}); $_[1] }
+ ],
+ [#Rule 98
+ 'metadata', 2, undef
+ ],
+ [#Rule 99
+ 'metadata', 1, undef
+ ],
+ [#Rule 100
+ 'meta', 3,
+sub
+#line 291 "Parser.yp"
+{ for ($_[3]) { s/^'//; s/'$//;
+ s/\\'/'/g };
+ [ @_[1,3] ] }
+ ],
+ [#Rule 101
+ 'meta', 5,
+sub
+#line 294 "Parser.yp"
+{ [ @_[1,4] ] }
+ ],
+ [#Rule 102
+ 'meta', 3,
+sub
+#line 295 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 103
+ 'term', 1, undef
+ ],
+ [#Rule 104
+ 'term', 1, undef
+ ],
+ [#Rule 105
+ 'lterm', 3,
+sub
+#line 307 "Parser.yp"
+{ "[ $_[2] ]" }
+ ],
+ [#Rule 106
+ 'lterm', 3,
+sub
+#line 308 "Parser.yp"
+{ "[ $_[2] ]" }
+ ],
+ [#Rule 107
+ 'lterm', 2,
+sub
+#line 309 "Parser.yp"
+{ "[ ]" }
+ ],
+ [#Rule 108
+ 'lterm', 3,
+sub
+#line 310 "Parser.yp"
+{ "{ $_[2] }" }
+ ],
+ [#Rule 109
+ 'sterm', 1,
+sub
+#line 313 "Parser.yp"
+{ $factory->ident($_[1]) }
+ ],
+ [#Rule 110
+ 'sterm', 2,
+sub
+#line 314 "Parser.yp"
+{ $factory->identref($_[2]) }
+ ],
+ [#Rule 111
+ 'sterm', 3,
+sub
+#line 315 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 112
+ 'sterm', 1, undef
+ ],
+ [#Rule 113
+ 'sterm', 1, undef
+ ],
+ [#Rule 114
+ 'list', 2,
+sub
+#line 320 "Parser.yp"
+{ "$_[1], $_[2]" }
+ ],
+ [#Rule 115
+ 'list', 2, undef
+ ],
+ [#Rule 116
+ 'list', 1, undef
+ ],
+ [#Rule 117
+ 'range', 3,
+sub
+#line 325 "Parser.yp"
+{ $_[1] . '..' . $_[3] }
+ ],
+ [#Rule 118
+ 'hash', 1, undef
+ ],
+ [#Rule 119
+ 'hash', 0,
+sub
+#line 330 "Parser.yp"
+{ "" }
+ ],
+ [#Rule 120
+ 'params', 2,
+sub
+#line 333 "Parser.yp"
+{ "$_[1], $_[2]" }
+ ],
+ [#Rule 121
+ 'params', 2, undef
+ ],
+ [#Rule 122
+ 'params', 1, undef
+ ],
+ [#Rule 123
+ 'param', 3,
+sub
+#line 338 "Parser.yp"
+{ "$_[1] => $_[3]" }
+ ],
+ [#Rule 124
+ 'param', 3,
+sub
+#line 339 "Parser.yp"
+{ "$_[1] => $_[3]" }
+ ],
+ [#Rule 125
+ 'ident', 3,
+sub
+#line 342 "Parser.yp"
+{ push(@{$_[1]}, @{$_[3]}); $_[1] }
+ ],
+ [#Rule 126
+ 'ident', 3,
+sub
+#line 343 "Parser.yp"
+{ push(@{$_[1]},
+ map {($_, 0)} split(/\./, $_[3]));
+ $_[1]; }
+ ],
+ [#Rule 127
+ 'ident', 1, undef
+ ],
+ [#Rule 128
+ 'node', 1,
+sub
+#line 349 "Parser.yp"
+{ [ $_[1], 0 ] }
+ ],
+ [#Rule 129
+ 'node', 4,
+sub
+#line 350 "Parser.yp"
+{ [ $_[1], $factory->args($_[3]) ] }
+ ],
+ [#Rule 130
+ 'item', 1,
+sub
+#line 353 "Parser.yp"
+{ "'$_[1]'" }
+ ],
+ [#Rule 131
+ 'item', 3,
+sub
+#line 354 "Parser.yp"
+{ $_[2] }
+ ],
+ [#Rule 132
+ 'item', 2,
+sub
+#line 355 "Parser.yp"
+{ $_[0]->{ V1DOLLAR }
+ ? "'$_[2]'"
+ : $factory->ident(["'$_[2]'", 0]) }
+ ],
+ [#Rule 133
+ 'expr', 3,
+sub
+#line 360 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 134
+ 'expr', 3,
+sub
+#line 361 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 135
+ 'expr', 3,
+sub
+#line 362 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 136
+ 'expr', 3,
+sub
+#line 363 "Parser.yp"
+{ "int($_[1] / $_[3])" }
+ ],
+ [#Rule 137
+ 'expr', 3,
+sub
+#line 364 "Parser.yp"
+{ "$_[1] % $_[3]" }
+ ],
+ [#Rule 138
+ 'expr', 3,
+sub
+#line 365 "Parser.yp"
+{ "$_[1] $CMPOP{ $_[2] } $_[3]" }
+ ],
+ [#Rule 139
+ 'expr', 3,
+sub
+#line 366 "Parser.yp"
+{ "$_[1] . $_[3]" }
+ ],
+ [#Rule 140
+ 'expr', 3,
+sub
+#line 367 "Parser.yp"
+{ "$_[1] && $_[3]" }
+ ],
+ [#Rule 141
+ 'expr', 3,
+sub
+#line 368 "Parser.yp"
+{ "$_[1] || $_[3]" }
+ ],
+ [#Rule 142
+ 'expr', 2,
+sub
+#line 369 "Parser.yp"
+{ "! $_[2]" }
+ ],
+ [#Rule 143
+ 'expr', 5,
+sub
+#line 370 "Parser.yp"
+{ "$_[1] ? $_[3] : $_[5]" }
+ ],
+ [#Rule 144
+ 'expr', 3,
+sub
+#line 371 "Parser.yp"
+{ $factory->assign(@{$_[2]}) }
+ ],
+ [#Rule 145
+ 'expr', 3,
+sub
+#line 372 "Parser.yp"
+{ "($_[2])" }
+ ],
+ [#Rule 146
+ 'expr', 1, undef
+ ],
+ [#Rule 147
+ 'setlist', 2,
+sub
+#line 376 "Parser.yp"
+{ push(@{$_[1]}, @{$_[2]}); $_[1] }
+ ],
+ [#Rule 148
+ 'setlist', 2, undef
+ ],
+ [#Rule 149
+ 'setlist', 1, undef
+ ],
+ [#Rule 150
+ 'assign', 3,
+sub
+#line 382 "Parser.yp"
+{ [ $_[1], $_[3] ] }
+ ],
+ [#Rule 151
+ 'assign', 3,
+sub
+#line 383 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 152
+ 'args', 2,
+sub
+#line 390 "Parser.yp"
+{ push(@{$_[1]}, $_[2]); $_[1] }
+ ],
+ [#Rule 153
+ 'args', 2,
+sub
+#line 391 "Parser.yp"
+{ push(@{$_[1]->[0]}, $_[2]); $_[1] }
+ ],
+ [#Rule 154
+ 'args', 4,
+sub
+#line 392 "Parser.yp"
+{ push(@{$_[1]->[0]}, "'', " .
+ $factory->assign(@_[2,4])); $_[1] }
+ ],
+ [#Rule 155
+ 'args', 2,
+sub
+#line 394 "Parser.yp"
+{ $_[1] }
+ ],
+ [#Rule 156
+ 'args', 0,
+sub
+#line 395 "Parser.yp"
+{ [ [ ] ] }
+ ],
+ [#Rule 157
+ 'lnameargs', 3,
+sub
+#line 405 "Parser.yp"
+{ push(@{$_[3]}, $_[1]); $_[3] }
+ ],
+ [#Rule 158
+ 'lnameargs', 1, undef
+ ],
+ [#Rule 159
+ 'lvalue', 1, undef
+ ],
+ [#Rule 160
+ 'lvalue', 3,
+sub
+#line 410 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 161
+ 'lvalue', 1, undef
+ ],
+ [#Rule 162
+ 'nameargs', 3,
+sub
+#line 414 "Parser.yp"
+{ [ [$factory->ident($_[2])], $_[3] ] }
+ ],
+ [#Rule 163
+ 'nameargs', 2,
+sub
+#line 415 "Parser.yp"
+{ [ @_[1,2] ] }
+ ],
+ [#Rule 164
+ 'nameargs', 4,
+sub
+#line 416 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 165
+ 'names', 3,
+sub
+#line 419 "Parser.yp"
+{ push(@{$_[1]}, $_[3]); $_[1] }
+ ],
+ [#Rule 166
+ 'names', 1,
+sub
+#line 420 "Parser.yp"
+{ [ $_[1] ] }
+ ],
+ [#Rule 167
+ 'name', 3,
+sub
+#line 423 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 168
+ 'name', 1,
+sub
+#line 424 "Parser.yp"
+{ "'$_[1]'" }
+ ],
+ [#Rule 169
+ 'name', 1, undef
+ ],
+ [#Rule 170
+ 'filename', 3,
+sub
+#line 436 "Parser.yp"
+{ "$_[1].$_[3]" }
+ ],
+ [#Rule 171
+ 'filename', 1, undef
+ ],
+ [#Rule 172
+ 'filepart', 1, undef
+ ],
+ [#Rule 173
+ 'filepart', 1, undef
+ ],
+ [#Rule 174
+ 'filepart', 1, undef
+ ],
+ [#Rule 175
+ 'quoted', 2,
+sub
+#line 450 "Parser.yp"
+{ push(@{$_[1]}, $_[2])
+ if defined $_[2]; $_[1] }
+ ],
+ [#Rule 176
+ 'quoted', 0,
+sub
+#line 452 "Parser.yp"
+{ [ ] }
+ ],
+ [#Rule 177
+ 'quotable', 1,
+sub
+#line 455 "Parser.yp"
+{ $factory->ident($_[1]) }
+ ],
+ [#Rule 178
+ 'quotable', 1,
+sub
+#line 456 "Parser.yp"
+{ $factory->text($_[1]) }
+ ],
+ [#Rule 179
+ 'quotable', 1,
+sub
+#line 457 "Parser.yp"
+{ undef }
+ ]
+];
+
+
+
+1;
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/Template/Iterator.pm b/lib/Template/Iterator.pm
new file mode 100644
index 0000000..710ecdc
--- /dev/null
+++ b/lib/Template/Iterator.pm
@@ -0,0 +1,456 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Iterator
+#
+# DESCRIPTION
+#
+# Module defining an iterator class which is used by the FOREACH
+# directive for iterating through data sets. This may be
+# sub-classed to define more specific iterator types.
+#
+# An iterator is an object which provides a consistent way to
+# navigate through data which may have a complex underlying form.
+# This implementation uses the get_first() and get_next() methods to
+# iterate through a dataset. The get_first() method is called once
+# to perform any data initialisation and return the first value,
+# then get_next() is called repeatedly to return successive values.
+# Both these methods return a pair of values which are the data item
+# itself and a status code. The default implementation handles
+# iteration through an array (list) of elements which is passed by
+# reference to the constructor. An empty list is used if none is
+# passed. The module may be sub-classed to provide custom
+# implementations which iterate through any kind of data in any
+# manner as long as it can conforms to the get_first()/get_next()
+# interface. The object also implements the get_all() method for
+# returning all remaining elements as a list reference.
+#
+# For further information on iterators see "Design Patterns", by the
+# "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, John
+# Vlissides), Addision-Wesley, ISBN 0-201-63361-2.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Iterator.pm,v 2.64 2004/01/13 16:19:15 abw Exp $
+#
+#============================================================================
+
+package Template::Iterator;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD ); # AUTO?
+use base qw( Template::Base );
+use Template::Constants;
+use Template::Exception;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\@target, \%options)
+#
+# Constructor method which creates and returns a reference to a new
+# Template::Iterator object. A reference to the target data (array
+# or hash) may be passed for the object to iterate through.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $data = shift || [ ];
+ my $params = shift || { };
+
+ if (ref $data eq 'HASH') {
+ # map a hash into a list of { key => ???, value => ??? } hashes,
+ # one for each key, sorted by keys
+ $data = [ map { { key => $_, value => $data->{ $_ } } }
+ sort keys %$data ];
+ }
+ elsif (UNIVERSAL::can($data, 'as_list')) {
+ $data = $data->as_list();
+ }
+ elsif (ref $data ne 'ARRAY') {
+ # coerce any non-list data into an array reference
+ $data = [ $data ] ;
+ }
+
+ bless {
+ _DATA => $data,
+ _ERROR => '',
+ }, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# get_first()
+#
+# Initialises the object for iterating through the target data set. The
+# first record is returned, if defined, along with the STATUS_OK value.
+# If there is no target data, or the data is an empty set, then undef
+# is returned with the STATUS_DONE value.
+#------------------------------------------------------------------------
+
+sub get_first {
+ my $self = shift;
+ my $data = $self->{ _DATA };
+
+ $self->{ _DATASET } = $self->{ _DATA };
+ my $size = scalar @$data;
+ my $index = 0;
+
+ return (undef, Template::Constants::STATUS_DONE) unless $size;
+
+ # initialise various counters, flags, etc.
+ @$self{ qw( SIZE MAX INDEX COUNT FIRST LAST ) }
+ = ( $size, $size - 1, $index, 1, 1, $size > 1 ? 0 : 1, undef );
+ @$self{ qw( PREV NEXT ) } = ( undef, $self->{ _DATASET }->[ $index + 1 ]);
+
+ return $self->{ _DATASET }->[ $index ];
+}
+
+
+
+#------------------------------------------------------------------------
+# get_next()
+#
+# Called repeatedly to access successive elements in the data set.
+# Should only be called after calling get_first() or a warning will
+# be raised and (undef, STATUS_DONE) returned.
+#------------------------------------------------------------------------
+
+sub get_next {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my $data = $self->{ _DATASET };
+
+ # warn about incorrect usage
+ unless (defined $index) {
+ my ($pack, $file, $line) = caller();
+ warn("iterator get_next() called before get_first() at $file line $line\n");
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ # update counters and flags
+ $index++;
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $index, $index + 1, 0, $index == $max ? 1 : 0 );
+ @$self{ qw( PREV NEXT ) } = @$data[ $index - 1, $index + 1 ];
+ return $data->[ $index ]; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# get_all()
+#
+# Method which returns all remaining items in the iterator as a Perl list
+# reference. May be called at any time in the life-cycle of the iterator.
+# The get_first() method will be called automatically if necessary, and
+# then subsequent get_next() calls are made, storing each returned
+# result until the list is exhausted.
+#------------------------------------------------------------------------
+
+sub get_all {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my @data;
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ $index++;
+ @data = @{ $self->{ _DATASET } } [ $index..$max ];
+
+ # update counters and flags
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $max, $max + 1, 0, 1 );
+
+ return \@data; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides access to internal fields (e.g. size, first, last, max, etc)
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ # alias NUMBER to COUNT for backwards compatability
+ $item = 'COUNT' if $item =~ /NUMBER/i;
+
+ return $self->{ uc $item };
+}
+
+
+#========================================================================
+# ----- PRIVATE DEBUG METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string detailing the internal state of
+# the iterator object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ join('',
+ " Data: ", $self->{ _DATA }, "\n",
+ " Index: ", $self->{ INDEX }, "\n",
+ "Number: ", $self->{ NUMBER }, "\n",
+ " Max: ", $self->{ MAX }, "\n",
+ " Size: ", $self->{ SIZE }, "\n",
+ " First: ", $self->{ FIRST }, "\n",
+ " Last: ", $self->{ LAST }, "\n",
+ "\n"
+ );
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Iterator - Data iterator used by the FOREACH directive
+
+=head1 SYNOPSIS
+
+ my $iter = Template::Iterator->new(\@data, \%options);
+
+=head1 DESCRIPTION
+
+The Template::Iterator module defines a generic data iterator for use
+by the FOREACH directive.
+
+It may be used as the base class for custom iterators.
+
+=head1 PUBLIC METHODS
+
+=head2 new($data)
+
+Constructor method. A reference to a list of values is passed as the
+first parameter. Subsequent calls to get_first() and get_next() calls
+will return each element from the list.
+
+ my $iter = Template::Iterator->new([ 'foo', 'bar', 'baz' ]);
+
+The constructor will also accept a reference to a hash array and will
+expand it into a list in which each entry is a hash array containing
+a 'key' and 'value' item, sorted according to the hash keys.
+
+ my $iter = Template::Iterator->new({
+ foo => 'Foo Item',
+ bar => 'Bar Item',
+ });
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([
+ { key => 'bar', value => 'Bar Item' },
+ { key => 'foo', value => 'Foo Item' },
+ ]);
+
+When passed a single item which is not an array reference, the constructor
+will automatically create a list containing that single item.
+
+ my $iter = Template::Iterator->new('foo');
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([ 'foo' ]);
+
+Note that a single item which is an object based on a blessed ARRAY
+references will NOT be treated as an array and will be folded into
+a list containing that one object reference.
+
+ my $list = bless [ 'foo', 'bar' ], 'MyListClass';
+ my $iter = Template::Iterator->new($list);
+
+equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+If the object provides an as_list() method then the Template::Iterator
+constructor will call that method to return the list of data. For example:
+
+ package MyListObject;
+
+ sub new {
+ my $class = shift;
+ bless [ @_ ], $class;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+This is then functionally equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+The iterator will return only one item, a reference to the MyListObject
+object, $list.
+
+By adding an as_list() method to the MyListObject class, we can force
+the Template::Iterator constructor to treat the object as a list and
+use the data contained within.
+
+ package MyListObject;
+
+ ...
+
+ sub as_list {
+ my $self = shift;
+ return $self;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+The iterator will now return the two item, 'foo' and 'bar', which the
+MyObjectList encapsulates.
+
+=head2 get_first()
+
+Returns a ($value, $error) pair for the first item in the iterator set.
+The $error returned may be zero or undefined to indicate a valid datum
+was successfully returned. Returns an error of STATUS_DONE if the list
+is empty.
+
+=head2 get_next()
+
+Returns a ($value, $error) pair for the next item in the iterator set.
+Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 get_all()
+
+Returns a (\@values, $error) pair for all remaining items in the iterator
+set. Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 size()
+
+Returns the size of the data set or undef if unknown.
+
+=head2 max()
+
+Returns the maximum index number (i.e. the index of the last element)
+which is equivalent to size() - 1.
+
+=head2 index()
+
+Returns the current index number which is in the range 0 to max().
+
+=head2 count()
+
+Returns the current iteration count in the range 1 to size(). This is
+equivalent to index() + 1. Note that number() is supported as an alias
+for count() for backwards compatability.
+
+=head2 first()
+
+Returns a boolean value to indicate if the iterator is currently on
+the first iteration of the set.
+
+=head2 last()
+
+Returns a boolean value to indicate if the iterator is currently on
+the last iteration of the set.
+
+=head2 prev()
+
+Returns the previous item in the data set, or undef if the iterator is
+on the first item.
+
+=head2 next()
+
+Returns the next item in the data set or undef if the iterator is on the
+last item.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Library/HTML.pod b/lib/Template/Library/HTML.pod
new file mode 100644
index 0000000..e39c120
--- /dev/null
+++ b/lib/Template/Library/HTML.pod
@@ -0,0 +1,316 @@
+#============================================================= -*-perl-*-
+#
+# Template::Library::HTML
+#
+# DESCRIPTION
+# The HTML library provides a number of basic templates for use in
+# building HTML pages.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# 2.69
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Library::HTML - Template library for building basic HTML pages
+
+=head1 DESCRIPTION
+
+B<NOTE:> This documentation is incomplete and may be incorrect
+in places.
+
+The 'html' template library is distributed as part of the Template
+Toolkit. It can be found in the 'templates' sub-directory of the
+installation directory.
+
+ use Template;
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => '/usr/local/tt2/templates',
+ });
+
+For a portable way to determine the installation 'templates' directory,
+you can use the C<Template::Config-E<gt>instdir()> class method.
+
+ use Template;
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => Template::Config->instdir('templates'),
+ });
+
+You should now be able to access the html library as, for example:
+
+ [% INCLUDE html/header %]
+
+Note that some of the more basic elements don't give you much more
+than the raw HTML tags. In many cases you might be well advised to
+stick to regular HTML rather than complicating matters by the use
+of template elements.
+
+e.g.
+
+ <table>
+ . . .
+ </table>
+
+vs
+
+ [% WRAPPER html/table %]
+ . . .
+ [% END %]
+
+However, the use of template elements to generate the underlying HTML
+does have some important benefits, particularly as the constructs start
+to get more complicated and more magical.
+
+See the example in the 'examples' sub-directory of the distribution
+directory for further examples and enlightenment on using this library.
+
+=head2 Headers, Footers and Pages
+
+=over 4
+
+=item header
+
+The 'header' element generates the regular header required as the
+pre-amble for an HTML document. That is, everything from the initial
+E<lt>htmlE<gt> to the opening E<lt>bodyE<gt>.
+
+ [% INCLUDE html/header
+ title = 'This is a Test'
+ bgcol = '#ffffff'
+ %]
+
+Additional header items can be provided by explicitly setting the 'headers'
+variable, e.g.
+
+ [% headers = BLOCK %]
+ <META name="description" content="Template Toolkit">
+ <META name="REVISIT-AFTER" content="14 days">
+ <META name="keywords" content="Templates, Web, ...etc...">
+ [% END %]
+
+ [% INCLUDE html/header
+ title = 'This is a Test'
+ bgcol = '#ffffff'
+ %]
+
+=item footer
+
+The 'footer' element generates the terminating E<lt>/bodyE<gt> and
+E<lt>/htmlE<gt> element to balance the header.
+
+ [% PROCESS html/header %]
+
+ ...page content here...
+
+ [% PROCESS html/footer %]
+
+=item page
+
+The 'page' element combines the 'html/header' and 'html/footer' elements.
+
+ [% WRAPPER html/page %]
+
+ ...page content here...
+
+ [% END %]
+
+Page content should be defined in the 'content' variable (e.g. via WRAPPER).
+Additional HTML headers should be defined in the 'headers' variable.
+
+ [% WRAPPER html/page
+ headers = '<META name="keywords" content="foo, bar, ...">'
+ %]
+
+ ...page content here...
+
+ [% END %]
+
+=back
+
+=head2 Tables, Bars and Boxes
+
+=over 4
+
+=item table
+
+A basic element for creating HTML tables.
+
+ [% WRAPPER html/table pad=10 space=4 col='#404040' %]
+ <tr>
+ <td>Hello</td> <td>World</td>
+ </tr>
+ [% END %]
+
+The following variables may be defined:
+
+=over 4
+
+=item border
+
+Set the border width (default: 0)
+
+=item col
+
+Set the background colour (default: none).
+
+=item width
+
+Set a fixed table width.
+
+=item pad
+
+Set the cellpadding.
+
+=item space
+
+Set the cellspacing.
+
+=item content
+
+Content for the box. Supplied automatically if used via WRAPPER.
+
+=back
+
+=item row
+
+A basic element for creating HTML table rows.
+
+ [% WRAPPER html/table %]
+ [% WRAPPER html/row %]
+ <td>Hello</td> <td>World</td>
+ [% END %]
+ [% END %]
+
+The following variables may be defined:
+
+=over 4
+
+=item col
+
+Set the background colour (default: none).
+
+=item valign
+
+Set the vertical alignment.
+
+=item rowspan
+
+Specify the number of rows to span.
+
+=item content
+
+Content for the box. Supplied automatically if used via WRAPPER.
+
+=back
+
+=item cell
+
+A basic element for creating HTML table cells.
+
+ [% WRAPPER html/table %]
+ [% WRAPPER html/row %]
+ [% INCLUDE html/cell
+ FOREACH content = ['Hello', 'World'] %]
+ [% END %]
+ [% END %]
+
+The following variables may be defined:
+
+=over 4
+
+=item col
+
+Set the background colour (default: none).
+
+=item align
+
+Set the horizontal alignment.
+
+=item colspan
+
+Specify the number of columns to span.
+
+=item content
+
+Content for the cell. Supplied automatically if used via WRAPPER.
+
+=back
+
+=item bar
+
+The bar element is a wrapping of html/table + html/row.
+
+ [% WRAPPER html/bar %]
+ <td>Foo</td> <td>Bar</td>
+ [% END %]
+
+=item box
+
+The box element is a wrapping of html/table + html/row + html/cell
+
+ [% WRAPPER html/box %]
+ Hello World!
+ [% END %]
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Library::Splash|Template::Library::Splash>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Library/PostScript.pod b/lib/Template/Library/PostScript.pod
new file mode 100644
index 0000000..c30246c
--- /dev/null
+++ b/lib/Template/Library/PostScript.pod
@@ -0,0 +1,78 @@
+#============================================================= -*-perl-*-
+#
+# Template::Library::PostScript
+#
+# DESCRIPTION
+# This library contains a number of useful templates for generating
+# PostScript pages.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# 2.69
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Library::PostScript - Template library for generating PostScript
+
+=head1 DESCRIPTION
+
+The PostScript library contains a number of templates for generating
+PostScript pages. It's very new, very incomplete, very ad-hoc and
+isn't yet documented.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Library/Splash.pod b/lib/Template/Library/Splash.pod
new file mode 100644
index 0000000..e8c4f8b
--- /dev/null
+++ b/lib/Template/Library/Splash.pod
@@ -0,0 +1,1030 @@
+#============================================================= -*-perl-*-
+#
+# Template::Library::Splash
+#
+# DESCRIPTION
+# The Splash! library is built on top of the HTML library and
+# implements a set of widgets for easy construction of stylish HTML
+# pages
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# 2.69
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Library::Splash - Template library for building stylish HTML user interfaces
+
+=head1 DESCRIPTION
+
+B<NOTE:> This documentation is incomplete, incorrect and outdated.
+The Splash! library is still evolving and subject to change. See
+the examples for a much more recent and accurate demonstration of
+use.
+
+=head2 Introduction
+
+The 'Splash' template library is distributed as part of the Template
+Toolkit. It can be found in the 'templates' sub-directory of the
+installation directory.
+
+ /your/tt2/installation
+ |
+ +-- docs
+ | ...
+ |
+ +-- images
+ | ...
+ |
+ +-- examples
+ | ...
+ |
+ +-- templates
+ |
+ +-- html
+ | ...
+ +-- pod
+ | ...
+ +-- splash <<<< YOU ARE HERE
+ ...
+
+
+To use the Splash library, you first need to tell the Template Toolkit
+where to find the template files.
+
+ use Template;
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => '/usr/local/tt2/templates',
+ });
+
+For a portable way to determine the installation 'templates' directory,
+you can use the C<Template::Config-E<gt>instdir()> class method.
+
+ use Template;
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => Template::Config->instdir('templates'),
+ });
+
+Note that you should set the INCLUDE_PATH to the 'templates' directory
+as shown here and don't be tempted to set the INCLUDE_PATH to
+'templates/splash'. Many of the Splash! components use elements in
+the 'html' directory and contain directives of the form:
+
+ [% INCLUDE html/something %].
+
+=head2 Configuration
+
+The 'splash/config' template defines a 'splash' hash array which
+contains numerous configuration items for the Splash library. You
+must PROCESS this template to ensure that the hash definition is
+imported into your calling template. An INCLUDE is not sufficient as
+it localises variables and prevents the 'splash' hash array from
+existing outside the splash/config template.
+
+ [% PROCESS splash/config %]
+
+Alternately, you can define the splash/config template as a PRE_PROCESS
+item when you create the Template processor.
+
+ use Template;
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => Template::Config->instdir('templates'),
+ PRE_PROCESS => 'splash/config',
+ });
+
+You can modify the default configuration by creating your own
+PRE_PROCESS config file which loads the 'splash/config' and then
+tweaks the settings to your own preferences.
+
+ my $tt2 = Template->new({
+ INCLUDE_PATH => [ '/home/abw/tt2/templates',
+ Template::Config->instdir('templates') ],
+ PRE_PROCESS => 'config'
+ });
+
+/home/abw/tt2/templates/config:
+
+ [% # load the 'splash' configuration
+ PROCESS splash/config;
+
+ # tweak values to personal preferences
+ splash.images = '/~abw/tt2/images/splash'
+ splash.select.col = 'leaf'
+ splash.unselect.col = 'bud'
+ %]
+
+The splash/config file includes some instructional comments on
+things you might like to tweak.
+
+=head2 Colours
+
+The Splash! library uses the colours defined in the html/rgb template.
+The 'rgb' hash defined therein is imported as the 'splash.rgb' hash.
+
+ [% INCLUDE splash/box col='grey75' %]
+
+See the examples for further enlightenment on using colour.
+
+=head2 Style
+
+There are two very primitive "styles" implemented called "select" and
+"unselect". These are used to indicate which item on a menu is
+selected, for example. Each style defines characteristics like
+background colour, font face, size and colour, text alignment, and so
+on.
+
+The styles are implemented as hashes within the 'splash' hash. Many
+of the components respond to a 'style' variable being set and you can
+pass a direct reference to splash.select or splash.unselect (or your
+own styles). e.g.
+
+ [% INCLUDE splash/button
+ content = "Unselected"
+ style = splash.unselect
+ %]
+ [% INCLUDE splash/button
+ content ="Selected"
+ style = splash.select
+ %]
+
+Alternately, you can use the 'select' variable to indicate either
+of the inbuilt styles: splash.select or splash.unselect.
+
+ [% INCLUDE splash/button
+ content = "Unselected"
+ select = 0
+ %]
+ [% INCLUDE splash/button
+ content = "Selected"
+ select = 1
+ %]
+
+=head1 COMPONENT TEMPLATES
+
+This section describes some of the component templates in the Splash!
+library. This documentation is incomplete and may also be inaccurate
+in places. The examples in the 'examples' directory are likely to be
+a much better reference.
+
+
+=head2 splash/text
+
+Simple template to format text according to a selected/unselected style,
+adding links, etc.
+
+ [% INCLUDE splash/text
+ content = 'Template Toolkit'
+ link = 'http://www.template-toolkit.org'
+ select = 0
+ bold = 1
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Text content.
+
+
+=item link
+
+URL which can be defined to make the text a link.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item font (style.font.face)
+
+
+=item bold (style.font.bold)
+
+
+=item size (style.font.size)
+
+
+=back
+
+
+
+=head2 splash/table
+
+A thin wrapper around html/table, allowing a colour to be specified
+by name.
+
+ [% WRAPPER splash/table
+ col = 'aqua'
+ pad = 4
+ width = '100%'
+ %]
+ <tr>
+ <td>Foo</td>
+ <td>Bar</td>
+ </tr>
+ [% END %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Table content.
+
+
+=item col
+
+Background colour.
+
+
+=item border
+
+Border width (default: 0)
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item pad
+
+Cell padding.
+
+
+=item space
+
+Cell padding.
+
+
+=back
+
+
+
+=head2 splash/row
+
+Creates a row for an HTML table.
+
+ [% WRAPPER splash/table %]
+
+ [% WRAPPER splash/row col='marine' %]
+ <td>Foo</td><td>Bar</td>
+ [% END %]
+
+ [% WRAPPER splash/row col='aqua' %]
+ <td>Foo</td><td>Bar</td>
+ [% END %]
+
+ [% END %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Row content.
+
+
+=item col
+
+Background colour.
+
+
+=item valign
+
+Vertical alignment
+
+
+=item rowspan
+
+Number of rows to span.
+
+
+=back
+
+
+
+=head2 splash/cell
+
+Creates a cell for an HTML table.
+
+ [% WRAPPER splash/table + splash/row + splash/cell col='grey75' %]
+ Hello World
+ [% END %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Cell content.
+
+
+=item col
+
+Background colour.
+
+
+=item align
+
+Horizontal alignment
+
+
+=item colspan
+
+Number of columns to span.
+
+
+=back
+
+
+
+=head2 splash/box
+
+A box created from a union of splash/table, splash/row and splash/cell.
+The following is equivalent to the previous example.
+
+ [% WRAPPER splash/box col='grey75' %]
+ Hello World
+ [% END %]
+
+Configuration items are as per the individual templates.
+
+
+=head2 splash/button
+
+Creates a small button with rounded corners.
+
+ [% INCLUDE splash/button
+ content = 'Template Toolkit'
+ select = 1
+ width = '50%'
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Button content.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item textcol (style.col.text)
+
+
+=item font (style.font.face)
+
+
+=item size (style.font.size)
+
+
+=item bold (style.font.bold)
+
+
+=item width (style.button.width)
+
+
+=item align (style.button.align)
+
+
+=back
+
+
+
+=head2 splash/bar
+
+Creates a bar with rounded corners at either the top or bottom, and
+square corners on the other. Default has rounded at the top, set
+'invert' to select bottom.
+
+ [% INCLUDE splash/bar
+ content = 'Hello World',
+ select = 1
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Bar content.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item invert
+
+Flag to invert bar to hang down instead of sitting
+upright.
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item textcol (style.col.text)
+
+
+=item font (style.font.face)
+
+
+=item size (style.font.size)
+
+
+=item bold (style.font.bold)
+
+
+=item width (style.button.width)
+
+
+=item align (style.button.align)
+
+
+=back
+
+
+=head2 splash/hair
+
+Generates a frame enclosing the content within crosshair corners.
+
+ [% INCLUDE splash/hair
+ content = 'Template Toolkit'
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Hair content.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item bgcol (style.col.back)
+
+
+=item align (style.button.align)
+
+
+=back
+
+
+=head2 splash/menu
+
+Creates a menu as a series of splash/button elements.
+
+ [% buttons = [
+ { text => 'One', link => 'one.html' }
+ { text => 'Two', link => 'two.html' }
+ ]
+ %]
+
+ [% INCLUDE splash/menu
+ select = 2 # Two
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item buttons
+
+A reference to a list of hash arrays containing 'text' and 'link' items.
+
+
+=item select (n or 0)
+
+Indicates which button should be selected. First item is 1. 0 indicates
+no button selected.
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item align
+
+Horizontal alignment
+
+
+=back
+
+
+
+=head2 splash/menubar
+
+As above, but incorporated into a wider bar.
+
+ [% WRAPPER splash/menubar %]
+ Section Title
+ [% END %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item buttons
+
+A reference to a list of hash arrays containing 'text' and 'link' items.
+
+
+=item select (n or 0)
+
+Indicates which button should be selected. First item is 1. 0 indicates
+no button selected.
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item align
+
+Horizontal alignment
+
+
+=back
+
+
+
+=head2 splash/panel
+
+A table with a coloured edge.
+
+ [% WRAPPER splash/panel edge='black' fill='grey75' border=2 %]
+ <tr>
+ <td>Hello World</td>
+ </tr>
+ [% END %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Panel content.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item align
+
+Horizontal alignment
+
+
+=item border
+
+Border width (default: 0)
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item edge (style.col.edge)
+
+
+=item fill (style.col.fill)
+
+
+=item pad (style.pad)
+
+
+=back
+
+
+
+=head2 splash/pane
+
+A union of splash/row + splash/cell.
+
+ [% WRAPPER splash/panel select=1 %]
+ [% WRAPPER splash/pane col='grey75' %]
+ Hello World
+ [% END %]
+
+ [% WRAPPER splash/pane col='grey50' %]
+ Hello Again
+ [% END %]
+ [% END %]
+
+
+=head2 splash/tab
+
+A simple button looking like a page tab.
+
+ [% INCLUDE splash/tab
+ content = 'Option 1'
+ col = 'aqua'
+ %]
+
+
+Configuration items:
+
+=over 4
+
+
+=item content
+
+Tab content.
+
+
+=item style
+
+Reference to a style hash.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item align
+
+Horizontal alignment
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item textcol (style.col.text)
+
+
+=item font (style.font.face)
+
+
+=item size (style.font.size)
+
+
+=item bold (style.font.bold)
+
+
+=item tabalign (style.tab.align)
+
+
+=back
+
+
+
+=head2 splash/tabset
+
+A set of splash/tab components, similar to a menu.
+
+
+Configuration items:
+
+=over 4
+
+
+=item tabs
+
+List of hash references containing text/link entries, as per
+menu buttons.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item invert
+
+Flag to invert tab to hang down instead of sitting
+upright.
+
+
+=back
+
+
+
+=head2 splash/tabbox
+
+Add a splash/tab to the top of a splash/box.
+
+
+Configuration items:
+
+=over 4
+
+
+=item title
+
+ title.
+
+
+=item content
+
+ content.
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item tabwidth
+
+Width of tabs.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item border
+
+Border width (default: 0)
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item fill (style.col.fill)
+
+
+=item tabalign (style.tab.align)
+
+
+=item tablocate (style.tab.locate)
+
+
+=back
+
+
+
+=head2 splash/tabsbox
+
+Add a splash/tabset to the top of a splash/box.
+
+
+Configuration items:
+
+=over 4
+
+
+=item tabs
+
+List of hash references containing text/link entries, as per
+menu buttons.
+
+
+=item select
+
+Flag to default the style to splash.select (select == true value) or
+splash.unselect (select == false value).
+
+
+=item content
+
+ content.
+
+
+=item width
+
+Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%').
+
+=item border
+
+Border width (default: 0)
+
+
+=item invert
+
+Flag to invert to hang down instead of sitting
+upright.
+
+
+=back
+
+The following items default to the relevant style values:
+
+=over 4
+
+
+=item col (style.col.text)
+
+
+=item fill (style.col.fill)
+
+
+=item tabalign (style.tab.align)
+
+
+=item tablocate (style.tab.locate)
+
+
+=back
+
+
+=head2 splash/tabspanel
+
+As per splash/tabsbox, but attached to a splash/panel instead of a
+splash/box.
+
+
+=head1 EXAMPLES
+
+See the examples in the 'examples' sub-directory of the installation
+for comprehensive examples showing use of the Splash! library.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Library::HTML|Template::Library::HTML>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual.pod b/lib/Template/Manual.pod
new file mode 100644
index 0000000..8775a5b
--- /dev/null
+++ b/lib/Template/Manual.pod
@@ -0,0 +1,180 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual
+#
+# DESCRIPTION
+# This is the comprehensive user guide and reference manual for the
+# Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual - User guide and reference manual for the Template Toolkit
+
+=head1 DESCRIPTION
+
+This is the comprehensive user guide and reference manual for the
+Template Toolkit.
+
+=over 4
+
+=item L<Template::Manual::Intro|Template::Manual::Intro>
+
+Introduction to the Template Toolkit
+
+This section provides a general introduction to the Template Toolkit,
+giving a quick overview of features, examples of template directives
+and use of the Template.pm module. It also described the basic concept
+underlying the toolkit: the separation of presentation elements from
+application logic and data.
+
+=item L<Template::Manual::Syntax|Template::Manual::Syntax>
+
+Directive syntax, structure and semantics
+
+This section describes the syntax, structure and semantics of the
+Template Toolkit directives and general presentation language.
+
+=item L<Template::Manual::Directives|Template::Manual::Directives>
+
+Template directives
+
+This section provides a reference of all Template Toolkit directives,
+complete with examples of use.
+
+=item L<Template::Manual::Variables|Template::Manual::Variables>
+
+Template variables and code bindings
+
+This section describes the different ways in which Perl data can be
+bound to template variables and accessed via Template Toolkit
+directives.
+
+=item L<Template::Manual::VMethods|Template::Manual::VMethods>
+
+Virtual Methods
+
+The Template Toolkit provides virtual methods for manipulating variable
+values. Most of them are analogous to regular Perl functions of the
+same names. This section describes the different virtual methods that
+can be applied to scalar, list and hash values.
+
+=item L<Template::Manual::Config|Template::Manual::Config>
+
+Configuration options
+
+This section contains details of all the configuration options that can
+be used to customise the behaviour and extend the features of the
+Template Toolkit.
+
+=item L<Template::Manual::Filters|Template::Manual::Filters>
+
+Standard filters
+
+This section lists all the standard filters distributed with the
+Template Toolkit for post-processing output.
+
+=item L<Template::Manual::Plugins|Template::Manual::Plugins>
+
+Standard plugins
+
+This section lists the standard plugins which can be used to extend the
+runtime functionality of the Template Toolkit. The plugins are
+distributed with the Template Toolkit but may required additional
+modules from CPAN.
+
+=item L<Template::Manual::Internals|Template::Manual::Internals>
+
+Template Toolkit internals
+
+This document provides an overview of the internal architecture of the
+Template Toolkit. It is a work in progress and is far from complete,
+currently providing little more than an overview of how the major
+components fit together. Nevertheless, it's a good starting point for
+anyone wishing to delve into the source code to find out how it all
+works.
+
+=item L<Template::Manual::Views|Template::Manual::Views>
+
+Template Toolkit views (experimental)
+
+This section describes dynamic views: a powerful but experimental new
+feature in version 2.01 of the Template Toolkit.
+
+=item L<Template::Manual::Refs|Template::Manual::Refs>
+
+Related modules, projects and other resources
+
+This section provides references to external modules, projects and
+other resources related to the Template Toolkit.
+
+=item L<Template::Manual::Credits|Template::Manual::Credits>
+
+Author and contributor credits
+
+This section provides a brief history of the Template Toolkit and
+details the primary author and numerous other people who have
+contributed to it.
+
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Config.pod b/lib/Template/Manual/Config.pod
new file mode 100644
index 0000000..5020556
--- /dev/null
+++ b/lib/Template/Manual/Config.pod
@@ -0,0 +1,2122 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Config
+#
+# DESCRIPTION
+# This section contains details of all the configuration options that
+# can be used to customise the behaviour and extend the features of
+# the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Config - Configuration options
+
+=head1 DESCRIPTION
+
+This section contains details of all the configuration options that can
+be used to customise the behaviour and extend the features of the
+Template Toolkit.
+
+=head2 Template Style and Parsing Options
+
+=over 4
+
+
+
+=item START_TAG, END_TAG
+
+The START_TAG and END_TAG options are used to specify character
+sequences or regular expressions that mark the start and end of a
+template directive. The default values for START_TAG and END_TAG are
+'[%' and '%]' respectively, giving us the familiar directive style:
+
+ [% example %]
+
+Any Perl regex characters can be used and therefore should be escaped
+(or use the Perl C<quotemeta> function) if they are intended to
+represent literal characters.
+
+ my $template = Template->new({
+ START_TAG => quotemeta('<+'),
+ END_TAG => quotemeta('+>'),
+ });
+
+example:
+
+ <+ INCLUDE foobar +>
+
+The TAGS directive can also be used to set the START_TAG and END_TAG values
+on a per-template file basis.
+
+ [% TAGS <+ +> %]
+
+
+
+
+
+
+=item TAG_STYLE
+
+The TAG_STYLE option can be used to set both START_TAG and END_TAG
+according to pre-defined tag styles.
+
+ my $template = Template->new({
+ TAG_STYLE => 'star',
+ });
+
+Available styles are:
+
+ template [% ... %] (default)
+ template1 [% ... %] or %% ... %% (TT version 1)
+ metatext %% ... %% (Text::MetaText)
+ star [* ... *] (TT alternate)
+ php <? ... ?> (PHP)
+ asp <% ... %> (ASP)
+ mason <% ... > (HTML::Mason)
+ html <!-- ... --> (HTML comments)
+
+Any values specified for START_TAG and/or END_TAG will over-ride
+those defined by a TAG_STYLE.
+
+The TAGS directive may also be used to set a TAG_STYLE
+
+ [% TAGS html %]
+ <!-- INCLUDE header -->
+
+
+
+
+
+
+=item PRE_CHOMP, POST_CHOMP
+
+Anything outside a directive tag is considered plain text and is
+generally passed through unaltered (but see the INTERPOLATE option).
+This includes all whitespace and newlines characters surrounding
+directive tags. Directives that don't generate any output will leave
+gaps in the output document.
+
+Example:
+
+ Foo
+ [% a = 10 %]
+ Bar
+
+Output:
+
+ Foo
+
+ Bar
+
+The PRE_CHOMP and POST_CHOMP options can help to clean up some of this
+extraneous whitespace. Both are disabled by default.
+
+ my $template = Template->new({
+ PRE_CHOMP => 1,
+ POST_CHOMP => 1,
+ });
+
+With PRE_CHOMP set to 1, the newline and whitespace preceding a directive
+at the start of a line will be deleted. This has the effect of
+concatenating a line that starts with a directive onto the end of the
+previous line.
+
+ Foo <----------.
+ |
+ ,---(PRE_CHOMP)----'
+ |
+ `-- [% a = 10 %] --.
+ |
+ ,---(POST_CHOMP)---'
+ |
+ `-> Bar
+
+With POST_CHOMP set to 1, any whitespace after a directive up to and
+including the newline will be deleted. This has the effect of joining
+a line that ends with a directive onto the start of the next line.
+
+If PRE_CHOMP or POST_CHOMP is set to 2, then instead of removing all
+the whitespace, the whitespace will be collapsed to a single space.
+This is useful for HTML, where (usually) a contiguous block of
+whitespace is rendered the same as a single space.
+
+You may use the CHOMP_NONE, CHOMP_ALL, and CHOMP_COLLAPSE constants
+from the Template::Constants module to deactivate chomping, remove
+all whitespace, or collapse whitespace to a single space.
+
+PRE_CHOMP and POST_CHOMP can be activated for individual directives by
+placing a '-' immediately at the start and/or end of the directive.
+
+ [% FOREACH user = userlist %]
+ [%- user -%]
+ [% END %]
+
+The '-' characters activate both PRE_CHOMP and POST_CHOMP for the one
+directive '[%- name -%]'. Thus, the template will be processed as if
+written:
+
+ [% FOREACH user = userlist %][% user %][% END %]
+
+Note that this is the same as if PRE_CHOMP and POST_CHOMP were set
+to CHOMP_ALL; the only way to get the CHOMP_COLLAPSE behavior is
+to set PRE_CHOMP or POST_CHOMP accordingly. If PRE_CHOMP or POST_CHOMP
+is already set to CHOMP_COLLAPSE, using '-' will give you CHOMP_COLLAPSE
+behavior, not CHOMP_ALL behavior.
+
+Similarly, '+' characters can be used to disable PRE_CHOMP or
+POST_CHOMP (i.e. leave the whitespace/newline intact) options on a
+per-directive basis.
+
+ [% FOREACH user = userlist %]
+ User: [% user +%]
+ [% END %]
+
+With POST_CHOMP enabled, the above example would be parsed as if written:
+
+ [% FOREACH user = userlist %]User: [% user %]
+ [% END %]
+
+
+
+
+
+=item TRIM
+
+The TRIM option can be set to have any leading and trailing whitespace
+automatically removed from the output of all template files and BLOCKs.
+
+By example, the following BLOCK definition
+
+ [% BLOCK foo %]
+ Line 1 of foo
+ [% END %]
+
+will be processed is as "\nLine 1 of foo\n". When INCLUDEd, the surrounding
+newlines will also be introduced.
+
+ before
+ [% INCLUDE foo %]
+ after
+
+output:
+ before
+
+ Line 1 of foo
+
+ after
+
+With the TRIM option set to any true value, the leading and trailing
+newlines (which count as whitespace) will be removed from the output
+of the BLOCK.
+
+ before
+ Line 1 of foo
+ after
+
+The TRIM option is disabled (0) by default.
+
+
+
+
+
+=item INTERPOLATE
+
+The INTERPOLATE flag, when set to any true value will cause variable
+references in plain text (i.e. not surrounded by START_TAG and END_TAG)
+to be recognised and interpolated accordingly.
+
+ my $template = Template->new({
+ INTERPOLATE => 1,
+ });
+
+Variables should be prefixed by a '$' to identify them. Curly braces
+can be used in the familiar Perl/shell style to explicitly scope the
+variable name where required.
+
+ # INTERPOLATE => 0
+ <a href="http://[% server %]/[% help %]">
+ <img src="[% images %]/help.gif"></a>
+ [% myorg.name %]
+
+ # INTERPOLATE => 1
+ <a href="http://$server/$help">
+ <img src="$images/help.gif"></a>
+ $myorg.name
+
+ # explicit scoping with { }
+ <img src="$images/${icon.next}.gif">
+
+Note that a limitation in Perl's regex engine restricts the maximum length
+of an interpolated template to around 32 kilobytes or possibly less. Files
+that exceed this limit in size will typically cause Perl to dump core with
+a segmentation fault. If you routinely process templates of this size
+then you should disable INTERPOLATE or split the templates in several
+smaller files or blocks which can then be joined backed together via
+PROCESS or INCLUDE.
+
+
+
+
+
+
+
+=item ANYCASE
+
+By default, directive keywords should be expressed in UPPER CASE. The
+ANYCASE option can be set to allow directive keywords to be specified
+in any case.
+
+ # ANYCASE => 0 (default)
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # ERROR
+ [% include = 10 %] # OK, 'include' is a variable
+
+ # ANYCASE => 1
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # OK
+ [% include = 10 %] # ERROR, 'include' is reserved word
+
+One side-effect of enabling ANYCASE is that you cannot use a variable
+of the same name as a reserved word, regardless of case. The reserved
+words are currently:
+
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER
+ IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE
+ USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META
+ TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP
+ CLEAR TO STEP AND OR NOT MOD DIV END
+
+
+The only lower case reserved words that cannot be used for variables,
+regardless of the ANYCASE option, are the operators:
+
+ and or not mod div
+
+
+
+
+
+
+=back
+
+=head2 Template Files and Blocks
+
+=over 4
+
+
+
+=item INCLUDE_PATH
+
+The INCLUDE_PATH is used to specify one or more directories in which
+template files are located. When a template is requested that isn't
+defined locally as a BLOCK, each of the INCLUDE_PATH directories is
+searched in turn to locate the template file. Multiple directories
+can be specified as a reference to a list or as a single string where
+each directory is delimited by ':'.
+
+ my $template = Template->new({
+ INCLUDE_PATH => '/usr/local/templates',
+ });
+
+ my $template = Template->new({
+ INCLUDE_PATH => '/usr/local/templates:/tmp/my/templates',
+ });
+
+ my $template = Template->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ '/tmp/my/templates' ],
+ });
+
+On Win32 systems, a little extra magic is invoked, ignoring delimiters
+that have ':' followed by a '/' or '\'. This avoids confusion when using
+directory names like 'C:\Blah Blah'.
+
+When specified as a list, the INCLUDE_PATH path can contain elements
+which dynamically generate a list of INCLUDE_PATH directories. These
+generator elements can be specified as a reference to a subroutine or
+an object which implements a paths() method.
+
+ my $template = Template->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ \&incpath_generator,
+ My::IncPath::Generator->new( ... ) ],
+ });
+
+Each time a template is requested and the INCLUDE_PATH examined, the
+subroutine or object method will be called. A reference to a list of
+directories should be returned. Generator subroutines should report
+errors using die(). Generator objects should return undef and make an
+error available via its error() method.
+
+For example:
+
+ sub incpath_generator {
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ die "cannot generate INCLUDE_PATH...\n";
+ }
+ }
+
+or:
+
+ package My::IncPath::Generator;
+
+ # Template::Base (or Class::Base) provides error() method
+ use Template::Base;
+ use base qw( Template::Base );
+
+ sub paths {
+ my $self = shift;
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ return $self->error("cannot generate INCLUDE_PATH...\n");
+ }
+ }
+
+ 1;
+
+
+
+
+
+=item DELIMITER
+
+Used to provide an alternative delimiter character sequence for
+separating paths specified in the INCLUDE_PATH. The default
+value for DELIMITER is ':'.
+
+ # tolerate Silly Billy's file system conventions
+ my $template = Template->new({
+ DELIMITER => '; ',
+ INCLUDE_PATH => 'C:/HERE/NOW; D:/THERE/THEN',
+ });
+
+ # better solution: install Linux! :-)
+
+On Win32 systems, the default delimiter is a little more intelligent,
+splitting paths only on ':' characters that aren't followed by a '/'.
+This means that the following should work as planned, splitting the
+INCLUDE_PATH into 2 separate directories, C:/foo and C:/bar.
+
+ # on Win32 only
+ my $template = Template->new({
+ INCLUDE_PATH => 'C:/Foo:C:/Bar'
+ });
+
+However, if you're using Win32 then it's recommended that you
+explicitly set the DELIMITER character to something else (e.g. ';')
+rather than rely on this subtle magic.
+
+
+
+
+=item ABSOLUTE
+
+The ABSOLUTE flag is used to indicate if templates specified with
+absolute filenames (e.g. '/foo/bar') should be processed. It is
+disabled by default and any attempt to load a template by such a
+name will cause a 'file' exception to be raised.
+
+ my $template = Template->new({
+ ABSOLUTE => 1,
+ });
+
+ # this is why it's disabled by default
+ [% INSERT /etc/passwd %]
+
+On Win32 systems, the regular expression for matching absolute
+pathnames is tweaked slightly to also detect filenames that start
+with a driver letter and colon, such as:
+
+ C:/Foo/Bar
+
+
+
+
+
+
+=item RELATIVE
+
+The RELATIVE flag is used to indicate if templates specified with
+filenames relative to the current directory (e.g. './foo/bar' or
+'../../some/where/else') should be loaded. It is also disabled by
+default, and will raise a 'file' error if such template names are
+encountered.
+
+ my $template = Template->new({
+ RELATIVE => 1,
+ });
+
+ [% INCLUDE ../logs/error.log %]
+
+
+
+
+
+=item DEFAULT
+
+The DEFAULT option can be used to specify a default template which should
+be used whenever a specified template can't be found in the INCLUDE_PATH.
+
+ my $template = Template->new({
+ DEFAULT => 'notfound.html',
+ });
+
+If a non-existant template is requested through the Template process()
+method, or by an INCLUDE, PROCESS or WRAPPER directive, then the
+DEFAULT template will instead be processed, if defined. Note that the
+DEFAULT template is not used when templates are specified with
+absolute or relative filenames, or as a reference to a input file
+handle or text string.
+
+
+
+
+
+=item BLOCKS
+
+The BLOCKS option can be used to pre-define a default set of template
+blocks. These should be specified as a reference to a hash array
+mapping template names to template text, subroutines or Template::Document
+objects.
+
+ my $template = Template->new({
+ BLOCKS => {
+ header => 'The Header. [% title %]',
+ footer => sub { return $some_output_text },
+ another => Template::Document->new({ ... }),
+ },
+ });
+
+
+
+
+=item AUTO_RESET
+
+The AUTO_RESET option is set by default and causes the local BLOCKS
+cache for the Template::Context object to be reset on each call to the
+Template process() method. This ensures that any BLOCKs defined
+within a template will only persist until that template is finished
+processing. This prevents BLOCKs defined in one processing request
+from interfering with other independent requests subsequently
+processed by the same context object.
+
+The BLOCKS item may be used to specify a default set of block definitions
+for the Template::Context object. Subsequent BLOCK definitions in templates
+will over-ride these but they will be reinstated on each reset if AUTO_RESET
+is enabled (default), or if the Template::Context reset() method is called.
+
+
+
+
+
+
+
+
+
+=item RECURSION
+
+The template processor will raise a file exception if it detects
+direct or indirect recursion into a template. Setting this option to
+any true value will allow templates to include each other recursively.
+
+
+
+=back
+
+=head2 Template Variables
+
+=over 4
+
+=item VARIABLES, PRE_DEFINE
+
+The VARIABLES option (or PRE_DEFINE - they're equivalent) can be used
+to specify a hash array of template variables that should be used to
+pre-initialise the stash when it is created. These items are ignored
+if the STASH item is defined.
+
+ my $template = Template->new({
+ VARIABLES => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+or
+
+ my $template = Template->new({
+ PRE_DEFINE => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+
+
+
+=item CONSTANTS
+
+The CONSTANTS option can be used to specify a hash array of template
+variables that are compile-time constants. These variables are
+resolved once when the template is compiled, and thus don't require
+further resolution at runtime. This results in significantly faster
+processing of the compiled templates and can be used for variables that
+don't change from one request to the next.
+
+ my $template = Template->new({
+ CONSTANTS => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+=item CONSTANT_NAMESPACE
+
+Constant variables are accessed via the 'constants' namespace by
+default.
+
+ [% constants.title %]
+
+The CONSTANTS_NAMESPACE option can be set to specify an alternate
+namespace.
+
+ my $template = Template->new({
+ CONSTANTS => {
+ title => 'A Demo Page',
+ # ...etc...
+ },
+ CONSTANTS_NAMESPACE => 'const',
+ };
+
+In this case the constants would then be accessed as:
+
+ [% const.title %]
+
+=item NAMESPACE
+
+The constant folding mechanism described above is an example of a
+namespace handler. Namespace handlers can be defined to provide
+alternate parsing mechanisms for variables in different namespaces.
+
+Under the hood, the Template module converts a constructor configuration
+such as:
+
+ my $template = Template->new({
+ CONSTANTS => {
+ title => 'A Demo Page',
+ # ...etc...
+ },
+ CONSTANTS_NAMESPACE => 'const',
+ };
+
+into one like:
+
+ my $template = Template->new({
+ NAMESPACE => {
+ const => Template:::Namespace::Constants->new({
+ title => 'A Demo Page',
+ # ...etc...
+ }),
+ },
+ };
+
+You can use this mechanism to define multiple constant namespaces, or
+to install custom handlers of your own.
+
+ my $template = Template->new({
+ NAMESPACE => {
+ site => Template:::Namespace::Constants->new({
+ title => "Wardley's Widgets",
+ version => 2.718,
+ }),
+ author => Template:::Namespace::Constants->new({
+ name => 'Andy Wardley',
+ email => 'abw@andywardley.com',
+ }),
+ voodoo => My::Namespace::Handler->new( ... ),
+ },
+ };
+
+Now you have 2 constant namespaces, for example:
+
+ [% site.title %]
+ [% author.name %]
+
+as well as your own custom namespace handler installed for the 'voodoo'
+namespace.
+
+ [% voodoo.magic %]
+
+See L<Template::Namespace::Constants|Template::Namespace::Constants>
+for an example of what a namespace handler looks like on the inside.
+
+
+
+
+
+=back
+
+=head2 Template Processing Options
+
+
+The following options are used to specify any additional templates
+that should be processed before, after, around or instead of the
+template passed as the first argument to the Template process()
+method. These options can be perform various useful tasks such as
+adding standard headers or footers to all pages, wrapping page output
+in other templates, pre-defining variables or performing
+initialisation or cleanup tasks, automatically generating page summary
+information, navigation elements, and so on.
+
+The task of processing the template is delegated internally to the
+Template::Service module which, unsurprisingly, also has a process()
+method. Any templates defined by the PRE_PROCESS option are processed
+first and any output generated is added to the output buffer. Then
+the main template is processed, or if one or more PROCESS templates
+are defined then they are instead processed in turn. In this case,
+one of the PROCESS templates is responsible for processing the main
+template, by a directive such as:
+
+ [% PROCESS $template %]
+
+The output of processing the main template or the PROCESS template(s)
+is then wrapped in any WRAPPER templates, if defined. WRAPPER
+templates don't need to worry about explicitly processing the template
+because it will have been done for them already. Instead WRAPPER
+templates access the content they are wrapping via the 'content'
+variable.
+
+ wrapper before
+ [% content %]
+ wrapper after
+
+This output generated from processing the main template, and/or any
+PROCESS or WRAPPER templates is added to the output buffer. Finally,
+any POST_PROCESS templates are processed and their output is also
+added to the output buffer which is then returned.
+
+If the main template throws an exception during processing then any
+relevant template(s) defined via the ERROR option will be processed
+instead. If defined and successfully processed, the output from the
+error template will be added to the output buffer in place of the
+template that generated the error and processing will continue,
+applying any WRAPPER and POST_PROCESS templates. If no relevant ERROR
+option is defined, or if the error occurs in one of the PRE_PROCESS,
+WRAPPER or POST_PROCESS templates, then the process will terminate
+immediately and the error will be returned.
+
+
+
+=over 4
+
+
+
+=item PRE_PROCESS, POST_PROCESS
+
+These values may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed immediately
+before and/or after each template. These do not get added to
+templates processed into a document via directives such as INCLUDE,
+PROCESS, WRAPPER etc.
+
+ my $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ };
+
+Multiple templates may be specified as a reference to a list. Each is
+processed in the order defined.
+
+ my $template = Template->new({
+ PRE_PROCESS => [ 'config', 'header' ],
+ POST_PROCESS => 'footer',
+ };
+
+Alternately, multiple template may be specified as a single string,
+delimited by ':'. This delimiter string can be changed via the
+DELIMITER option.
+
+ my $template = Template->new({
+ PRE_PROCESS => 'config:header',
+ POST_PROCESS => 'footer',
+ };
+
+The PRE_PROCESS and POST_PROCESS templates are evaluated in the same
+variable context as the main document and may define or update
+variables for subsequent use.
+
+config:
+
+ [% # set some site-wide variables
+ bgcolor = '#ffffff'
+ version = 2.718
+ %]
+
+header:
+
+ [% DEFAULT title = 'My Funky Web Site' %]
+ <html>
+ <head>
+ <title>[% title %]</title>
+ </head>
+ <body bgcolor="[% bgcolor %]">
+
+footer:
+
+ <hr>
+ Version [% version %]
+ </body>
+ </html>
+
+The Template::Document object representing the main template being processed
+is available within PRE_PROCESS and POST_PROCESS templates as the 'template'
+variable. Metadata items defined via the META directive may be accessed
+accordingly.
+
+ $template->process('mydoc.html', $vars);
+
+mydoc.html:
+
+ [% META title = 'My Document Title' %]
+ blah blah blah
+ ...
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title></head>
+ <body bgcolor="[% bgcolor %]">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=item PROCESS
+
+The PROCESS option may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed instead of the
+main template passed to the Template process() method. This can
+be used to apply consistent wrappers around all templates, similar to
+the use of PRE_PROCESS and POST_PROCESS templates.
+
+ my $template = Template->new({
+ PROCESS => 'content',
+ };
+
+ # processes 'content' instead of 'foo.html'
+ $template->process('foo.html');
+
+A reference to the original template is available in the 'template'
+variable. Metadata items can be inspected and the template can be
+processed by specifying it as a variable reference (i.e. prefixed by
+'$') to an INCLUDE, PROCESS or WRAPPER directive.
+
+content:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title>
+ </head>
+
+ <body>
+ [% PROCESS $template %]
+ <hr>
+ &copy; Copyright [% template.copyright %]
+ </body>
+ </html>
+
+foo.html:
+
+ [% META
+ title = 'The Foo Page'
+ author = 'Fred Foo'
+ copyright = '2000 Fred Foo'
+ %]
+ <h1>[% template.title %]</h1>
+ Welcome to the Foo Page, blah blah blah
+
+output:
+
+ <html>
+ <head>
+ <title>The Foo Page</title>
+ </head>
+
+ <body>
+ <h1>The Foo Page</h1>
+ Welcome to the Foo Page, blah blah blah
+ <hr>
+ &copy; Copyright 2000 Fred Foo
+ </body>
+ </html>
+
+
+
+
+
+
+
+=item WRAPPER
+
+The WRAPPER option can be used to specify one or more templates which
+should be used to wrap around the output of the main page template.
+The main template is processed first (or any PROCESS template(s)) and
+the output generated is then passed as the 'content' variable to the
+WRAPPER template(s) as they are processed.
+
+ my $template = Template->new({
+ WRAPPER => 'wrapper',
+ };
+
+ # process 'foo' then wrap in 'wrapper'
+ $template->process('foo', { message => 'Hello World!' });
+
+wrapper:
+
+ <wrapper>
+ [% content %]
+ </wrapper>
+
+foo:
+
+ This is the foo file!
+ Message: [% message %]
+
+The output generated from this example is:
+
+ <wrapper>
+ This is the foo file!
+ Message: Hello World!
+ </wrapper>
+
+You can specify more than one WRAPPER template by setting the value to
+be a reference to a list of templates. The WRAPPER templates will be
+processed in reverse order with the output of each being passed to the
+next (or previous, depending on how you look at it) as the 'content'
+variable. It sounds complicated, but the end result is that it just
+"Does The Right Thing" to make wrapper templates nest in the order you
+specify.
+
+ my $template = Template->new({
+ WRAPPER => [ 'outer', 'inner' ],
+ };
+
+ # process 'foo' then wrap in 'inner', then in 'outer'
+ $template->process('foo', { message => 'Hello World!' });
+
+outer:
+
+ <outer>
+ [% content %]
+ </outer>
+
+inner:
+
+ <inner>
+ [% content %]
+ </inner>
+
+The output generated is then:
+
+ <outer>
+ <inner>
+ This is the foo file!
+ Message: Hello World!
+ </inner>
+ </outer>
+
+One side-effect of the "inside-out" processing of the WRAPPER
+configuration item (and also the WRAPPER directive) is that any
+variables set in the template being wrapped will be visible to the
+template doing the wrapping, but not the other way around.
+
+You can use this to good effect in allowing page templates to set
+pre-defined values which are then used in the wrapper templates. For
+example, our main page template 'foo' might look like this:
+
+foo:
+
+ [% page = {
+ title = 'Foo Page'
+ subtitle = 'Everything There is to Know About Foo'
+ author = 'Frank Oliver Octagon'
+ }
+ %]
+
+ <p>
+ Welcome to the page that tells you everything about foo
+ blah blah blah...
+ </p>
+
+The 'foo' template is processed before the wrapper template meaning
+that the 'page' data structure will be defined for use in the wrapper
+template.
+
+wrapper:
+
+ <html>
+ <head>
+ <title>[% page.title %]</title>
+ </head>
+ <body>
+ <h1>[% page.title %]</h1>
+ <h2>[% page.subtitle %]</h1>
+ <h3>by [% page.author %]</h3>
+
+ [% content %]
+ </body>
+ </html>
+
+It achieves the same effect as defining META items which are then
+accessed via the 'template' variable (which you are still free to
+use within WRAPPER templates), but gives you more flexibility in
+the type and complexity of data that you can define.
+
+
+
+
+
+=item ERROR
+
+The ERROR (or ERRORS if you prefer) configuration item can be used to
+name a single template or specify a hash array mapping exception types
+to templates which should be used for error handling. If an uncaught
+exception is raised from within a template then the appropriate error
+template will instead be processed.
+
+If specified as a single value then that template will be processed
+for all uncaught exceptions.
+
+ my $template = Template->new({
+ ERROR => 'error.html'
+ });
+
+If the ERROR item is a hash reference the keys are assumed to be
+exception types and the relevant template for a given exception will
+be selected. A 'default' template may be provided for the general
+case. Note that 'ERROR' can be pluralised to 'ERRORS' if you find
+it more appropriate in this case.
+
+ my $template = Template->new({
+ ERRORS => {
+ user => 'user/index.html',
+ dbi => 'error/database',
+ default => 'error/default',
+ },
+ });
+
+In this example, any 'user' exceptions thrown will cause the
+'user/index.html' template to be processed, 'dbi' errors are handled
+by 'error/database' and all others by the 'error/default' template.
+Any PRE_PROCESS and/or POST_PROCESS templates will also be applied
+to these error templates.
+
+Note that exception types are hierarchical and a 'foo' handler will
+catch all 'foo.*' errors (e.g. foo.bar, foo.bar.baz) if a more
+specific handler isn't defined. Be sure to quote any exception types
+that contain periods to prevent Perl concatenating them into a single
+string (i.e. C<user.passwd> is parsed as 'user'.'passwd').
+
+ my $template = Template->new({
+ ERROR => {
+ 'user.login' => 'user/login.html',
+ 'user.passwd' => 'user/badpasswd.html',
+ 'user' => 'user/index.html',
+ 'default' => 'error/default',
+ },
+ });
+
+In this example, any template processed by the $template object, or
+other templates or code called from within, can raise a 'user.login'
+exception and have the service redirect to the 'user/login.html'
+template. Similarly, a 'user.passwd' exception has a specific
+handling template, 'user/badpasswd.html', while all other 'user' or
+'user.*' exceptions cause a redirection to the 'user/index.html' page.
+All other exception types are handled by 'error/default'.
+
+
+Exceptions can be raised in a template using the THROW directive,
+
+ [% THROW user.login 'no user id: please login' %]
+
+or by calling the throw() method on the current Template::Context object,
+
+ $context->throw('user.passwd', 'Incorrect Password');
+ $context->throw('Incorrect Password'); # type 'undef'
+
+or from Perl code by calling die() with a Template::Exception object,
+
+ die (Template::Exception->new('user.denied', 'Invalid User ID'));
+
+or by simply calling die() with an error string. This is
+automagically caught and converted to an exception of 'undef'
+type which can then be handled in the usual way.
+
+ die "I'm sorry Dave, I can't do that";
+
+
+
+
+
+
+=back
+
+=head2 Template Runtime Options
+
+=over 4
+
+
+
+
+=item EVAL_PERL
+
+This flag is used to indicate if PERL and/or RAWPERL blocks should be
+evaluated. By default, it is disabled and any PERL or RAWPERL blocks
+encountered will raise exceptions of type 'perl' with the message
+'EVAL_PERL not set'. Note however that any RAWPERL blocks should
+always contain valid Perl code, regardless of the EVAL_PERL flag. The
+parser will fail to compile templates that contain invalid Perl code
+in RAWPERL blocks and will throw a 'file' exception.
+
+When using compiled templates (see
+L<COMPILE_EXT|Template::Manual::Config/Caching_and_Compiling_Options> and
+L<COMPILE_DIR|Template::Manual::Config/Caching_and_Compiling_Options>),
+the EVAL_PERL has an affect when the template is compiled, and again
+when the templates is subsequently processed, possibly in a different
+context to the one that compiled it.
+
+If the EVAL_PERL is set when a template is compiled, then all PERL and
+RAWPERL blocks will be included in the compiled template. If the
+EVAL_PERL option isn't set, then Perl code will be generated which
+B<always> throws a 'perl' exception with the message 'EVAL_PERL not
+set' B<whenever> the compiled template code is run.
+
+Thus, you must have EVAL_PERL set if you want your compiled templates
+to include PERL and RAWPERL blocks.
+
+At some point in the future, using a different invocation of the
+Template Toolkit, you may come to process such a pre-compiled
+template. Assuming the EVAL_PERL option was set at the time the
+template was compiled, then the output of any RAWPERL blocks will be
+included in the compiled template and will get executed when the
+template is processed. This will happen regardless of the runtime
+EVAL_PERL status.
+
+Regular PERL blocks are a little more cautious, however. If the
+EVAL_PERL flag isn't set for the I<current> context, that is, the
+one which is trying to process it, then it will throw the familiar 'perl'
+exception with the message, 'EVAL_PERL not set'.
+
+Thus you can compile templates to include PERL blocks, but optionally
+disable them when you process them later. Note however that it is
+possible for a PERL block to contain a Perl "BEGIN { # some code }"
+block which will always get run regardless of the runtime EVAL_PERL
+status. Thus, if you set EVAL_PERL when compiling templates, it is
+assumed that you trust the templates to Do The Right Thing. Otherwise
+you must accept the fact that there's no bulletproof way to prevent
+any included code from trampling around in the living room of the
+runtime environment, making a real nuisance of itself if it really
+wants to. If you don't like the idea of such uninvited guests causing
+a bother, then you can accept the default and keep EVAL_PERL disabled.
+
+
+
+
+
+
+
+=item OUTPUT
+
+Default output location or handler. This may be specified as one of:
+a file name (relative to OUTPUT_PATH, if defined, or the current
+working directory if not specified absolutely); a file handle
+(e.g. GLOB or IO::Handle) opened for writing; a reference to a text
+string to which the output is appended (the string isn't cleared); a
+reference to a subroutine which is called, passing the output text as
+an argument; as a reference to an array, onto which the content will be
+push()ed; or as a reference to any object that supports the print()
+method. This latter option includes the Apache::Request object which
+is passed as the argument to Apache/mod_perl handlers.
+
+example 1 (file name):
+
+ my $template = Template->new({
+ OUTPUT => "/tmp/foo",
+ });
+
+example 2 (text string):
+
+ my $output = '';
+
+ my $template = Template->new({
+ OUTPUT => \$output,
+ });
+
+example 3 (file handle):
+
+ open (TOUT, "> $file") || die "$file: $!\n";
+
+ my $template = Template->new({
+ OUTPUT => \*TOUT,
+ });
+
+example 4 (subroutine):
+
+ sub output { my $out = shift; print "OUTPUT: $out" }
+
+ my $template = Template->new({
+ OUTPUT => \&output,
+ });
+
+example 5 (array reference):
+
+ my $template = Template->new({
+ OUTPUT => \@output,
+ })
+
+example 6 (Apache/mod_perl handler):
+
+ sub handler {
+ my $r = shift;
+
+ my $t = Template->new({
+ OUTPUT => $r,
+ });
+ ...
+ }
+
+The default OUTPUT location be overridden by passing a third parameter
+to the Template process() method. This can be specified as any of the
+above argument types.
+
+ $t->process($file, $vars, "/tmp/foo");
+ $t->process($file, $vars, "bar");
+ $t->process($file, $vars, \*MYGLOB);
+ $t->process($file, $vars, \@output);
+ $t->process($file, $vars, $r); # Apache::Request
+ ...
+
+
+
+
+
+
+
+
+=item OUTPUT_PATH
+
+The OUTPUT_PATH allows a directory to be specified into which output
+files should be written. An output file can be specified by the
+OUTPUT option, or passed by name as the third parameter to the
+Template process() method.
+
+ my $template = Template->new({
+ INCLUDE_PATH => "/tmp/src",
+ OUTPUT_PATH => "/tmp/dest",
+ });
+
+ my $vars = {
+ ...
+ };
+
+ foreach my $file ('foo.html', 'bar.html') {
+ $template->process($file, $vars, $file)
+ || die $template->error();
+ }
+
+This example will read the input files '/tmp/src/foo.html' and
+'/tmp/src/bar.html' and write the processed output to '/tmp/dest/foo.html'
+and '/tmp/dest/bar.html', respectively.
+
+
+
+
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging within the various
+different modules that comprise the Template Toolkit. The
+L<Template::Constants|Template::Constants> module defines a set of
+DEBUG_XXXX constants which can be combined using the logical OR
+operator, '|'.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_PARSER | DEBUG_PROVIDER,
+ });
+
+For convenience, you can also provide a string containing a list
+of lower case debug options, separated by any non-word characters.
+
+ my $template = Template->new({
+ DEBUG => 'parser, provider',
+ });
+
+The following DEBUG_XXXX flags can be used:
+
+=over 4
+
+=item DEBUG_SERVICE
+
+Enables general debugging messages for the
+L<Template::Service|Template::Service> module.
+
+=item DEBUG_CONTEXT
+
+Enables general debugging messages for the
+L<Template::Context|Template::Context> module.
+
+=item DEBUG_PROVIDER
+
+Enables general debugging messages for the
+L<Template::Provider|Template::Provider> module.
+
+=item DEBUG_PLUGINS
+
+Enables general debugging messages for the
+L<Template::Plugins|Template::Plugins> module.
+
+=item DEBUG_FILTERS
+
+Enables general debugging messages for the
+L<Template::Filters|Template::Filters> module.
+
+=item DEBUG_PARSER
+
+This flag causes the L<Template::Parser|Template::Parser> to generate
+debugging messages that show the Perl code generated by parsing and
+compiling each template.
+
+=item DEBUG_UNDEF
+
+This option causes the Template Toolkit to throw an 'undef' error
+whenever it encounters an undefined variable value.
+
+=item DEBUG_DIRS
+
+This option causes the Template Toolkit to generate comments
+indicating the source file, line and original text of each directive
+in the template. These comments are embedded in the template output
+using the format defined in the DEBUG_FORMAT configuration item, or a
+simple default format if unspecified.
+
+For example, the following template fragment:
+
+
+ Hello World
+
+would generate this output:
+
+ ## input text line 1 : ##
+ Hello
+ ## input text line 2 : World ##
+ World
+
+=item DEBUG_ALL
+
+Enables all debugging messages.
+
+=item DEBUG_CALLER
+
+This option causes all debug messages that aren't newline terminated
+to have the file name and line number of the caller appended to them.
+
+
+=back
+
+=item DEBUG_FORMAT
+
+The DEBUG_FORMAT option can be used to specify a format string for the
+debugging messages generated via the DEBUG_DIRS option described
+above. Any occurances of C<$file>, C<$line> or C<$text> will be
+replaced with the current file name, line or directive text,
+respectively. Notice how the format is single quoted to prevent Perl
+from interpolating those tokens as variables.
+
+ my $template = Template->new({
+ DEBUG => 'dirs',
+ DEBUG_FORMAT => '<!-- $file line $line : [% $text %] -->',
+ });
+
+The following template fragment:
+
+ [% foo = 'World' %]
+ Hello [% foo %]
+
+would then generate this output:
+
+ <!-- input text line 2 : [% foo = 'World' %] -->
+ Hello <!-- input text line 3 : [% foo %] -->World
+
+The DEBUG directive can also be used to set a debug format within
+a template.
+
+ [% DEBUG format '<!-- $file line $line : [% $text %] -->' %]
+
+
+=back
+
+=head2 Caching and Compiling Options
+
+=over 4
+
+
+
+=item CACHE_SIZE
+
+The Template::Provider module caches compiled templates to avoid the need
+to re-parse template files or blocks each time they are used. The CACHE_SIZE
+option is used to limit the number of compiled templates that the module
+should cache.
+
+By default, the CACHE_SIZE is undefined and all compiled templates are
+cached. When set to any positive value, the cache will be limited to
+storing no more than that number of compiled templates. When a new
+template is loaded and compiled and the cache is full (i.e. the number
+of entries == CACHE_SIZE), the least recently used compiled template
+is discarded to make room for the new one.
+
+The CACHE_SIZE can be set to 0 to disable caching altogether.
+
+ my $template = Template->new({
+ CACHE_SIZE => 64, # only cache 64 compiled templates
+ });
+
+ my $template = Template->new({
+ CACHE_SIZE => 0, # don't cache any compiled templates
+ });
+
+
+
+
+
+
+=item COMPILE_EXT
+
+From version 2 onwards, the Template Toolkit has the ability to
+compile templates to Perl code and save them to disk for subsequent
+use (i.e. cache persistence). The COMPILE_EXT option may be
+provided to specify a filename extension for compiled template files.
+It is undefined by default and no attempt will be made to read or write
+any compiled template files.
+
+ my $template = Template->new({
+ COMPILE_EXT => '.ttc',
+ });
+
+If COMPILE_EXT is defined (and COMPILE_DIR isn't, see below) then compiled
+template files with the COMPILE_EXT extension will be written to the same
+directory from which the source template files were loaded.
+
+Compiling and subsequent reuse of templates happens automatically
+whenever the COMPILE_EXT or COMPILE_DIR options are set. The Template
+Toolkit will automatically reload and reuse compiled files when it
+finds them on disk. If the corresponding source file has been modified
+since the compiled version as written, then it will load and re-compile
+the source and write a new compiled version to disk.
+
+This form of cache persistence offers significant benefits in terms of
+time and resources required to reload templates. Compiled templates can
+be reloaded by a simple call to Perl's require(), leaving Perl to handle
+all the parsing and compilation. This is a Good Thing.
+
+=item COMPILE_DIR
+
+The COMPILE_DIR option is used to specify an alternate directory root
+under which compiled template files should be saved.
+
+ my $template = Template->new({
+ COMPILE_DIR => '/tmp/ttc',
+ });
+
+The COMPILE_EXT option may also be specified to have a consistent file
+extension added to these files.
+
+ my $template1 = Template->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc1',
+ });
+
+ my $template2 = Template->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc2',
+ });
+
+
+When COMPILE_EXT is undefined, the compiled template files have the
+same name as the original template files, but reside in a different
+directory tree.
+
+Each directory in the INCLUDE_PATH is replicated in full beneath the
+COMPILE_DIR directory. This example:
+
+ my $template = Template->new({
+ COMPILE_DIR => '/tmp/ttc',
+ INCLUDE_PATH => '/home/abw/templates:/usr/share/templates',
+ });
+
+would create the following directory structure:
+
+ /tmp/ttc/home/abw/templates/
+ /tmp/ttc/usr/share/templates/
+
+Files loaded from different INCLUDE_PATH directories will have their
+compiled forms save in the relevant COMPILE_DIR directory.
+
+On Win32 platforms a filename may by prefixed by a drive letter and
+colon. e.g.
+
+ C:/My Templates/header
+
+The colon will be silently stripped from the filename when it is added
+to the COMPILE_DIR value(s) to prevent illegal filename being generated.
+Any colon in COMPILE_DIR elements will be left intact. For example:
+
+ # Win32 only
+ my $template = Template->new({
+ DELIMITER => ';',
+ COMPILE_DIR => 'C:/TT2/Cache',
+ INCLUDE_PATH => 'C:/TT2/Templates;D:/My Templates',
+ });
+
+This would create the following cache directories:
+
+ C:/TT2/Cache/C/TT2/Templates
+ C:/TT2/Cache/D/My Templates
+
+
+=back
+
+=head2 Plugins and Filters
+
+=over 4
+
+
+
+=item PLUGINS
+
+The PLUGINS options can be used to provide a reference to a hash array
+that maps plugin names to Perl module names. A number of standard
+plugins are defined (e.g. 'table', 'cgi', 'dbi', etc.) which map to
+their corresponding Template::Plugin::* counterparts. These can be
+redefined by values in the PLUGINS hash.
+
+ my $template = Template->new({
+ PLUGINS => {
+ cgi => 'MyOrg::Template::Plugin::CGI',
+ foo => 'MyOrg::Template::Plugin::Foo',
+ bar => 'MyOrg::Template::Plugin::Bar',
+ },
+ });
+
+The USE directive is used to create plugin objects and does so by
+calling the plugin() method on the current Template::Context object.
+If the plugin name is defined in the PLUGINS hash then the
+corresponding Perl module is loaded via require(). The context then
+calls the load() class method which should return the class name
+(default and general case) or a prototype object against which the
+new() method can be called to instantiate individual plugin objects.
+
+If the plugin name is not defined in the PLUGINS hash then the PLUGIN_BASE
+and/or LOAD_PERL options come into effect.
+
+
+
+
+
+=item PLUGIN_BASE
+
+If a plugin is not defined in the PLUGINS hash then the PLUGIN_BASE is used
+to attempt to construct a correct Perl module name which can be successfully
+loaded.
+
+The PLUGIN_BASE can be specified as a single value or as a reference
+to an array of multiple values. The default PLUGIN_BASE value,
+'Template::Plugin', is always added the the end of the PLUGIN_BASE
+list (a single value is first converted to a list). Each value should
+contain a Perl package name to which the requested plugin name is
+appended.
+
+example 1:
+
+ my $template = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+example 2:
+
+ my $template = Template->new({
+ PLUGIN_BASE => [ 'MyOrg::Template::Plugin',
+ 'YourOrg::Template::Plugin' ],
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or YourOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+
+
+
+
+
+=item LOAD_PERL
+
+If a plugin cannot be loaded using the PLUGINS or PLUGIN_BASE
+approaches then the provider can make a final attempt to load the
+module without prepending any prefix to the module path. This allows
+regular Perl modules (i.e. those that don't reside in the
+Template::Plugin or some other such namespace) to be loaded and used
+as plugins.
+
+By default, the LOAD_PERL option is set to 0 and no attempt will be made
+to load any Perl modules that aren't named explicitly in the PLUGINS
+hash or reside in a package as named by one of the PLUGIN_BASE
+components.
+
+Plugins loaded using the PLUGINS or PLUGIN_BASE receive a reference to
+the current context object as the first argument to the new()
+constructor. Modules loaded using LOAD_PERL are assumed to not
+conform to the plugin interface. They must provide a new() class
+method for instantiating objects but it will not receive a reference
+to the context as the first argument. Plugin modules should provide a
+load() class method (or inherit the default one from the
+Template::Plugin base class) which is called the first time the plugin
+is loaded. Regular Perl modules need not. In all other respects,
+regular Perl objects and Template Toolkit plugins are identical.
+
+If a particular Perl module does not conform to the common, but not
+unilateral, new() constructor convention then a simple plugin wrapper
+can be written to interface to it.
+
+
+
+
+=item FILTERS
+
+The FILTERS option can be used to specify custom filters which can
+then be used with the FILTER directive like any other. These are
+added to the standard filters which are available by default. Filters
+specified via this option will mask any standard filters of the same
+name.
+
+The FILTERS option should be specified as a reference to a hash array
+in which each key represents the name of a filter. The corresponding
+value should contain a reference to an array containing a subroutine
+reference and a flag which indicates if the filter is static (0) or
+dynamic (1). A filter may also be specified as a solitary subroutine
+reference and is assumed to be static.
+
+ $template = Template->new({
+ FILTERS => {
+ 'sfilt1' => \&static_filter, # static
+ 'sfilt2' => [ \&static_filter, 0 ], # same as above
+ 'dfilt1' => [ \&dyanamic_filter_factory, 1 ],
+ },
+ });
+
+Additional filters can be specified at any time by calling the
+define_filter() method on the current Template::Context object.
+The method accepts a filter name, a reference to a filter
+subroutine and an optional flag to indicate if the filter is
+dynamic.
+
+ my $context = $template->context();
+ $context->define_filter('new_html', \&new_html);
+ $context->define_filter('new_repeat', \&new_repeat, 1);
+
+Static filters are those where a single subroutine reference is used
+for all invocations of a particular filter. Filters that don't accept
+any configuration parameters (e.g. 'html') can be implemented
+statically. The subroutine reference is simply returned when that
+particular filter is requested. The subroutine is called to filter
+the output of a template block which is passed as the only argument.
+The subroutine should return the modified text.
+
+ sub static_filter {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+
+The following template fragment:
+
+ [% FILTER sfilt1 %]
+ Blah blah blah.
+ [% END %]
+
+is approximately equivalent to:
+
+ &static_filter("\nBlah blah blah.\n");
+
+Filters that can accept parameters (e.g. 'truncate') should be
+implemented dynamically. In this case, the subroutine is taken to be
+a filter 'factory' that is called to create a unique filter subroutine
+each time one is requested. A reference to the current
+Template::Context object is passed as the first parameter, followed by
+any additional parameters specified. The subroutine should return
+another subroutine reference (usually a closure) which implements the
+filter.
+
+ sub dynamic_filter_factory {
+ my ($context, @args) = @_;
+
+ return sub {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+ }
+
+The following template fragment:
+
+ [% FILTER dfilt1(123, 456) %]
+ Blah blah blah
+ [% END %]
+
+is approximately equivalent to:
+
+ my $filter = &dynamic_filter_factory($context, 123, 456);
+ &$filter("\nBlah blah blah.\n");
+
+See the FILTER directive for further examples.
+
+
+=back
+
+=head2 Compatibility, Customisation and Extension
+
+=over 4
+
+
+
+=item V1DOLLAR
+
+In version 1 of the Template Toolkit, an optional leading '$' could be placed
+on any template variable and would be silently ignored.
+
+ # VERSION 1
+ [% $foo %] === [% foo %]
+ [% $hash.$key %] === [% hash.key %]
+
+To interpolate a variable value the '${' ... '}' construct was used.
+Typically, one would do this to index into a hash array when the key
+value was stored in a variable.
+
+example:
+
+ my $vars = {
+ users => {
+ aba => { name => 'Alan Aardvark', ... },
+ abw => { name => 'Andy Wardley', ... },
+ ...
+ },
+ uid => 'aba',
+ ...
+ };
+
+ $template->process('user/home.html', $vars)
+ || die $template->error(), "\n";
+
+'user/home.html':
+
+ [% user = users.${uid} %] # users.aba
+ Name: [% user.name %] # Alan Aardvark
+
+This was inconsistent with double quoted strings and also the
+INTERPOLATE mode, where a leading '$' in text was enough to indicate a
+variable for interpolation, and the additional curly braces were used
+to delimit variable names where necessary. Note that this use is
+consistent with UNIX and Perl conventions, among others.
+
+ # double quoted string interpolation
+ [% name = "$title ${user.name}" %]
+
+ # INTERPOLATE = 1
+ <img src="$images/help.gif"></a>
+ <img src="$images/${icon.next}.gif">
+
+For version 2, these inconsistencies have been removed and the syntax
+clarified. A leading '$' on a variable is now used exclusively to
+indicate that the variable name should be interpolated
+(e.g. subsituted for its value) before being used. The earlier example
+from version 1:
+
+ # VERSION 1
+ [% user = users.${uid} %]
+ Name: [% user.name %]
+
+can now be simplified in version 2 as:
+
+ # VERSION 2
+ [% user = users.$uid %]
+ Name: [% user.name %]
+
+The leading dollar is no longer ignored and has the same effect of
+interpolation as '${' ... '}' in version 1. The curly braces may
+still be used to explicitly scope the interpolated variable name
+where necessary.
+
+e.g.
+
+ [% user = users.${me.id} %]
+ Name: [% user.name %]
+
+The rule applies for all variables, both within directives and in
+plain text if processed with the INTERPOLATE option. This means that
+you should no longer (if you ever did) add a leading '$' to a variable
+inside a directive, unless you explicitly want it to be interpolated.
+
+One obvious side-effect is that any version 1 templates with variables
+using a leading '$' will no longer be processed as expected. Given
+the following variable definitions,
+
+ [% foo = 'bar'
+ bar = 'baz'
+ %]
+
+version 1 would interpret the following as:
+
+ # VERSION 1
+ [% $foo %] => [% GET foo %] => bar
+
+whereas version 2 interprets it as:
+
+ # VERSION 2
+ [% $foo %] => [% GET $foo %] => [% GET bar %] => baz
+
+In version 1, the '$' is ignored and the value for the variable 'foo' is
+retrieved and printed. In version 2, the variable '$foo' is first interpolated
+to give the variable name 'bar' whose value is then retrieved and printed.
+
+The use of the optional '$' has never been strongly recommended, but
+to assist in backwards compatibility with any version 1 templates that
+may rely on this "feature", the V1DOLLAR option can be set to 1
+(default: 0) to revert the behaviour and have leading '$' characters
+ignored.
+
+ my $template = Template->new({
+ V1DOLLAR => 1,
+ });
+
+
+
+
+=item LOAD_TEMPLATES
+
+The LOAD_TEMPLATE option can be used to provide a reference to a list
+of Template::Provider objects or sub-classes thereof which will take
+responsibility for loading and compiling templates.
+
+ my $template = Template->new({
+ LOAD_TEMPLATES => [
+ MyOrg::Template::Provider->new({ ... }),
+ Template::Provider->new({ ... }),
+ ],
+ });
+
+When a PROCESS, INCLUDE or WRAPPER directive is encountered, the named
+template may refer to a locally defined BLOCK or a file relative to
+the INCLUDE_PATH (or an absolute or relative path if the appropriate
+ABSOLUTE or RELATIVE options are set). If a BLOCK definition can't be
+found (see the Template::Context template() method for a discussion of
+BLOCK locality) then each of the LOAD_TEMPLATES provider objects is
+queried in turn via the fetch() method to see if it can supply the
+required template. Each provider can return a compiled template, an
+error, or decline to service the request in which case the
+responsibility is passed to the next provider. If none of the
+providers can service the request then a 'not found' error is
+returned. The same basic provider mechanism is also used for the
+INSERT directive but it bypasses any BLOCK definitions and doesn't
+attempt is to parse or process the contents of the template file.
+
+This is an implementation of the 'Chain of Responsibility'
+design pattern as described in
+"Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John
+Vlissides), Addision-Wesley, ISBN 0-201-63361-2, page 223
+.
+
+If LOAD_TEMPLATES is undefined, a single default provider will be
+instantiated using the current configuration parameters. For example,
+the Template::Provider INCLUDE_PATH option can be specified in the Template configuration and will be correctly passed to the provider's
+constructor method.
+
+ my $template = Template->new({
+ INCLUDE_PATH => '/here:/there',
+ });
+
+
+
+
+
+=item LOAD_PLUGINS
+
+The LOAD_PLUGINS options can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for loading and instantiating template plugin objects. The
+Template::Content plugin() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and filter() methods.
+
+ my $template = Template->new({
+ LOAD_PLUGINS => [
+ MyOrg::Template::Plugins->new({ ... }),
+ Template::Plugins->new({ ... }),
+ ],
+ });
+
+By default, a single Template::Plugins object is created using the
+current configuration hash. Configuration items destined for the
+Template::Plugins constructor may be added to the Template
+constructor.
+
+ my $template = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugins',
+ LOAD_PERL => 1,
+ });
+
+
+
+
+
+=item LOAD_FILTERS
+
+The LOAD_FILTERS option can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for returning and/or creating filter subroutines. The
+Template::Context filter() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and plugin() methods.
+
+ my $template = Template->new({
+ LOAD_FILTERS => [
+ MyTemplate::Filters->new(),
+ Template::Filters->new(),
+ ],
+ });
+
+By default, a single Template::Filters object is created for the
+LOAD_FILTERS list.
+
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+
+
+=item SERVICE
+
+A reference to a Template::Service object, or sub-class thereof, to which
+the Template module should delegate. If unspecified, a Template::Service
+object is automatically created using the current configuration hash.
+
+ my $template = Template->new({
+ SERVICE => MyOrg::Template::Service->new({ ... }),
+ });
+
+
+
+
+
+=item CONTEXT
+
+A reference to a Template::Context object which is used to define a
+specific environment in which template are processed. A Template::Context
+object is passed as the only parameter to the Perl subroutines that
+represent "compiled" template documents. Template subroutines make
+callbacks into the context object to access Template Toolkit functionality,
+for example, to to INCLUDE or PROCESS another template (include() and
+process() methods, respectively), to USE a plugin (plugin()) or
+instantiate a filter (filter()) or to access the stash (stash()) which
+manages variable definitions via the get() and set() methods.
+
+ my $template = Template->new({
+ CONTEXT => MyOrg::Template::Context->new({ ... }),
+ });
+
+
+
+=item STASH
+
+A reference to a Template::Stash object or sub-class which will take
+responsibility for managing template variables.
+
+ my $stash = MyOrg::Template::Stash->new({ ... });
+ my $template = Template->new({
+ STASH => $stash,
+ });
+
+If unspecified, a default stash object is created using the VARIABLES
+configuration item to initialise the stash variables. These may also
+be specified as the PRE_DEFINE option for backwards compatibility with
+version 1.
+
+ my $template = Template->new({
+ VARIABLES => {
+ id => 'abw',
+ name => 'Andy Wardley',
+ },
+ };
+
+
+
+
+
+=item PARSER
+
+The Template::Parser module implements a parser object for compiling
+templates into Perl code which can then be executed. A default object
+of this class is created automatically and then used by the
+Template::Provider whenever a template is loaded and requires
+compilation. The PARSER option can be used to provide a reference to
+an alternate parser object.
+
+ my $template = Template->new({
+ PARSER => MyOrg::Template::Parser->new({ ... }),
+ });
+
+
+
+
+
+=item GRAMMAR
+
+The GRAMMAR configuration item can be used to specify an alternate
+grammar for the parser. This allows a modified or entirely new
+template language to be constructed and used by the Template Toolkit.
+
+Source templates are compiled to Perl code by the Template::Parser
+using the Template::Grammar (by default) to define the language
+structure and semantics. Compiled templates are thus inherently
+"compatible" with each other and there is nothing to prevent any
+number of different template languages being compiled and used within
+the same Template Toolkit processing environment (other than the usual
+time and memory constraints).
+
+The Template::Grammar file is constructed from a YACC like grammar
+(using Parse::YAPP) and a skeleton module template. These files are
+provided, along with a small script to rebuild the grammar, in the
+'parser' sub-directory of the distribution. You don't have to know or
+worry about these unless you want to hack on the template language or
+define your own variant. There is a README file in the same directory
+which provides some small guidance but it is assumed that you know
+what you're doing if you venture herein. If you grok LALR parsers,
+then you should find it comfortably familiar.
+
+By default, an instance of the default Template::Grammar will be
+created and used automatically if a GRAMMAR item isn't specified.
+
+ use MyOrg::Template::Grammar;
+
+ my $template = Template->new({
+ GRAMMAR = MyOrg::Template::Grammar->new();
+ });
+
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Credits.pod b/lib/Template/Manual/Credits.pod
new file mode 100644
index 0000000..64999ac
--- /dev/null
+++ b/lib/Template/Manual/Credits.pod
@@ -0,0 +1,188 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Credits
+#
+# DESCRIPTION
+# This section provides a brief history of the Template Toolkit and
+# details the primary author and numerous other people who have
+# contributed to it.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Credits - Author and contributor credits
+
+=head1 DESCRIPTION
+
+This section provides a brief history of the Template Toolkit and
+details the primary author and numerous other people who have
+contributed to it.
+
+=head1 HISTORY
+
+The Template Toolkit began its life as the Text::MetaText module,
+originally released to CPAN around 1996. This itself was the public
+manifestation of an earlier template processing system I developed
+while working at Peritas (now Knowledge Pool -
+ http://www.knowledgepool.com/)
+
+Text::MetaText was the prototype - the one we always planned to throw
+away. It did the job well, showing us what worked and what didn't, what
+was good and what was bad, and gave us some ideas about what could be
+done better, given the chance to start again from scratch.
+
+Some time late in 1998 I threw away the prototype and started work on
+the Template Toolkit. By then I was working at Canon Research Centre
+Europe Ltd. (http://www.cre.canon.co.uk), involved in a general
+research programme related to web publishing and dynamic content
+generation. The first alpha release was in June 1999, followed by
+numerous more alpha and beta releases culminating in 1.00 being
+released on 2nd December 1999.
+
+A month or so later, work had begun on version 2.00. The plan was to
+get the template language relatively stable in version 1.00 and not
+worry too much about performance or other internal matters. Then,
+version 2.00 would follow to improve performance, clean up the
+architecture and fix anything that, with the benefit of hindsight, we
+thought could be improved. As it happens, me starting work on version
+2.00 coincided with Doug Steinwand sending me his parser variant which
+compiled templates to Perl code, giving a major performance boost.
+As well as the speedups, there are a whole host of significant new
+features in version 2.00, and a greatly improved internal architecture.
+Apart from a few minor "fixups" the template directives and language
+have remained the same as in version 1.00
+
+Version 2.00 was available in beta release form in July 2000, just
+in time for the 4th Perl Conference where version 1.00 was awarded
+"Best New Perl Module". After another extended beta release period,
+version 2.00 was released on 1st December 2000.
+
+
+
+
+=head1 CONTRIBUTORS
+
+Many people have contributed ideas, inspiration, fixes and features to
+the Template Toolkit. Their efforts continue to be very much appreciated.
+Please let me know if you think anyone is missing from this list.
+
+ Chuck Adams <scrytch@uswest.net>
+ Stephen Adkins <stephen.adkins@officevision.com>
+ Ivan Adzhubey <iadzhubey@rics.bwh.harvard.edu>
+ Mark Anderson <mda@discerning.com>
+ Bradley Baetz <bbaetz@student.usyd.edu.au>
+ Thierry-Michel Barral <kktos@electron-libre.com>
+ Craig Barratt <craig@arraycomm.com>
+ Stas Bekman <stas@stason.org>
+ Tony Bowden <tony-tt@kasei.com>
+ Neil Bowers <neilb@cre.canon.co.uk>
+ Leon Brocard <acme@astray.com>
+ Lyle Brooks <brooks@deseret.com>
+ Dave Cash <dave@gnofn.org>
+ Piers Cawley <pdcawley@bofh.org.uk>
+ Darren Chamberlain <dlc@users.sourceforge.net>
+ Eric Cholet <cholet@logilune.com>
+ Dave Cross <dave@dave.org.uk>
+ Chris Dean <ctdean@babycenter.com>
+ Francois Desarmenien <desar@club-internet.fr>
+ Horst Dumcke <hdumcke@cisco.com>
+ Mark Fowler <mark@indicosoftware.com>
+ Michael Fowler <michael@shoebox.net>
+ Axel Gerstmair <anthill@web.de>
+ Dylan William Hardison <dylanwh@tampabay.rr.com>
+ Perrin Harkins <pharkins@etoys.com>
+ Bryce Harrington <bryce@osdl.org>
+ Dave Hodgkinson <daveh@davehodgkinson.com>
+ Harald Joerg <Harald.Joerg@fujitsu-siemens.com>
+ Colin Johnson <colin@knowledgepool.com>
+ Vivek Khera <khera@kciLink.com>
+ Rafael Kitover <caelum@debian.org>
+ Ivan Kurmanov <http://www.ahinea.com>
+ Hans von Lengerke <hans@lengerke.org>
+ Jonas Liljegren <jonas@paranormal.se>
+ Simon Luff <simon@sports.com>
+ Paul Makepeace <Paul.Makepeace@realprogrammers.com>
+ Gervase Markham <gerv@mozilla.org>
+ Simon Matthews <sam@knowledgepool.com>
+ Robert McArthur <mcarthur@dstc.edu.au>
+ Craig McLane <mclanec@oxy.edu>
+ Leslie Michael Orchard <deus_x@ninjacode.com>
+ Eugene Miretskiy <eugene@invision.net>
+ Tatsuhiko Miyagawa <miyagawa@edge.co.jp>
+ Keith G. Murphy <keithmur@mindspring.com>
+ Chris Nandor <pudge@pobox.com>
+ Briac Pilpré <briac@pilpre.com>
+ Martin Portman <mrp@cre.canon.co.uk>
+ Slaven Rezic <slaven.rezic@berlin.de>
+ Christian Schaffner <schaffner@eeh.ee.ethz.ch>
+ Randal L. Schwartz <merlyn@stonehenge.com>
+ Paul Sharpe <paul@miraclefish.com>
+ Ville Skyttä <ville.skytta@iki.fi>
+ Doug Steinwand <dsteinwand@etoys.com>
+ Michael Stevens <michael@etla.org>
+ Drew Taylor <dtaylor@vialogix.com>
+ Swen Thuemmler <Swen.Thuemmler@paderlinx.de>
+ Richard Tietjen <Richard_Tietjen@mcgraw-hill.com>
+ Stathy G. Touloumis <stathy.touloumis@edventions.com>
+ Jim Vaughan <jim@mrjim.com>
+ Simon Wilcox <simonw@simonwilcox.co.uk>
+ Chris Winters <cwinters@intes.net>
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Directives.pod b/lib/Template/Manual/Directives.pod
new file mode 100644
index 0000000..3b8af3e
--- /dev/null
+++ b/lib/Template/Manual/Directives.pod
@@ -0,0 +1,2179 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Directives
+#
+# DESCRIPTION
+# This section provides a reference of all Template Toolkit
+# directives, complete with examples of use.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Directives - Template directives
+
+=head1 DESCRIPTION
+
+This section provides a reference of all Template Toolkit directives,
+complete with examples of use.
+
+=head2 Accessing and Updating Template Variables
+
+=over 4
+
+
+=item GET
+
+The GET directive retrieves and outputs the value of the named variable.
+
+ [% GET foo %]
+
+The GET keyword is optional. A variable can be specified in a directive
+tag by itself.
+
+ [% foo %]
+
+The variable can have an unlimited number of elements, each separated
+by a dot '.'. Each element can have arguments specified within
+parentheses.
+
+ [% foo %]
+ [% bar.baz %]
+ [% biz.baz(10) %]
+ ...etc...
+
+See L<Template::Manual::Variables> for a full discussion on template
+variables.
+
+You can also specify expressions using the logical (and, or, not, ?:) and
+mathematic operators (+ - * / % mod div).
+
+ [% template.title or default.title %]
+
+ [% score * 100 %]
+
+ [% order.nitems ? checkout(order.total) : 'no items' %]
+
+The 'div' operator returns the integer result of division. Both '%' and
+'mod' return the modulus (i.e. remainder) of division. 'mod' is provided
+as an alias for '%' for backwards compatibility with version 1.
+
+ [% 15 / 6 %] # 2.5
+ [% 15 div 6 %] # 2
+ [% 15 mod 6 %] # 3
+
+
+
+=item CALL
+
+The CALL directive is similar to GET in evaluating the variable named,
+but doesn't print the result returned. This can be useful when a
+variable is bound to a sub-routine or object method which you want to
+call but aren't interested in the value returned.
+
+ [% CALL dbi.disconnect %]
+
+ [% CALL inc_page_counter(page_count) %]
+
+
+
+
+=item SET
+
+The SET directive allows you to assign new values to existing variables
+or create new temporary variables.
+
+ [% SET title = 'Hello World' %]
+
+The SET keyword is also optional.
+
+ [% title = 'Hello World' %]
+
+Variables may be assigned the values of other variables, unquoted
+numbers (digits), literal text ('single quotes') or quoted text
+("double quotes"). In the latter case, any variable references within
+the text will be interpolated when the string is evaluated. Variables
+should be prefixed by '$', using curly braces to explicitly scope
+the variable name where necessary.
+
+ [% foo = 'Foo' %] # literal value 'Foo'
+ [% bar = foo %] # value of variable 'foo'
+ [% cost = '$100' %] # literal value '$100'
+ [% item = "$bar: ${cost}.00" %] # value "Foo: $100.00"
+
+Multiple variables may be assigned in the same directive and are
+evaluated in the order specified. Thus, the above could have been
+written:
+
+ [% foo = 'Foo'
+ bar = foo
+ cost = '$100'
+ item = "$bar: ${cost}.00"
+ %]
+
+Simple expressions can also be used, as per GET.
+
+ [% ten = 10
+ twenty = 20
+ thirty = twenty + ten
+ forty = 2 * twenty
+ fifty = 100 div 2
+ six = twenty mod 7
+ %]
+
+You can concatenate strings together using the ' _ ' operator. In Perl 5,
+the '.' is used for string concatenation, but in Perl 6, as in the Template
+Toolkit, the '.' will be used as the method calling operator and ' _ ' will
+be used for string concatenation. Note that the operator must be
+specified with surrounding whitespace which, as Larry says, is construed as
+a feature:
+
+ [% copyright = '(C) Copyright' _ year _ ' ' _ author %]
+
+You can, of course, achieve a similar effect with double quoted string
+interpolation.
+
+ [% copyright = "(C) Copyright $year $author" %]
+
+
+
+
+
+=item DEFAULT
+
+The DEFAULT directive is similar to SET but only updates variables
+that are currently undefined or have no "true" value (in the Perl
+sense).
+
+ [% DEFAULT
+ name = 'John Doe'
+ id = 'jdoe'
+ %]
+
+This can be particularly useful in common template components to
+ensure that some sensible default are provided for otherwise
+undefined variables.
+
+ [% DEFAULT
+ title = 'Hello World'
+ bgcol = '#ffffff'
+ %]
+ <html>
+ <head>
+ <title>[% title %]</title>
+ </head>
+
+ <body bgcolor="[% bgcol %]">
+
+
+=back
+
+=head2 Processing Other Template Files and Blocks
+
+=over 4
+
+
+=item INSERT
+
+The INSERT directive is used to insert the contents of an external file
+at the current position.
+
+ [% INSERT myfile %]
+
+No attempt to parse or process the file is made. The contents,
+possibly including any embedded template directives, are inserted
+intact.
+
+The filename specified should be relative to one of the INCLUDE_PATH
+directories. Absolute (i.e. starting with C</>) and relative
+(i.e. starting with C<.>) filenames may be used if the ABSOLUTE and
+RELATIVE options are set, respectively. Both these options are
+disabled by default.
+
+ my $template = Template->new({
+ INCLUDE_PATH => '/here:/there',
+ });
+
+ $template->process('myfile');
+
+'myfile':
+
+ [% INSERT foo %] # looks for /here/foo then /there/foo
+ [% INSERT /etc/passwd %] # file error: ABSOLUTE not set
+ [% INSERT ../secret %] # file error: RELATIVE not set
+
+For convenience, the filename does not need to be quoted as long as it
+contains only alphanumeric characters, underscores, dots or forward
+slashes. Names containing any other characters should be quoted.
+
+ [% INSERT misc/legalese.txt %]
+ [% INSERT 'dos98/Program Files/stupid' %]
+
+To evaluate a variable to specify a filename, you should explicitly
+prefix it with a '$' or use double-quoted string interpolation.
+
+ [% language = 'en'
+ legalese = 'misc/legalese.txt'
+ %]
+
+ [% INSERT $legalese %] # 'misc/legalese.txt'
+ [% INSERT "$language/$legalese" %] # 'en/misc/legalese.txt'
+
+Multiple files can be specified using '+' as a delimiter. All files
+should be unquoted names or quoted strings. Any variables should be
+interpolated into double-quoted strings.
+
+ [% INSERT legalese.txt + warning.txt %]
+ [% INSERT "$legalese" + warning.txt %] # requires quoting
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=item INCLUDE
+
+The INCLUDE directive is used to process and include the output of
+another template file or block.
+
+ [% INCLUDE header %]
+
+If a BLOCK of the specified name is defined in the same file, or in a file
+from which the current template has been called (i.e. a parent template)
+then it will be used in preference to any file of the same name.
+
+ [% INCLUDE table %] # uses BLOCK defined below
+
+ [% BLOCK table %]
+ <table>
+ ...
+ </table>
+ [% END %]
+
+If a BLOCK definition is not currently visible then the template name
+should be a file relative to one of the INCLUDE_PATH directories, or
+an absolute or relative file name if the ABSOLUTE/RELATIVE options are
+appropriately enabled. The INCLUDE directive automatically quotes the
+filename specified, as per INSERT described above. When a variable
+contains the name of the template for the INCLUDE directive, it should
+be explicitly prefixed by '$' or double-quoted
+
+ [% myheader = 'my/misc/header' %]
+ [% INCLUDE myheader %] # 'myheader'
+ [% INCLUDE $myheader %] # 'my/misc/header'
+ [% INCLUDE "$myheader" %] # 'my/misc/header'
+
+Any template directives embedded within the file will be processed
+accordingly. All variables currently defined will be visible and
+accessible from within the included template.
+
+ [% title = 'Hello World' %]
+ [% INCLUDE header %]
+ <body>
+ ...
+
+'header':
+
+ <html>
+ <title>[% title %]</title>
+
+output:
+
+ <html>
+ <title>Hello World</title>
+ <body>
+ ...
+
+Local variable definitions may be specified after the template name,
+temporarily masking any existing variables. Insignificant whitespace
+is ignored within directives so you can add variable definitions on the
+same line, the next line or split across several line with comments
+interspersed, if you prefer.
+
+ [% INCLUDE table %]
+
+ [% INCLUDE table title="Active Projects" %]
+
+ [% INCLUDE table
+ title = "Active Projects"
+ bgcolor = "#80ff00" # chartreuse
+ border = 2
+ %]
+
+The INCLUDE directive localises (i.e. copies) all variables before
+processing the template. Any changes made within the included
+template will not affect variables in the including template.
+
+ [% foo = 10 %]
+
+ foo is originally [% foo %]
+ [% INCLUDE bar %]
+ foo is still [% foo %]
+
+ [% BLOCK bar %]
+ foo was [% foo %]
+ [% foo = 20 %]
+ foo is now [% foo %]
+ [% END %]
+
+output:
+ foo is originally 10
+ foo was 10
+ foo is now 20
+ foo is still 10
+
+Technical Note: the localisation of the stash (that is, the process by
+which variables are copied before an INCLUDE to prevent being
+overwritten) is only skin deep. The top-level variable namespace
+(hash) is copied, but no attempt is made to perform a deep-copy of
+other structures (hashes, arrays, objects, etc.) Therefore, a 'foo'
+variable referencing a hash will be copied to create a new 'foo'
+variable but which points to the same hash array. Thus, if you update
+compound variables (e.g. foo.bar) then you will change the original
+copy, regardless of any stash localisation. If you're not worried
+about preserving variable values, or you trust the templates you're
+including then you might prefer to use the PROCESS directive which is
+faster by virtue of not performing any localisation.
+
+From version 2.04 onwards, you can specify dotted variables as "local"
+variables to an INCLUDE directive. However, be aware that because of
+the localisation issues explained above (if you skipped the previous
+Technical Note above then you might want to go back and read it or
+skip this section too), the variables might not actualy be "local".
+If the first element of the variable name already references a hash
+array then the variable update will affect the original variable.
+
+ [% foo = {
+ bar = 'Baz'
+ }
+ %]
+
+ [% INCLUDE somefile foo.bar='Boz' %]
+
+ [% foo.bar %] # Boz
+
+This behaviour can be a little unpredictable (and may well be improved
+upon in a future version). If you know what you're doing with it and
+you're sure that the variables in question are defined (nor not) as you
+expect them to be, then you can rely on this feature to implement some
+powerful "global" data sharing techniques. Otherwise, you might prefer
+to steer well clear and always pass simple (undotted) variables as
+parameters to INCLUDE and other similar directives.
+
+If you want to process several templates in one go then you can
+specify each of their names (quoted or unquoted names only, no unquoted
+'$variables') joined together by '+'. The INCLUDE directive
+will then process them in order.
+
+ [% INCLUDE html/header + "site/$header" + site/menu
+ title = "My Groovy Web Site"
+ %]
+
+The variable stash is localised once and then the templates specified
+are processed in order, all within that same variable context. This
+makes it slightly faster than specifying several separate INCLUDE
+directives (because you only clone the variable stash once instead of
+n times), but not quite as "safe" because any variable changes in the
+first file will be visible in the second, third and so on. This
+might be what you want, of course, but then again, it might not.
+
+
+
+=item PROCESS
+
+The PROCESS directive is similar to INCLUDE but does not perform any
+localisation of variables before processing the template. Any changes
+made to variables within the included template will be visible in the
+including template.
+
+ [% foo = 10 %]
+
+ foo is [% foo %]
+ [% PROCESS bar %]
+ foo is [% foo %]
+
+ [% BLOCK bar %]
+ [% foo = 20 %]
+ changed foo to [% foo %]
+ [% END %]
+
+output:
+
+ foo is 10
+ changed foo to 20
+ foo is 20
+
+Parameters may be specified in the PROCESS directive, but these too will
+become visible changes to current variable values.
+
+ [% foo = 10 %]
+ foo is [% foo %]
+ [% PROCESS bar
+ foo = 20
+ %]
+ foo is [% foo %]
+
+ [% BLOCK bar %]
+ this is bar, foo is [% foo %]
+ [% END %]
+
+output:
+
+ foo is 10
+ this is bar, foo is 20
+ foo is 20
+
+The PROCESS directive is slightly faster than INCLUDE because it
+avoids the need to localise (i.e. copy) the variable stash before
+processing the template. As with INSERT and INCLUDE, the first
+parameter does not need to be quoted as long as it contains only
+alphanumeric characters, underscores, periods or forward slashes.
+A '$' prefix can be used to explicitly indicate a variable which
+should be interpolated to provide the template name:
+
+ [% myheader = 'my/misc/header' %]
+ [% PROCESS myheader %] # 'myheader'
+ [% PROCESS $myheader %] # 'my/misc/header'
+
+As with INCLUDE, multiple templates can be specified, delimited by
+'+', and are processed in order.
+
+ [% PROCESS html/header + my/header %]
+
+
+
+
+
+=item WRAPPER
+
+It's not unusual to find yourself adding common headers and footers to
+pages or sub-sections within a page. Something like this:
+
+ [% INCLUDE section/header
+ title = 'Quantum Mechanics'
+ %]
+ Quantum mechanics is a very interesting subject wish
+ should prove easy for the layman to fully comprehend.
+ [% INCLUDE section/footer %]
+
+ [% INCLUDE section/header
+ title = 'Desktop Nuclear Fusion for under $50'
+ %]
+ This describes a simple device which generates significant
+ sustainable electrical power from common tap water by process
+ of nuclear fusion.
+ [% INCLUDE section/footer %]
+
+The individual template components being included might look like these:
+
+section/header:
+
+ <p>
+ <h2>[% title %]</h2>
+
+section/footer:
+
+ </p>
+
+The WRAPPER directive provides a way of simplifying this a little. It
+encloses a block up to a matching END directive, which is first
+processed to generate some output. This is then passed to the named
+template file or BLOCK as the 'content' variable.
+
+ [% WRAPPER section
+ title = 'Quantum Mechanics'
+ %]
+ Quantum mechanics is a very interesting subject wish
+ should prove easy for the layman to fully comprehend.
+ [% END %]
+
+ [% WRAPPER section
+ title = 'Desktop Nuclear Fusion for under $50'
+ %]
+ This describes a simple device which generates significant
+ sustainable electrical power from common tap water by process
+ of nuclear fusion.
+ [% END %]
+
+The single 'section' template can then be defined as:
+
+ <p>
+ <h2>[% title %]</h2>
+ [% content %]
+ </p>
+
+Like other block directives, it can be used in side-effect notation:
+
+ [% INSERT legalese.txt WRAPPER big_bold_table %]
+
+It's also possible to specify multiple templates to a WRAPPER directive.
+The specification order indicates outermost to innermost wrapper templates.
+For example, given the following template block definitions:
+
+ [% BLOCK bold %]<b>[% content %]</b>[% END %]
+ [% BLOCK italic %]<i>[% content %]</i>[% END %]
+
+the directive
+
+ [% WRAPPER bold+italic %]Hello World[% END %]
+
+would generate the following output:
+
+ <b><i>Hello World</i></b>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=item BLOCK
+
+The BLOCK ... END construct can be used to define template component
+blocks which can be processed with the INCLUDE, PROCESS and WRAPPER
+directives.
+
+ [% BLOCK tabrow %]
+ <tr><td>[% name %]<td><td>[% email %]</td></tr>
+ [% END %]
+
+ <table>
+ [% PROCESS tabrow name='Fred' email='fred@nowhere.com' %]
+ [% PROCESS tabrow name='Alan' email='alan@nowhere.com' %]
+ </table>
+
+A BLOCK definition can be used before it is defined, as long as the
+definition resides in the same file. The block definition itself does
+not generate any output.
+
+ [% PROCESS tmpblk %]
+
+ [% BLOCK tmpblk %] This is OK [% END %]
+
+You can use an anonymous BLOCK to capture the output of a template
+fragment.
+
+ [% julius = BLOCK %]
+ And Caesar's spirit, ranging for revenge,
+ With Ate by his side come hot from hell,
+ Shall in these confines with a monarch's voice
+ Cry 'Havoc', and let slip the dogs of war;
+ That this foul deed shall smell above the earth
+ With carrion men, groaning for burial.
+ [% END %]
+
+Like a named block, it can contain any other template directives which
+are processed when the block is defined. The output generated by the
+block is then assigned to the variable 'julius'.
+
+Anonymous BLOCKs can also be used to define block macros. The
+enclosing block is processed each time the macro is called.
+
+ [% MACRO locate BLOCK %]
+ The [% animal %] sat on the [% place %].
+ [% END %]
+
+ [% locate(animal='cat', place='mat') %] # The cat sat on the mat
+ [% locate(animal='dog', place='log') %] # The dog sat on the log
+
+
+
+=back
+
+=head2 Conditional Processing
+
+=over 4
+
+
+=item IF / UNLESS / ELSIF / ELSE
+
+The IF and UNLESS directives can be used to process or ignore a
+block based on some run-time condition.
+
+ [% IF frames %]
+ [% INCLUDE frameset %]
+ [% END %]
+
+ [% UNLESS text_mode %]
+ [% INCLUDE biglogo %]
+ [% END %]
+
+Multiple conditions may be joined with ELSIF and/or ELSE blocks.
+
+ [% IF age < 10 %]
+ Hello [% name %], does your mother know you're
+ using her AOL account?
+ [% ELSIF age < 18 %]
+ Sorry, you're not old enough to enter
+ (and too dumb to lie about your age)
+ [% ELSE %]
+ Welcome [% name %].
+ [% END %]
+
+The following conditional and boolean operators may be used:
+
+ == != < <= > >= && || ! and or not
+
+Note that C<and>, C<or> and C<not> are also provided as aliases for
+C<&&>, C<||> and C<!>, respectively.
+
+Conditions may be arbitrarily complex and are evaluated with the same
+precedence as in Perl. Parenthesis may be used to explicitly
+determine evaluation order.
+
+ # ridiculously contrived complex example
+ [% IF (name == 'admin' || uid <= 0) && mode == 'debug' %]
+ I'm confused.
+ [% ELSIF more > less %]
+ That's more or less correct.
+ [% END %]
+
+
+
+
+
+
+=item SWITCH / CASE
+
+The SWITCH / CASE construct can be used to perform a multi-way
+conditional test. The SWITCH directive expects an expression which is
+first evaluated and then compared against each CASE statement in turn.
+Each CASE directive should contain a single value or a list of values
+which should match. CASE may also be left blank or written as [% CASE
+DEFAULT %] to specify a default match. Only one CASE matches, there
+is no drop-through between CASE statements.
+
+ [% SWITCH myvar %]
+ [% CASE value1 %]
+ ...
+ [% CASE [ value2 value3 ] %] # multiple values
+ ...
+ [% CASE myhash.keys %] # ditto
+ ...
+ [% CASE %] # default
+ ...
+ [% END %]
+
+
+
+
+=back
+
+=head2 Loop Processing
+
+=over 4
+
+
+=item FOREACH
+
+The FOREACH directive will iterate through the items in a list, processing
+the enclosed block for each one.
+
+ my $vars = {
+ foo => 'Foo',
+ items => [ 'one', 'two', 'three' ],
+ };
+
+template:
+
+ Things:
+ [% FOREACH thing = [ foo 'Bar' "$foo Baz" ] %]
+ * [% thing %]
+ [% END %]
+
+ Items:
+ [% FOREACH i = items %]
+ * [% i %]
+ [% END %]
+
+ Stuff:
+ [% stuff = [ foo "$foo Bar" ] %]
+ [% FOREACH s = stuff %]
+ * [% s %]
+ [% END %]
+
+output:
+
+ Things:
+ * Foo
+ * Bar
+ * Foo Baz
+
+ Items:
+ * one
+ * two
+ * three
+
+ Stuff:
+ * Foo
+ * Foo Bar
+
+You can use also use 'IN' instead of '=' if you prefer.
+
+ [% FOREACH crook IN government %]
+
+When the FOREACH directive is used without specifying a target variable,
+any iterated values which are hash references will be automatically
+imported.
+
+ [% userlist = [
+ { id => 'tom', name => 'Thomas' },
+ { id => 'dick', name => 'Richard' },
+ { id => 'larry', name => 'Lawrence' },
+ ]
+ %]
+
+ [% FOREACH user IN userlist %]
+ [% user.id %] [% user.name %]
+ [% END %]
+
+short form:
+
+ [% FOREACH userlist %]
+ [% id %] [% name %]
+ [% END %]
+
+Note that this particular usage creates a localised variable context
+to prevent the imported hash keys from overwriting any existing
+variables. The imported definitions and any other variables defined
+in such a FOREACH loop will be lost at the end of the loop, when the
+previous context and variable values are restored.
+
+However, under normal operation, the loop variable remains in scope
+after the FOREACH loop has ended (caveat: overwriting any variable
+previously in scope). This is useful as the loop variable is secretly
+an iterator object (see below) and can be used to analyse the last
+entry processed by the loop.
+
+The FOREACH directive can also be used to iterate through the entries
+in a hash array. Each entry in the hash is returned in sorted order
+(based on the key) as a hash array containing 'key' and 'value' items.
+
+ [% users = {
+ tom => 'Thomas',
+ dick => 'Richard',
+ larry => 'Lawrence',
+ }
+ %]
+
+ [% FOREACH u IN users %]
+ * [% u.key %] : [% u.value %]
+ [% END %]
+
+Output:
+
+ * dick : Richard
+ * larry : Lawrence
+ * tom : Thomas
+
+The NEXT directive starts the next iteration in the FOREACH loop.
+
+ [% FOREACH user IN userlist %]
+ [% NEXT IF user.isguest %]
+ Name: [% user.name %] Email: [% user.email %]
+ [% END %]
+
+The LAST directive can be used to prematurely exit the loop. BREAK is
+also provided as an alias for LAST.
+
+ [% FOREACH match IN results.nsort('score').reverse %]
+ [% LAST IF match.score < 50 %]
+ [% match.score %] : [% match.url %]
+ [% END %]
+
+The FOREACH directive is implemented using the Template::Iterator
+module. A reference to the iterator object for a FOREACH directive is
+implicitly available in the 'loop' variable. The following methods
+can be called on the 'loop' iterator.
+
+ size() number of elements in the list
+ max() index number of last element (size - 1)
+ index() index of current iteration from 0 to max()
+ count() iteration counter from 1 to size() (i.e. index() + 1)
+ first() true if the current iteration is the first
+ last() true if the current iteration is the last
+ prev() return the previous item in the list
+ next() return the next item in the list
+
+See L<Template::Iterator> for further details.
+
+Example:
+
+ [% FOREACH item IN [ 'foo', 'bar', 'baz' ] -%]
+ [%- "<ul>\n" IF loop.first %]
+ <li>[% loop.count %]/[% loop.size %]: [% item %]
+ [%- "</ul>\n" IF loop.last %]
+ [% END %]
+
+Output:
+
+ <ul>
+ <li>1/3: foo
+ <li>2/3: bar
+ <li>3/3: baz
+ </ul>
+
+Note that the number() method is supported as an alias for count() for
+backwards compatibility but may be deprecated in some future version.
+
+Nested loops will work as expected, with the 'loop' variable correctly
+referencing the innermost loop and being restored to any previous
+value (i.e. an outer loop) at the end of the loop.
+
+ [% FOREACH group IN grouplist;
+ # loop => group iterator
+ "Groups:\n" IF loop.first;
+
+ FOREACH user IN group.userlist;
+ # loop => user iterator
+ "$loop.count: $user.name\n";
+ END;
+
+ # loop => group iterator
+ "End of Groups\n" IF loop.last;
+ END
+ %]
+
+The 'iterator' plugin can also be used to explicitly create an
+iterator object. This can be useful within nested loops where you
+need to keep a reference to the outer iterator within the inner loop.
+The iterator plugin effectively allows you to create an iterator by a
+name other than 'loop'. See Template::Plugin::Iterator for further
+details.
+
+ [% USE giter = iterator(grouplist) %]
+
+ [% FOREACH group IN giter %]
+ [% FOREACH user IN group.userlist %]
+ user #[% loop.count %] in
+ group [% giter.count %] is
+ named [% user.name %]
+ [% END %]
+ [% END %]
+
+
+
+
+=item WHILE
+
+The WHILE directive can be used to repeatedly process a template block
+while a conditional expression evaluates true. The expression may
+be arbitrarily complex as per IF / UNLESS.
+
+ [% WHILE total < 100 %]
+ ...
+ [% total = calculate_new_total %]
+ [% END %]
+
+An assignment can be enclosed in parenthesis to evaluate the assigned
+value.
+
+ [% WHILE (user = get_next_user_record) %]
+ [% user.name %]
+ [% END %]
+
+The NEXT directive can be used to start the next iteration of a
+WHILE loop and BREAK can be used to exit the loop, both as per FOREACH.
+
+The Template Toolkit uses a failsafe counter to prevent runaway WHILE
+loops which would otherwise never terminate. If the loop exceeds 1000
+iterations then an 'undef' exception will be thrown, reporting the
+error:
+
+ WHILE loop terminated (> 1000 iterations)
+
+The $Template::Directive::WHILE_MAX variable controls this behaviour
+and can be set to a higher value if necessary.
+
+
+=back
+
+=head2 Filters, Plugins, Macros and Perl
+
+=over 4
+
+
+=item FILTER
+
+The FILTER directive can be used to post-process the output of a
+block. A number of standard filters are provided with the Template
+Toolkit. The 'html' filter, for example, escapes the 'E<lt>', 'E<gt>'
+and '&' characters to prevent them from being interpreted as HTML tags
+or entity reference markers.
+
+ [% FILTER html %]
+ HTML text may have < and > characters embedded
+ which you want converted to the correct HTML entities.
+ [% END %]
+
+output:
+
+ HTML text may have &lt; and &gt; characters embedded
+ which you want converted to the correct HTML entities.
+
+The FILTER directive can also follow various other non-block directives.
+For example:
+
+ [% INCLUDE mytext FILTER html %]
+
+The '|' character can also be used as an alias for 'FILTER'.
+
+ [% INCLUDE mytext | html %]
+
+Multiple filters can be chained together and will be called in sequence.
+
+ [% INCLUDE mytext FILTER html FILTER html_para %]
+
+or
+
+ [% INCLUDE mytext | html | html_para %]
+
+Filters come in two flavours, known as 'static' or 'dynamic'. A
+static filter is a simple subroutine which accepts a text string as
+the only argument and returns the modified text. The 'html' filter is
+an example of a static filter, implemented as:
+
+ sub html_filter {
+ my $text = shift;
+ for ($text) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ }
+ return $text;
+ }
+
+Dynamic filters can accept arguments which are specified when the filter
+is called from a template. The 'repeat' filter is such an example,
+accepting a numerical argument which specifies the number of times
+that the input text should be repeated.
+
+ [% FILTER repeat(3) %]blah [% END %]
+
+output:
+
+ blah blah blah
+
+These are implemented as filter 'factories'. The factory subroutine
+is passed a reference to the current Template::Context object along
+with any additional arguments specified. It should then return a
+subroutine reference (e.g. a closure) which implements the filter.
+The 'repeat' filter factory is implemented like this:
+
+ sub repeat_filter_factory {
+ my ($context, $iter) = @_;
+ $iter = 1 unless defined $iter;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ return join('\n', $text) x $iter;
+ }
+ }
+
+The FILTERS option, described in L<Template::Manual::Config>, allows
+custom filters to be defined when a Template object is instantiated.
+The Template::Context define_filter() method allows further filters
+to be defined at any time.
+
+When using a filter, it is possible to assign an alias to it for
+further use. This is most useful for dynamic filters that you want
+to re-use with the same configuration.
+
+ [% FILTER echo = repeat(2) %]
+ Is there anybody out there?
+ [% END %]
+
+ [% FILTER echo %]
+ Mother, should I build a wall?
+ [% END %]
+
+Output:
+
+ Is there anybody out there?
+ Is there anybody out there?
+
+ Mother, should I build a wall?
+ Mother, should I build a wall?
+
+The FILTER directive automatically quotes the name of the filter. As
+with INCLUDE et al, you can use a variable to provide the name of the
+filter, prefixed by '$'.
+
+ [% myfilter = 'html' %]
+ [% FILTER $myfilter %] # same as [% FILTER html %]
+ ...
+ [% END %]
+
+A template variable can also be used to define a static filter
+subroutine. However, the Template Toolkit will automatically call any
+subroutine bound to a variable and use the value returned. Thus, the
+above example could be implemented as:
+
+ my $vars = {
+ myfilter => sub { return 'html' },
+ };
+
+template:
+
+ [% FILTER $myfilter %] # same as [% FILTER html %]
+ ...
+ [% END %]
+
+To define a template variable that evaluates to a subroutine reference
+that can be used by the FILTER directive, you should create a
+subroutine that, when called automatically by the Template Toolkit,
+returns another subroutine reference which can then be used to perform
+the filter operation. Note that only static filters can be
+implemented in this way.
+
+ my $vars = {
+ myfilter => sub { \&my_filter_sub },
+ };
+
+ sub my_filter_sub {
+ my $text = shift;
+ # do something
+ return $text;
+ }
+
+template:
+
+ [% FILTER $myfilter %]
+ ...
+ [% END %]
+
+Alternately, you can bless a subroutine reference into a class (any
+class will do) to fool the Template Toolkit into thinking it's an
+object rather than a subroutine. This will then bypass the automatic
+"call-a-subroutine-to-return-a-value" magic.
+
+ my $vars = {
+ myfilter => bless(\&my_filter_sub, 'anything_you_like'),
+ };
+
+template:
+
+ [% FILTER $myfilter %]
+ ...
+ [% END %]
+
+Filters bound to template variables remain local to the variable
+context in which they are defined. That is, if you define a filter in
+a PERL block within a template that is loaded via INCLUDE, then the
+filter definition will only exist until the end of that template when
+the stash is delocalised, restoring the previous variable state. If
+you want to define a filter which persists for the lifetime of the
+processor, or define additional dynamic filter factories, then you can
+call the define_filter() method on the current Template::Context
+object.
+
+See L<Template::Manual::Filters> for a complete list of available filters,
+their descriptions and examples of use.
+
+
+
+
+
+
+=item USE
+
+The USE directive can be used to load and initialise "plugin"
+extension modules.
+
+ [% USE myplugin %]
+
+A plugin is a regular Perl module that conforms to a particular
+object-oriented interface, allowing it to be loaded into and used
+automatically by the Template Toolkit. For details of this interface
+and information on writing plugins, consult L<Template::Plugin>.
+
+The plugin name is case-sensitive and will be appended to the
+PLUGIN_BASE value (default: 'Template::Plugin') to construct a full
+module name. Any periods, '.', in the name will be converted to '::'.
+
+ [% USE MyPlugin %] # => Template::Plugin::MyPlugin
+ [% USE Foo.Bar %] # => Template::Plugin::Foo::Bar
+
+Various standard plugins are included with the Template Toolkit (see
+below and L<Template::Manual::Plugins>). These can be specified in lower
+case and are mapped to the appropriate name.
+
+ [% USE cgi %] # => Template::Plugin::CGI
+ [% USE table %] # => Template::Plugin::Table
+
+Any additional parameters supplied in parenthesis after the plugin
+name will be also be passed to the new() constructor. A reference to
+the current Template::Context object is always passed as the first
+parameter.
+
+ [% USE MyPlugin('foo', 123) %]
+
+equivalent to:
+
+ Template::Plugin::MyPlugin->new($context, 'foo', 123);
+
+Named parameters may also be specified. These are collated into a
+hash which is passed by reference as the last parameter to the
+constructor, as per the general code calling interface.
+
+ [% USE url('/cgi-bin/foo', mode='submit', debug=1) %]
+
+equivalent to:
+
+ Template::Plugin::URL->new($context, '/cgi-bin/foo'
+ { mode => 'submit', debug => 1 });
+
+The plugin may represent any data type; a simple variable, hash, list or
+code reference, but in the general case it will be an object reference.
+Methods can be called on the object (or the relevant members of the
+specific data type) in the usual way:
+
+ [% USE table(mydata, rows=3) %]
+
+ [% FOREACH row = table.rows %]
+ <tr>
+ [% FOREACH item = row %]
+ <td>[% item %]</td>
+ [% END %]
+ </tr>
+ [% END %]
+
+An alternative name may be provided for the plugin by which it can be
+referenced:
+
+ [% USE scores = table(myscores, cols=5) %]
+
+ [% FOREACH row = scores.rows %]
+ ...
+ [% END %]
+
+You can use this approach to create multiple plugin objects with
+different configurations. This example shows how the 'format' plugin
+is used to create sub-routines bound to variables for formatting text
+as per printf().
+
+ [% USE bold = format('<b>%s</b>') %]
+ [% USE ital = format('<i>%s</i>') %]
+
+ [% bold('This is bold') %]
+ [% ital('This is italic') %]
+
+Output:
+
+ <b>This is bold</b>
+ <i>This is italic</i>
+
+This next example shows how the URL plugin can be used to build
+dynamic URLs from a base part and optional query parameters.
+
+ [% USE mycgi = URL('/cgi-bin/foo.pl', debug=1) %]
+ <a href="[% mycgi %]">...
+ <a href="[% mycgi(mode='submit') %]"...
+
+Output:
+
+ <a href="/cgi-bin/foo.pl?debug=1">...
+ <a href="/cgi-bin/foo.pl?mode=submit&debug=1">...
+
+The CGI plugin is an example of one which delegates to another Perl
+module. In this this case, it is to Lincoln Stein's CGI.pm module.
+All of the methods provided by CGI.pm are available via the plugin.
+
+ [% USE CGI %]
+
+ [% CGI.start_form %]
+
+ [% CGI.checkbox_group(name => 'colours',
+ values => [ 'red' 'green' 'blue' ])
+ %]
+
+ [% CGI.popup_menu(name => 'items',
+ values => [ 'foo' 'bar' 'baz' ])
+ %]
+
+ [% CGI.end_form %]
+
+Simon Matthews has written the DBI plugin which provides an interface
+to Tim Bunce's DBI module (available from CPAN). Here's a short
+example:
+
+ [% USE DBI('DBI:mSQL:mydbname') %]
+
+ [% FOREACH user = DBI.query('SELECT * FROM users') %]
+ [% user.id %] [% user.name %] [% user.etc.etc %]
+ [% END %]
+
+See L<Template::Manual::Plugins> for more information on the plugins
+distributed with the toolkit or available from CPAN.
+
+The LOAD_PERL option (disabled by default) provides a further way by
+which external Perl modules may be loaded. If a regular Perl module
+(i.e. not a Template::Plugin::* or other module relative to some
+PLUGIN_BASE) supports an object-oriented interface and a new()
+constructor then it can be loaded and instantiated automatically. The
+following trivial example shows how the IO::File module might be used.
+
+ [% USE file = IO.File('/tmp/mydata') %]
+
+ [% WHILE (line = file.getline) %]
+ <!-- [% line %] -->
+ [% END %]
+
+
+
+
+
+
+=item MACRO
+
+The MACRO directive allows you to define a directive or directive block
+which is then evaluated each time the macro is called.
+
+ [% MACRO header INCLUDE header %]
+
+Calling the macro as:
+
+ [% header %]
+
+is then equivalent to:
+
+ [% INCLUDE header %]
+
+Macros can be passed named parameters when called. These values remain
+local to the macro.
+
+ [% header(title='Hello World') %]
+
+equivalent to:
+
+ [% INCLUDE header title='Hello World' %]
+
+A MACRO definition may include parameter names. Values passed to the
+macros are then mapped to these local variables. Other named parameters
+may follow these.
+
+ [% MACRO header(title) INCLUDE header %]
+
+ [% header('Hello World') %]
+ [% header('Hello World', bgcol='#123456') %]
+
+equivalent to:
+
+ [% INCLUDE header title='Hello World' %]
+ [% INCLUDE header title='Hello World' bgcol='#123456' %]
+
+Here's another example, defining a macro for display numbers
+in comma-delimited groups of 3, using the chunk and join virtual
+method.
+
+ [% MACRO number(n) GET n.chunk(-3).join(',') %]
+
+ [% number(1234567) %] # 1,234,567
+
+A MACRO may precede any directive and must conform to the structure
+of the directive.
+
+ [% MACRO header IF frames %]
+ [% INCLUDE frames/header %]
+ [% ELSE %]
+ [% INCLUDE header %]
+ [% END %]
+
+ [% header %]
+
+A MACRO may also be defined as an anonymous BLOCK. The block will be
+evaluated each time the macro is called.
+
+ [% MACRO header BLOCK %]
+ ...content...
+ [% END %]
+
+ [% header %]
+
+If you've got the EVAL_PERL option set, then you can even define a
+MACRO as a PERL block (see below):
+
+ [% MACRO triple(n) PERL %]
+ my $n = $stash->get('n');
+ print $n * 3;
+ [% END -%]
+
+
+
+
+
+
+
+=item PERL
+
+(for the advanced reader)
+
+The PERL directive is used to mark the start of a block which contains
+Perl code for evaluation. The EVAL_PERL option must be enabled for Perl
+code to be evaluated or a 'perl' exception will be thrown with the
+message 'EVAL_PERL not set'.
+
+Perl code is evaluated in the Template::Perl package. The $context
+package variable contains a reference to the current Template::Context
+object. This can be used to access the functionality of the Template
+Toolkit to process other templates, load plugins, filters, etc.
+See L<Template::Context> for further details.
+
+ [% PERL %]
+ print $context->include('myfile');
+ [% END %]
+
+The $stash variable contains a reference to the top-level stash object
+which manages template variables. Through this, variable values can
+be retrieved and updated. See L<Template::Stash> for further details.
+
+ [% PERL %]
+ $stash->set(foo => 'bar');
+ print "foo value: ", $stash->get('foo');
+ [% END %]
+
+Output
+ foo value: bar
+
+Output is generated from the PERL block by calling print(). Note that
+the Template::Perl::PERLOUT handle is selected (tied to an output
+buffer) instead of STDOUT.
+
+ [% PERL %]
+ print "foo\n"; # OK
+ print PERLOUT "bar\n"; # OK, same as above
+ print Template::Perl::PERLOUT "baz\n"; # OK, same as above
+ print STDOUT "qux\n"; # WRONG!
+ [% END %]
+
+The PERL block may contain other template directives. These are
+processed before the Perl code is evaluated.
+
+ [% name = 'Fred Smith' %]
+
+ [% PERL %]
+ print "[% name %]\n";
+ [% END %]
+
+Thus, the Perl code in the above example is evaluated as:
+
+ print "Fred Smith\n";
+
+Exceptions may be thrown from within PERL blocks via die() and will be
+correctly caught by enclosing TRY blocks.
+
+ [% TRY %]
+ [% PERL %]
+ die "nothing to live for\n";
+ [% END %]
+ [% CATCH %]
+ error: [% error.info %]
+ [% END %]
+
+output:
+ error: nothing to live for
+
+
+
+
+=item RAWPERL
+
+(for the very advanced reader)
+
+The Template Toolkit parser reads a source template and generates the
+text of a Perl subroutine as output. It then uses eval() to evaluate
+it into a subroutine reference. This subroutine is then called to
+process the template, passing a reference to the current
+Template::Context object through which the functionality of the
+Template Toolkit can be accessed. The subroutine reference can be
+cached, allowing the template to be processed repeatedly without
+requiring any further parsing.
+
+For example, a template such as:
+
+ [% PROCESS header %]
+ The [% animal %] sat on the [% location %]
+ [% PROCESS footer %]
+
+is converted into the following Perl subroutine definition:
+
+ sub {
+ my $context = shift;
+ my $stash = $context->stash;
+ my $output = '';
+ my $error;
+
+ eval { BLOCK: {
+ $output .= $context->process('header');
+ $output .= "The ";
+ $output .= $stash->get('animal');
+ $output .= " sat on the ";
+ $output .= $stash->get('location');
+ $output .= $context->process('footer');
+ $output .= "\n";
+ } };
+ if ($@) {
+ $error = $context->catch($@, \$output);
+ die $error unless $error->type eq 'return';
+ }
+
+ return $output;
+ }
+
+To examine the Perl code generated, such as in the above example, set
+the $Template::Parser::DEBUG package variable to any true value. You
+can also set the $Template::Directive::PRETTY variable true to have
+the code formatted in a readable manner for human consumption. The
+source code for each generated template subroutine will be printed to
+STDERR on compilation (i.e. the first time a template is used).
+
+ $Template::Parser::DEBUG = 1;
+ $Template::Directive::PRETTY = 1;
+
+ ...
+
+ $template->process($file, $vars)
+ || die $template->error(), "\n";
+
+The PERL ... END construct allows Perl code to be embedded into a
+template (when the EVAL_PERL option is set), but it is evaluated at
+"runtime" using eval() each time the template subroutine is called.
+This is inherently flexible, but not as efficient as it could be,
+especially in a persistent server environment where a template may be
+processed many times.
+
+The RAWPERL directive allows you to write Perl code that is integrated
+directly into the generated Perl subroutine text. It is evaluated
+once at compile time and is stored in cached form as part of the
+compiled template subroutine. This makes RAWPERL blocks more
+efficient than PERL blocks.
+
+The downside is that you must code much closer to the metal. Within
+PERL blocks, you can call print() to generate some output. RAWPERL
+blocks don't afford such luxury. The code is inserted directly into
+the generated subroutine text and should conform to the convention of
+appending to the '$output' variable.
+
+ [% PROCESS header %]
+
+ [% RAWPERL %]
+ $output .= "Some output\n";
+ ...
+ $output .= "Some more output\n";
+ [% END %]
+
+The critical section of the generated subroutine for this example would
+then look something like:
+
+ ...
+ eval { BLOCK: {
+ $output .= $context->process('header');
+ $output .= "\n";
+ $output .= "Some output\n";
+ ...
+ $output .= "Some more output\n";
+ $output .= "\n";
+ } };
+ ...
+
+As with PERL blocks, the $context and $stash references are pre-defined
+and available for use within RAWPERL code.
+
+
+=back
+
+=head2 Exception Handling and Flow Control
+
+=over 4
+
+
+=item TRY / THROW / CATCH / FINAL
+
+(more advanced material)
+
+The Template Toolkit supports fully functional, nested exception
+handling. The TRY directive introduces an exception handling scope
+which continues until the matching END directive. Any errors that
+occur within that block will be caught and can be handled by one
+of the CATCH blocks defined.
+
+ [% TRY %]
+ ...blah...blah...
+ [% CALL somecode %]
+ ...etc...
+ [% INCLUDE someblock %]
+ ...and so on...
+ [% CATCH %]
+ An error occurred!
+ [% END %]
+
+Errors are raised as exceptions (objects of the Template::Exception
+class) and contain two fields, 'type' and 'info'. The exception
+'type' can be any string containing letters, numbers, '_' or '.', and
+is used to indicate the kind of error that occurred. The 'info' field
+contains an error message indicating what actually went wrong. Within
+a catch block, the exception object is aliased to the 'error' variable.
+You can access the 'type' and 'info' fields directly.
+
+ [% mydsn = 'dbi:MySQL:foobar' %]
+ ...
+
+ [% TRY %]
+ [% USE DBI(mydsn) %]
+ [% CATCH %]
+ ERROR! Type: [% error.type %]
+ Info: [% error.info %]
+ [% END %]
+
+output (assuming a non-existant database called 'foobar'):
+
+ ERROR! Type: DBI
+ Info: Unknown database "foobar"
+
+The 'error' variable can also be specified by itself and will return a
+string of the form "$type error - $info".
+
+ ...
+ [% CATCH %]
+ ERROR: [% error %]
+ [% END %]
+
+output:
+
+ ERROR: DBI error - Unknown database "foobar"
+
+Each CATCH block may be specified with a particular exception type
+denoting the kind of error that it should catch. Multiple CATCH
+blocks can be provided to handle different types of exception that may
+be thrown in the TRY block. A CATCH block specified without any type,
+as in the previous example, is a default handler which will catch any
+otherwise uncaught exceptions. This can also be specified as
+[% CATCH DEFAULT %].
+
+ [% TRY %]
+ [% INCLUDE myfile %]
+ [% USE DBI(mydsn) %]
+ [% CALL somecode %]
+ ...
+ [% CATCH file %]
+ File Error! [% error.info %]
+ [% CATCH DBI %]
+ [% INCLUDE database/error.html %]
+ [% CATCH %]
+ [% error %]
+ [% END %]
+
+Remember that you can specify multiple directives within a single tag,
+each delimited by ';'. Thus, you might prefer to write your simple
+CATCH blocks more succinctly as:
+
+ [% TRY %]
+ ...
+ [% CATCH file; "File Error! $error.info" %]
+ [% CATCH DBI; INCLUDE database/error.html %]
+ [% CATCH; error %]
+ [% END %]
+
+or even:
+
+ [% TRY %]
+ ...
+ [% CATCH file ;
+ "File Error! $error.info" ;
+ CATCH DBI ;
+ INCLUDE database/error.html ;
+ CATCH ;
+ error ;
+ END
+ %]
+
+The DBI plugin throws exceptions of the 'DBI' type (in case that
+wasn't already obvious). The other specific exception caught here is
+of the 'file' type.
+
+A 'file' error is automatically thrown by the Template Toolkit when it
+can't find a file, or fails to load, parse or process a file that has
+been requested by an INCLUDE, PROCESS, INSERT or WRAPPER directive.
+If 'myfile' can't be found in the example above, the [% INCLUDE myfile
+%] directive will raise a 'file' exception which is then caught by the
+[% CATCH file %] block, generating the output:
+
+ File Error! myfile: not found
+
+Note that the DEFAULT option (disabled by default) allows you to
+specify a default file to be used any time a template file can't be
+found. This will prevent file exceptions from ever being raised when
+a non-existant file is requested (unless, of course, the DEFAULT file
+doesn't exist). Errors encountered once the file has been found
+(i.e. read error, parse error) will be raised as file exceptions as per
+usual.
+
+Uncaught exceptions (i.e. the TRY block doesn't have a type specific
+or default CATCH handler) may be caught by enclosing TRY blocks which
+can be nested indefinitely across multiple templates. If the error
+isn't caught at any level then processing will stop and the Template
+process() method will return a false value to the caller. The
+relevant Template::Exception object can be retrieved by calling the
+error() method.
+
+ [% TRY %]
+ ...
+ [% TRY %]
+ [% INCLUDE $user.header %]
+ [% CATCH file %]
+ [% INCLUDE header %]
+ [% END %]
+ ...
+ [% CATCH DBI %]
+ [% INCLUDE database/error.html %]
+ [% END %]
+
+In this example, the inner TRY block is used to ensure that the first
+INCLUDE directive works as expected. We're using a variable to
+provide the name of the template we want to include, user.header, and
+it's possible this contains the name of a non-existant template, or
+perhaps one containing invalid template directives. If the INCLUDE fails
+ with a 'file' error then we CATCH it in the inner block and INCLUDE
+the default 'header' file instead. Any DBI errors that occur within
+the scope of the outer TRY block will be caught in the relevant CATCH
+block, causing the 'database/error.html' template to be processed.
+Note that included templates inherit all currently defined template
+variable so these error files can quite happily access the 'error'
+variable to retrieve information about the currently caught exception.
+e.g.
+
+'database/error.html':
+
+ <h2>Database Error</h2>
+ A database error has occurred: [% error.info %]
+
+You can also specify a FINAL block. This is always processed
+regardless of the outcome of the TRY and/or CATCH block. If an
+exception is uncaught then the FINAL block is processed before jumping
+to the enclosing block or returning to the caller.
+
+ [% TRY %]
+ ...
+ [% CATCH this %]
+ ...
+ [% CATCH that %]
+ ...
+ [% FINAL %]
+ All done!
+ [% END %]
+
+The output from the TRY block is left intact up to the point where an
+exception occurs. For example, this template:
+
+ [% TRY %]
+ This gets printed
+ [% THROW food 'carrots' %]
+ This doesn't
+ [% CATCH food %]
+ culinary delights: [% error.info %]
+ [% END %]
+
+generates the following output:
+
+ This gets printed
+ culinary delights: carrots
+
+The CLEAR directive can be used in a CATCH or FINAL block to clear
+any output created in the TRY block.
+
+ [% TRY %]
+ This gets printed
+ [% THROW food 'carrots' %]
+ This doesn't
+ [% CATCH food %]
+ [% CLEAR %]
+ culinary delights: [% error.info %]
+ [% END %]
+
+output:
+
+ culinary delights: carrots
+
+Exception types are hierarchical, with each level being separated by
+the familiar dot operator. A 'DBI.connect' exception is a more
+specific kind of 'DBI' error. Similarly, a 'myown.error.barf' is a
+more specific kind of 'myown.error' type which itself is also a
+'myown' error. A CATCH handler that specifies a general exception
+type (such as 'DBI' or 'myown.error') will also catch more specific
+types that have the same prefix as long as a more specific handler
+isn't defined. Note that the order in which CATCH handlers are
+defined is irrelevant; a more specific handler will always catch an
+exception in preference to a more generic or default one.
+
+ [% TRY %]
+ ...
+ [% CATCH DBI ;
+ INCLUDE database/error.html ;
+ CATCH DBI.connect ;
+ INCLUDE database/connect.html ;
+ CATCH ;
+ INCLUDE error.html ;
+ END
+ %]
+
+In this example, a 'DBI.connect' error has it's own handler, a more
+general 'DBI' block is used for all other DBI or DBI.* errors and a
+default handler catches everything else.
+
+Exceptions can be raised in a template using the THROW directive. The
+first parameter is the exception type which doesn't need to be quoted
+(but can be, it's the same as INCLUDE) followed by the relevant error
+message which can be any regular value such as a quoted string,
+variable, etc.
+
+ [% THROW food "Missing ingredients: $recipe.error" %]
+
+ [% THROW user.login 'no user id: please login' %]
+
+ [% THROW $myerror.type "My Error: $myerror.info" %]
+
+It's also possible to specify additional positional or named
+parameters to the THROW directive if you want to pass more than
+just a simple message back as the error info field.
+
+ [% THROW food 'eggs' 'flour' msg='Missing Ingredients' %]
+
+In this case, the error 'info' field will be a hash array containing
+the named arguments, in this case 'msg' =E<gt> 'Missing Ingredients',
+and an 'args' item which contains a list of the positional arguments,
+in this case 'eggs' and 'flour'. The error 'type' field remains
+unchanged, here set to 'food'.
+
+ [% CATCH food %]
+ [% error.info.msg %]
+ [% FOREACH item = error.info.args %]
+ * [% item %]
+ [% END %]
+ [% END %]
+
+This produces the output:
+
+ Missing Ingredients
+ * eggs
+ * flour
+
+In addition to specifying individual positional arguments as
+[% error.info.args.n %], the 'info' hash contains keys directly
+pointing to the positional arguments, as a convenient shortcut.
+
+ [% error.info.0 %] # same as [% error.info.args.0 %]
+
+Exceptions can also be thrown from Perl code which you've bound to
+template variables, or defined as a plugin or other extension. To
+raise an exception, call die() passing a reference to a
+Template::Exception object as the argument. This will then be caught
+by any enclosing TRY blocks from where the code was called.
+
+ use Template::Exception;
+ ...
+
+ my $vars = {
+ foo => sub {
+ # ... do something ...
+ die Template::Exception->new('myerr.naughty',
+ 'Bad, bad error');
+ },
+ };
+
+template:
+
+ [% TRY %]
+ ...
+ [% foo %]
+ ...
+ [% CATCH myerr ;
+ "Error: $error" ;
+ END
+ %]
+
+output:
+
+ Error: myerr.naughty error - Bad, bad error
+
+The 'info' field can also be a reference to another object or data
+structure, if required.
+
+ die Template::Exception->new('myerror', {
+ module => 'foo.pl',
+ errors => [ 'bad permissions', 'naughty boy' ],
+ });
+
+Later, in a template:
+
+ [% TRY %]
+ ...
+ [% CATCH myerror %]
+ [% error.info.errors.size or 'no';
+ error.info.errors.size == 1 ? ' error' : ' errors' %]
+ in [% error.info.module %]:
+ [% error.info.errors.join(', ') %].
+ [% END %]
+
+Generating the output:
+
+ 2 errors in foo.pl:
+ bad permissions, naughty boy.
+
+You can also call die() with a single string, as is common in much
+existing Perl code. This will automatically be converted to an
+exception of the 'undef' type (that's the literal string 'undef',
+not the undefined value). If the string isn't terminated with a
+newline then Perl will append the familiar " at $file line $line"
+message.
+
+ sub foo {
+ # ... do something ...
+ die "I'm sorry, Dave, I can't do that\n";
+ }
+
+If you're writing a plugin, or some extension code that has the
+current Template::Context in scope (you can safely skip this section
+if this means nothing to you) then you can also raise an exception by
+calling the context throw() method. You can pass it an
+Template::Exception object reference, a pair of ($type, $info) parameters
+or just an $info string to create an exception of 'undef' type.
+
+ $context->throw($e); # exception object
+ $context->throw('Denied'); # 'undef' type
+ $context->throw('user.passwd', 'Bad Password');
+
+
+
+
+
+
+
+=item NEXT
+
+The NEXT directive can be used to start the next iteration of a FOREACH
+or WHILE loop.
+
+ [% FOREACH user = userlist %]
+ [% NEXT IF user.isguest %]
+ Name: [% user.name %] Email: [% user.email %]
+ [% END %]
+
+
+
+
+
+=item LAST
+
+The LAST directive can be used to prematurely exit a FOREACH or WHILE
+loop.
+
+ [% FOREACH user = userlist %]
+ Name: [% user.name %] Email: [% user.email %]
+ [% LAST IF some.condition %]
+ [% END %]
+
+BREAK can also be used as an alias for LAST.
+
+
+
+
+=item RETURN
+
+The RETURN directive can be used to stop processing the current
+template and return to the template from which it was called, resuming
+processing at the point immediately after the INCLUDE, PROCESS or
+WRAPPER directive. If there is no enclosing template then the
+Template process() method will return to the calling code with a
+true value.
+
+ Before
+ [% INCLUDE half_wit %]
+ After
+
+ [% BLOCK half_wit %]
+ This is just half...
+ [% RETURN %]
+ ...a complete block
+ [% END %]
+
+output:
+
+ Before
+ This is just half...
+ After
+
+
+
+
+=item STOP
+
+The STOP directive can be used to indicate that the processor should
+stop gracefully without processing any more of the template document.
+This is a planned stop and the Template process() method will return a
+B<true> value to the caller. This indicates that the template was
+processed successfully according to the directives within it.
+
+ [% IF something.terrible.happened %]
+ [% INCLUDE fatal/error.html %]
+ [% STOP %]
+ [% END %]
+
+ [% TRY %]
+ [% USE DBI(mydsn) %]
+ ...
+ [% CATCH DBI.connect %]
+ <p>Cannot connect to the database: [% error.info %]</p>
+ <br>
+ We apologise for the inconvenience. The cleaning lady
+ has removed the server power to plug in her vacuum cleaner.
+ Please try again later.
+ </p>
+ [% INCLUDE footer %]
+ [% STOP %]
+ [% END %]
+
+
+
+
+=item CLEAR
+
+The CLEAR directive can be used to clear the output buffer for the current
+enclosing block. It is most commonly used to clear the output generated
+from a TRY block up to the point where the error occurred.
+
+ [% TRY %]
+ blah blah blah # this is normally left intact
+ [% THROW some 'error' %] # up to the point of error
+ ...
+ [% CATCH %]
+ [% CLEAR %] # clear the TRY output
+ [% error %] # print error string
+ [% END %]
+
+
+
+
+=back
+
+=head2 Miscellaneous
+
+=over 4
+
+
+=item META
+
+The META directive allows simple metadata items to be defined within a
+template. These are evaluated when the template is parsed and as such
+may only contain simple values (e.g. it's not possible to interpolate
+other variables values into META variables).
+
+ [% META
+ title = 'The Cat in the Hat'
+ author = 'Dr. Seuss'
+ version = 1.23
+ %]
+
+The 'template' variable contains a reference to the main template
+being processed. These metadata items may be retrieved as attributes
+of the template.
+
+ <h1>[% template.title %]</h1>
+ <h2>[% template.author %]</h2>
+
+The 'name' and 'modtime' metadata items are automatically defined for
+each template to contain its name and modification time in seconds
+since the epoch.
+
+ [% USE date %] # use Date plugin to format time
+ ...
+ [% template.name %] last modified
+ at [% date.format(template.modtime) %]
+
+The PRE_PROCESS and POST_PROCESS options allow common headers and
+footers to be added to all templates. The 'template' reference is
+correctly defined when these templates are processed, allowing headers
+and footers to reference metadata items from the main template.
+
+ $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ $template->process('cat_in_hat');
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title>
+ </head>
+ <body>
+
+cat_in_hat:
+
+ [% META
+ title = 'The Cat in the Hat'
+ author = 'Dr. Seuss'
+ version = 1.23
+ year = 2000
+ %]
+
+ The cat in the hat sat on the mat.
+
+footer:
+
+ <hr>
+ &copy; [% template.year %] [% template.author %]
+ </body>
+ </html>
+
+The output generated from the above example is:
+
+ <html>
+ <head>
+ <title>The Cat in the Hat</title>
+ </head>
+ <body>
+
+ The cat in the hat sat on the mat.
+
+ <hr>
+ &copy; 2000 Dr. Seuss
+ </body>
+ </html>
+
+
+
+=item TAGS
+
+The TAGS directive can be used to set the START_TAG and END_TAG values
+on a per-template file basis.
+
+ [% TAGS <+ +> %]
+
+ <+ INCLUDE header +>
+
+The TAGS directive may also be used to set a named TAG_STYLE
+
+ [% TAGS html %]
+ <!-- INCLUDE header -->
+
+See the TAGS and TAG_STYLE configuration options for further details.
+
+
+
+
+
+
+
+
+=item DEBUG
+
+The DEBUG directive can be used to enable or disable directive debug
+messages within a template. The DEBUG configuration option must be
+set to include DEBUG_DIRS for the DEBUG directives to have any effect.
+If DEBUG_DIRS is not set then the parser will automatically ignore and
+remove any DEBUG directives.
+
+The DEBUG directive can be used with an 'on' or 'off' parameter to
+enable or disable directive debugging messages from that point
+forward. When enabled, the output of each directive in the generated
+output will be prefixed by a comment indicate the file, line and
+original directive text.
+
+ [% DEBUG on %]
+ directive debugging is on (assuming DEBUG option is set true)
+ [% DEBUG off %]
+ directive debugging is off
+
+The 'format' parameter can be used to change the format of the debugging
+message.
+
+ [% DEBUG format '<!-- $file line $line : [% $text %] -->' %]
+
+
+
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Filters.pod b/lib/Template/Manual/Filters.pod
new file mode 100644
index 0000000..c42f2ef
--- /dev/null
+++ b/lib/Template/Manual/Filters.pod
@@ -0,0 +1,529 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Filters
+#
+# DESCRIPTION
+# This section lists all the standard filters distributed with the
+# Template Toolkit for post-processing output.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Filters - Standard filters
+
+=head1 DESCRIPTION
+
+This section lists all the standard filters distributed with the
+Template Toolkit for post-processing output.
+
+=head1 STANDARD FILTERS
+
+
+
+=head2 format(format)
+
+The 'format' filter takes a format string as a parameter (as per
+printf()) and formats each line of text accordingly.
+
+ [% FILTER format('<!-- %-40s -->') %]
+ This is a block of text filtered
+ through the above format.
+ [% END %]
+
+output:
+
+ <!-- This is a block of text filtered -->
+ <!-- through the above format. -->
+
+=head2 upper
+
+Folds the input to UPPER CASE.
+
+ [% "hello world" FILTER upper %]
+
+output:
+
+ HELLO WORLD
+
+=head2 lower
+
+Folds the input to lower case.
+
+ [% "Hello World" FILTER lower %]
+
+output:
+
+ hello world
+
+=head2 ucfirst
+
+Folds the first character of the input to UPPER CASE.
+
+ [% "hello" FILTER ucfirst %]
+
+output:
+
+ Hello
+
+=head2 lcfirst
+
+Folds the first character of the input to lower case.
+
+ [% "HELLO" FILTER lcfirst %]
+
+output:
+
+ hELLO
+
+=head2 trim
+
+Trims any leading or trailing whitespace from the input text. Particularly
+useful in conjunction with INCLUDE, PROCESS, etc., having the same effect
+as the TRIM configuration option.
+
+ [% INCLUDE myfile | trim %]
+
+=head2 collapse
+
+Collapse any whitespace sequences in the input text into a single space.
+Leading and trailing whitespace (which would be reduced to a single space)
+is removed, as per trim.
+
+ [% FILTER collapse %]
+
+ The cat
+
+ sat on
+
+ the mat
+
+ [% END %]
+
+output:
+
+ The cat sat on the mat
+
+=head2 html
+
+Converts the characters 'E<lt>', 'E<gt>' and '&' to '&lt;', '&gt;' and
+'&amp;', respectively, protecting them from being interpreted as
+representing HTML tags or entities.
+
+ [% FILTER html %]
+ Binary "<=>" returns -1, 0, or 1 depending on...
+ [% END %]
+
+output:
+
+ Binary "&lt;=&gt;" returns -1, 0, or 1 depending on...
+
+=head2 html_entity
+
+The html filter is fast and simple but it doesn't encode the full
+range of HTML entities that your text may contain. The html_entity
+filter uses either the Apache::Util module (which is written in C and
+is therefore faster) or the HTML::Entities module (written in Perl but
+equally as comprehensive) to perform the encoding. If one or other of
+these modules are installed on your system then the text will be
+encoded (via the escape_html() or encode_entities() subroutines
+respectively) to convert all extended characters into their
+appropriate HTML entities (e.g. converting 'é' to '&eacute;'). If
+neither module is available on your system then an 'html_entity' exception
+will be thrown reporting an appropriate message.
+
+For further information on HTML entity encoding, see
+http://www.w3.org/TR/REC-html40/sgml/entities.html.
+
+=head2 html_para
+
+This filter formats a block of text into HTML paragraphs. A sequence of
+two or more newlines is used as the delimiter for paragraphs which are
+then wrapped in HTML E<lt>pE<gt>...E<lt>/pE<gt> tags.
+
+ [% FILTER html_para %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ <p>
+ The cat sat on the mat.
+ </p>
+
+ <p>
+ Mary had a little lamb.
+ </p>
+
+=head2 html_break / html_para_break
+
+Similar to the html_para filter described above, but uses the HTML tag
+sequence E<lt>brE<gt>E<lt>brE<gt> to join paragraphs.
+
+ [% FILTER html_break %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.
+ <br>
+ <br>
+ Mary had a little lamb.
+
+=head2 html_line_break
+
+This filter replaces any newlines with E<lt>brE<gt> HTML tags,
+thus preserving the line breaks of the original text in the
+HTML output.
+
+ [% FILTER html_line_break %]
+ The cat sat on the mat.
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.<br>
+ Mary had a little lamb.<br>
+
+=head2 uri
+
+This filter URI escapes the input text, converting any characters
+outside of the permitted URI character set (as defined by RFC 2396)
+into a C<%nn> hex escape.
+
+ [% 'my file.html' | uri %]
+
+output:
+
+ my%20file.html
+
+Note that URI escaping isn't always enough when generating hyperlinks in
+an HTML document. The C<&> character, for example, is valid in a URI and
+will not be escaped by the URI filter. In this case you should also filter
+the text through the 'html' filter.
+
+ <a href="[% filename | uri | html %]">click here</a>
+
+=head2 indent(pad)
+
+Indents the text block by a fixed pad string or width. The 'pad' argument
+can be specified as a string, or as a numerical value to indicate a pad
+width (spaces). Defaults to 4 spaces if unspecified.
+
+ [% FILTER indent('ME> ') %]
+ blah blah blah
+ cabbages, rhubard, onions
+ [% END %]
+
+output:
+
+ ME> blah blah blah
+ ME> cabbages, rhubard, onions
+
+=head2 truncate(length)
+
+Truncates the text block to the length specified, or a default length of
+32. Truncated text will be terminated with '...' (i.e. the '...' falls
+inside the required length, rather than appending to it).
+
+ [% FILTER truncate(21) %]
+ I have much to say on this matter that has previously
+ been said on more than one occasion.
+ [% END %]
+
+output:
+
+ I have much to say...
+
+=head2 repeat(iterations)
+
+Repeats the text block for as many iterations as are specified (default: 1).
+
+ [% FILTER repeat(3) %]
+ We want more beer and we want more beer,
+ [% END %]
+ We are the more beer wanters!
+
+output:
+
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We are the more beer wanters!
+
+=head2 remove(string)
+
+Searches the input text for any occurrences of the specified string and
+removes them. A Perl regular expression may be specified as the search
+string.
+
+ [% "The cat sat on the mat" FILTER remove('\s+') %]
+
+output:
+
+ Thecatsatonthemat
+
+=head2 replace(search, replace)
+
+Similar to the remove filter described above, but taking a second parameter
+which is used as a replacement string for instances of the search string.
+
+ [% "The cat sat on the mat" | replace('\s+', '_') %]
+
+output:
+
+ The_cat_sat_on_the_mat
+
+=head2 redirect(file, options)
+
+The 'redirect' filter redirects the output of the block into a separate
+file, specified relative to the OUTPUT_PATH configuration item.
+
+ [% FOREACH user = myorg.userlist %]
+ [% FILTER redirect("users/${user.id}.html") %]
+ [% INCLUDE userinfo %]
+ [% END %]
+ [% END %]
+
+or more succinctly, using side-effect notation:
+
+ [% INCLUDE userinfo
+ FILTER redirect("users/${user.id}.html")
+ FOREACH user = myorg.userlist
+ %]
+
+A 'file' exception will be thrown if the OUTPUT_PATH option is undefined.
+
+An optional 'binmode' argument can follow the filename to explicitly set
+the output file to binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", binmode=1) %]
+
+For backwards compatibility with earlier versions, a single true/false
+value can be used to set binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", 1) %]
+
+For the sake of future compatibility and clarity, if nothing else, we
+would strongly recommend you explicitly use the named 'binmode' option
+as shown in the first example.
+
+=head2 eval / evaltt
+
+The 'eval' filter evaluates the block as template text, processing
+any directives embedded within it. This allows template variables to
+contain template fragments, or for some method to be provided for
+returning template fragments from an external source such as a
+database, which can then be processed in the template as required.
+
+ my $vars = {
+ fragment => "The cat sat on the [% place %]",
+ };
+ $template->process($file, $vars);
+
+The following example:
+
+ [% fragment | eval %]
+
+is therefore equivalent to
+
+ The cat sat on the [% place %]
+
+The 'evaltt' filter is provided as an alias for 'eval'.
+
+=head2 perl / evalperl
+
+The 'perl' filter evaluates the block as Perl code. The EVAL_PERL
+option must be set to a true value or a 'perl' exception will be
+thrown.
+
+ [% my_perl_code | perl %]
+
+In most cases, the [% PERL %] ... [% END %] block should suffice for
+evaluating Perl code, given that template directives are processed
+before being evaluate as Perl. Thus, the previous example could have
+been written in the more verbose form:
+
+ [% PERL %]
+ [% my_perl_code %]
+ [% END %]
+
+as well as
+
+ [% FILTER perl %]
+ [% my_perl_code %]
+ [% END %]
+
+The 'evalperl' filter is provided as an alias for 'perl' for backwards
+compatibility.
+
+=head2 stdout(options)
+
+The stdout filter prints the output generated by the enclosing block to
+STDOUT. The 'binmode' option can be passed as either a named parameter
+or a single argument to set STDOUT to binary mode (see the
+binmode perl function).
+
+ [% PROCESS something/cool
+ FILTER stdout(binmode=1) # recommended %]
+
+ [% PROCESS something/cool
+ FILTER stdout(1) # alternate %]
+
+The stdout filter can be used to force binmode on STDOUT, or also inside
+redirect, null or stderr blocks to make sure that particular output goes
+to stdout. See the null filter below for an example.
+
+=head2 stderr
+
+The stderr filter prints the output generated by the enclosing block to
+STDERR.
+
+=head2 null
+
+The null filter prints nothing. This is useful for plugins whose
+methods return values that you don't want to appear in the output.
+Rather than assigning every plugin method call to a dummy variable
+to silence it, you can wrap the block in a null filter:
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ im.arc(50,50,95,75,0,360,blue);
+ im.fill(50,50,red);
+ im.png | stdout(1);
+ END;
+ -%]
+
+Notice the use of the stdout filter to ensure that a particular expression
+generates output to stdout (in this case in binary mode).
+
+=head2 latex(outputType)
+
+Passes the text block to LaTeX and produces either PDF, DVI or
+PostScript output. The 'outputType' argument determines the output
+format and it should be set to one of the strings: "pdf" (default),
+"dvi", or "ps".
+
+The text block should be a complete LaTeX source file.
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+
+ \begin{document}
+
+ \title{A Sample TT2 \LaTeX\ Source File}
+ \author{Craig Barratt}
+ \maketitle
+
+ \section{Introduction}
+ This is some text.
+
+ \end{document}
+ [% END -%]
+
+The output will be a PDF file. You should be careful not to prepend or
+append any extraneous characters or text outside the FILTER block,
+since this text will wrap the (binary) output of the latex filter.
+Notice the END directive uses '-%]' for the END_TAG to remove the
+trailing new line.
+
+One example where you might prepend text is in a CGI script where
+you might include the Content-Type before the latex output, eg:
+
+ Content-Type: application/pdf
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END -%]
+
+In other cases you might use the redirect filter to put the output
+into a file, rather than delivering it to stdout. This might be
+suitable for batch scripts:
+
+ [% output = FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END; output | redirect("document.pdf", 1) -%]
+
+(Notice the second argument to redirect to force binary mode.)
+
+Note that the latex filter runs one or two external programs, so it
+isn't very fast. But for modest documents the performance is adequate,
+even for interactive applications.
+
+A error of type 'latex' will be thrown if there is an error reported
+by latex, pdflatex or dvips.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Internals.pod b/lib/Template/Manual/Internals.pod
new file mode 100644
index 0000000..b8cf80b
--- /dev/null
+++ b/lib/Template/Manual/Internals.pod
@@ -0,0 +1,556 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Internals
+#
+# DESCRIPTION
+# This document provides an overview of the internal architecture of
+# the Template Toolkit. It is a work in progress and is far from
+# complete, currently providing little more than an overview of how
+# the major components fit together. Nevertheless, it's a good
+# starting point for anyone wishing to delve into the source code to
+# find out how it all works.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Internals - Template Toolkit internals
+
+=head1 DESCRIPTION
+
+This document provides an overview of the internal architecture of the
+Template Toolkit. It is a work in progress and is far from complete,
+currently providing little more than an overview of how the major
+components fit together. Nevertheless, it's a good starting point for
+anyone wishing to delve into the source code to find out how it all
+works.
+
+=head2 Outside Looking In
+
+The B<Template> module is simply a front end module which creates and
+uses a Template::Service and pipes the output wherever you want it to
+go (STDOUT by default, or maybe a file, scalar, etc). The
+Apache::Template module (available separately from CPAN) is another
+front end. That creates a Template::Service::Apache object, calls on
+it as required and sends the output back to the relevant
+Apache::Request object.
+
+These front-end modules are really only there to handle any specifics
+of the environment in which they're being used. The Apache::Template
+front end, for example, handles Apache::Request specifics and
+configuration via the httpd.conf. The regular Template front-end
+deals with STDOUT, variable refs, etc. Otherwise it is
+Template::Service (or subclass) which does all the work.
+
+The B<Template::Service> module provides a high-quality template
+delivery service, with bells, whistles, signed up service level
+agreement and a 30-day no quibble money back guarantee. "Have
+a good time, all the time", that's our motto.
+
+Within the lower levels of the Template Toolkit, there are lots of
+messy details that we generally don't want to have to worry about most
+of the time. Things like templates not being found, or failing to
+parse correctly, uncaught exceptions being thrown, missing plugin
+modules or dependencies, and so on. Template::Service hides that all
+away and makes everything look simple to the outsider. It provides
+extra features, like PRE_PROCESS, PROCESS and POST_PROCESS, and also
+provides the error recovery mechanism via ERROR. You ask it to
+process a template and it takes care of everything for you. The
+Template::Service::Apache module goes a little bit further, adding
+some extra headers to the Apache::Request, setting a few extra template
+variables, and so on.
+
+For the most part, the job of a service is really just one of
+scheduling and dispatching. It receives a request in the form of a
+call to its process() method and schedules the named template
+specified as an argument, and possibly several other templates
+(PRE_PROCESS, etc) to be processed in order. It doesn't actually
+process the templates itself, but instead makes a process() call
+against a Template::Context object.
+
+B<Template::Context> is the runtime engine for the Template Toolkit -
+the module that hangs everything together in the lower levels of the
+Template Toolkit and that one that does most of the real work, albeit
+by crafty delegation to various other friendly helper modules.
+
+Given a template name (or perhaps a reference to a scalar or file
+handle) the context process() method must load and compile, or fetch a
+cached copy of a previously compiled template, corresponding to that
+name. It does this by calling on a list of one or more
+Template::Provider objects (the LOAD_TEMPLATES posse) who themselves
+might get involved with a Template::Parser to help turn source
+templates into executable Perl code (but more on that later). Thankfully,
+all of this complexity is hidden away behind a simple template()
+method. You call it passing a template name as an argument, and it
+returns a compiled template in the form of a Template::Document
+object, or otherwise raises an exception.
+
+A B<Template::Document> is a thin object wrapper around a compiled
+template subroutine. The object implements a process() method which
+performs a little bit of housekeeping and then calls the template
+subroutine. The object also defines template metadata (defined in
+C<[% META ... %]> directives) and has a block() method which returns
+a hash of any additional C<[% BLOCK xxxx %]> definitions found in the
+template source.
+
+So the context fetches a compiled document via its own template()
+method and then gets ready to process it. It first updates the stash
+(the place where template variables get defined - more on that
+shortly) to set any template variable definitions specified as the
+second argument by reference to hash array. Then, it calls the
+document process() method, passing a reference to itself, the context
+object, as an argument. In doing this, it provides itself as an
+object against which template code can make callbacks to access
+runtime resources and Template Toolkit functionality.
+
+What we're trying to say here is this: not only does the Template::Context
+object receive calls from the I<outside>, i.e. those originating in user
+code calling the process() method on a Template object, but it also
+receives calls from the I<inside>, i.e. those originating in template
+directives of the form C<[% PROCESS template %]>.
+
+Before we move on to that, here's a simple structure diagram showing
+the outer layers of the Template Toolkit heading inwards, with pseudo
+code annotations showing a typical invocation sequence.
+
+ ,--------.
+ | Caller | use Template;
+ `--------' my $tt = Template->new( ... );
+ | $tt->process($template, \%vars);
+ | Outside
+ - - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - T T
+ | package Template; Inside
+ V
+ +----------+ sub process($template, \%vars) {
+ | Template | $out = $self->SERVICE->process($template, $vars);
+ +----------+ print $out or send it to $self->OUTPUT;
+ | }
+ |
+ | package Template::Service;
+ |
+ | sub process($template, \%vars) {
+ | try {
+ +----------+ foreach $p in @self->PRE_PROCESS
+ | Service | $self->CONTEXT->process($p, $vars);
+ +----------+
+ | $self->CONTEXT->process($template, $vars);
+ |
+ | foreach $p @self->POST_PROCESS
+ | $self->CONTEXT->process($p, $vars);
+ | }
+ | catch {
+ | $self->CONTEXT->process($self->ERROR);
+ | }
+ | }
+ |
+ V package Template::Context;
+ +----------+
+ | Context | sub process($template, \%vars) {
+ +----------+ # fetch compiled template
+ | $template = $self->template($template)
+ | # update stash
+ | $self->STASH->update($vars);
+ | # process template
+ | $template->process($self)
+ | }
+ V
+ +----------+ package Template::Document;
+ | Document |
+ +----------+ sub process($context) {
+ $output = &{ $self->BLOCK }($context);
+ }
+
+
+=head2 Inside Looking Out
+
+To understand more about what's going on in these lower levels, we
+need to look at what a compiled template looks like. In fact, a
+compiled template is just a regular Perl sub-routine. Here's a very
+simple one.
+
+ sub my_compiled_template {
+ return "This is a compiled template.\n";
+ }
+
+You're unlikely to see a compiled template this simple unless you
+wrote it yourself but it is entirely valid. All a template subroutine
+is obliged to do is return some output (which may be an empty of
+course). If it can't for some reason, then it should raise an error
+via die().
+
+ sub my_todo_template {
+ die "This template not yet implemented\n";
+ }
+
+If it wants to get fancy, it can raise an error as a
+Template::Exception object. An exception object is really just a
+convenient wrapper for the 'type' and 'info' fields.
+
+ sub my_solilique_template {
+ die (Template::Exception->new('yorrick', 'Fellow of infinite jest'));
+ }
+
+Templates generally need to do a lot more than just generate static
+output or raise errors. They may want to inspect variable values,
+process another template, load a plugin, run a filter, and so on.
+Whenever a template subroutine is called, it gets passed a reference
+to a Template::Context object. It is through this context object that
+template code can access the features of the Template Toolkit.
+
+We described earlier how the Template::Service object calls on
+Template::Context to handle a process() request from the I<outside>.
+We can make a similar request on a context to process a template, but
+from within the code of another template. This is a call from the
+I<inside>.
+
+ sub my_process_template {
+ my $context = shift;
+
+ my $output = $context->process('header', { title => 'Hello World' })
+ . "\nsome content\n"
+ . $context->process('footer');
+ }
+
+This is then roughly equivalent to a source template something
+like this:
+
+ [% PROCESS header
+ title = 'Hello World'
+ %]
+ some content
+ [% PROCESS footer %]
+
+Template variables are stored in, and managed by a B<Template::Stash>
+object. This is a blessed hash array in which template variables are
+defined. The object wrapper provides get() and set() method which
+implement all the magical.variable.features of the Template Toolkit.
+
+Each context object has its own stash, a reference to which can be
+returned by the appropriately named stash() method. So to print the
+value of some template variable, or for example, to represent the
+following source template:
+
+ <title>[% title %]</title>
+
+we might have a subroutine definition something like this:
+
+ sub {
+ my $context = shift;
+ my $stash = $context->stash();
+ return '<title>' . $stash->get('title') . '</title>';
+ }
+
+The stash get() method hides the details of the underlying variable
+types, automatically calling code references, checking return values,
+and performing other such tricks. If 'title' happens to be bound to a
+subroutine then we can specify additional parameters as a list
+reference passed as the second argument to get().
+
+ [% title('The Cat Sat on the Mat') %]
+
+This translates to the stash get() call:
+
+ $stash->get([ 'title', ['The Cat Sat on the Mat'] ]);
+
+Dotted compound variables can be requested by passing a single
+list reference to the get() method in place of the variable
+name. Each pair of elements in the list should correspond to the
+variable name and reference to a list of arguments for each
+dot-delimited element of the variable.
+
+ [% foo(1, 2).bar(3, 4).baz(5) %]
+
+is thus equivalent to
+
+ $stash->get([ foo => [1,2], bar => [3,4], baz => [5] ]);
+
+If there aren't any arguments for an element, you can specify an
+empty, zero or null argument list.
+
+ [% foo.bar %]
+ $stash->get([ 'foo', 0, 'bar', 0 ]);
+
+The set() method works in a similar way. It takes a variable
+name and a variable value which should be assigned to it.
+
+ [% x = 10 %]
+ $stash->set('x', 10);
+
+ [% x.y = 10 %]
+ $stash->set([ 'x', 0, 'y', 0 ], 10);
+
+So the stash gives us access to template variables and the context
+provides the higher level functionality. Alongside the process()
+method lies the include() method. Just as with the PROCESS / INCLUDE
+directives, the key difference is in variable localisation. Before
+processing a template, the process() method simply updates the stash
+to set any new variable definitions, overwriting any existing values.
+In contrast, the include() method creates a copy of the existing
+stash, in a process known as I<cloning> the stash, and then uses that
+as a temporary variable store. Any previously existing variables are
+still defined, but any changes made to variables, including setting
+the new variable values passed aas arguments will affect only the
+local copy of the stash (although note that it's only a shallow copy,
+so it's not foolproof). When the template has been processed, the include()
+method restores the previous variable state by I<decloning> the stash.
+
+The context also provides an insert() method to implement the INSERT
+directive, but no wrapper() method. This functionality can be implemented
+by rewriting the Perl code and calling include().
+
+ [% WRAPPER foo -%]
+ blah blah [% x %]
+ [%- END %]
+
+ $context->include('foo', {
+ content => 'blah blah ' . $stash->get('x'),
+ });
+
+Other than the template processing methods process(), include() and insert(),
+the context defines methods for fetching plugin objects, plugin(), and
+filters, filter().
+
+ [% USE foo = Bar(10) %]
+
+ $stash->set('foo', $context->plugin('Bar', [10]));
+
+ [% FILTER bar(20) %]
+ blah blah blah
+ [% END %]
+
+ my $filter = $context->filter('bar', [20]);
+ &$filter('blah blah blah');
+
+Pretty much everything else you might want to do in a template can be done
+in Perl code. Things like IF, UNLESS, FOREACH and so on all have direct
+counterparts in Perl.
+
+ [% IF msg %]
+ Message: [% msg %]
+ [% END %];
+
+ if ($stash->get('msg')) {
+ $output .= 'Message: ';
+ $output .= $stash->get('msg');
+ }
+
+The best way to get a better understanding of what's going on underneath
+the hood is to set the C<$Template::Parser::DEBUG> flag to a true value
+and start processing templates. This will cause the parser to print the
+generated Perl code for each template it compiles to STDERR. You'll
+probably also want to set the C<$Template::Directive::PRETTY> option to
+have the Perl pretty-printed for human consumption.
+
+ use Template;
+ use Template::Parser;
+ use Template::Directive;
+
+ $Template::Parser::DEBUG = 1;
+ $Template::Directive::PRETTY = 1;
+
+ my $template = Template->new();
+ $template->process(\*DATA, { cat => 'dog', mat => 'log' });
+
+ __DATA__
+ The [% cat %] sat on the [% mat %]
+
+The output sent to STDOUT remains as you would expect:
+
+ The dog sat on the log
+
+The output sent to STDERR would look something like this:
+
+ compiled main template document block:
+ sub {
+ my $context = shift || die "template sub called without context\n";
+ my $stash = $context->stash;
+ my $output = '';
+ my $error;
+
+ eval { BLOCK: {
+ $output .= "The ";
+ $output .= $stash->get('cat');
+ $output .= " sat on the ";
+ $output .= $stash->get('mat');
+ $output .= "\n";
+ } };
+ if ($@) {
+ $error = $context->catch($@, \$output);
+ die $error unless $error->type eq 'return';
+ }
+
+ return $output;
+ }
+
+
+=head1 HACKING ON THE TEMPLATE TOOLKIT
+
+Please feel free to hack on the Template Toolkit. If you find a bug
+that needs fixing, if you have an idea for something that's missing,
+or you feel inclined to tackle something on the TODO list, then by all
+means go ahead and do it!
+
+If you're contemplating something non-trivial then you'll probably
+want to bring it up on the mailing list first to get an idea about the
+current state of play, find out if anyone's already working on it, and
+so on.
+
+When you start to hack on the Template Toolkit, please make sure you
+start from the latest developer release. Stable releases are uploaded
+to CPAN and have all-numerical version numbers, e.g. 2.04, 2.05.
+Developer releases are available from the Template Toolkit web site
+and have a character suffix on the version, e.g. 2.04a, 2.04b, etc.
+
+Once you've made your changes, please remember to update the test
+suite by adding extra tests to one of the existing test scripts in
+the 't' sub-directory, or by adding a new test script of your own.
+And of course, run C<make test> to ensure that all the tests pass
+with your new code.
+
+Don't forget that any files you do add will need to be added to the
+MANIFEST. Running 'make manifest' will do this for you, but you need
+to make sure you haven't got any other temporary files lying around
+that might also get added to it.
+
+Documentation is often something that gets overlooked but it's just
+as important as the code. If you're updating existing documentation
+then you should download the 'docsrc' bundle from which all the
+Template Toolkit documentation is built and make your changes in there.
+It's also available from the Template Toolkit web site. See the
+README distributed in the archive for further information.
+
+If you're adding a new module, a plugin module, for example, then it's
+OK to include the POD documentation in with the module, but I<please>
+write it all in one piece at the end of the file, I<after> the code
+(just look at any other Template::* module for an example). It's a
+religious issue, I know, but I have a strong distaste for POD documentation
+interspersed throughout the code. In my not-so-humble opinion, it makes
+both the code and the documentation harder to read (same kinda problem
+as embedding Perl in HTML).
+
+Aesthetics aside, if I do want to extract the documentation into the
+docsrc bundle then it's easy for me to do it if it's all written in
+one chunk and extremely tedious if not. So for practical reasons
+alone, please keep Perl and POD sections separate. Comment blocks
+within the code are of course welcome.
+
+To share your changes with the rest of the world, you'll need to
+prepare a patch file. To do this you should have 2 directories
+side-by-side, one which is the original, unmodified distribution
+directory for the latest developer release, and the other is a
+copy of that same directory which includes your changes.
+
+The following example shows a typical hacking session. First we
+unpack the latest developer release.
+
+ $ tar zxf Template-Toolkit-2.05c.tar.gz
+
+At this point, it's a good idea to rename the directory to give
+some indicate of what it contains.
+
+ $ mv Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack
+
+Then go hack!
+
+ $ cd Template-Toolkit-2.05c-abw-xyz-hack
+
+ [ hacking ]
+
+ $ cd ..
+
+When you're all done and ready to prepare a patch, unpack the
+distribution archive again so that you've got the original to
+diff against your new code.
+
+ $ tar zxf Template-Toolkit-2.05c.tar.gz
+
+You should now have an original distribution directory and a modified
+version of that same directory, side-by-side.
+
+ $ ls
+ Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack
+
+Now run diff and save the output into an appropriately named patch
+file.
+
+ $ diff -Naur Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack > patch-TT205c-abw-xyz-hack
+
+You can then post the generated patch file to the mailing list,
+describing what it does, why it does it, how it does it and any
+other relevant information.
+
+If you want to apply someone else's patch then you should start with the
+same original distribution source on which the patch is based. From within
+the root of the distribution, run 'patch' feeding in the patch file as
+standard input. The 'p1' option is required to strip the first element
+of the path name (e.g. Template-Toolkit-2.05c/README becomes README which
+is then the correct path).
+
+ $ tar zxf Template-Toolkit-2.05c.tar.gz
+ $ cd Template-Toolkit-2.05c
+ $ patch -p1 < ../patch-TT205c-abw-xyz-hack
+
+The output generated by 'patch' should be something like the following:
+
+ patching file README
+ patching file lib/Template.pm
+ patching file lib/Template/Provider.pm
+ patching file t/provider.t
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Intro.pod b/lib/Template/Manual/Intro.pod
new file mode 100644
index 0000000..c50c9e8
--- /dev/null
+++ b/lib/Template/Manual/Intro.pod
@@ -0,0 +1,295 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Intro
+#
+# DESCRIPTION
+# This section provides a general introduction to the Template
+# Toolkit, giving a quick overview of features, examples of template
+# directives and use of the Template.pm module. It also described the
+# basic concept underlying the toolkit: the separation of
+# presentation elements from application logic and data.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Intro - Introduction to the Template Toolkit
+
+=head1 DESCRIPTION
+
+This section provides a general introduction to the Template Toolkit,
+giving a quick overview of features, examples of template directives
+and use of the Template.pm module. It also described the basic concept
+underlying the toolkit: the separation of presentation elements from
+application logic and data.
+
+The Template Toolkit is a collection of modules which implement a
+fast, flexible, powerful and extensible template processing system.
+It was originally designed and remains primarily useful for generating
+dynamic web content, but it can be used equally well for processing
+any kind of text documents. This POD documentation is all generated
+using the Template Toolkit batch mode utility F<ttree>, for example.
+
+At the simplest level it provides an easy way to process template
+files, filling in embedded variable references with their equivalent
+values.
+
+ Dear [% name %],
+
+ It has come to our attention that your account is in
+ arrears to the sum of [% debt %].
+
+ Please settle your account before [% deadline %] or we
+ will be forced to revoke your Licence to Thrill.
+
+ The Management.
+
+By default, template directives are embedded within the character
+sequences '[%' ... '%]' but you can change these and various other
+options to configure how the Template Toolkit looks, feels and works.
+You can set the INTERPOLATE option, for example, if you prefer to
+embed your variables in Perl style:
+
+ Dear $name,
+
+ It has come to our attention that your account is in
+ arrears to the sum of $debt.
+ ...
+
+=head2 Template.pm
+
+The Template.pm module is the front end to the Template Toolkit,
+providing access to the full range of functionality through a single
+module with a simple interface. It loads the other modules as
+required and instantiates a default set of objects to handle
+subsequent template processing requests. Configuration parameters may
+be passed to the Template.pm constructor, new(), which are then used
+to configure the underlying objects.
+
+ use Template;
+
+ my $tt = Template->new({
+ INCLUDE_PATH => '/usr/local/templates',
+ INTERPOLATE => 1,
+ }) || die "$Template::ERROR\n";
+
+The Template object implements a process() method for processing template
+files or text. The name of the input template (or various other sources)
+is passed as the first argument, followed by a reference to a hash array
+of variable definitions for substitution in the template.
+
+ my $vars = {
+ name => 'Count Edward van Halen',
+ debt => '3 riffs and a solo',
+ deadline => 'the next chorus',
+ };
+
+ $tt->process('letters/overdrawn', $vars)
+ || die $tt->error(), "\n";
+
+
+The process() method returns true (1) on success and prints the
+template output to STDOUT, by default. On error, the process() method
+returns false (undef). The error() method can then be called to
+retrieve details of the error.
+
+=head2 Component Based Content Construction
+
+A number of special directives are provided, such as INSERT, INCLUDE
+and PROCESS, which allow content to be built up from smaller template
+components. This permits a modular approach to building a web site or
+other content repository, promoting reusability, cross-site
+consistency, ease of construction and subsequent maintenance. Common
+elements such as headers, footers, menu bars, tables, and so on, can
+be created as separate template files which can then be processed into
+other documents as required. All defined variables are inherited by
+these templates along with any additional "local" values specified.
+
+ [% PROCESS header
+ title = "The Cat Sat on the Mat"
+ %]
+
+ [% PROCESS menu %]
+
+ The location of the missing feline has now been established.
+ Thank you for your assistance.
+
+ [% INSERT legal/disclaimer %]
+
+ [% PROCESS footer %]
+
+You can also define a template as a BLOCK within the same file and
+PROCESS it just like any other template file. This can be invaluable
+for building up repetitive elements such as tables, menus, etc.
+
+ [% BLOCK tabrow %]
+ <tr><td>[% name %]</td><td>[% email %]</td></tr>
+ [% END %]
+
+ <table>
+ [% PROCESS tabrow name="tom" email="tom@here.org" %]
+ [% PROCESS tabrow name="dick" email="disk@there.org" %]
+ [% PROCESS tabrow name="larry" email="larry@where.org" %]
+ </table>
+
+=head2 Data and Code Binding
+
+One of the key features that sets the Template Toolkit apart from
+other template processors is the ability to bind template variables to
+any kind of Perl data: scalars, lists, hash arrays, sub-routines and
+objects.
+
+ my $vars = {
+ root => 'http://here.com/there',
+ menu => [ 'modules', 'authors', 'scripts' ],
+ client => {
+ name => 'Doctor Joseph von Satriani',
+ id => 'JVSAT',
+ },
+ checkout => sub { my $total = shift; ...; return $something },
+ shopcart => My::Cool::Shopping::Cart->new(),
+ };
+
+The Template Toolkit will automatically Do The Right Thing to access
+the data in an appropriate manner to return some value which can then
+be output. The dot operator '.' is used to access into lists and
+hashes or to call object methods. The FOREACH directive is provided for
+iterating through lists, and various logical tests are available using
+directives such as IF, UNLESS, ELSIF, ELSE, SWITCH, CASE, etc.
+
+ [% FOREACH section = menu %]
+ <a href="[% root %]/[% section %]/index.html">[% section %]</a>
+ [% END %]
+
+ <b>Client</a>: [% client.name %] (id: [% client.id %])
+
+ [% IF shopcart.nitems %]
+ Your shopping cart contains the following items:
+ <ul>
+ [% FOREACH item = shopcart.contents %]
+ <li>[% item.name %] : [% item.qty %] @ [% item.price %]
+ [% END %]
+ </ul>
+
+ [% checkout(shopcart.total) %]
+
+ [% ELSE %]
+ No items currently in shopping cart.
+ [% END %]
+
+=head2 Advanced Features: Filters, Macros, Exceptions, Plugins
+
+The Template Toolkit also provides a number of additional directives
+for advanced processing and programmatical functionality. It supports
+output filters (FILTER), allows custom macros to be defined (MACRO),
+has a fully-featured exception handling system (TRY, THROW, CATCH,
+FINAL) and supports a plugin architecture (USE) which allows special
+plugin modules and even regular Perl modules to be loaded and used
+with the minimum of fuss. The Template Toolkit is "just" a template
+processor but you can trivially extend it to incorporate the
+functionality of any Perl module you can get your hands on. Thus, it
+is also a scalable and extensible template framework, ideally suited
+for managing the presentation layer for application servers, content
+management systems and other web applications.
+
+=head2 Separating Presentation and Application Logic
+
+Rather than embedding Perl code or some other scripting language
+directly into template documents, it encourages you to keep functional
+components (i.e. Perl code) separate from presentation components
+(e.g. HTML templates). The template variables provide the interface
+between the two layers, allowing data to be generated in code and then
+passed to a template component for displaying (pipeline model) or for
+sub-routine or object references to be bound to variables which can
+then be called from the template as and when required (callback
+model).
+
+The directives that the Template Toolkit provide implement their own
+mini programming language, but they're not really designed for
+serious, general purpose programming. Perl is a far more appropriate
+language for that. If you embed application logic (e.g. Perl or other
+scripting language fragments) in HTML templates then you risk losing
+the clear separation of concerns between functionality and
+presentation. It becomes harder to maintain the two elements in
+isolation and more difficult, if not impossible, to reuse code or
+presentation elements by themselves. It is far better to write your
+application code in separate Perl modules, libraries or scripts and
+then use templates to control how the resulting data is presented as
+output. Thus you should think of the Template Toolkit language as a
+set of layout directives for displaying data, not calculating it.
+
+Having said that, the Template Toolkit doesn't force you into one
+approach or the other. It attempts to be pragmatic rather than
+dogmatic in allowing you to do whatever best gets the job done.
+Thus, if you enable the EVAL_PERL option then you can happily embed
+real Perl code in your templates within PERL ... END directives.
+
+=head2 Performance
+
+The Template Toolkit uses a fast YACC-like parser which compiles
+templates into Perl code for maximum runtime efficiency. It also has
+an advanced caching mechanism which manages in-memory and on-disk
+(i.e. persistent) versions of compiled templates. The modules that
+comprise the toolkit are highly configurable and the architecture
+around which they're built is designed to be extensible. The Template
+Toolkit provides a powerful framework around which content creation
+and delivery systems can be built while also providing a simple
+interface through the Template front-end module for general use.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Plugins.pod b/lib/Template/Manual/Plugins.pod
new file mode 100644
index 0000000..7955640
--- /dev/null
+++ b/lib/Template/Manual/Plugins.pod
@@ -0,0 +1,552 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Plugins
+#
+# DESCRIPTION
+# This section lists the standard plugins which can be used to extend
+# the runtime functionality of the Template Toolkit. The plugins are
+# distributed with the Template Toolkit but may required additional
+# modules from CPAN.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Plugins - Standard plugins
+
+=head1 DESCRIPTION
+
+This section lists the standard plugins which can be used to extend the
+runtime functionality of the Template Toolkit. The plugins are
+distributed with the Template Toolkit but may required additional
+modules from CPAN.
+
+
+
+=head1 TEMPLATE TOOLKIT PLUGINS
+
+The following plugin modules are distributed with the Template
+Toolkit. Some of the plugins interface to external modules (detailed
+below) which should be downloaded from any CPAN site and installed
+before using the plugin.
+
+=head2 Autoformat
+
+The Autoformat plugin is an interface to Damian Conway's Text::Autoformat
+Perl module which provides advanced text wrapping and formatting. See
+L<Template::Plugin::Autoformat> and L<Text::Autoformat> for further
+details.
+
+ [% USE autoformat(left=10, right=20) %]
+ [% autoformat(mytext) %] # call autoformat sub
+ [% mytext FILTER autoformat %] # or use autoformat filter
+
+The Text::Autoformat module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 CGI
+
+The CGI plugin is a wrapper around Lincoln Stein's
+E<lt>lstein@genome.wi.mit.eduE<gt> CGI.pm module. The plugin is
+distributed with the Template Toolkit (see L<Template::Plugin::CGI>)
+and the CGI module itself is distributed with recent versions Perl,
+or is available from CPAN.
+
+ [% USE CGI %]
+ [% CGI.param('param_name') %]
+ [% CGI.start_form %]
+ [% CGI.popup_menu( Name => 'color',
+ Values => [ 'Green', 'Brown' ] ) %]
+ [% CGI.end_form %]
+
+=head2 Datafile
+
+Provides an interface to data stored in a plain text file in a simple
+delimited format. The first line in the file specifies field names
+which should be delimiter by any non-word character sequence.
+Subsequent lines define data using the same delimiter as int he first
+line. Blank lines and comments (lines starting '#') are ignored. See
+L<Template::Plugin::Datafile> for further details.
+
+/tmp/mydata:
+
+ # define names for each field
+ id : email : name : tel
+ # here's the data
+ fred : fred@here.com : Fred Smith : 555-1234
+ bill : bill@here.com : Bill White : 555-5678
+
+example:
+
+ [% USE userlist = datafile('/tmp/mydata') %]
+
+ [% FOREACH user = userlist %]
+ [% user.name %] ([% user.id %])
+ [% END %]
+
+=head2 Date
+
+The Date plugin provides an easy way to generate formatted time and date
+strings by delegating to the POSIX strftime() routine. See
+L<Template::Plugin::Date> and L<POSIX> for further details.
+
+ [% USE date %]
+ [% date.format %] # current time/date
+
+ File last modified: [% date.format(template.modtime) %]
+
+=head2 Directory
+
+The Directory plugin provides a simple interface to a directory and
+the files within it. See L<Template::Plugin::Directory> for further
+details.
+
+ [% USE dir = Directory('/tmp') %]
+ [% FOREACH file = dir.files %]
+ # all the plain files in the directory
+ [% END %]
+ [% FOREACH file = dir.dirs %]
+ # all the sub-directories
+ [% END %]
+
+=head2 DBI
+
+The DBI plugin, developed by Simon Matthews
+E<lt>sam@knowledgepool.comE<gt>, brings the full power of Tim Bunce's
+E<lt>Tim.Bunce@ig.co.ukE<gt> database interface module (DBI) to your
+templates. See L<Template::Plugin::DBI> and L<DBI> for further details.
+
+ [% USE DBI('dbi:driver:database', 'user', 'pass') %]
+
+ [% FOREACH user = DBI.query( 'SELECT * FROM users' ) %]
+ [% user.id %] [% user.name %]
+ [% END %]
+
+The DBI and relevant DBD modules are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/DBI/
+
+=head2 Dumper
+
+The Dumper plugin provides an interface to the Data::Dumper module. See
+L<Template::Plugin::Dumper> and L<Data::Dumper> for futher details.
+
+ [% USE dumper(indent=0, pad="<br>") %]
+ [% dumper.dump(myvar, yourvar) %]
+
+=head2 File
+
+The File plugin provides a general abstraction for files and can be
+used to fetch information about specific files within a filesystem.
+See L<Template::Plugin::File> for further details.
+
+ [% USE File('/tmp/foo.html') %]
+ [% File.name %] # foo.html
+ [% File.dir %] # /tmp
+ [% File.mtime %] # modification time
+
+=head2 Filter
+
+This module implements a base class plugin which can be subclassed
+to easily create your own modules that define and install new filters.
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ # ...mungify $text...
+
+ return $text;
+ }
+
+ # now load it...
+ [% USE MyFilter %]
+
+ # ...and use the returned object as a filter
+ [% FILTER $MyFilter %]
+ ...
+ [% END %]
+
+See L<Template::Plugin::Filter> for further details.
+
+=head2 Format
+
+The Format plugin provides a simple way to format text according to a
+printf()-like format. See L<Template::Plugin::Format> for further
+details.
+
+ [% USE bold = format('<b>%s</b>') %]
+ [% bold('Hello') %]
+
+=head2 GD::Image, GD::Polygon, GD::Constants
+
+These plugins provide access to the GD graphics library via Lincoln
+D. Stein's GD.pm interface. These plugins allow PNG, JPEG and other
+graphical formats to be generated.
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ # allocate some colors
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+ # And fill it with red
+ im.fill(50,50,red);
+ # Output image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Image> for further details.
+
+=head2 GD::Text, GD::Text::Align, GD::Text::Wrap
+
+These plugins provide access to Martien Verbruggen's GD::Text,
+GD::Text::Align and GD::Text::Wrap modules. These plugins allow the
+layout, alignment and wrapping of text when drawing text in GD images.
+
+ [% FILTER null;
+ USE gd = GD.Image(200,400);
+ USE gdc = GD.Constants;
+ black = gd.colorAllocate(0, 0, 0);
+ green = gd.colorAllocate(0, 255, 0);
+ txt = "This is some long text. " | repeat(10);
+ USE wrapbox = GD.Text.Wrap(gd,
+ line_space => 4,
+ color => green,
+ text => txt,
+ );
+ wrapbox.set_font(gdc.gdMediumBoldFont);
+ wrapbox.set(align => 'center', width => 160);
+ wrapbox.draw(20, 20);
+ gd.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Align>
+and L<Template::Plugin::GD::Text::Wrap> for further details.
+
+=head2 GD::Graph::lines, GD::Graph::bars, GD::Graph::points, GD::Graph::linespoin
+ts, GD::Graph::area, GD::Graph::mixed, GD::Graph::pie
+
+These plugins provide access to Martien Verbruggen's GD::Graph module
+that allows graphs, plots and charts to be created. These plugins allow
+graphs, plots and charts to be generated in PNG, JPEG and other
+graphical formats.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th"],
+ [ 4, 2, 3, 4, 3, 3.5]
+ ];
+ USE my_graph = GD.Graph.pie(250, 200);
+ my_graph.set(
+ title => 'A Pie Chart',
+ label => 'Label',
+ axislabelclr => 'black',
+ pie_height => 36,
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines>,
+L<Template::Plugin::GD::Graph::bars>,
+L<Template::Plugin::GD::Graph::points>,
+L<Template::Plugin::GD::Graph::linespoints>,
+L<Template::Plugin::GD::Graph::area>,
+L<Template::Plugin::GD::Graph::mixed>,
+L<Template::Plugin::GD::Graph::pie>, and
+L<GD::Graph>,
+for more details.
+
+=head2 GD::Graph::bars3d, GD::Graph::lines3d, GD::Graph::pie3d
+
+These plugins provide access to Jeremy Wadsack's GD::Graph3d
+module. This allows 3D bar charts and 3D lines plots to
+be generated.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
+ ];
+ USE my_graph = GD.Graph.bars3d();
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A 3d Bar Chart',
+ y_max_value => 8,
+ y_tick_number => 8,
+ y_label_skip => 2,
+ # shadows
+ bar_spacing => 8,
+ shadow_depth => 4,
+ shadowclr => 'dred',
+ transparent => 0,
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines3d>,
+L<Template::Plugin::GD::Graph::bars3d>, and
+L<Template::Plugin::GD::Graph::pie3d>
+for more details.
+
+=head2 HTML
+
+The HTML plugin is very new and very basic, implementing a few useful
+methods for generating HTML. It is likely to be extended in the future
+or integrated with a larger project to generate HTML elements in a generic
+way (as discussed recently on the mod_perl mailing list).
+
+ [% USE HTML %]
+ [% HTML.escape("if (a < b && c > d) ..." %]
+ [% HTML.attributes(border => 1, cellpadding => 2) %]
+ [% HTML.element(table => { border => 1, cellpadding => 2 }) %]
+
+See L<Template::Plugin::HTML> for further details.
+
+=head2 Iterator
+
+The Iterator plugin provides a way to create a Template::Iterator
+object to iterate over a data set. An iterator is created
+automatically by the FOREACH directive and is aliased to the 'loop'
+variable. This plugin allows an iterator to be explicitly created
+with a given name, or the default plugin name, 'iterator'. See
+L<Template::Plugin::Iterator> for further details.
+
+ [% USE iterator(list, args) %]
+
+ [% FOREACH item = iterator %]
+ [% '<ul>' IF iterator.first %]
+ <li>[% item %]
+ [% '</ul>' IF iterator.last %]
+ [% END %]
+
+=head2 Pod
+
+This plugin provides an interface to the L<Pod::POM|Pod::POM> module
+which parses POD documents into an internal object model which can
+then be traversed and presented through the Template Toolkit.
+
+ [% USE Pod(podfile) %]
+
+ [% FOREACH head1 = Pod.head1;
+ FOREACH head2 = head1/head2;
+ ...
+ END;
+ END
+ %]
+
+=head2 String
+
+The String plugin implements an object-oriented interface for
+manipulating strings. See L<Template::Plugin::String> for further
+details.
+
+ [% USE String 'Hello' %]
+ [% String.append(' World') %]
+
+ [% msg = String.new('Another string') %]
+ [% msg.replace('string', 'text') %]
+
+ The string "[% msg %]" is [% msg.length %] characters long.
+
+=head2 Table
+
+The Table plugin allows you to format a list of data items into a
+virtual table by specifying a fixed number of rows or columns, with
+an optional overlap. See L<Template::Plugin::Table> for further
+details.
+
+ [% USE table(list, rows=10, overlap=1) %]
+
+ [% FOREACH item = table.col(3) %]
+ [% item %]
+ [% END %]
+
+=head2 URL
+
+The URL plugin provides a simple way of contructing URLs from a base
+part and a variable set of parameters. See L<Template::Plugin::URL>
+for further details.
+
+ [% USE mycgi = url('/cgi-bin/bar.pl', debug=1) %]
+
+ [% mycgi %]
+ # ==> /cgi/bin/bar.pl?debug=1
+
+ [% mycgi(mode='submit') %]
+ # ==> /cgi/bin/bar.pl?mode=submit&debug=1
+
+=head2 Wrap
+
+The Wrap plugin uses the Text::Wrap module by David Muir Sharnoff
+E<lt>muir@idiom.comE<gt> (with help from Tim Pierce and many many others)
+to provide simple paragraph formatting. See L<Template::Plugin::Wrap>
+and L<Text::Wrap> for further details.
+
+ [% USE wrap %]
+ [% wrap(mytext, 40, '* ', ' ') %] # use wrap sub
+ [% mytext FILTER wrap(40) -%] # or wrap FILTER
+
+The Text::Wrap module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 XML::DOM
+
+The XML::DOM plugin gives access to the XML Document Object Module via
+Clark Cooper E<lt>cooper@sch.ge.comE<gt> and Enno Derksen's
+E<lt>enno@att.comE<gt> XML::DOM module. See L<Template::Plugin::XML::DOM>
+and L<XML::DOM> for further details.
+
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse(filename) %]
+
+ [% FOREACH node = doc.getElementsByTagName('CODEBASE') %]
+ * [% node.getAttribute('href') %]
+ [% END %]
+
+The plugin requires the XML::DOM module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::RSS
+
+The XML::RSS plugin is a simple interface to Jonathan Eisenzopf's
+E<lt>eisen@pobox.comE<gt> XML::RSS module. A RSS (Rich Site Summary)
+file is typically used to store short news 'headlines' describing
+different links within a site. This plugin allows you to parse RSS
+files and format the contents accordingly using templates.
+See L<Template::Plugin::XML::RSS> and L<XML::RSS> for further details.
+
+ [% USE news = XML.RSS(filename) %]
+
+ [% FOREACH item = news.items %]
+ <a href="[% item.link %]">[% item.title %]</a>
+ [% END %]
+
+The XML::RSS module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::Simple
+
+This plugin implements an interface to the L<XML::Simple|XML::Simple>
+module.
+
+ [% USE xml = XML.Simple(xml_file_or_text) %]
+
+ [% xml.head.title %]
+
+See L<Template::Plugin::XML::Simple> for further details.
+
+=head2 XML::Style
+
+This plugin defines a filter for performing simple stylesheet based
+transformations of XML text.
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ cellspacing = 1
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle %]
+ <table>
+ <tr>
+ <td>Foo</td> <td>Bar</td> <td>Baz</td>
+ </tr>
+ </table>
+ [% END %]
+
+See L<Template::Plugin::XML::Style> for further details.
+
+=head2 XML::XPath
+
+The XML::XPath plugin provides an interface to Matt Sergeant's
+E<lt>matt@sergeant.orgE<gt> XML::XPath module. See
+L<Template::Plugin::XML::XPath> and L<XML::XPath> for further details.
+
+ [% USE xpath = XML.XPath(xmlfile) %]
+ [% FOREACH page = xpath.findnodes('/html/body/page') %]
+ [% page.getAttribute('title') %]
+ [% END %]
+
+The plugin requires the XML::XPath module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Refs.pod b/lib/Template/Manual/Refs.pod
new file mode 100644
index 0000000..b0c9719
--- /dev/null
+++ b/lib/Template/Manual/Refs.pod
@@ -0,0 +1,171 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Refs
+#
+# DESCRIPTION
+# This section provides references to external modules, projects and
+# other resources related to the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Refs - Related modules, projects and other resources
+
+=head1 DESCRIPTION
+
+This section provides references to external modules, projects and
+other resources related to the Template Toolkit.
+
+=head2 Resources
+
+The Template Toolkit web site contains the latest information, news and
+other resources.
+
+ http://www.template-toolkit.org/
+
+A mailing list exists for up-to-date information on the Template Toolkit
+and for following and contributing to the development process. To
+subscribe, send an email to
+
+ templates-request@template-toolkit.org
+
+with the message 'subscribe' in the body. You can also use the web
+interface to subscribe or browse the archives:
+
+ http://www.template-toolkit.org/mailman/listinfo/templates
+
+The F<tpage> and F<ttree> scripts are distributed and installed along
+with the Template Toolkit. The F<tpage> script simply processes named
+files or STDIN if unspecified, using a default Template object. The
+F<ttree> script can be used to process entire directory trees of templates,
+allowing large content systems such as web sites to be rebuilt from a
+single command or configuration file.
+
+ perldoc tpage
+ perldoc ttree
+
+The F<Template::Tutorial> document provides an introduction to the Template
+Toolkit and shows some typical examples of usage.
+
+ perldoc Template::Tutorial
+
+You may also like to consult the paper 'Building and Managing Web Systems
+with the Template Toolkit' and accompanying slides from the presentation
+at the 4th Perl Conference. These are available from the Template
+Toolkit web site:
+
+ http://www.template-toolkit.org/docs.html
+
+
+
+=head2 Projects
+
+There are a number of other projects related to the Template Toolkit.
+
+=over 4
+
+=item OpenInteract
+
+OpenInteract is a robust web application framework built to run under
+Apache and mod_perl using the Template Toolkit as a foundation.
+
+ http://www.openinteract.org/
+
+=item Apache::Template
+
+This is an Apache/mod_perl interface to the Template Toolkit. Available
+from CPAN in the directory:
+
+ http://www.cpan.org/modules/by-module/Apache/
+
+=item AxKit::Template
+
+AxKit is Matt Sergeant's Apache XML Delivery Toolkit. AxKit::Template
+provides an interface between AxKit and the Template Toolkit. Available
+from CPAN in the directory:
+
+ http://www.cpan.org/modules/by-module/Apache/
+
+=item Slashcode
+
+Slashcode is the code which runs Slashdot. Version 2 uses the
+Template Toolkit for generating the user interface from database
+driven template.
+
+ http://slashcode.org/
+
+=item OpenFrame
+
+OpenFrame is an open source application framework for distributed
+media applications. It ships with a generator for the Template
+Toolkit.
+
+ http://openframe.fotango.com/
+
+=item PCMT
+
+PCMT is the Personal Content Management Toolkit. It uses the Template
+Toolkit as the presentation engine.
+
+ http://pcmt.sf.net/
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Syntax.pod b/lib/Template/Manual/Syntax.pod
new file mode 100644
index 0000000..cc1b6c8
--- /dev/null
+++ b/lib/Template/Manual/Syntax.pod
@@ -0,0 +1,306 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Syntax
+#
+# DESCRIPTION
+# This section describes the syntax, structure and semantics of the
+# Template Toolkit directives and general presentation language.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Syntax - Directive syntax, structure and semantics
+
+=head1 DESCRIPTION
+
+This section describes the syntax, structure and semantics of the
+Template Toolkit directives and general presentation language.
+
+=head2 Tag Styles
+
+By default, template directives are embedded within the character sequences
+'[%' and '%]'. e.g.
+
+ [% PROCESS header %]
+
+ <h1>Hello World!</h1>
+ <a href="[% page.next %]"><img src="[% icon.next %].gif"></a>
+
+ [% PROCESS footer %]
+
+You can change the tag characters using the START_TAG, END_TAG and
+TAG_STYLE configuration options. You can also use the TAGS directive
+to define a new tag style for the current template file.
+
+You can also set the INTERPOLATE option to allow simple variable
+references to be embedded directly in templates, prefixed by a '$'.
+
+ # INTERPOLATE => 0
+ <td>[% name %]</td> <td>[% email %]</td>
+
+ # INTERPOLATE => 1
+ <td>$name</td> <td>$email</td>
+
+Directives may be embedded anywhere in a line of text and can be split
+across several lines. Insignificant whitespace is generally ignored
+within the directive.
+
+ [% INCLUDE header
+ title = 'Hello World'
+ bgcol = '#ffffff'
+ %]
+
+ [%INCLUDE menu align='right'%]
+
+ Name: [% name %] ([%id%])
+
+=head2 Comments
+
+The '#' character is used to indicate comments within a directive.
+When placed immediately inside the opening directive tag, it causes
+the entire directive to be ignored.
+
+ [%# this entire directive is ignored no
+ matter how many lines it wraps onto
+ %]
+
+In any other position, it causes the remainder of the current line to
+be treated as a comment.
+
+ [% # this is a comment
+ theta = 20 # so is this
+ rho = 30 # <aol>me too!</aol>
+ %]
+
+=head2 Chomping Whitespace
+
+You can add '-' or '+' to the immediate start or end of a directive
+tag to control the whitespace chomping options. See the PRE_CHOMP and
+POST_CHOMP options for further details.
+
+ [% BLOCK foo -%] # remove trailing newline
+ This is block foo
+ [%- END %] # remove leading newline
+
+=head2 Implicit Directives: GET and SET
+
+The simplest directives are GET and SET which retrieve and update
+variable values respectively. The GET and SET keywords are actually
+optional as the parser is smart enough to see them for what they
+really are (but note the caveat below on using side-effect notation).
+Thus, you'll generally see:
+
+ [% SET foo = 10 %]
+ [% GET foo %]
+
+written as:
+
+ [% foo = 10 %]
+ [% foo %]
+
+You can also express simple logical statements as implicit GET directives:
+
+ [% title or template.title or 'Default Title' %]
+
+ [% mode == 'graphics' ? "Graphics Mode Enabled" : "Text Mode" %]
+
+All other directives should start with a keyword specified in UPPER
+CASE (but see the ANYCASE option). All directives keywords are in
+UPPER CASE to make them visually distinctive and to distinguish them
+from variables of the same name but different case. It is perfectly
+valid, for example, to define a variable called 'stop' which is
+entirely separate from the STOP directive.
+
+ [% stop = 'Clackett Lane Bus Depot' %]
+
+ The bus will next stop at [% stop %] # variable
+
+ [% STOP %] # directive
+
+=head2 Block Directives
+
+Directives such as FOREACH, WHILE, BLOCK, FILTER, etc., mark the start
+of a block which may contain text or other directives up to the
+matching END directive. Blocks may be nested indefinitely. The
+IF, UNLESS, ELSIF and ELSE directives also define blocks and may be
+grouped together in the usual manner.
+
+ [% FOREACH item = [ 'foo' 'bar' 'baz' ] %]
+ * Item: [% item %]
+ [% END %]
+
+ [% BLOCK footer %]
+ Copyright 2000 [% me %]
+ [% INCLUDE company/logo %]
+ [% END %]
+
+ [% IF foo %]
+ [% FOREACH thing = foo.things %]
+ [% thing %]
+ [% END %]
+ [% ELSIF bar %]
+ [% INCLUDE barinfo %]
+ [% ELSE %]
+ do nothing...
+ [% END %]
+
+Block directives can also be used in a convenient side-effect notation.
+
+ [% INCLUDE userinfo FOREACH user = userlist %]
+
+ [% INCLUDE debugtxt msg="file: $error.info"
+ IF debugging %]
+
+ [% "Danger Will Robinson" IF atrisk %]
+
+versus:
+
+ [% FOREACH user = userlist %]
+ [% INCLUDE userinfo %]
+ [% END %]
+
+ [% IF debugging %]
+ [% INCLUDE debugtxt msg="file: $error.info" %]
+ [% END %]
+
+ [% IF atrisk %]
+ Danger Will Robinson
+ [% END %]
+
+=head2 Capturing Block Output
+
+The output of a directive can be captured by simply assigning the directive
+to a variable.
+
+ [% headtext = PROCESS header title="Hello World" %]
+
+ [% people = PROCESS userinfo FOREACH user = userlist %]
+
+This can be used in conjunction with the BLOCK directive for defining large
+blocks of text or other content.
+
+ [% poem = BLOCK %]
+ The boy stood on the burning deck,
+ His fleece was white as snow.
+ A rolling stone gathers no moss,
+ And Keith is sure to follow.
+ [% END %]
+
+Note one important caveat of using this syntax in conjunction with side-effect
+notation. The following directive does not behave as might be expected:
+
+ [% var = 'value' IF some_condition %]
+
+In this case, the directive is interpreted as (spacing added for clarity)
+
+ [% var = IF some_condition %]
+ value
+ [% END %]
+
+rather than
+
+ [% IF some_condition %]
+ [% var = 'value' %]
+ [% END %]
+
+The variable is assigned the output of the IF block which returns
+'value' if true, but nothing if false. In other words, the following
+directive will always cause 'var' to be cleared.
+
+ [% var = 'value' IF 0 %]
+
+To achieve the expected behaviour, the directive should be written as:
+
+ [% SET var = 'value' IF some_condition %]
+
+=head2 Chaining Filters
+
+Multiple FILTER directives can be chained together in sequence. They
+are called in the order defined, piping the output of one into the
+input of the next.
+
+ [% PROCESS somefile FILTER truncate(100) FILTER html %]
+
+The pipe character, '|', can also be used as an alias for FILTER.
+
+ [% PROCESS somefile | truncate(100) | html %]
+
+=head2 Multiple Directive Blocks
+
+Multiple directives can be included within a single tag when delimited
+by semi-colons, ';'. Note however that the TAGS directive must always
+be specified in a tag by itself.
+
+ [% IF title;
+ INCLUDE header;
+ ELSE;
+ INCLUDE other/header title="Some Other Title";
+ END
+ %]
+
+versus
+
+ [% IF title %]
+ [% INCLUDE header %]
+ [% ELSE %]
+ [% INCLUDE other/header title="Some Other Title" %]
+ [% END %]
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/VMethods.pod b/lib/Template/Manual/VMethods.pod
new file mode 100644
index 0000000..7e380fa
--- /dev/null
+++ b/lib/Template/Manual/VMethods.pod
@@ -0,0 +1,529 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::VMethods
+#
+# DESCRIPTION
+# The Template Toolkit provides virtual methods for manipulating
+# variable values. Most of them are analogous to regular Perl
+# functions of the same names. This section describes the different
+# virtual methods that can be applied to scalar, list and hash
+# values.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::VMethods - Virtual Methods
+
+=head1 DESCRIPTION
+
+The Template Toolkit provides virtual methods for manipulating variable
+values. Most of them are analogous to regular Perl functions of the
+same names. This section describes the different virtual methods that
+can be applied to scalar, list and hash values.
+
+=head2 Scalar Virtual Methods
+
+=over 4
+
+=item defined
+
+Returns true if the value is defined.
+
+ [% user = get_user(uid) IF uid.defined %]
+
+=item length
+
+Returns the length of the string representation of the item:
+
+ [% IF password.length < 8 %]
+ Password too short, dumbass!
+ [% END %]
+
+=item repeat(n)
+
+Repeat the string a specified number of times.
+
+ [% name = 'foo' %]
+ [% name.repeat(3) %] # foofoofoo
+
+=item replace(search, replace)
+
+Outputs the string with all instances of the first argument (specified
+as a Perl regular expression) with the second.
+
+ [% name = 'foo, bar & baz' %]
+ [% name.replace('\W+', '_') %] # foo_bar_baz
+
+=item match(pattern)
+
+Performs a regular expression match on the string using the pattern
+passed as an argument. If the pattern matches the string then the
+method returns a reference to a list of any strings captured within
+parenthesis in the pattern.
+
+ [% name = 'Larry Wall' %]
+ [% matches = name.match('(\w+) (\w+)') %]
+ [% matches.1 %], [% matches.0 %] # Wall, Larry
+
+If the pattern does not match then the method returns false, rather
+than returning an empty list which Perl and the Template Toolkit both
+consider to be a true value. This allows you to write expression like
+this.
+
+ [% "We're not worthy!" IF name.match('Larry Wall') %]
+
+ [% IF (matches = name.match('(\w+) (\w+)')) %]
+ pattern matches: [% matches.join(', ') %]
+ [% ELSE %]
+ pattern does not match
+ [% END %]
+
+Any regex modifiers, like C</s>, should be added in the regex using
+the C<(?s)> syntax. For example, to modify the regex to disregard
+whitespace (the C</x> switch), use:
+
+ [% re = '(?x)
+ (\w+)
+ [ ]
+ (\w+)
+ ';
+ matches = name.match(re);
+ %]
+
+=item search(pattern)
+
+Performs a similar function to 'match' but simply returns true if the
+string matches the regular expression pattern passed as an argument.
+
+ [% name = 'foo bar baz' %]
+ [% name.search('bar') ? 'bar' : 'no bar' %] # bar
+
+This virtual method is now deprecated in favour of 'match'. Move along
+now, there's nothing more to see here.
+
+=item split(pattern)
+
+Calls Perl's split() function to split a string into a list of
+strings.
+
+ [% FOREACH dir = mypath.split(':') %]
+ [% dir %]
+ [% END %]
+
+=item chunk(size)
+
+Splits the value into a list of chunks of a certain size.
+
+ [% ccard_no = "1234567824683579";
+ ccard_no.chunk(4).join
+ %]
+
+Output:
+
+ 1234 5678 2468 3579
+
+If the size is specified as a negative number then the text will
+be chunked from right-to-left. This gives the correct grouping
+for numbers, for example.
+
+ [% number = 1234567;
+ number.chunk(-3).join(',')
+ %]
+
+Output:
+
+ 1,234,567
+
+=item list
+
+Return the value as a single element list. This can be useful if you
+have a variable which may contain a single item or a list and you want
+to treat them equally. The 'list' method can be called against a list
+reference and will simply return the original reference, effectively
+a no-op.
+
+ [% thing.list.size %] # thing can be a scalar or a list
+
+=item hash
+
+Return the value as a hash reference containing a single entry with
+the key 'value' indicating the original scalar value. As with the
+'list' virtual method, this is generally used to help massage data
+into different formats.
+
+=item size
+
+Always returns 1 for scalar values. This method is provided for
+consistency with the hash and list size methods.
+
+=back
+
+
+=head2 Hash Virtual Methods
+
+=over 4
+
+=item keys, values, each
+
+The regular hash operators returning lists of keys, values or both.
+Note how we use a '$' prefix on the 'key' variable in this example to
+have it interpolated (i.e. replaced with its value) before use.
+
+ [% FOREACH key = product.keys %]
+ [% key %] => [% product.$key %]
+ [% END %]
+
+=item sort, nsort
+
+Return a list of the keys, sorted alphabetically (sort) or numerically
+(nsort) according to the corresponding values in the hash.
+
+ [% FOREACH n = phones.sort %]
+ [% phones.$n %] is [% n %],
+ [% END %]
+
+=item import
+
+The import method can be called on a hash array to import the contents
+of another hash array.
+
+ [% hash1 = {
+ foo => 'Foo',
+ bar => 'Bar',
+ }
+ hash2 = {
+ wiz => 'Wiz',
+ woz => 'Woz',
+ }
+ %]
+
+ [% hash1.import(hash2) %]
+ [% hash1.wiz %] # Wiz
+
+You can also call the import() method by itself to import a hash array
+into the current namespace hash.
+
+ [% user = { id => 'lwall', name => 'Larry Wall' } %]
+ [% import(user) %]
+ [% id %]: [% name %] # lwall: Larry Wall
+
+=item defined, exists
+
+Returns a true or false value if an item in the hash denoted by the key
+passed as an argument is defined or exists, respectively.
+
+ [% hash.defined('somekey') ? 'yes' : 'no' %]
+ [% hash.exists('somekey') ? 'yes' : 'no' %]
+
+=item size
+
+Returns the number of key =E<gt> value pairs in the hash.
+
+=item item
+
+Returns an item from the hash using a key passed as an argument.
+
+ [% hash.item('foo') %] # same as hash.foo
+
+=item list
+
+Returns the contents of the hash in list form. An argument can be
+passed to indicate the desired items required in the list: 'keys' to
+return a list of the keys (same as hash.keys), 'values' to return a
+list of the values (same as hash.values), or 'each' to return as list
+of (key, value) pairs (same as hash.each). When called without an
+argument it returns a list of hash references, each of which contains
+a 'key' and 'value' item representing a single key =E<gt> value pair
+in the hash.
+
+=back
+
+
+=head2 List Virtual Methods
+
+=over 4
+
+=item first, last
+
+Returns the first/last item in the list. The item is not removed from the
+list.
+
+ [% results.first %] to [% results.last %]
+
+If either is given a numeric argument C<n>, they return the first or
+last C<n> elements:
+
+ The first 5 results are [% results.first(5).join(", ") %].
+
+=item size, max
+
+Returns the size of a list (number of elements) and the maximum
+index number (size - 1), respectively.
+
+ [% results.size %] search results matched your query
+
+=item reverse
+
+Returns the items of the list in reverse order.
+
+ [% FOREACH s = scores.reverse %]
+ ...
+ [% END %]
+
+=item join
+
+Joins the items in the list into a single string, using Perl's join
+function.
+
+ [% items.join(', ') %]
+
+=item grep
+
+Returns a list of the items in the list that match a regular expression
+pattern.
+
+ [% FOREACH directory.files.grep('\.txt$') %]
+ ...
+ [% END %]
+
+=item sort, nsort
+
+Returns the items in alpha (sort) or numerical (nsort) order.
+
+ [% library = books.sort %]
+
+An argument can be provided to specify a search key. Where an item in
+the list is a hash reference, the search key will be used to retrieve a
+value from the hash which will then be used as the comparison value.
+Where an item is an object which implements a method of that name, the
+method will be called to return a comparison value.
+
+ [% library = books.sort('author') %]
+
+In the example, the 'books' list can contains hash references with
+an 'author' key or objects with an 'author' method.
+
+=item unshift(item), push(item)
+
+Adds an item to the start/end of a list.
+
+ [% mylist.unshift('prev item') %]
+ [% mylist.push('next item') %]
+
+=item shift, pop
+
+Removes the first/last item from the list and returns it.
+
+ [% first = mylist.shift %]
+ [% last = mylist.pop %]
+
+=item unique
+
+Returns a list of the unique elements in a list, in the same order
+as in the list itself.
+
+ [% mylist = [ 1, 2, 3, 2, 3, 4, 1, 4, 3, 4, 5 ] %]
+ [% numbers = mylist.unique %]
+
+While this can be explicitly sorted, it is not required that the list
+be sorted before the unique elements are pulled out (unlike the Unix
+command line utility).
+
+ [% numbers = mylist.unique.sort %]
+
+=item merge
+
+Returns a list composed of zero or more other lists:
+
+ [% list_one = [ 1 2 3 ];
+ list_two = [ 4 5 6 ];
+ list_three = [ 7 8 9 ];
+ list_four = list_one.merge(list_two, list_three);
+ %]
+
+The original lists are not modified.
+
+=item slice(from, to)
+
+Returns a slice of items in the list between the bounds passed as
+arguments. If the second argument, 'to', isn't specified, then it
+defaults to the last item in the list. The original list is not
+modified.
+
+ [% first_three = list.slice(0,2) %]
+
+ [% last_three = list.slice(-3, -1) %]
+
+=item splice(offset, length, list)
+
+Behaves just like Perl's splice() function allowing you to selectively
+remove and/or replace elements in a list. It removes 'length' items
+from the list, starting at 'offset' and replaces them with the items
+in 'list'.
+
+ [% play_game = [ 'play', 'scrabble' ];
+ ping_pong = [ 'ping', 'pong' ];
+ redundant = play_game.splice(1, 1, ping_pong);
+
+ redundant.join; # scrabble
+ play_game.join; # play ping pong
+ %]
+
+The method returns a list of the items removed by the splice.
+You can use the CALL directive to ignore the output if you're
+not planning to do anything with it.
+
+ [% CALL play_game.splice(1, 1, ping_pong) %]
+
+As well as providing a reference to a list of replacement values,
+you can pass in a list of items.
+
+ [% CALL list.splice(-1, 0, 'foo', 'bar') %]
+
+Be careful about passing just one item in as a replacement value.
+If it is a reference to a list then the contents of the list will
+be used. If it's not a list, then it will be treated as a single
+value. You can use square brackets around a single item if you
+need to be explicit:
+
+ [% # push a single item, an_item
+ CALL list.splice(-1, 0, an_item);
+
+ # push the items from another_list
+ CALL list.splice(-1, 0, another_list);
+
+ # push a reference to another_list
+ CALL list.splice(-1, 0, [ another_list ]);
+ %]
+
+=back
+
+=head2 Automagic Promotion of Scalar to List for Virtual Methods
+
+In addition to the scalar virtual methods listed in the previous
+section, you can also call any list virtual method against a scalar.
+The item will be automagically promoted to a single element list and
+the appropriate list virtual method will be called.
+
+One particular benefit of this comes when calling subroutines or
+object methods that return a list of items, rather than the
+preferred reference to a list of items. In this case, the
+Template Toolkit automatically folds the items returned into
+a list.
+
+The upshot is that you can continue to use existing Perl modules or
+code that returns lists of items, without having to refactor it
+just to keep the Template Toolkit happy (by returning references
+to list). Class::DBI module is just one example of a particularly
+useful module which returns values this way.
+
+If only a single item is returned from a subroutine then the
+Template Toolkit assumes it meant to return a single item (rather
+than a list of 1 item) and leaves it well alone, returning the
+single value as it is. If you're executing a database query,
+for example, you might get 1 item returned, or perhaps many
+items which are then folded into a list.
+
+The FOREACH directive will happily accept either a list or a single
+item which it will treat as a list. So it's safe to write directives
+like this, where we assume that 'something' is bound to a subroutine
+which might return 1 or more items:
+
+ [% FOREACH item = something %]
+ ...
+ [% END %]
+
+The automagic promotion of scalars to single item lists means
+that you can also use list virtual methods safely, even if you
+only get one item returned. For example:
+
+ [% something.first %]
+ [% something.join %]
+ [% something.reverse.join(', ') %]
+
+Note that this is very much a last-ditch behaviour. If the single
+item return is an object with a 'first' method, for example, then that
+will be called, as expected, in preference to the list virtual method.
+
+=head2 Defining Custom Virtual Methods
+
+You can define your own virtual methods for scalars, lists and hash
+arrays. The Template::Stash package variables $SCALAR_OPS, $LIST_OPS
+and $HASH_OPS are references to hash arrays that define these virtual
+methods. HASH_OPS and LIST_OPS methods are subroutines that accept a
+hash/list reference as the first item. SCALAR_OPS are subroutines
+that accept a scalar value as the first item. Any other arguments
+specified when the method is called will be passed to the subroutine.
+
+ # load Template::Stash to make method tables visible
+ use Template::Stash;
+
+ # define list method to return new list of odd numbers only
+ $Template::Stash::LIST_OPS->{ odd } = sub {
+ my $list = shift;
+ return [ grep { $_ % 2 } @$list ];
+ };
+
+template:
+
+ [% primes = [ 2, 3, 5, 7, 9 ] %]
+ [% primes.odd.join(', ') %] # 3, 5, 7, 9
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Variables.pod b/lib/Template/Manual/Variables.pod
new file mode 100644
index 0000000..e8d998c
--- /dev/null
+++ b/lib/Template/Manual/Variables.pod
@@ -0,0 +1,868 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Variables
+#
+# DESCRIPTION
+# This section describes the different ways in which Perl data can be
+# bound to template variables and accessed via Template Toolkit
+# directives.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Variables - Template variables and code bindings
+
+=head1 DESCRIPTION
+
+This section describes the different ways in which Perl data can be
+bound to template variables and accessed via Template Toolkit
+directives.
+
+=head2 Template Variables
+
+A reference to a hash array may be passed as the second argument to
+the process() method, containing definitions of template variables.
+The VARIABLES (a.k.a. PRE_DEFINE) option can also be used to pre-define
+variables for all templates processed by the object.
+
+ my $tt = Template->new({
+ VARIABLES => {
+ version => 3.14,
+ release => 'Sahara',
+ },
+ });
+
+ my $vars = {
+ serial_no => 271828,
+ };
+
+ $tt->process('myfile', $vars);
+
+'myfile':
+
+ This is version [% version %] ([% release %]).
+ Serial number: [% serial_no %]
+
+output:
+
+ This is version 3.14 (Sahara)
+ Serial number: 271828
+
+Variable names may contain any alphanumeric characters or underscores.
+They may be lower, upper or mixed case although the usual convention
+is to use lower case. The case I<is> significant however, and 'foo',
+'Foo' and 'FOO' are all different variables. Upper case variable
+names are permitted, but not recommended due to a possible conflict
+with an existing or future reserved word. As of version 2.00, these
+are:
+
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER
+ IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE
+ USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META
+ TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP
+ CLEAR TO STEP AND OR NOT MOD DIV END
+
+
+The variable values may be of virtually any Perl type, including
+simple scalars, references to lists, hash arrays, subroutines or
+objects. The Template Toolkit will automatically apply the correct
+procedure to accessing these values as they are used in the template.
+
+Example:
+
+ my $vars = {
+ article => 'The Third Shoe',
+ person => {
+ id => 314,
+ name => 'Mr. Blue',
+ email => 'blue@nowhere.org',
+ },
+ primes => [ 2, 3, 5, 7, 11, 13 ],
+ wizard => sub { return join(' ', 'Abracadabra!', @_) },
+ cgi => CGI->new('mode=submit&debug=1'),
+ };
+
+template:
+
+ [% article %]
+
+ [% person.id %]: [% person.name %] <[% person.email %]>
+
+ [% primes.first %] - [% primes.last %], including [% primes.3 %]
+ [% primes.size %] prime numbers: [% primes.join(', ') %]
+
+ [% wizard %]
+ [% wizard('Hocus Pocus!') %]
+
+ [% cgi.param('mode') %]
+
+output:
+
+ The Third Shoe
+
+ 314: Mr. Blue <blue@nowhere.org>
+
+ 2 - 13, including 7
+ 6 prime numbers: 2, 3, 5, 7, 11, 13
+
+ Abracadabra!
+ Abracadabra! Hocus Pocus!
+
+ submit
+
+=head2 Scalar Values
+
+Regular scalar variables are accessed by simply specifying their name.
+As these are just entries in the top-level variable hash they can be
+considered special cases of hash array referencing as described below,
+with the main namespace hash automatically implied.
+
+ [% article %]
+
+=head2 Hash Array References
+
+Members of hash arrays are accessed by specifying the hash reference
+and key separated by the dot '.' operator.
+
+ my $vars = {
+ 'home' => 'http://www.myserver.com/homepage.html',
+ 'page' => {
+ 'this' => 'mypage.html',
+ 'next' => 'nextpage.html',
+ 'prev' => 'prevpage.html',
+ },
+ };
+
+template:
+
+ <a href="[% home %]">Home</a>
+ <a href="[% page.prev %]">Previous Page</a>
+ <a href="[% page.next %]">Next Page</a>
+
+output:
+
+ <a href="http://www.myserver.com/homepage.html">Home</a>
+ <a href="prevpage.html">Previous Page</a>
+ <a href="nextpage.html">Next Page</a>
+
+Any key in a hash which starts with a '_' or '.' character will be
+considered private and cannot be evaluated or updated from within a
+template. The undefined value will be returned for any such variable
+accessed which the Template Toolkit will silently ignore (unless the
+DEBUG option is enabled).
+
+ my $vars = {
+ message => 'Hello World!',
+ _secret => "On the Internet, no-one knows you're a dog",
+ thing => {
+ public => 123,
+ _private => 456,
+ '.hidden' => 789,
+ },
+ };
+
+template:
+
+ [% message %] # outputs "Hello World!"
+ [% _secret %] # no output
+ [% thing.public %] # outputs "123"
+ [% thing._private %] # no output
+ [% thing..hidden %] # ERROR: unexpected token (..)
+
+To access a hash entry using a key stored in another variable, prefix
+the key variable with '$' to have it interpolated before use (see
+L<Variable Interpolation>).
+
+ [% pagename = 'next' %]
+ [% page.$pagename %] # same as [% page.next %]
+
+When you assign to a variable that contains multiple namespace
+elements (i.e. it has one or more '.' characters in the name),
+any hashes required to represent intermediate namespaces will be
+created automatically. In this following example, the 'product'
+variable automatically springs into life as a hash array unless
+otherwise defined.
+
+ [% product.id = 'XYZ-2000'
+ product.desc = 'Bogon Generator'
+ product.price = 666
+ %]
+
+ The [% product.id %] [% product.desc %]
+ costs $[% product.price %].00
+
+output:
+
+ The XYZ-2000 Bogon Generator
+ costs $666.00
+
+You can use Perl's familiar '{' ... '}' construct to explicitly create
+a hash and assign it to a variable. Note that commas are optional
+between key/value pairs and '=' can be used in place of '=E<gt>'.
+
+ [% product = {
+ id => 'XYZ-2000',
+ desc => 'Bogon Generator',
+ price => 666,
+ }
+ %]
+
+=head2 List References
+
+Items in lists are also accessed by use of the dot operator.
+
+ my $vars = {
+ 'people' => [ 'Tom', 'Dick', 'Larry' ],
+ };
+
+template:
+
+ [% people.0 %] # Tom
+ [% people.1 %] # Dick
+ [% people.2 %] # Larry
+
+The FOREACH directive can be used to iterate through items in a list.
+
+ [% FOREACH person = people %]
+ Hello [% person %]
+ [% END %]
+
+output:
+
+ Hello Tom
+ Hello Dick
+ Hello Larry
+
+Lists can be constructed in-situ using the regular anonymous list
+'[' ... ']' construct. Commas between items are optional.
+
+ [% cols = [ 'red', 'green', 'blue' ] %]
+
+ [% FOREACH c = cols %]
+ ...
+
+or:
+
+ [% FOREACH c = [ 'red', 'green', 'blue' ] %]
+ ...
+
+You can also create simple numerical sequences using the familiar '..'
+operator:
+
+ [% n = [ 1 .. 4 ] %] # n is [ 1, 2, 3, 4 ]
+
+ [% x = 4
+ y = 8
+ z = [x..y] # z is [ 4, 5, 6, 7, 8 ]
+ %]
+
+=head2 Subroutines
+
+Template variables can contain references to Perl subroutines. When
+the variable is used, the Template Toolkit will automatically call the
+subroutine, passing any additional arguments specified. The return
+value from the subroutine is used as the variable value and inserted
+into the document output.
+
+ my $vars = {
+ wizard => sub { return join(' ', 'Abracadabra!', @_) },
+ };
+
+template:
+
+ [% wizard %] # Abracadabra!
+ [% wizard('Hocus Pocus!') %] # Abracadabra! Hocus Pocus!
+
+
+=head2 Objects
+
+Template variables can also contain references to Perl objects.
+Methods are called using the dot operator to specify the method
+against the object variable. Additional arguments can be specified
+as with subroutines.
+
+ use CGI;
+
+ ...
+
+ my $vars = {
+ # hard coded CGI params for purpose of example
+ cgi => CGI->new('mode=submit&debug=1'),
+ };
+
+template:
+
+ [% FOREACH p = cgi.param %] # returns list of param keys
+ [% p %] => [% cgi.param(p) %] # fetch each param value
+ [% END %]
+
+output:
+
+ mode => submit
+ debug => 1
+
+Object methods can also be called as lvalues. That is, they can appear on
+the left side of an assignment. The method will be called passing the
+assigning value as an argument.
+
+ [% myobj.method = 10 %]
+
+equivalent to:
+
+ [% myobj.method(10) %]
+
+=head2 Parameters and Return Values
+
+Subroutines and methods will be passed any arguments specified in the
+template. Any template variables in the argument list will first be
+evaluated and their resultant values passed to the code.
+
+ my $vars = {
+ mycode => sub { return 'received ' . join(', ', @_) },
+ };
+
+template:
+
+ [% foo = 10 %]
+ [% mycode(foo, 20) %] # received 10, 20
+
+Named parameters may also be specified. These are automatically collected
+into a single hash array which is passed by reference as the B<last>
+parameter to the sub-routine. Named parameters can be specified using
+either '=E<gt>' or '=' and can appear anywhere in the argument list.
+
+ my $vars = {
+ myjoin => \&myjoin,
+ };
+
+ sub myjoin {
+ # look for hash ref as last argument
+ my $params = ref $_[-1] eq 'HASH' ? pop : { };
+ return join($params->{ joint } || ' + ', @_);
+ }
+
+template:
+
+ [% myjoin(10, 20, 30) %]
+ [% myjoin(10, 20, 30, joint = ' - ' %]
+ [% myjoin(joint => ' * ', 10, 20, 30 %]
+
+output:
+
+ 10 + 20 + 30
+ 10 - 20 - 30
+ 10 * 20 * 30
+
+Parenthesised parameters may be added to any element of a variable,
+not just those that are bound to code or object methods. At present,
+parameters will be ignored if the variable isn't "callable" but are
+supported for future extensions. Think of them as "hints" to that
+variable, rather than just arguments passed to a function.
+
+ [% r = 'Romeo' %]
+ [% r(100, 99, s, t, v) %] # outputs "Romeo"
+
+User code should return a value for the variable it represents. This
+can be any of the Perl data types described above: a scalar, or
+reference to a list, hash, subroutine or object. Where code returns a
+list of multiple values the items will automatically be folded into a
+list reference which can be accessed as per normal.
+
+ my $vars = {
+ # either is OK, first is recommended
+ items1 => sub { return [ 'foo', 'bar', 'baz' ] },
+ items2 => sub { return ( 'foo', 'bar', 'baz' ) },
+ };
+
+template:
+
+ [% FOREACH i = items1 %]
+ ...
+ [% END %]
+
+ [% FOREACH i = items2 %]
+ ...
+ [% END %]
+
+=head2 Error Handling
+
+Errors can be reported from user code by calling die(). Errors raised
+in this way are caught by the Template Toolkit and converted to
+structured exceptions which can be handled from within the template.
+A reference to the exception object is then available as the 'error'
+variable.
+
+ my $vars = {
+ barf => sub {
+ die "a sick error has occurred\n";
+ },
+ };
+
+template:
+
+ [% TRY %]
+ [% barf %] # calls sub which throws error via die()
+ [% CATCH %]
+ [% error.info %] # outputs "a sick error has occurred\n"
+ [% END %]
+
+Error messages thrown via die() are converted to exceptions of type
+'undef'. Exceptions of user-defined types can be thrown by calling
+die() with a reference to a Template::Exception object.
+
+ use Template::Exception;
+
+ ...
+
+ my $vars = {
+ login => sub {
+ ...
+ die Template::Exception->new('badpwd',
+ 'password too silly');
+ },
+ };
+
+template:
+
+ [% TRY %]
+ [% login %]
+ [% CATCH badpwd %]
+ Bad password: [% error.info %]
+ [% CATCH %]
+ Some other '[% error.type %]' error: [% error.info %]
+ [% END %]
+
+The exception types 'stop' and 'return' are used to implement the
+STOP and RETURN directives. Throwing an exception as:
+
+ die (Template::Exception->new('stop'));
+
+has the same effect as the directive:
+
+ [% STOP %]
+
+Subroutines and methods can also raise errors by returning a list or
+reference to a list containing the undefined value (undef) followed by
+an exception object or error message. This is supported for backwards
+compatibility with version 1 but may be deprecated in some future
+version.
+
+ my $vars = {
+ # currently equivalent
+ barf => sub {
+ die "I'm sorry Dave, I can't do that";
+ },
+ yack => sub {
+ return (undef, "I'm sorry Dave, I can't do that");
+ },
+ };
+
+=head2 Virtual Methods
+
+The Template Toolkit implements a number of "virtual methods" which
+can be applied to scalars, hashes or lists. For example:
+
+ [% mylist = [ 'foo', 'bar', 'baz' ] %]
+ [% newlist = mylist.sort %]
+
+Here 'mylist' is a regular reference to a list, and 'sort' is
+a virtual method that returns a new list of the items in sorted
+order. You can chain multiple virtual methods together. For
+example:
+
+ [% mylist.sort.join(', ') %]
+
+Here the 'join' virtual method is called to join the sorted list into
+a single string, generating the following output:
+
+ bar, baz, foo
+
+See L<Template::Manual::VMethods> for details of all the virtual
+methods available.
+
+=head2 Variable Interpolation
+
+The Template Toolkit uses '$' consistently to indicate that a variable
+should be interpolated in position. Most frequently, you see this in
+double-quoted strings:
+
+ [% fullname = "$honorific $firstname $surname" %]
+
+Or embedded in plain text when the INTERPOLATE option is set:
+
+ Dear $honorific $firstname $surname,
+
+The same rules apply within directives. If a variable is prefixed
+with a '$' then it is replaced with its value before being used. The
+most common use is to retrieve an element from a hash where the key is
+stored in a variable.
+
+ [% uid = 'abw' %]
+ [% userlist.$uid %] # same as 'userlist.abw'
+
+Curly braces can be used to delimit interpolated variable names where
+necessary.
+
+ [% userlist.${me.id}.name %]
+
+Directives such as INCLUDE, PROCESS, etc., that accept a template name
+as the first argument, will automatically quote it for convenience.
+
+ [% INCLUDE foo/bar.txt %]
+
+equivalent to:
+
+ [% INCLUDE "foo/bar.txt" %]
+
+To INCLUDE a template whose name is stored in a variable, simply
+prefix the variable name with '$' to have it interpolated.
+
+ [% myfile = 'header' %]
+ [% INCLUDE $myfile %]
+
+equivalent to:
+
+ [% INCLUDE header %]
+
+Note also that a variable containing a reference to a Template::Document
+object can also be processed in this way.
+
+ my $vars = {
+ header => Template::Document->new({ ... }),
+ };
+
+template:
+
+ [% INCLUDE $header %]
+
+=head2 Local and Global Variables
+
+Any simple variables that you create, or any changes you make to
+existing variables, will only persist while the template is being
+processed. The top-level variable hash is copied before processing
+begins and any changes to variables are made in this copy, leaving the
+original intact. The same thing happens when you INCLUDE another
+template. The current namespace hash is cloned to prevent any
+variable changes made in the included template from interfering with
+existing variables. The PROCESS option bypasses the localisation step
+altogether making it slightly faster, but requiring greater attention
+to the possibility of side effects caused by creating or changing any
+variables within the processed template.
+
+ [% BLOCK change_name %]
+ [% name = 'bar' %]
+ [% END %]
+
+ [% name = 'foo' %]
+ [% INCLUDE change_name %]
+ [% name %] # foo
+ [% PROCESS change_name %]
+ [% name %] # bar
+
+Dotted compound variables behave slightly differently because the
+localisation process is only skin deep. The current variable
+namespace hash is copied, but no attempt is made to perform a
+deep-copy of other structures within it (hashes, arrays, objects,
+etc). A variable referencing a hash, for example, will be copied to
+create a new reference but which points to the same hash. Thus, the
+general rule is that simple variables (undotted variables) are
+localised, but existing complex structures (dotted variables) are not.
+
+ [% BLOCK all_change %]
+ [% x = 20 %] # changes copy
+ [% y.z = 'zulu' %] # changes original
+ [% END %]
+
+ [% x = 10
+ y = { z => 'zebra' }
+ %]
+ [% INCLUDE all_change %]
+ [% x %] # still '10'
+ [% y.z %] # now 'zulu'
+
+
+If you create a complex structure such as a hash or list reference
+within a local template context then it will cease to exist when
+the template is finished processing.
+
+ [% BLOCK new_stuff %]
+ [% # define a new 'y' hash array in local context
+ y = { z => 'zulu' }
+ %]
+ [% END %]
+
+ [% x = 10 %]
+ [% INCLUDE new_stuff %]
+ [% x %] # outputs '10'
+ [% y %] # nothing, y is undefined
+
+Similarly, if you update an element of a compound variable which
+I<doesn't> already exists then a hash will be created automatically
+and deleted again at the end of the block.
+
+ [% BLOCK new_stuff %]
+ [% y.z = 'zulu' %]
+ [% END %]
+
+However, if the hash I<does> already exist then you will modify the
+original with permanent effect. To avoid potential confusion, it is
+recommended that you don't update elements of complex variables from
+within blocks or templates included by another.
+
+If you want to create or update truly global variables then you can
+use the 'global' namespace. This is a hash array automatically created
+in the top-level namespace which all templates, localised or otherwise
+see the same reference to. Changes made to variables within this
+hash are visible across all templates.
+
+ [% global.version = 123 %]
+
+=head2 Compile Time Constant Folding
+
+In addition to variables that get resolved each time a template is
+processed, you can also define variables that get resolved just once
+when the template is compiled. This generally results in templates
+processing faster because there is less work to be done.
+
+To define compile-time constants, specify a CONSTANTS hash as a
+constructor item as per VARIABLES. The CONSTANTS hash can contain any
+kind of complex, nested, or dynamic data structures, just like regular
+variables.
+
+ my $tt = Template->new({
+ CONSTANTS => {
+ version => 3.14,
+ release => 'skyrocket',
+ col => {
+ back => '#ffffff',
+ fore => '#000000',
+ },
+ myobj => My::Object->new(),
+ mysub => sub { ... },
+ joint => ', ',
+ },
+ });
+
+Within a template, you access these variables using the 'constants'
+namespace prefix.
+
+ Version [% constants.version %] ([% constants.release %])
+
+ Background: [% constants.col.back %]
+
+When the template is compiled, these variable references are replaced
+with the corresponding value. No further variable lookup is then
+required when the template is processed.
+
+You can call subroutines, object methods, and even virtual methods on
+constant variables.
+
+ [% constants.mysub(10, 20) %]
+ [% constants.myobj(30, 40) %]
+ [% constants.col.keys.sort.join(', ') %]
+
+One important proviso is that any arguments you pass to subroutines
+or methods must also be literal values or compile time constants.
+
+For example, these are both fine:
+
+ # literal argument
+ [% constants.col.keys.sort.join(', ') %]
+
+ # constant argument
+ [% constants.col.keys.sort.join(constants.joint) %]
+
+But this next example will raise an error at parse time because
+'joint' is a runtime variable and cannot be determined at compile
+time.
+
+ # ERROR: runtime variable argument!
+ [% constants.col.keys.sort.join(joint) %]
+
+The CONSTANTS_NAMESPACE option can be used to provide a different
+namespace prefix for constant variables. For example:
+
+ my $tt = Template->new({
+ CONSTANTS => {
+ version => 3.14,
+ # ...etc...
+ },
+ CONSTANTS_NAMESPACE => 'const',
+ });
+
+Constants would then be referenced in templates as:
+
+ [% const.version %]
+
+=head2 Special Variables
+
+A number of special variables are automatically defined by the Template
+Toolkit.
+
+=over 4
+
+=item template
+
+The 'template' variable contains a reference to the main template
+being processed, in the form of a Template::Document object. This
+variable is correctly defined within PRE_PROCESS, PROCESS and
+POST_PROCESS templates, allowing standard headers, footers, etc., to
+access metadata items from the main template. The 'name' and
+'modtime' metadata items are automatically provided, giving the
+template name and modification time in seconds since the epoch.
+
+Note that the 'template' variable always references the top-level
+template, even when processing other template components via INCLUDE,
+PROCESS, etc.
+
+=item component
+
+The 'component' variable is like 'template' but always contains a
+reference to the current, innermost template component being processed.
+In the main template, the 'template' and 'component' variable will
+reference the same Template::Document object. In any other template
+component called from the main template, the 'template' variable
+will remain unchanged, but 'component' will contain a new reference
+to the current component.
+
+This example should demonstrate the difference:
+
+ $template->process('foo')
+ || die $template->error(), "\n";
+
+'foo':
+
+ [% template.name %] # foo
+ [% component.name %] # foo
+ [% PROCESS footer %]
+
+'footer':
+
+ [% template.name %] # foo
+ [% component.name %] # footer
+
+=item loop
+
+Within a FOREACH loop, the 'loop' variable references the Template::Iterator
+object responsible for controlling the loop.
+
+ [% FOREACH item = [ 'foo', 'bar', 'baz' ] -%]
+ [% "Items:\n" IF loop.first -%]
+ [% loop.count %]/[% loop.size %]: [% item %]
+ [% END %]
+
+=item error
+
+Within a CATCH block, the 'error' variable contains a reference to the
+Template::Exception object thrown from within the TRY block. The
+'type' and 'info' methods can be called or the variable itself can
+be printed for automatic stringification into a message of the form
+"$type error - $info". See L<Template::Exception> for further details.
+
+ [% TRY %]
+ ...
+ [% CATCH %]
+ [% error %]
+ [% END %]
+
+=item content
+
+The WRAPPER method captures the output from a template block and then
+includes a named template, passing the captured output as the 'content'
+variable.
+
+ [% WRAPPER box %]
+ Be not afeard; the isle is full of noises,
+ Sounds and sweet airs, that give delight and hurt not.
+ [% END %]
+
+ [% BLOCK box %]
+ <table border=1>
+ <tr>
+ <td>
+ [% content %]
+ </td>
+ </tr>
+ </table>
+ [% END %]
+
+=back
+
+=head2 Compound Variables
+
+Compound 'dotted' variables may contain any number of separate
+elements. Each element may evaluate to any of the permitted variable
+types and the processor will then correctly use this value to evaluate
+the rest of the variable. Arguments may be passed to any of the
+intermediate elements.
+
+ [% myorg.people.sort('surname').first.fullname %]
+
+Intermediate variables may be used and will behave entirely as expected.
+
+ [% sorted = myorg.people.sort('surname') %]
+ [% sorted.first.fullname %]
+
+This simplified dotted notation has the benefit of hiding the
+implementation details of your data. For example, you could implement
+a data structure as a hash array one day and then change it to an
+object the next without requiring any change to the templates.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Manual/Views.pod b/lib/Template/Manual/Views.pod
new file mode 100644
index 0000000..7bc53b0
--- /dev/null
+++ b/lib/Template/Manual/Views.pod
@@ -0,0 +1,642 @@
+#============================================================= -*-perl-*-
+#
+# Template::Manual::Views
+#
+# DESCRIPTION
+# This section describes dynamic views: a powerful but experimental
+# new feature in version 2.01 of the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Manual::Views - Template Toolkit views (experimental)
+
+=head1 DESCRIPTION
+
+This section describes dynamic views: a powerful but experimental new
+feature in version 2.01 of the Template Toolkit.
+
+A view is effectively a collection of templates and/or variable
+definitions which can be passed around as a self-contained unit. This
+then represents a particular interface or presentation style for other
+objects or items of data.
+
+You can use views to implement custom "skins" for an application or
+content set. You can use them to help simplify the presentation of
+common objects or data types. You can even use then to automate the
+presentation of complex data structures such as that generated in an
+XML::DOM tree or similar. You let an iterator do the walking, and the
+view does the talking (or in this case, the presenting). Voila - you
+have view independant, structure shy traversal using templates.
+
+In general, views can be used in a number of different ways to achieve
+several different things. They elegantly solve some problems which
+were otherwise difficult or complicated, and make easy some things
+that were previously hard.
+
+At the moment, they're still very experimental. The directive syntax
+and underlying API are likely to change quite considerably over the
+next version or two. Please be very wary about building your
+multi-million dollar e-commerce solutions based around this feature.
+
+=head2 Views as Template Collectors/Providers
+
+The VIEW directive starts a view definition and includes a name by
+which the view can be referenced. The view definition continues up to
+the matching END directive.
+
+ [% VIEW myview %]
+ ...
+ [% END %]
+
+The first role of a view is to act as a collector and provider of templates.
+The include() method can be called on a view to effectively do the same
+thing as the INCLUDE directive. The template name is passed as the first
+argument, followed by any local variable definitions for the template.
+
+ [% myview.include('header', title='The Title') %]
+
+ # equivalent to
+ [% INCLUDE header title='The Title' %]
+
+Views accept a number of configuration options which can be used to control
+different aspects of their behaviour. The 'prefix' and 'suffix' options
+can be specified to add a fixed prefix and/or suffix to the name of each template.
+
+ [% VIEW myview
+ prefix = 'my/'
+ suffix = '.tt2' ;
+ END
+ %]
+
+Now the call
+
+ [% myview.include('header', title='The Title') %]
+
+is equivalent to
+
+ [% INCLUDE my/header.tt2 title='The Title' %]
+
+Views provide an AUTOLOAD method which maps method names to the
+include() method. Thus, the following are all equivalent:
+
+ [% myview.include('header', title='Hello World') %]
+ [% myview.include_header(title='Hello World') %]
+ [% myview.header(title='Hello World') %]
+
+=head2 Local BLOCK Definitions
+
+A VIEW definition can include BLOCK definitions which remain local to
+the view. A request for a particular template will return a BLOCK,
+if defined, in preference to any other template of the same name.
+
+ [% BLOCK foo %]
+ public foo block
+ [% END %]
+
+ [% VIEW plain %]
+ [% BLOCK foo %]
+ plain foo block
+ [% END %]
+ [% END %]
+
+ [% VIEW fancy %]
+ [% BLOCK foo %]
+ fancy foo block
+ [% END %]
+ [% END %]
+
+ [% INCLUDE foo %] # public foo block
+ [% plain.foo %] # plain foo block
+ [% fancy.foo %] # fancy foo block
+
+In addition to BLOCK definitions, a VIEW can contain any other
+template directives. The entire VIEW definition block is processed to
+initialise the view but no output is generated (this may change RSN -
+and get stored as 'output' item, subsequently accessible as [%
+view.output %]). However, directives that have side-effects, such as
+those that update a variable, will have noticable consequences.
+
+=head2 Preserving Variable State within Views
+
+Views can also be used to save the values of any existing variables,
+or to create new ones at the point at which the view is defined.
+Unlike simple template metadata (META) which can only contain static
+string values, the view initialisation block can contain any template
+directives and generate any kind of dynamic output and/or data items.
+
+ [% VIEW my_web_site %]
+ [% view.title = title or 'My Cool Web Site' %]
+ [% view.author = "$abw.name, $abw.email" %]
+ [% view.sidebar = INCLUDE my/sidebar.tt2 %]
+ [% END %]
+
+Note that additional data items can be specified as arguments to the VIEW
+directive. Anything that doesn't look like a configuration parameter is
+assumed to be a data item. This can be a little hazardous, of course, because
+you never know when a new configuration item might get added which interferes
+with your data.
+
+ [% VIEW my_web_site
+ # config options
+ prefix = 'my/'
+ # misc data
+ title = title or 'My Cool Web Site'
+ author = "$abw.name, $abw.email"
+ sidebar = INCLUDE my/sidebar.tt2
+ %]
+ ...
+ [% END %]
+
+Outside of the view definition you can access the view variables as, for
+example:
+
+ [% my_web_site.title %]
+
+One important feature is the equivalence of simple variables and templates.
+You can implement the view item 'title' as a simple variable, a template
+defined in an external file, possibly with a prefix/suffix automatically
+appended, or as a local BLOCK definition within the [% VIEW %] ... [% END %]
+definition. If you use the syntax above then the view will Do The Right
+Thing to return the appropriate output.
+
+At the END of the VIEW definition the view is "sealed" to prevent you
+from accidentally updating any variable values. If you attempt to change
+the value of a variable after the END of the VIEW definition block then
+an 'view' error will be thrown.
+
+ [% TRY;
+ my_web_site.title = 'New Title';
+ CATCH;
+ error;
+ END
+ %]
+
+The error above will be reported as:
+
+ view error - cannot update item in sealed view: title
+
+The same is true if you pass a parameter to a view variable. This is
+interpreted as an attempt to update the variable and will raise the same
+warning.
+
+ [% my_web_site.title('New Title') %] # view error!
+
+You can set the 'silent' parameter to have the view ignore these
+parameters and simply return the variable value.
+
+ [% VIEW my_web_site
+ silent = 1
+ title = title or 'My Cool Web Site'
+ # ... ;
+ END
+ %]
+
+ [% my_web_site.title('Blah Blah') %] # My Cool Web Site
+
+Alternately, you can specify that a view is unsealed allowing existing
+variables to be updated and new variables defined.
+
+ [% VIEW my_web_site
+ sealed = 0
+ title = title or 'My Cool Web Site'
+ # ... ;
+ END
+ %]
+
+ [% my_web_site.title('Blah Blah') %] # Blah Blah
+ [% my_web_site.title %] # Blah Blah
+
+=head2 Inheritance, Delegation and Reuse
+
+Views can be inherited from previously defined views by use of the 'base'
+parameter. This example shows how a base class view is defined which
+applies a 'view/default/' prefix to all template names.
+
+ [% VIEW my.view.default
+ prefix = 'view/default/';
+ END
+ %]
+
+Thus the directive:
+
+ [% my.view.default.header(title='Hello World') %]
+
+is now equivalent to:
+
+ [% INCLUDE view/default/header title='Hello World' %]
+
+A second view can be defined which specifies the default view as a
+base.
+
+ [% VIEW my.view.fancy
+ base = my.view.default
+ prefix = 'view/fancy/';
+ END
+ %]
+
+Now the directive:
+
+ [% my.view.fancy.header(title='Hello World') %]
+
+will resolve to:
+
+ [% INCLUDE view/fancy/header title='Hello World' %]
+
+or if that doesn't exist, it will be handled by the base view as:
+
+ [% INCLUDE view/default/header title='Hello World' %]
+
+When a parent view is specified via the 'base' parameter, the
+delegation of a view to its parent for fetching templates and accessing
+user defined variables is automatic. You can also implement your own
+inheritance, delegation or other reuse patterns by explicitly
+delegating to other views.
+
+ [% BLOCK foo %]
+ public foo block
+ [% END %]
+
+ [% VIEW plain %]
+ [% BLOCK foo %]
+ <plain>[% PROCESS foo %]</plain>
+ [% END %]
+ [% END %]
+
+ [% VIEW fancy %]
+ [% BLOCK foo %]
+ [% plain.foo | replace('plain', 'fancy') %]
+ [% END %]
+ [% END %]
+
+ [% plain.foo %] # <plain>public foo block</plain>
+ [% fancy.foo %] # <fancy>public foo block</fancy>
+
+Note that the regular INCLUDE/PROCESS/WRAPPER directives work entirely
+independantly of views and will always get the original, unaltered
+template name rather than any local per-view definition.
+
+=head2 Self-Reference
+
+A reference to the view object under definition is available with the
+VIEW ... END block by its specified name and also by the special name
+'view' (similar to the C<my $self = shift;> in a Perl method or the
+'this' pointer in C++, etc). The view is initially unsealed allowing
+any data items to be defined and updated within the VIEW ... END
+block. The view is automatically sealed at the end of the definition
+block, preventing any view data from being subsequently changed.
+
+(NOTE: sealing should be optional. As well as sealing a view to prevent
+updates (SEALED), it should be possible to set an option in the view to
+allow external contexts to update existing variables (UPDATE) or even
+create totally new view variables (CREATE)).
+
+ [% VIEW fancy %]
+ [% fancy.title = 'My Fancy Title' %]
+ [% fancy.author = 'Frank Open' %]
+ [% fancy.col = { bg => '#ffffff', bar => '#a0a0ff' } %]
+ [% END %]
+
+or
+
+ [% VIEW fancy %]
+ [% view.title = 'My Fancy Title' %]
+ [% view.author = 'Frank Open' %]
+ [% view.col = { bg => '#ffffff', bar => '#a0a0ff' } %]
+ [% END %]
+
+It makes no real difference in this case if you refer to the view by
+its name, 'fancy', or by the general name, 'view'. Outside of the
+view block, however, you should always use the given name, 'fancy':
+
+ [% fancy.title %]
+ [% fancy.author %]
+ [% fancy.col.bg %]
+
+The choice of given name or 'view' is much more important when it
+comes to BLOCK definitions within a VIEW. It is generally recommended
+that you use 'view' inside a VIEW definition because this is guaranteed
+to be correctly defined at any point in the future when the block gets
+called. The original name of the view might have long since been changed
+or reused but the self-reference via 'view' should always be intact and
+valid.
+
+Take the following VIEW as an example:
+
+ [% VIEW foo %]
+ [% view.title = 'Hello World' %]
+ [% BLOCK header %]
+ Title: [% view.title %]
+ [% END %]
+ [% END %]
+
+Even if we rename the view, or create a new 'foo' variable, the header
+block still correctly accesses the 'title' attribute of the view to
+which it belongs. Whenever a view BLOCK is processed, the 'view'
+variable is always updated to contain the correct reference to the
+view object to which it belongs.
+
+ [% bar = foo %]
+ [% foo = { title => "New Foo" } %] # no problem
+ [% bar.header %] # => Title: Hello World
+
+=head2 Saving References to External Views
+
+When it comes to view inheritance, it's always a good idea to take a
+local copy of a parent or delegate view and store it as an attribute
+within the view for later use. This ensures that the correct view
+reference is always available, even if the external name of a view
+has been changed.
+
+ [% VIEW plain %]
+ ...
+ [% END %]
+
+ [% VIEW fancy %]
+ [% view.plain = plain %]
+ [% BLOCK foo %]
+ [% view.plain.foo | replace('plain', 'fancy') %]
+ [% END %]
+ [% END %]
+
+ [% plain.foo %] # => <plain>public foo block</plain>
+ [% plain = 'blah' %] # no problem
+ [% fancy.foo %] # => <fancy>public foo block</fancy>
+
+
+=head2 Views as Data Presenters
+
+Another key role of a view is to act as a dispatcher to automatically
+apply the correct template to present a particular object or data
+item. This is handled via the print() method.
+
+Here's an example:
+
+ [% VIEW foo %]
+
+ [% BLOCK text %]
+ Some text: [% item %]
+ [% END %]
+
+ [% BLOCK hash %]
+ a hash:
+ [% FOREACH key = item.keys.sort -%]
+ [% key %] => [% item.$key %]
+ [% END -%]
+ [% END %]
+
+ [% BLOCK list %]
+ a list: [% item.sort.join(', ') %]
+ [% END %]
+
+ [% END %]
+
+We can now use the view to print text, hashes or lists. The print()
+method includes the right template depending on the typing of the
+argument (or arguments) passed.
+
+ [% some_text = 'I read the news today, oh boy.' %]
+ [% a_hash = { house => 'Lords', hall => 'Albert' } %]
+ [% a_list = [ 'sure', 'Nobody', 'really' ] %]
+
+ [% view.print(some_text) %]
+ # Some text: I read the news today, oh boy.
+
+ [% view.print(a_hash) %]
+ # a hash:
+ hall => Albert
+ house => Lords
+ [% view.print(a_list) %]
+ # a list: Nobody, really, sure
+
+
+You can also provide templates to print objects of any other class.
+The class name is mapped to a template name with all non-word
+character sequences such as '::' converted to a single '_'.
+
+ [% VIEW foo %]
+ [% BLOCK Foo_Bar %]
+ a Foo::Bar object:
+ thingies: [% view.print(item.thingies) %]
+ doodahs: [% view.print(item.doodahs) %]
+ [% END %]
+ [% END %]
+
+ [% USE fubar = Foo::Bar(...) %]
+
+ [% foo.print(fubar) %]
+
+Note how we use the view object to display various items within the
+objects ('thingies' and 'doodahs'). We don't need to worry what
+kind of data these represent (text, list, hash, etc) because we can
+let the view worry about it, automatically mapping the data type to
+the correct template.
+
+Views may define their own type =E<gt> template map.
+
+ [% VIEW foo
+ map = { TEXT => 'plain_text',
+ ARRAY => 'show_list',
+ HASH => 'show_hash',
+ My::Module => 'template_name'
+ default => 'any_old_data'
+ }
+ %]
+ [% BLOCK plain_text %]
+ ...
+ [% END %]
+
+ ...
+
+ [% END %]
+
+They can also provide a 'default' map entry, specified as part of the 'map'
+hash or as a parameter by itself.
+
+
+ [% VIEW foo
+ map = { ... },
+ default = 'whatever'
+ %]
+ ...
+ [% END %]
+
+or
+
+ [% VIEW foo %]
+ [% view.map = { ... }
+ view.default = 'whatever'
+ %]
+ ...
+ [% END %]
+
+The print() method provides one more piece of magic. If you pass it a
+reference to an object which provides a present() method, then the
+method will be called passing the view as an argument. This then gives
+any object a chance to determine how it should be presented via the
+view.
+
+ package Foo::Bar;
+
+ ...
+
+ sub present {
+ my ($self, $view) = @_;
+ return "a Foo::Bar object:\n"
+ . "thingies: " . $view.print($self->{ _THINGIES }) . "\n"
+ . "doodahs: " . $view.print($self->{ _DOODAHS }) . "\n";
+ }
+
+The object is free to delve deeply into its innards and mess around with
+its own private data, before presenting the relevant data via the view.
+In a more complex example, a present() method might walk part of a tree
+making calls back against the view to present different nodes within the
+tree. We may not want to expose the internal structure of the tree
+(because that would break encapsulation and make our presentation code
+dependant on it) but we want to have some way of walking the tree and
+presenting items found in a particular manner.
+
+This is known as Structure Shy Traversal. Our view object doesn't require
+prior knowledge about the internal structure of any data set to be able
+to traverse it and present the data contained therein. The data items
+themselves, via the present() method, can implement the internal iterators
+to guide the view along the right path to presentation happiness.
+
+The upshot is that you can use views to greatly simplify the display
+of data structures like XML::DOM trees. The documentation for the
+Template::Plugins::XML::DOM module contains an example of this. In
+essence, it looks something like this:
+
+XML source:
+
+ <user name="Andy Wardley">
+ <project id="iCan" title="iCan, but theyCan't"/>
+ <project id="p45" title="iDid, but theyDidn't"/>
+ </user>
+
+TT View:
+
+ [% VIEW fancy %]
+ [% BLOCK user %]
+ User: [% item.name %]
+ [% item.content(myview) %]
+ [% END %]
+
+ [% BLOCK project %]
+ Project: [% project.id %] - [% project.name %]
+ [% END %]
+ [% END %]
+
+Generate view:
+
+ [% USE dom = XML.DOM %]
+ [% fancy.print(dom.parse(xml_source)) %]
+
+Output:
+
+ User: Andy Wardley
+ Project: iCan - iCan, but theyCan't
+ Project: p45 - iDid, but theyDidn't
+
+The same approach can be applied to many other areas. Here's an example from
+the File/Directory plugins.
+
+ [% VIEW myview %]
+ [% BLOCK file %]
+ - [% item.name %]
+ [% END %]
+
+ [% BLOCK directory %]
+ * [% item.name %]
+ [% item.content(myview) FILTER indent %]
+ [% END %]
+ [% END %]
+
+ [% USE dir = Directory(dirpath) %]
+ [% myview.print(dir) %]
+
+And here's the same approach use to convert Pod documentation to any
+other format via template.
+
+ [% # load Pod plugin and parse source file into Pod Object Model
+ USE Pod;
+ pom = Pod.parse_file(my_pod_file);
+
+ # define view to map all Pod elements to "pod/html/xxx" templates
+ VIEW pod2html
+ prefix='pod/html';
+ END;
+
+ # now print document via view (i.e. as HTML)
+ pod2html.print(pom)
+ %]
+
+Here we simply define a template prefix for the view which causes the
+view to look for 'pod/html/head1', 'pod/html/head2', 'pod/html/over'
+as templates to present the different sections of the parsed Pod document.
+
+There are some examples in the Template Toolkit test suite: t/pod.t and
+t/view.t which may shed some more light on this. See the distribution
+sub-directory 'examples/pod/html' for examples of Pod -E<gt> HTML templates.
+
+(This documentation is incomplete but I'm not going to get it 100% pefect
+until the syntax and API stabilise).
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Modules.pod b/lib/Template/Modules.pod
new file mode 100644
index 0000000..78dadb8
--- /dev/null
+++ b/lib/Template/Modules.pod
@@ -0,0 +1,448 @@
+#============================================================= -*-perl-*-
+#
+# Template::Modules
+#
+# DESCRIPTION
+# This section contains the documentation for the modules that
+# comprise the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Modules - Core modules comprising the Template Toolkit
+
+=head1 DESCRIPTION
+
+This section contains the documentation for the modules that comprise
+the Template Toolkit.
+
+=over 4
+
+=item L<Template|Template>
+
+Front-end module to the Template Toolkit
+
+
+
+=item L<Template::Base|Template::Base>
+
+Base class module implementing common functionality
+
+
+
+=item L<Template::Config|Template::Config>
+
+Factory module for instantiating other TT2 modules
+
+
+
+=item L<Template::Constants|Template::Constants>
+
+Defines constants for the Template Toolkit
+
+
+
+=item L<Template::Context|Template::Context>
+
+Runtime context in which templates are processed
+
+
+
+=item L<Template::Document|Template::Document>
+
+Compiled template document object
+
+
+
+=item L<Template::Exception|Template::Exception>
+
+Exception handling class module
+
+
+
+=item L<Template::Filters|Template::Filters>
+
+Post-processing filters for template blocks
+
+
+
+=item L<Template::Iterator|Template::Iterator>
+
+Data iterator used by the FOREACH directive
+
+
+
+=item L<Template::Namespace::Constants|Template::Namespace::Constants>
+
+Compile time constant folding
+
+
+
+=item L<Template::Parser|Template::Parser>
+
+LALR(1) parser for compiling template documents
+
+
+
+=item L<Template::Plugin|Template::Plugin>
+
+Base class for Template Toolkit plugins
+
+
+
+=item L<Template::Plugins|Template::Plugins>
+
+Plugin provider module
+
+
+
+=item L<Template::Provider|Template::Provider>
+
+Provider module for loading/compiling templates
+
+
+
+=item L<Template::Service|Template::Service>
+
+General purpose template processing service
+
+
+
+=item L<Template::Stash|Template::Stash>
+
+Magical storage for template variables
+
+
+
+=item L<Template::Stash::Context|Template::Stash::Context>
+
+Experimetal stash allowing list/scalar context definition
+
+
+
+=item L<Template::Stash::XS|Template::Stash::XS>
+
+Experimetal high-speed stash written in XS
+
+
+
+=item L<Template::Test|Template::Test>
+
+Module for automating TT2 test scripts
+
+
+
+=item L<Template::Plugin::Autoformat|Template::Plugin::Autoformat>
+
+Interface to Text::Autoformat module
+
+
+
+=item L<Template::Plugin::CGI|Template::Plugin::CGI>
+
+Interface to the CGI module
+
+
+
+=item L<Template::Plugin::DBI|Template::Plugin::DBI>
+
+Template interface to the DBI module
+
+
+
+=item L<Template::Plugin::Datafile|Template::Plugin::Datafile>
+
+Plugin to construct records from a simple data file
+
+
+
+=item L<Template::Plugin::Date|Template::Plugin::Date>
+
+Plugin to generate formatted date strings
+
+
+
+=item L<Template::Plugin::Directory|Template::Plugin::Directory>
+
+Plugin for generating directory listings
+
+
+
+=item L<Template::Plugin::Dumper|Template::Plugin::Dumper>
+
+Plugin interface to Data::Dumper
+
+
+
+=item L<Template::Plugin::File|Template::Plugin::File>
+
+Plugin providing information about files
+
+
+
+=item L<Template::Plugin::Filter|Template::Plugin::Filter>
+
+Base class for plugin filters
+
+
+
+=item L<Template::Plugin::Format|Template::Plugin::Format>
+
+Plugin to create formatting functions
+
+
+
+=item L<Template::Plugin::GD::Image|Template::Plugin::GD::Image>
+
+Interface to GD Graphics Library
+
+
+
+=item L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon>
+
+Interface to GD module Polygon class
+
+
+
+=item L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants>
+
+Interface to GD module constants
+
+
+
+=item L<Template::Plugin::GD::Text|Template::Plugin::GD::Text>
+
+Text utilities for use with GD
+
+
+
+=item L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align>
+
+Draw aligned strings in GD images
+
+
+
+=item L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap>
+
+Break and wrap strings in GD images
+
+
+
+=item L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>
+
+Create line graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>
+
+Create 3D line graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>
+
+Create bar graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>
+
+Create 3D bar graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>
+
+Create point graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>
+
+Create line/point graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>
+
+Create area graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>
+
+Create mixed graphs with axes and legends
+
+
+
+=item L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>
+
+Create pie charts with legends
+
+
+
+=item L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>
+
+Create 3D pie charts with legends
+
+
+
+=item L<Template::Plugin::HTML|Template::Plugin::HTML>
+
+Plugin to create HTML elements
+
+
+
+=item L<Template::Plugin::Image|Template::Plugin::Image>
+
+Plugin access to image sizes
+
+
+
+=item L<Template::Plugin::Iterator|Template::Plugin::Iterator>
+
+Plugin to create iterators (Template::Iterator)
+
+
+
+=item L<Template::Plugin::Math|Template::Plugin::Math>
+
+Plugin interface to mathematical functions
+
+
+
+=item L<Template::Plugin::Pod|Template::Plugin::Pod>
+
+Plugin interface to Pod::POM (Pod Object Model)
+
+
+
+=item L<Template::Plugin::Procedural|Template::Plugin::Procedural>
+
+Base class for procedural plugins
+
+
+
+=item L<Template::Plugin::String|Template::Plugin::String>
+
+Object oriented interface for string manipulation
+
+
+
+=item L<Template::Plugin::Table|Template::Plugin::Table>
+
+Plugin to present data in a table
+
+
+
+=item L<Template::Plugin::URL|Template::Plugin::URL>
+
+Plugin to construct complex URLs
+
+
+
+=item L<Template::Plugin::View|Template::Plugin::View>
+
+Plugin to create views (Template::View)
+
+
+
+=item L<Template::Plugin::Wrap|Template::Plugin::Wrap>
+
+Plugin interface to Text::Wrap
+
+
+
+=item L<Template::Plugin::XML::DOM|Template::Plugin::XML::DOM>
+
+Plugin interface to XML::DOM
+
+
+
+=item L<Template::Plugin::XML::RSS|Template::Plugin::XML::RSS>
+
+Plugin interface to XML::RSS
+
+
+
+=item L<Template::Plugin::XML::Simple|Template::Plugin::XML::Simple>
+
+Plugin interface to XML::Simple
+
+
+
+=item L<Template::Plugin::XML::Style|Template::Plugin::XML::Style>
+
+Simple XML stylesheet transfomations
+
+
+
+=item L<Template::Plugin::XML::XPath|Template::Plugin::XML::XPath>
+
+Plugin interface to XML::XPath
+
+
+
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Namespace/Constants.pm b/lib/Template/Namespace/Constants.pm
new file mode 100644
index 0000000..e1b5114
--- /dev/null
+++ b/lib/Template/Namespace/Constants.pm
@@ -0,0 +1,205 @@
+#================================================================= -*-Perl-*-
+#
+# Template::Namespace::Constants
+#
+# DESCRIPTION
+# Plugin compiler module for performing constant folding at compile time
+# on variables in a particular namespace.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2002 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Constants.pm,v 1.22 2004/01/13 16:20:36 abw Exp $
+#
+#============================================================================
+
+package Template::Namespace::Constants;
+
+use strict;
+use Template::Base;
+use Template::Config;
+use Template::Directive;
+use Template::Exception;
+
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.22 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+sub _init {
+ my ($self, $config) = @_;
+ $self->{ STASH } = Template::Config->stash($config)
+ || return $self->error(Template::Config->error());
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# ident(\@ident) foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub ident {
+ my ($self, $ident) = @_;
+ my @save = @$ident;
+
+ # discard first node indicating constants namespace
+ splice(@$ident, 0, 2);
+
+ my $nelems = @$ident / 2;
+ my ($e, $result);
+ local $" = ', ';
+
+ print STDERR "constant ident [ @$ident ] " if $DEBUG;
+
+ foreach $e (0..$nelems-1) {
+ # node name must be a constant
+ unless ($ident->[$e * 2] =~ s/^'(.+)'$/$1/s) {
+ $self->DEBUG(" * deferred (non-constant item: ", $ident->[$e * 2], ")\n")
+ if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+
+ # if args is non-zero then it must be eval'ed
+ if ($ident->[$e * 2 + 1]) {
+ my $args = $ident->[$e * 2 + 1];
+ my $comp = eval "$args";
+ if ($@) {
+ $self->DEBUG(" * deferred (non-constant args: $args)\n") if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+ $self->DEBUG("($args) ") if $comp && $DEBUG;
+ $ident->[$e * 2 + 1] = $comp;
+ }
+ }
+
+
+ $result = $self->{ STASH }->get($ident);
+
+ if (! length $result || ref $result) {
+ my $reason = length $result ? 'reference' : 'no result';
+ $self->DEBUG(" * deferred ($reason)\n") if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+
+ $result =~ s/'/\\'/g;
+
+ $self->DEBUG(" * resolved => '$result'\n") if $DEBUG;
+
+ return "'$result'";
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Namespace::Constants - Compile time constant folding
+
+=head1 SYNOPSIS
+
+ # easy way to define constants
+ use Template;
+
+ my $tt = Template->new({
+ CONSTANTS => {
+ pi => 3.14,
+ e => 2.718,
+ },
+ });
+
+ # nitty-gritty, hands-dirty way
+ use Template::Namespace::Constants;
+
+ my $tt = Template->new({
+ NAMESPACE => {
+ constants => Template::Namespace::Constants->new({
+ pi => 3.14,
+ e => 2.718,
+ },
+ },
+ });
+
+=head1 DESCRIPTION
+
+The Template::Namespace::Constants module implements a namespace handler
+which is plugged into the Template::Directive compiler module. This then
+performs compile time constant folding of variables in a particular namespace.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%constants)
+
+The new() constructor method creates and returns a reference to a new
+Template::Namespace::Constants object. This creates an internal stash
+to store the constant variable definitions passed as arguments.
+
+ my $handler = Template::Namespace::Constants->new({
+ pi => 3.14,
+ e => 2.718,
+ });
+
+=head2 ident(\@ident)
+
+Method called to resolve a variable identifier into a compiled form. In this
+case, the method fetches the corresponding constant value from its internal
+stash and returns it.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.22, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Directive|Template::Directive>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Parser.pm b/lib/Template/Parser.pm
new file mode 100644
index 0000000..68bf9e0
--- /dev/null
+++ b/lib/Template/Parser.pm
@@ -0,0 +1,1446 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Parser
+#
+# DESCRIPTION
+# This module implements a LALR(1) parser and assocated support
+# methods to parse template documents into the appropriate "compiled"
+# format. Much of the parser DFA code (see _parse() method) is based
+# on Francois Desarmenien's Parse::Yapp module. Kudos to him.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# The following copyright notice appears in the Parse::Yapp
+# documentation.
+#
+# The Parse::Yapp module and its related modules and shell
+# scripts are copyright (c) 1998 Francois Desarmenien,
+# France. All rights reserved.
+#
+# You may use and distribute them under the terms of either
+# the GNU General Public License or the Artistic License, as
+# specified in the Perl README file.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Parser.pm,v 2.81 2004/01/13 16:19:15 abw Exp $
+#
+#============================================================================
+
+package Template::Parser;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR );
+use base qw( Template::Base );
+use vars qw( $TAG_STYLE $DEFAULT_STYLE $QUOTED_ESCAPES );
+
+use Template::Constants qw( :status :chomp );
+use Template::Directive;
+use Template::Grammar;
+
+# parser state constants
+use constant CONTINUE => 0;
+use constant ACCEPT => 1;
+use constant ERROR => 2;
+use constant ABORT => 3;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.81 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+$ERROR = '';
+
+
+#========================================================================
+# -- COMMON TAG STYLES --
+#========================================================================
+
+$TAG_STYLE = {
+ 'default' => [ '\[%', '%\]' ],
+ 'template1' => [ '[\[%]%', '%[\]%]' ],
+ 'metatext' => [ '%%', '%%' ],
+ 'html' => [ '<!--', '-->' ],
+ 'mason' => [ '<%', '>' ],
+ 'asp' => [ '<%', '%>' ],
+ 'php' => [ '<\?', '\?>' ],
+ 'star' => [ '\[\*', '\*\]' ],
+};
+$TAG_STYLE->{ template } = $TAG_STYLE->{ tt2 } = $TAG_STYLE->{ default };
+
+
+$DEFAULT_STYLE = {
+ START_TAG => $TAG_STYLE->{ default }->[0],
+ END_TAG => $TAG_STYLE->{ default }->[1],
+# TAG_STYLE => 'default',
+ ANYCASE => 0,
+ INTERPOLATE => 0,
+ PRE_CHOMP => 0,
+ POST_CHOMP => 0,
+ V1DOLLAR => 0,
+ EVAL_PERL => 0,
+};
+
+$QUOTED_ESCAPES = {
+ n => "\n",
+ r => "\r",
+ t => "\t",
+};
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%config)
+#
+# Constructor method.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $config = $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift(@_) : { @_ };
+ my ($tagstyle, $debug, $start, $end, $defaults, $grammar, $hash, $key, $udef);
+
+ my $self = bless {
+ START_TAG => undef,
+ END_TAG => undef,
+ TAG_STYLE => 'default',
+ ANYCASE => 0,
+ INTERPOLATE => 0,
+ PRE_CHOMP => 0,
+ POST_CHOMP => 0,
+ V1DOLLAR => 0,
+ EVAL_PERL => 0,
+ FILE_INFO => 1,
+ GRAMMAR => undef,
+ _ERROR => '',
+ FACTORY => 'Template::Directive',
+ }, $class;
+
+ # update self with any relevant keys in config
+ foreach $key (keys %$self) {
+ $self->{ $key } = $config->{ $key } if defined $config->{ $key };
+ }
+ $self->{ FILEINFO } = [ ];
+
+ # DEBUG config item can be a bitmask
+ if (defined ($debug = $config->{ DEBUG })) {
+ $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PARSER
+ | Template::Constants::DEBUG_FLAGS );
+ $self->{ DEBUG_DIRS } = $debug & Template::Constants::DEBUG_DIRS;
+ }
+ # package variable can be set to 1 to support previous behaviour
+ elsif ($DEBUG == 1) {
+ $self->{ DEBUG } = Template::Constants::DEBUG_PARSER;
+ $self->{ DEBUG_DIRS } = 0;
+ }
+ # otherwise let $DEBUG be a bitmask
+ else {
+ $self->{ DEBUG } = $DEBUG & ( Template::Constants::DEBUG_PARSER
+ | Template::Constants::DEBUG_FLAGS );
+ $self->{ DEBUG_DIRS } = $DEBUG & Template::Constants::DEBUG_DIRS;
+ }
+
+ $grammar = $self->{ GRAMMAR } ||= do {
+ require Template::Grammar;
+ Template::Grammar->new();
+ };
+
+ # build a FACTORY object to include any NAMESPACE definitions,
+ # but only if FACTORY isn't already an object
+ if ($config->{ NAMESPACE } && ! ref $self->{ FACTORY }) {
+ my $fclass = $self->{ FACTORY };
+ $self->{ FACTORY } = $fclass->new( NAMESPACE => $config->{ NAMESPACE } )
+ || return $class->error($fclass->error());
+ }
+
+ # load grammar rules, states and lex table
+ @$self{ qw( LEXTABLE STATES RULES ) }
+ = @$grammar{ qw( LEXTABLE STATES RULES ) };
+
+ $self->new_style($config)
+ || return $class->error($self->error());
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# new_style(\%config)
+#
+# Install a new (stacked) parser style. This feature is currently
+# experimental but should mimic the previous behaviour with regard to
+# TAG_STYLE, START_TAG, END_TAG, etc.
+#------------------------------------------------------------------------
+
+sub new_style {
+ my ($self, $config) = @_;
+ my $styles = $self->{ STYLE } ||= [ ];
+ my ($tagstyle, $tags, $start, $end, $key);
+
+ # clone new style from previous or default style
+ my $style = { %{ $styles->[-1] || $DEFAULT_STYLE } };
+
+ # expand START_TAG and END_TAG from specified TAG_STYLE
+ if ($tagstyle = $config->{ TAG_STYLE }) {
+ return $self->error("Invalid tag style: $tagstyle")
+ unless defined ($tags = $TAG_STYLE->{ $tagstyle });
+ ($start, $end) = @$tags;
+ $config->{ START_TAG } ||= $start;
+ $config->{ END_TAG } ||= $end;
+ }
+
+ foreach $key (keys %$DEFAULT_STYLE) {
+ $style->{ $key } = $config->{ $key } if defined $config->{ $key };
+ }
+ push(@$styles, $style);
+ return $style;
+}
+
+
+#------------------------------------------------------------------------
+# old_style()
+#
+# Pop the current parser style and revert to the previous one. See
+# new_style(). ** experimental **
+#------------------------------------------------------------------------
+
+sub old_style {
+ my $self = shift;
+ my $styles = $self->{ STYLE };
+ return $self->error('only 1 parser style remaining')
+ unless (@$styles > 1);
+ pop @$styles;
+ return $styles->[-1];
+}
+
+
+#------------------------------------------------------------------------
+# parse($text, $data)
+#
+# Parses the text string, $text and returns a hash array representing
+# the compiled template block(s) as Perl code, in the format expected
+# by Template::Document.
+#------------------------------------------------------------------------
+
+sub parse {
+ my ($self, $text, $info) = @_;
+ my ($tokens, $block);
+
+ $info->{ DEBUG } = $self->{ DEBUG_DIRS }
+ unless defined $info->{ DEBUG };
+
+# print "info: { ", join(', ', map { "$_ => $info->{ $_ }" } keys %$info), " }\n";
+
+ # store for blocks defined in the template (see define_block())
+ my $defblock = $self->{ DEFBLOCK } = { };
+ my $metadata = $self->{ METADATA } = [ ];
+
+ $self->{ _ERROR } = '';
+
+ # split file into TEXT/DIRECTIVE chunks
+ $tokens = $self->split_text($text)
+ || return undef; ## RETURN ##
+
+ push(@{ $self->{ FILEINFO } }, $info);
+
+ # parse chunks
+ $block = $self->_parse($tokens, $info);
+
+ pop(@{ $self->{ FILEINFO } });
+
+ return undef unless $block; ## RETURN ##
+
+ $self->debug("compiled main template document block:\n$block")
+ if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
+
+ return {
+ BLOCK => $block,
+ DEFBLOCKS => $defblock,
+ METADATA => { @$metadata },
+ };
+}
+
+
+
+#------------------------------------------------------------------------
+# split_text($text)
+#
+# Split input template text into directives and raw text chunks.
+#------------------------------------------------------------------------
+
+sub split_text {
+ my ($self, $text) = @_;
+ my ($pre, $dir, $prelines, $dirlines, $postlines, $chomp, $tags, @tags);
+ my $style = $self->{ STYLE }->[-1];
+ my ($start, $end, $prechomp, $postchomp, $interp ) =
+ @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) };
+
+ my @tokens = ();
+ my $line = 1;
+
+ return \@tokens ## RETURN ##
+ unless defined $text && length $text;
+
+ # extract all directives from the text
+ while ($text =~ s/
+ ^(.*?) # $1 - start of line up to directive
+ (?:
+ $start # start of tag
+ (.*?) # $2 - tag contents
+ $end # end of tag
+ )
+ //sx) {
+
+ ($pre, $dir) = ($1, $2);
+ $pre = '' unless defined $pre;
+ $dir = '' unless defined $dir;
+
+ $postlines = 0; # denotes lines chomped
+ $prelines = ($pre =~ tr/\n//); # NULL - count only
+ $dirlines = ($dir =~ tr/\n//); # ditto
+
+ # the directive CHOMP options may modify the preceding text
+ for ($dir) {
+ # remove leading whitespace and check for a '-' chomp flag
+ s/^([-+\#])?\s*//s;
+ if ($1 && $1 eq '#') {
+ # comment out entire directive except for any chomp flag
+ $dir = ($dir =~ /([-+])$/) ? $1 : '';
+ }
+ else {
+ $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $prechomp);
+# my $space = $prechomp == &Template::Constants::CHOMP_COLLAPSE
+ my $space = $prechomp == CHOMP_COLLAPSE
+ ? ' ' : '';
+
+ # chomp off whitespace and newline preceding directive
+ $chomp and $pre =~ s/(\n|^)([ \t]*)\Z/($1||$2) ? $space : ''/me
+ and $1 eq "\n"
+ and $prelines++;
+ }
+
+ # remove trailing whitespace and check for a '-' chomp flag
+ s/\s*([-+])?\s*$//s;
+ $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $postchomp);
+ my $space = $postchomp == &Template::Constants::CHOMP_COLLAPSE
+ ? ' ' : '';
+
+ $postlines++
+ if $chomp and $text =~ s/
+ ^
+ ([ \t]*)\n # whitespace to newline
+ (?:(.|\n)|$) # any char (not EOF)
+ /
+ (($1||$2) ? $space : '') . (defined $2 ? $2 : '')
+ /ex;
+ }
+
+ # any text preceding the directive can now be added
+ if (length $pre) {
+ push(@tokens, $interp
+ ? [ $pre, $line, 'ITEXT' ]
+ : ('TEXT', $pre) );
+ $line += $prelines;
+ }
+
+ # and now the directive, along with line number information
+ if (length $dir) {
+ # the TAGS directive is a compile-time switch
+ if ($dir =~ /^TAGS\s+(.*)/i) {
+ my @tags = split(/\s+/, $1);
+ if (scalar @tags > 1) {
+ ($start, $end) = map { quotemeta($_) } @tags;
+ }
+ elsif ($tags = $TAG_STYLE->{ $tags[0] }) {
+ ($start, $end) = @$tags;
+ }
+ else {
+ warn "invalid TAGS style: $tags[0]\n";
+ }
+ }
+ else {
+ # DIRECTIVE is pushed as:
+ # [ $dirtext, $line_no(s), \@tokens ]
+ push(@tokens,
+ [ $dir,
+ ($dirlines
+ ? sprintf("%d-%d", $line, $line + $dirlines)
+ : $line),
+ $self->tokenise_directive($dir) ]);
+ }
+ }
+
+ # update line counter to include directive lines and any extra
+ # newline chomped off the start of the following text
+ $line += $dirlines + $postlines;
+ }
+
+ # anything remaining in the string is plain text
+ push(@tokens, $interp
+ ? [ $text, $line, 'ITEXT' ]
+ : ( 'TEXT', $text) )
+ if length $text;
+
+ return \@tokens; ## RETURN ##
+}
+
+
+
+#------------------------------------------------------------------------
+# interpolate_text($text, $line)
+#
+# Examines $text looking for any variable references embedded like
+# $this or like ${ this }.
+#------------------------------------------------------------------------
+
+sub interpolate_text {
+ my ($self, $text, $line) = @_;
+ my @tokens = ();
+ my ($pre, $var, $dir);
+
+
+ while ($text =~
+ /
+ ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1]
+ |
+ ( \$ (?: # embedded variable [$2]
+ (?: \{ ([^\}]*) \} ) # ${ ... } [$3]
+ |
+ ([\w\.]+) # $word [$4]
+ )
+ )
+ /gx) {
+
+ ($pre, $var, $dir) = ($1, $3 || $4, $2);
+
+ # preceding text
+ if (defined($pre) && length($pre)) {
+ $line += $pre =~ tr/\n//;
+ $pre =~ s/\\\$/\$/g;
+ push(@tokens, 'TEXT', $pre);
+ }
+ # $variable reference
+ if ($var) {
+ $line += $dir =~ tr/\n/ /;
+ push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]);
+ }
+ # other '$' reference - treated as text
+ elsif ($dir) {
+ $line += $dir =~ tr/\n//;
+ push(@tokens, 'TEXT', $dir);
+ }
+ }
+
+ return \@tokens;
+}
+
+
+
+#------------------------------------------------------------------------
+# tokenise_directive($text)
+#
+# Called by the private _parse() method when it encounters a DIRECTIVE
+# token in the list provided by the split_text() or interpolate_text()
+# methods. The directive text is passed by parameter.
+#
+# The method splits the directive into individual tokens as recognised
+# by the parser grammar (see Template::Grammar for details). It
+# constructs a list of tokens each represented by 2 elements, as per
+# split_text() et al. The first element contains the token type, the
+# second the token itself.
+#
+# The method tokenises the string using a complex (but fast) regex.
+# For a deeper understanding of the regex magic at work here, see
+# Jeffrey Friedl's excellent book "Mastering Regular Expressions",
+# from O'Reilly, ISBN 1-56592-257-3
+#
+# Returns a reference to the list of chunks (each one being 2 elements)
+# identified in the directive text. On error, the internal _ERROR string
+# is set and undef is returned.
+#------------------------------------------------------------------------
+
+sub tokenise_directive {
+ my ($self, $text, $line) = @_;
+ my ($token, $uctoken, $type, $lookup);
+ my $lextable = $self->{ LEXTABLE };
+ my $style = $self->{ STYLE }->[-1];
+ my ($anycase, $start, $end) = @$style{ qw( ANYCASE START_TAG END_TAG ) };
+ my @tokens = ( );
+
+ while ($text =~
+ /
+ # strip out any comments
+ (\#[^\n]*)
+ |
+ # a quoted phrase matches in $3
+ (["']) # $2 - opening quote, ' or "
+ ( # $3 - quoted text buffer
+ (?: # repeat group (no backreference)
+ \\\\ # an escaped backslash \\
+ | # ...or...
+ \\\2 # an escaped quote \" or \' (match $1)
+ | # ...or...
+ . # any other character
+ | \n
+ )*? # non-greedy repeat
+ ) # end of $3
+ \2 # match opening quote
+ |
+ # an unquoted number matches in $4
+ (-?\d+(?:\.\d+)?) # numbers
+ |
+ # filename matches in $5
+ ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+)
+ |
+ # an identifier matches in $6
+ (\w+) # variable identifier
+ |
+ # an unquoted word or symbol matches in $7
+ ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols
+# | \-> # arrow operator (for future?)
+ | [+\-*] # math operations
+ | \$\{? # dollar with option left brace
+ | => # like '='
+ | [=!<>]?= | [!<>] # eqality tests
+ | &&? | \|\|? # boolean ops
+ | \.\.? # n..n sequence
+ | \S+ # something unquoted
+ ) # end of $7
+ /gmxo) {
+
+ # ignore comments to EOL
+ next if $1;
+
+ # quoted string
+ if (defined ($token = $3)) {
+ # double-quoted string may include $variable references
+ if ($2 eq '"') {
+ if ($token =~ /[\$\\]/) {
+ $type = 'QUOTED';
+ # unescape " and \ but leave \$ escaped so that
+ # interpolate_text() doesn't incorrectly treat it
+ # as a variable reference
+# $token =~ s/\\([\\"])/$1/g;
+ for ($token) {
+ s/\\([^\$nrt])/$1/g;
+ s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge;
+ }
+ push(@tokens, ('"') x 2,
+ @{ $self->interpolate_text($token) },
+ ('"') x 2);
+ next;
+ }
+ else {
+ $type = 'LITERAL';
+ $token =~ s['][\\']g;
+ $token = "'$token'";
+ }
+ }
+ else {
+ $type = 'LITERAL';
+ $token = "'$token'";
+ }
+ }
+ # number
+ elsif (defined ($token = $4)) {
+ $type = 'NUMBER';
+ }
+ elsif (defined($token = $5)) {
+ $type = 'FILENAME';
+ }
+ elsif (defined($token = $6)) {
+ # reserved words may be in lower case unless case sensitive
+ $uctoken = $anycase ? uc $token : $token;
+ if (defined ($type = $lextable->{ $uctoken })) {
+ $token = $uctoken;
+ }
+ else {
+ $type = 'IDENT';
+ }
+ }
+ elsif (defined ($token = $7)) {
+ # reserved words may be in lower case unless case sensitive
+ $uctoken = $anycase ? uc $token : $token;
+ unless (defined ($type = $lextable->{ $uctoken })) {
+ $type = 'UNQUOTED';
+ }
+ }
+
+ push(@tokens, $type, $token);
+
+# print(STDERR " +[ $type, $token ]\n")
+# if $DEBUG;
+ }
+
+# print STDERR "tokenise directive() returning:\n [ @tokens ]\n"
+# if $DEBUG;
+
+ return \@tokens; ## RETURN ##
+}
+
+
+#------------------------------------------------------------------------
+# define_block($name, $block)
+#
+# Called by the parser 'defblock' rule when a BLOCK definition is
+# encountered in the template. The name of the block is passed in the
+# first parameter and a reference to the compiled block is passed in
+# the second. This method stores the block in the $self->{ DEFBLOCK }
+# hash which has been initialised by parse() and will later be used
+# by the same method to call the store() method on the calling cache
+# to define the block "externally".
+#------------------------------------------------------------------------
+
+sub define_block {
+ my ($self, $name, $block) = @_;
+ my $defblock = $self->{ DEFBLOCK }
+ || return undef;
+
+ $self->debug("compiled block '$name':\n$block")
+ if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
+
+ $defblock->{ $name } = $block;
+
+ return undef;
+}
+
+sub push_defblock {
+ my $self = shift;
+ my $stack = $self->{ DEFBLOCK_STACK } ||= [];
+ push(@$stack, $self->{ DEFBLOCK } );
+ $self->{ DEFBLOCK } = { };
+}
+
+sub pop_defblock {
+ my $self = shift;
+ my $defs = $self->{ DEFBLOCK };
+ my $stack = $self->{ DEFBLOCK_STACK } || return $defs;
+ return $defs unless @$stack;
+ $self->{ DEFBLOCK } = pop @$stack;
+ return $defs;
+}
+
+
+#------------------------------------------------------------------------
+# add_metadata(\@setlist)
+#------------------------------------------------------------------------
+
+sub add_metadata {
+ my ($self, $setlist) = @_;
+ my $metadata = $self->{ METADATA }
+ || return undef;
+
+ push(@$metadata, @$setlist);
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# location()
+#
+# Return Perl comment indicating current parser file and line
+#------------------------------------------------------------------------
+
+sub location {
+ my $self = shift;
+ return "\n" unless $self->{ FILE_INFO };
+ my $line = ${ $self->{ LINE } };
+ my $info = $self->{ FILEINFO }->[-1];
+ my $file = $info->{ path } || $info->{ name }
+ || '(unknown template)';
+ $line =~ s/\-.*$//; # might be 'n-n'
+ return "#line $line \"$file\"\n";
+}
+
+
+#========================================================================
+# ----- PRIVATE METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _parse(\@tokens, \@info)
+#
+# Parses the list of input tokens passed by reference and returns a
+# Template::Directive::Block object which contains the compiled
+# representation of the template.
+#
+# This is the main parser DFA loop. See embedded comments for
+# further details.
+#
+# On error, undef is returned and the internal _ERROR field is set to
+# indicate the error. This can be retrieved by calling the error()
+# method.
+#------------------------------------------------------------------------
+
+sub _parse {
+ my ($self, $tokens, $info) = @_;
+ my ($token, $value, $text, $line, $inperl);
+ my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars);
+ my ($lhs, $len, $code); # rule contents
+ my $stack = [ [ 0, undef ] ]; # DFA stack
+
+# DEBUG
+# local $" = ', ';
+
+ # retrieve internal rule and state tables
+ my ($states, $rules) = @$self{ qw( STATES RULES ) };
+
+ # call the grammar set_factory method to install emitter factory
+ $self->{ GRAMMAR }->install_factory($self->{ FACTORY });
+
+ $line = $inperl = 0;
+ $self->{ LINE } = \$line;
+ $self->{ FILE } = $info->{ name };
+ $self->{ INPERL } = \$inperl;
+
+ $status = CONTINUE;
+ my $in_string = 0;
+
+ while(1) {
+ # get state number and state
+ $stateno = $stack->[-1]->[0];
+ $state = $states->[$stateno];
+
+ # see if any lookaheads exist for the current state
+ if (exists $state->{'ACTIONS'}) {
+
+ # get next token and expand any directives (i.e. token is an
+ # array ref) onto the front of the token list
+ while (! defined $token && @$tokens) {
+ $token = shift(@$tokens);
+ if (ref $token) {
+ ($text, $line, $token) = @$token;
+ if (ref $token) {
+ if ($info->{ DEBUG } && ! $in_string) {
+ # - - - - - - - - - - - - - - - - - - - - - - - - -
+ # This is gnarly. Look away now if you're easily
+ # frightened. We're pushing parse tokens onto the
+ # pending list to simulate a DEBUG directive like so:
+ # [% DEBUG msg line='20' text='INCLUDE foo' %]
+ # - - - - - - - - - - - - - - - - - - - - - - - - -
+ my $dtext = $text;
+ $dtext =~ s[(['\\])][\\$1]g;
+ unshift(@$tokens,
+ DEBUG => 'DEBUG',
+ IDENT => 'msg',
+ IDENT => 'line',
+ ASSIGN => '=',
+ LITERAL => "'$line'",
+ IDENT => 'text',
+ ASSIGN => '=',
+ LITERAL => "'$dtext'",
+ IDENT => 'file',
+ ASSIGN => '=',
+ LITERAL => "'$info->{ name }'",
+ (';') x 2,
+ @$token,
+ (';') x 2);
+ }
+ else {
+ unshift(@$tokens, @$token, (';') x 2);
+ }
+ $token = undef; # force redo
+ }
+ elsif ($token eq 'ITEXT') {
+ if ($inperl) {
+ # don't perform interpolation in PERL blocks
+ $token = 'TEXT';
+ $value = $text;
+ }
+ else {
+ unshift(@$tokens,
+ @{ $self->interpolate_text($text, $line) });
+ $token = undef; # force redo
+ }
+ }
+ }
+ else {
+ # toggle string flag to indicate if we're crossing
+ # a string boundary
+ $in_string = ! $in_string if $token eq '"';
+ $value = shift(@$tokens);
+ }
+ };
+ # clear undefined token to avoid 'undefined variable blah blah'
+ # warnings and let the parser logic pick it up in a minute
+ $token = '' unless defined $token;
+
+ # get the next state for the current lookahead token
+ $action = defined ($lookup = $state->{'ACTIONS'}->{ $token })
+ ? $lookup
+ : defined ($lookup = $state->{'DEFAULT'})
+ ? $lookup
+ : undef;
+ }
+ else {
+ # no lookahead actions
+ $action = $state->{'DEFAULT'};
+ }
+
+ # ERROR: no ACTION
+ last unless defined $action;
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # shift (+ive ACTION)
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if ($action > 0) {
+ push(@$stack, [ $action, $value ]);
+ $token = $value = undef;
+ redo;
+ };
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # reduce (-ive ACTION)
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ($lhs, $len, $code) = @{ $rules->[ -$action ] };
+
+ # no action imples ACCEPTance
+ $action
+ or $status = ACCEPT;
+
+ # use dummy sub if code ref doesn't exist
+ $code = sub { $_[1] }
+ unless $code;
+
+ @codevars = $len
+ ? map { $_->[1] } @$stack[ -$len .. -1 ]
+ : ();
+
+ eval {
+ $coderet = &$code( $self, @codevars );
+ };
+ if ($@) {
+ my $err = $@;
+ chomp $err;
+ return $self->_parse_error($err);
+ }
+
+ # reduce stack by $len
+ splice(@$stack, -$len, $len);
+
+ # ACCEPT
+ return $coderet ## RETURN ##
+ if $status == ACCEPT;
+
+ # ABORT
+ return undef ## RETURN ##
+ if $status == ABORT;
+
+ # ERROR
+ last
+ if $status == ERROR;
+ }
+ continue {
+ push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs },
+ $coderet ]),
+ }
+
+ # ERROR ## RETURN ##
+ return $self->_parse_error('unexpected end of input')
+ unless defined $value;
+
+ # munge text of last directive to make it readable
+# $text =~ s/\n/\\n/g;
+
+ return $self->_parse_error("unexpected end of directive", $text)
+ if $value eq ';'; # end of directive SEPARATOR
+
+ return $self->_parse_error("unexpected token ($value)", $text);
+}
+
+
+
+#------------------------------------------------------------------------
+# _parse_error($msg, $dirtext)
+#
+# Method used to handle errors encountered during the parse process
+# in the _parse() method.
+#------------------------------------------------------------------------
+
+sub _parse_error {
+ my ($self, $msg, $text) = @_;
+ my $line = $self->{ LINE };
+ $line = ref($line) ? $$line : $line;
+ $line = 'unknown' unless $line;
+
+ $msg .= "\n [% $text %]"
+ if defined $text;
+
+ return $self->error("line $line: $msg");
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method returns a string representing the internal state of the
+# object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Parser] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE
+ PRE_CHOMP POST_CHOMP V1DOLLAR )) {
+ my $val = $self->{ $key };
+ $val = '<undef>' unless defined $val;
+ $output .= sprintf($format, $key, $val);
+ }
+
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Parser - LALR(1) parser for compiling template documents
+
+=head1 SYNOPSIS
+
+ use Template::Parser;
+
+ $parser = Template::Parser->new(\%config);
+ $template = $parser->parse($text)
+ || die $parser->error(), "\n";
+
+=head1 DESCRIPTION
+
+The Template::Parser module implements a LALR(1) parser and associated methods
+for parsing template documents into Perl code.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%params)
+
+The new() constructor creates and returns a reference to a new
+Template::Parser object. A reference to a hash may be supplied as a
+parameter to provide configuration values. These may include:
+
+=over
+
+
+
+
+=item START_TAG, END_TAG
+
+The START_TAG and END_TAG options are used to specify character
+sequences or regular expressions that mark the start and end of a
+template directive. The default values for START_TAG and END_TAG are
+'[%' and '%]' respectively, giving us the familiar directive style:
+
+ [% example %]
+
+Any Perl regex characters can be used and therefore should be escaped
+(or use the Perl C<quotemeta> function) if they are intended to
+represent literal characters.
+
+ my $parser = Template::Parser->new({
+ START_TAG => quotemeta('<+'),
+ END_TAG => quotemeta('+>'),
+ });
+
+example:
+
+ <+ INCLUDE foobar +>
+
+The TAGS directive can also be used to set the START_TAG and END_TAG values
+on a per-template file basis.
+
+ [% TAGS <+ +> %]
+
+
+
+
+
+
+=item TAG_STYLE
+
+The TAG_STYLE option can be used to set both START_TAG and END_TAG
+according to pre-defined tag styles.
+
+ my $parser = Template::Parser->new({
+ TAG_STYLE => 'star',
+ });
+
+Available styles are:
+
+ template [% ... %] (default)
+ template1 [% ... %] or %% ... %% (TT version 1)
+ metatext %% ... %% (Text::MetaText)
+ star [* ... *] (TT alternate)
+ php <? ... ?> (PHP)
+ asp <% ... %> (ASP)
+ mason <% ... > (HTML::Mason)
+ html <!-- ... --> (HTML comments)
+
+Any values specified for START_TAG and/or END_TAG will over-ride
+those defined by a TAG_STYLE.
+
+The TAGS directive may also be used to set a TAG_STYLE
+
+ [% TAGS html %]
+ <!-- INCLUDE header -->
+
+
+
+
+
+
+=item PRE_CHOMP, POST_CHOMP
+
+Anything outside a directive tag is considered plain text and is
+generally passed through unaltered (but see the INTERPOLATE option).
+This includes all whitespace and newlines characters surrounding
+directive tags. Directives that don't generate any output will leave
+gaps in the output document.
+
+Example:
+
+ Foo
+ [% a = 10 %]
+ Bar
+
+Output:
+
+ Foo
+
+ Bar
+
+The PRE_CHOMP and POST_CHOMP options can help to clean up some of this
+extraneous whitespace. Both are disabled by default.
+
+ my $parser = Template::Parser->new({
+ PRE_CHOMP => 1,
+ POST_CHOMP => 1,
+ });
+
+With PRE_CHOMP set to 1, the newline and whitespace preceding a directive
+at the start of a line will be deleted. This has the effect of
+concatenating a line that starts with a directive onto the end of the
+previous line.
+
+ Foo <----------.
+ |
+ ,---(PRE_CHOMP)----'
+ |
+ `-- [% a = 10 %] --.
+ |
+ ,---(POST_CHOMP)---'
+ |
+ `-> Bar
+
+With POST_CHOMP set to 1, any whitespace after a directive up to and
+including the newline will be deleted. This has the effect of joining
+a line that ends with a directive onto the start of the next line.
+
+If PRE_CHOMP or POST_CHOMP is set to 2, then instead of removing all
+the whitespace, the whitespace will be collapsed to a single space.
+This is useful for HTML, where (usually) a contiguous block of
+whitespace is rendered the same as a single space.
+
+You may use the CHOMP_NONE, CHOMP_ALL, and CHOMP_COLLAPSE constants
+from the Template::Constants module to deactivate chomping, remove
+all whitespace, or collapse whitespace to a single space.
+
+PRE_CHOMP and POST_CHOMP can be activated for individual directives by
+placing a '-' immediately at the start and/or end of the directive.
+
+ [% FOREACH user = userlist %]
+ [%- user -%]
+ [% END %]
+
+The '-' characters activate both PRE_CHOMP and POST_CHOMP for the one
+directive '[%- name -%]'. Thus, the template will be processed as if
+written:
+
+ [% FOREACH user = userlist %][% user %][% END %]
+
+Note that this is the same as if PRE_CHOMP and POST_CHOMP were set
+to CHOMP_ALL; the only way to get the CHOMP_COLLAPSE behavior is
+to set PRE_CHOMP or POST_CHOMP accordingly. If PRE_CHOMP or POST_CHOMP
+is already set to CHOMP_COLLAPSE, using '-' will give you CHOMP_COLLAPSE
+behavior, not CHOMP_ALL behavior.
+
+Similarly, '+' characters can be used to disable PRE_CHOMP or
+POST_CHOMP (i.e. leave the whitespace/newline intact) options on a
+per-directive basis.
+
+ [% FOREACH user = userlist %]
+ User: [% user +%]
+ [% END %]
+
+With POST_CHOMP enabled, the above example would be parsed as if written:
+
+ [% FOREACH user = userlist %]User: [% user %]
+ [% END %]
+
+
+
+
+
+=item INTERPOLATE
+
+The INTERPOLATE flag, when set to any true value will cause variable
+references in plain text (i.e. not surrounded by START_TAG and END_TAG)
+to be recognised and interpolated accordingly.
+
+ my $parser = Template::Parser->new({
+ INTERPOLATE => 1,
+ });
+
+Variables should be prefixed by a '$' to identify them. Curly braces
+can be used in the familiar Perl/shell style to explicitly scope the
+variable name where required.
+
+ # INTERPOLATE => 0
+ <a href="http://[% server %]/[% help %]">
+ <img src="[% images %]/help.gif"></a>
+ [% myorg.name %]
+
+ # INTERPOLATE => 1
+ <a href="http://$server/$help">
+ <img src="$images/help.gif"></a>
+ $myorg.name
+
+ # explicit scoping with { }
+ <img src="$images/${icon.next}.gif">
+
+Note that a limitation in Perl's regex engine restricts the maximum length
+of an interpolated template to around 32 kilobytes or possibly less. Files
+that exceed this limit in size will typically cause Perl to dump core with
+a segmentation fault. If you routinely process templates of this size
+then you should disable INTERPOLATE or split the templates in several
+smaller files or blocks which can then be joined backed together via
+PROCESS or INCLUDE.
+
+
+
+
+
+
+
+=item ANYCASE
+
+By default, directive keywords should be expressed in UPPER CASE. The
+ANYCASE option can be set to allow directive keywords to be specified
+in any case.
+
+ # ANYCASE => 0 (default)
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # ERROR
+ [% include = 10 %] # OK, 'include' is a variable
+
+ # ANYCASE => 1
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # OK
+ [% include = 10 %] # ERROR, 'include' is reserved word
+
+One side-effect of enabling ANYCASE is that you cannot use a variable
+of the same name as a reserved word, regardless of case. The reserved
+words are currently:
+
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER
+ IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE
+ USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META
+ TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP
+ CLEAR TO STEP AND OR NOT MOD DIV END
+
+
+The only lower case reserved words that cannot be used for variables,
+regardless of the ANYCASE option, are the operators:
+
+ and or not mod div
+
+
+
+
+
+
+
+
+=item V1DOLLAR
+
+In version 1 of the Template Toolkit, an optional leading '$' could be placed
+on any template variable and would be silently ignored.
+
+ # VERSION 1
+ [% $foo %] === [% foo %]
+ [% $hash.$key %] === [% hash.key %]
+
+To interpolate a variable value the '${' ... '}' construct was used.
+Typically, one would do this to index into a hash array when the key
+value was stored in a variable.
+
+example:
+
+ my $vars = {
+ users => {
+ aba => { name => 'Alan Aardvark', ... },
+ abw => { name => 'Andy Wardley', ... },
+ ...
+ },
+ uid => 'aba',
+ ...
+ };
+
+ $template->process('user/home.html', $vars)
+ || die $template->error(), "\n";
+
+'user/home.html':
+
+ [% user = users.${uid} %] # users.aba
+ Name: [% user.name %] # Alan Aardvark
+
+This was inconsistent with double quoted strings and also the
+INTERPOLATE mode, where a leading '$' in text was enough to indicate a
+variable for interpolation, and the additional curly braces were used
+to delimit variable names where necessary. Note that this use is
+consistent with UNIX and Perl conventions, among others.
+
+ # double quoted string interpolation
+ [% name = "$title ${user.name}" %]
+
+ # INTERPOLATE = 1
+ <img src="$images/help.gif"></a>
+ <img src="$images/${icon.next}.gif">
+
+For version 2, these inconsistencies have been removed and the syntax
+clarified. A leading '$' on a variable is now used exclusively to
+indicate that the variable name should be interpolated
+(e.g. subsituted for its value) before being used. The earlier example
+from version 1:
+
+ # VERSION 1
+ [% user = users.${uid} %]
+ Name: [% user.name %]
+
+can now be simplified in version 2 as:
+
+ # VERSION 2
+ [% user = users.$uid %]
+ Name: [% user.name %]
+
+The leading dollar is no longer ignored and has the same effect of
+interpolation as '${' ... '}' in version 1. The curly braces may
+still be used to explicitly scope the interpolated variable name
+where necessary.
+
+e.g.
+
+ [% user = users.${me.id} %]
+ Name: [% user.name %]
+
+The rule applies for all variables, both within directives and in
+plain text if processed with the INTERPOLATE option. This means that
+you should no longer (if you ever did) add a leading '$' to a variable
+inside a directive, unless you explicitly want it to be interpolated.
+
+One obvious side-effect is that any version 1 templates with variables
+using a leading '$' will no longer be processed as expected. Given
+the following variable definitions,
+
+ [% foo = 'bar'
+ bar = 'baz'
+ %]
+
+version 1 would interpret the following as:
+
+ # VERSION 1
+ [% $foo %] => [% GET foo %] => bar
+
+whereas version 2 interprets it as:
+
+ # VERSION 2
+ [% $foo %] => [% GET $foo %] => [% GET bar %] => baz
+
+In version 1, the '$' is ignored and the value for the variable 'foo' is
+retrieved and printed. In version 2, the variable '$foo' is first interpolated
+to give the variable name 'bar' whose value is then retrieved and printed.
+
+The use of the optional '$' has never been strongly recommended, but
+to assist in backwards compatibility with any version 1 templates that
+may rely on this "feature", the V1DOLLAR option can be set to 1
+(default: 0) to revert the behaviour and have leading '$' characters
+ignored.
+
+ my $parser = Template::Parser->new({
+ V1DOLLAR => 1,
+ });
+
+
+
+
+
+
+=item GRAMMAR
+
+The GRAMMAR configuration item can be used to specify an alternate
+grammar for the parser. This allows a modified or entirely new
+template language to be constructed and used by the Template Toolkit.
+
+Source templates are compiled to Perl code by the Template::Parser
+using the Template::Grammar (by default) to define the language
+structure and semantics. Compiled templates are thus inherently
+"compatible" with each other and there is nothing to prevent any
+number of different template languages being compiled and used within
+the same Template Toolkit processing environment (other than the usual
+time and memory constraints).
+
+The Template::Grammar file is constructed from a YACC like grammar
+(using Parse::YAPP) and a skeleton module template. These files are
+provided, along with a small script to rebuild the grammar, in the
+'parser' sub-directory of the distribution. You don't have to know or
+worry about these unless you want to hack on the template language or
+define your own variant. There is a README file in the same directory
+which provides some small guidance but it is assumed that you know
+what you're doing if you venture herein. If you grok LALR parsers,
+then you should find it comfortably familiar.
+
+By default, an instance of the default Template::Grammar will be
+created and used automatically if a GRAMMAR item isn't specified.
+
+ use MyOrg::Template::Grammar;
+
+ my $parser = Template::Parser->new({
+ GRAMMAR = MyOrg::Template::Grammar->new();
+ });
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable various debugging features
+of the Template::Parser module.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_PARSER | DEBUG_DIRS,
+ });
+
+The DEBUG value can include any of the following. Multiple values
+should be combined using the logical OR operator, '|'.
+
+=over 4
+
+=item DEBUG_PARSER
+
+This flag causes the L<Template::Parser|Template::Parser> to generate
+debugging messages that show the Perl code generated by parsing and
+compiling each template.
+
+=item DEBUG_DIRS
+
+This option causes the Template Toolkit to generate comments
+indicating the source file, line and original text of each directive
+in the template. These comments are embedded in the template output
+using the format defined in the DEBUG_FORMAT configuration item, or a
+simple default format if unspecified.
+
+For example, the following template fragment:
+
+
+ Hello World
+
+would generate this output:
+
+ ## input text line 1 : ##
+ Hello
+ ## input text line 2 : World ##
+ World
+
+
+=back
+
+
+
+
+=back
+
+=head2 parse($text)
+
+The parse() method parses the text passed in the first parameter and
+returns a reference to a Template::Document object which contains the
+compiled representation of the template text. On error, undef is
+returned.
+
+Example:
+
+ $doc = $parser->parse($text)
+ || die $parser->error();
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+
+
+=head1 VERSION
+
+2.81, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+The original Template::Parser module was derived from a standalone
+parser generated by version 0.16 of the Parse::Yapp module. The
+following copyright notice appears in the Parse::Yapp documentation.
+
+ The Parse::Yapp module and its related modules and shell
+ scripts are copyright (c) 1998 Francois Desarmenien,
+ France. All rights reserved.
+
+ You may use and distribute them under the terms of either
+ the GNU General Public License or the Artistic License, as
+ specified in the Perl README file.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Grammar|Template::Grammar>, L<Template::Directive|Template::Directive>
+
diff --git a/lib/Template/Plugin.pm b/lib/Template/Plugin.pm
new file mode 100644
index 0000000..a6c9df2
--- /dev/null
+++ b/lib/Template/Plugin.pm
@@ -0,0 +1,409 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin
+#
+# DESCRIPTION
+#
+# Module defining a base class for a plugin object which can be loaded
+# and instantiated via the USE directive.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Plugin.pm,v 2.65 2004/01/13 16:19:15 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin;
+
+require 5.004;
+
+use strict;
+use Template::Base;
+
+use vars qw( $VERSION $DEBUG $ERROR $AUTOLOAD );
+use base qw( Template::Base );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.65 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0;
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# load()
+#
+# Class method called when the plugin module is first loaded. It
+# returns the name of a class (by default, its own class) or a prototype
+# object which will be used to instantiate new objects. The new()
+# method is then called against the class name (class method) or
+# prototype object (object method) to create a new instances of the
+# object.
+#------------------------------------------------------------------------
+
+sub load {
+ return $_[0];
+}
+
+
+#------------------------------------------------------------------------
+# new($context, $delegate, @params)
+#
+# Object constructor which is called by the Template::Context to
+# instantiate a new Plugin object. This base class constructor is
+# used as a general mechanism to load and delegate to other Perl
+# modules. The context is passed as the first parameter, followed by
+# a reference to a delegate object or the name of the module which
+# should be loaded and instantiated. Any additional parameters passed
+# to the USE directive are forwarded to the new() constructor.
+#
+# A plugin object is returned which has an AUTOLOAD method to delegate
+# requests to the underlying object.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ bless {
+ }, $class;
+}
+
+sub old_new {
+ my ($class, $context, $delclass, @params) = @_;
+ my ($delegate, $delmod);
+
+ return $class->error("no context passed to $class constructor\n")
+ unless defined $context;
+
+ if (ref $delclass) {
+ # $delclass contains a reference to a delegate object
+ $delegate = $delclass;
+ }
+ else {
+ # delclass is the name of a module to load and instantiate
+ ($delmod = $delclass) =~ s|::|/|g;
+
+ eval {
+ require "$delmod.pm";
+ $delegate = $delclass->new(@params)
+ || die "failed to instantiate $delclass object\n";
+ };
+ return $class->error($@) if $@;
+ }
+
+ bless {
+ _CONTEXT => $context,
+ _DELEGATE => $delegate,
+ _PARAMS => \@params,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# fail($error)
+#
+# Version 1 error reporting function, now replaced by error() inherited
+# from Template::Base. Raises a "deprecated function" warning and then
+# calls error().
+#------------------------------------------------------------------------
+
+sub fail {
+ my $class = shift;
+ my ($pkg, $file, $line) = caller();
+ warn "Template::Plugin::fail() is deprecated at $file line $line. Please use error()\n";
+ $class->error(@_);
+}
+
+
+#========================================================================
+# ----- OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# General catch-all method which delegates all calls to the _DELEGATE
+# object.
+#------------------------------------------------------------------------
+
+sub OLD_AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ if (ref $self eq 'HASH') {
+ my $delegate = $self->{ _DELEGATE } || return;
+ return $delegate->$method(@_);
+ }
+ my ($pkg, $file, $line) = caller();
+# warn "no such '$method' method called on $self at $file line $line\n";
+ return undef;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin - Base class for Template Toolkit plugins
+
+=head1 SYNOPSIS
+
+ package MyOrg::Template::Plugin::MyPlugin;
+ use base qw( Template::Plugin );
+ use Template::Plugin;
+ use MyModule;
+
+ sub new {
+ my $class = shift;
+ my $context = shift;
+ bless {
+ ...
+ }, $class;
+ }
+
+=head1 DESCRIPTION
+
+A "plugin" for the Template Toolkit is simply a Perl module which
+exists in a known package location (e.g. Template::Plugin::*) and
+conforms to a regular standard, allowing it to be loaded and used
+automatically.
+
+The Template::Plugin module defines a base class from which other
+plugin modules can be derived. A plugin does not have to be derived
+from Template::Plugin but should at least conform to its object-oriented
+interface.
+
+It is recommended that you create plugins in your own package namespace
+to avoid conflict with toolkit plugins. e.g.
+
+ package MyOrg::Template::Plugin::FooBar;
+
+Use the PLUGIN_BASE option to specify the namespace that you use. e.g.
+
+ use Template;
+ my $template = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+=head1 PLUGIN API
+
+The following methods form the basic interface between the Template
+Toolkit and plugin modules.
+
+=over 4
+
+=item load($context)
+
+This method is called by the Template Toolkit when the plugin module
+is first loaded. It is called as a package method and thus implicitly
+receives the package name as the first parameter. A reference to the
+Template::Context object loading the plugin is also passed. The
+default behaviour for the load() method is to simply return the class
+name. The calling context then uses this class name to call the new()
+package method.
+
+ package MyPlugin;
+
+ sub load { # called as MyPlugin->load($context)
+ my ($class, $context) = @_;
+ return $class; # returns 'MyPlugin'
+ }
+
+=item new($context, @params)
+
+This method is called to instantiate a new plugin object for the USE
+directive. It is called as a package method against the class name
+returned by load(). A reference to the Template::Context object creating
+the plugin is passed, along with any additional parameters specified in
+the USE directive.
+
+ sub new { # called as MyPlugin->new($context)
+ my ($class, $context, @params) = @_;
+ bless {
+ _CONTEXT => $context,
+ }, $class; # returns blessed MyPlugin object
+ }
+
+=item error($error)
+
+This method, inherited from the Template::Base module, is used for
+reporting and returning errors. It can be called as a package method
+to set/return the $ERROR package variable, or as an object method to
+set/return the object _ERROR member. When called with an argument, it
+sets the relevant variable and returns undef. When called without an
+argument, it returns the value of the variable.
+
+ sub new {
+ my ($class, $context, $dsn) = @_;
+
+ return $class->error('No data source specified')
+ unless $dsn;
+
+ bless {
+ _DSN => $dsn,
+ }, $class;
+ }
+
+ ...
+
+ my $something = MyModule->new()
+ || die MyModule->error(), "\n";
+
+ $something->do_something()
+ || die $something->error(), "\n";
+
+=back
+
+=head1 DEEPER MAGIC
+
+The Template::Context object that handles the loading and use of
+plugins calls the new() and error() methods against the package name
+returned by the load() method. In pseudo-code terms, it might look
+something like this:
+
+ $class = MyPlugin->load($context); # returns 'MyPlugin'
+
+ $object = $class->new($context, @params) # MyPlugin->new(...)
+ || die $class->error(); # MyPlugin->error()
+
+The load() method may alterately return a blessed reference to an
+object instance. In this case, new() and error() are then called as
+I<object> methods against that prototype instance.
+
+ package YourPlugin;
+
+ sub load {
+ my ($class, $context) = @_;
+ bless {
+ _CONTEXT => $context,
+ }, $class;
+ }
+
+ sub new {
+ my ($self, $context, @params) = @_;
+ return $self;
+ }
+
+In this example, we have implemented a 'Singleton' plugin. One object
+gets created when load() is called and this simply returns itself for
+each call to new().
+
+Another implementation might require individual objects to be created
+for every call to new(), but with each object sharing a reference to
+some other object to maintain cached data, database handles, etc.
+This pseudo-code example demonstrates the principle.
+
+ package MyServer;
+
+ sub load {
+ my ($class, $context) = @_;
+ bless {
+ _CONTEXT => $context,
+ _CACHE => { },
+ }, $class;
+ }
+
+ sub new {
+ my ($self, $context, @params) = @_;
+ MyClient->new($self, @params);
+ }
+
+ sub add_to_cache { ... }
+
+ sub get_from_cache { ... }
+
+
+ package MyClient;
+
+ sub new {
+ my ($class, $server, $blah) = @_;
+ bless {
+ _SERVER => $server,
+ _BLAH => $blah,
+ }, $class;
+ }
+
+ sub get {
+ my $self = shift;
+ $self->{ _SERVER }->get_from_cache(@_);
+ }
+
+ sub put {
+ my $self = shift;
+ $self->{ _SERVER }->add_to_cache(@_);
+ }
+
+When the plugin is loaded, a MyServer instance is created. The new()
+method is called against this object which instantiates and returns a
+MyClient object, primed to communicate with the creating MyServer.
+
+=head1 Template::Plugin Delegation
+
+As of version 2.01, the Template::Plugin module no longer provides an
+AUTOLOAD method to delegate to other objects or classes. This was a
+badly designed feature that caused more trouble than good. You can
+easily add your own AUTOLOAD method to perform delegation if you
+require this kind of functionality.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.65, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Plugins|Template::Plugins>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Autoformat.pm b/lib/Template/Plugin/Autoformat.pm
new file mode 100644
index 0000000..b7153e4
--- /dev/null
+++ b/lib/Template/Plugin/Autoformat.pm
@@ -0,0 +1,242 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Autoformat
+#
+# DESCRIPTION
+# Plugin interface to Damian Conway's Text::Autoformat module.
+#
+# AUTHORS
+# Robert McArthur <mcarthur@dstc.edu.au>
+# - original plugin code
+#
+# Andy Wardley <abw@kfs.org>
+# - added FILTER registration, support for forms and some additional
+# documentation
+#
+# COPYRIGHT
+# Copyright (C) 2000 Robert McArthur & Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Autoformat.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Autoformat;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+use Text::Autoformat;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+sub new {
+ my ($class, $context, $options) = @_;
+ my $filter_factory;
+ my $plugin;
+
+ if ($options) {
+ # create a closure to generate filters with additional options
+ $filter_factory = sub {
+ my $context = shift;
+ my $filtopt = ref $_[-1] eq 'HASH' ? pop : { };
+ @$filtopt{ keys %$options } = values %$options;
+ return sub {
+ tt_autoformat(@_, $filtopt);
+ };
+ };
+
+ # and a closure to represent the plugin
+ $plugin = sub {
+ my $plugopt = ref $_[-1] eq 'HASH' ? pop : { };
+ @$plugopt{ keys %$options } = values %$options;
+ tt_autoformat(@_, $plugopt);
+ };
+ }
+ else {
+ # simple filter factory closure (no legacy options from constructor)
+ $filter_factory = sub {
+ my $context = shift;
+ my $filtopt = ref $_[-1] eq 'HASH' ? pop : { };
+ return sub {
+ tt_autoformat(@_, $filtopt);
+ };
+ };
+
+ # plugin without options can be static
+ $plugin = \&tt_autoformat;
+ }
+
+ # now define the filter and return the plugin
+ $context->define_filter('autoformat', [ $filter_factory => 1 ]);
+ return $plugin;
+}
+
+sub tt_autoformat {
+ my $options = ref $_[-1] eq 'HASH' ? pop : { };
+ my $form = $options->{ form };
+ my $out = $form ? Text::Autoformat::form($options, $form, @_)
+ : Text::Autoformat::autoformat(join('', @_), $options);
+ return $out;
+}
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Autoformat - Interface to Text::Autoformat module
+
+=head1 SYNOPSIS
+
+ [% USE autoformat(options) %]
+
+ [% autoformat(text, more_text, ..., options) %]
+
+ [% FILTER autoformat(options) %]
+ a block of text
+ [% END %]
+
+=head1 EXAMPLES
+
+ # define some text for the examples
+ [% text = BLOCK %]
+ Be not afeard. The isle is full of noises, sounds and sweet
+ airs that give delight but hurt not.
+ [% END %]
+
+ # pass options to constructor...
+ [% USE autoformat(case => 'upper') %]
+ [% autoformat(text) %]
+
+ # and/or pass options to the autoformat subroutine itself
+ [% USE autoformat %]
+ [% autoformat(text, case => 'upper') %]
+
+ # using the autoformat filter
+ [% USE autoformat(left => 10, right => 30) %]
+ [% FILTER autoformat %]
+ Be not afeard. The isle is full of noises, sounds and sweet
+ airs that give delight but hurt not.
+ [% END %]
+
+ # another filter example with configuration options
+ [% USE autoformat %]
+ [% FILTER autoformat(left => 20) %]
+ Be not afeard. The isle is full of noises, sounds and sweet
+ airs that give delight but hurt not.
+ [% END %]
+
+ # another FILTER example, defining a 'poetry' filter alias
+ [% USE autoformat %]
+ [% text FILTER poetry = autoformat(left => 20, right => 40) %]
+
+ # reuse the 'poetry' filter alias
+ [% text FILTER poetry %]
+
+ # shorthand form ('|' is an alias for 'FILTER')
+ [% text | autoformat %]
+
+ # using forms
+ [% USE autoformat(form => '>>>>.<<<', numeric => 'AllPlaces') %]
+ [% autoformat(10, 20.32, 11.35) %]
+
+=head1 DESCRIPTION
+
+The autoformat plugin is an interface to Damian Conway's Text::Autoformat
+Perl module which provides advanced text wrapping and formatting.
+
+Configuration options may be passed to the plugin constructor via the
+USE directive.
+
+ [% USE autoformat(right => 30) %]
+
+The autoformat subroutine can then be called, passing in text items which
+will be wrapped and formatted according to the current configuration.
+
+ [% autoformat('The cat sat on the mat') %]
+
+Additional configuration items can be passed to the autoformat subroutine
+and will be merged with any existing configuration specified via the
+constructor.
+
+ [% autoformat(text, left => 20) %]
+
+Configuration options are passed directly to the Text::Autoformat plugin.
+At the time of writing, the basic configuration items are:
+
+ left left margin (default: 1)
+ right right margin (default 72)
+ justify justification as one of 'left', 'right', 'full'
+ or 'centre' (default: left)
+ case case conversion as one of 'lower', 'upper',
+ 'sentence', 'title', or 'highlight' (default: none)
+ squeeze squeeze whitespace (default: enabled)
+
+The plugin also accepts a 'form' item which can be used to define a
+format string. When a form is defined, the plugin will call the
+underlying form() subroutine in preference to autoformat().
+
+ [% USE autoformat(form => '>>>>.<<') %]
+ [% autoformat(123.45, 666, 3.14) %]
+
+Additional configuration items relevant to forms can also be specified.
+
+ [% USE autoformat(form => '>>>>.<<', numeric => 'AllPlaces') %]
+ [% autoformat(123.45, 666, 3.14) %]
+
+These can also be passed directly to the autoformat subroutine.
+
+ [% USE autoformat %]
+ [% autoformat( 123.45, 666, 3.14,
+ form => '>>>>.<<',
+ numeric => 'AllPlaces' )
+ %]
+
+See L<Text::Autoformat> for further details.
+
+=head1 AUTHORS
+
+Robert McArthur E<lt>mcarthur@dstc.edu.auE<gt> wrote the original plugin
+code, with some modifications and additions from Andy Wardley
+E<lt>abw@wardley.orgE<gt>.
+
+Damian Conway E<lt>damian@conway.orgE<gt> wrote the Text::Autoformat
+module (in his copious spare time :-) which does all the clever stuff.
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Robert McArthur & Andy Wardley. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Text::Autoformat|Text::Autoformat>
+
diff --git a/lib/Template/Plugin/CGI.pm b/lib/Template/Plugin/CGI.pm
new file mode 100644
index 0000000..53b19d8
--- /dev/null
+++ b/lib/Template/Plugin/CGI.pm
@@ -0,0 +1,168 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::CGI
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the CGI.pm module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: CGI.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::CGI;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+use CGI;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ CGI->new(@_);
+}
+
+package CGI;
+
+sub params {
+ my $self = shift;
+ local $" = ', ';
+
+ return $self->{ _TT_PARAMS } ||= do {
+ # must call Vars() in a list context to receive
+ # plain list of key/vals rather than a tied hash
+ my $params = { $self->Vars() };
+
+ # convert any null separated values into lists
+ @$params{ keys %$params } = map {
+ /\0/ ? [ split /\0/ ] : $_
+ } values %$params;
+
+ $params;
+ };
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::CGI - Interface to the CGI module
+
+=head1 SYNOPSIS
+
+ [% USE CGI %]
+ [% CGI.param('parameter') %]
+
+ [% USE things = CGI %]
+ [% things.param('name') %]
+
+ # see CGI docs for other methods provided by the CGI object
+
+=head1 DESCRIPTION
+
+This is a very simple Template Toolkit Plugin interface to the CGI module.
+A CGI object will be instantiated via the following directive:
+
+ [% USE CGI %]
+
+CGI methods may then be called as follows:
+
+ [% CGI.header %]
+ [% CGI.param('parameter') %]
+
+An alias can be used to provide an alternate name by which the object should
+be identified.
+
+ [% USE mycgi = CGI %]
+ [% mycgi.start_form %]
+ [% mycgi.popup_menu({ Name => 'Color'
+ Values => [ 'Green' 'Black' 'Brown' ] }) %]
+
+Parenthesised parameters to the USE directive will be passed to the plugin
+constructor:
+
+ [% USE cgiprm = CGI('uid=abw&name=Andy+Wardley') %]
+ [% cgiprm.param('uid') %]
+
+=head1 METHODS
+
+In addition to all the methods supported by the CGI module, this
+plugin defines the following.
+
+=head2 params()
+
+This method returns a reference to a hash of all the CGI parameters.
+Any parameters that have multiple values will be returned as lists.
+
+ [% USE CGI('user=abw&item=foo&item=bar') %]
+
+ [% CGI.params.user %] # abw
+ [% CGI.params.item.join(', ') %] # foo, bar
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<CGI|CGI>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/DBI.pm b/lib/Template/Plugin/DBI.pm
new file mode 100644
index 0000000..b916bfc
--- /dev/null
+++ b/lib/Template/Plugin/DBI.pm
@@ -0,0 +1,947 @@
+#==============================================================================
+#
+# Template::Plugin::DBI
+#
+# DESCRIPTION
+# A Template Toolkit plugin to provide access to a DBI data source.
+#
+# AUTHORS
+# Original version by Simon Matthews <sam@knowledgepool.com>
+# with some reworking by Andy Wardley <abw@kfs.org> and other
+# contributions from Craig Barratt <craig@arraycomm.com>,
+# Dave Hodgkinson <daveh@davehodgkinson.com> and Rafael Kitover
+# <caelum@debian.org>
+#
+# COPYRIGHT
+# Copyright (C) 1999-2000 Simon Matthews. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: DBI.pm,v 2.62 2004/01/13 16:20:38 abw Exp $
+#
+#==============================================================================
+
+package Template::Plugin::DBI;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use Template::Exception;
+use DBI;
+
+use vars qw( $VERSION $DEBUG $QUERY $ITERATOR );
+use base qw( Template::Plugin );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+$QUERY = 'Template::Plugin::DBI::Query';
+$ITERATOR = 'Template::Plugin::DBI::Iterator';
+
+# alias _connect() to connect() for backwards compatability
+*_connect = \*connect;
+
+
+#------------------------------------------------------------------------
+# new($context, @params)
+#
+# Constructor which returns a reference to a new DBI plugin object.
+# A connection string (dsn), user name and password may be passed as
+# positional arguments or a hash array of connection parameters can be
+# passed to initialise a connection. Otherwise, an unconnected DBI
+# plugin object is returned.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ my $self = ref $class ? $class : bless {
+ _CONTEXT => $context,
+ _STH => [ ],
+ }, $class;
+
+ $self->connect(@_) if @_;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# connect( $data_source, $username, $password, $attributes )
+# connect( { data_source => 'dbi:driver:database'
+# username => 'foo'
+# password => 'bar' } )
+#
+# Opens a DBI connection for the plugin.
+#------------------------------------------------------------------------
+
+sub connect {
+ my $self = shift;
+ my $params = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+ my ($dbh, $dsn, $user, $pass, $klobs);
+
+ # set debug flag
+ $DEBUG = $params->{ debug } if exists $params->{ debug };
+ $self->{ _DEBUG } = $params->{ debug } || 0;
+
+ # fetch 'dbh' named paramater or use positional arguments or named
+ # parameters to specify 'dsn', 'user' and 'pass'
+
+ if ($dbh = $params->{ dbh }) {
+ # disconnect any existing database handle that we previously opened
+ $self->{ _DBH }->disconnect()
+ if $self->{ _DBH } && $self->{ _DBH_CONNECT };
+
+ # store new dbh but leave _DBH_CONNECT false to prevent us
+ # from automatically closing it in the future
+ $self->{ _DBH } = $dbh;
+ $self->{ _DBH_CONNECT } = 0;
+ }
+ else {
+
+ # certain Perl programmers are known to have problems with short
+ # term memory loss (see Tie::Hash::Cannabinol) so we let the poor
+ # blighters fumble any kind of argument that looks like it might
+ # identify the database
+
+ $dsn = shift
+ || $params->{ data_source }
+ || $params->{ database }
+ || $params->{ connect }
+ || $params->{ dsn }
+ || $params->{ db }
+ || $ENV{DBI_DSN}
+ || return $self->_throw('data source not defined');
+
+ # add 'dbi:' prefix if it's not there
+ $dsn = "dbi:$dsn" unless $dsn =~ /^dbi:/i;
+
+ $user = shift
+ || $params->{ username }
+ || $params->{ user };
+
+ $pass = shift
+ || $params->{ password }
+ || $params->{ pass };
+
+ # save connection data because we might need it later to do a tie()
+ @$self{ qw( _DSN _USER _PASS ) } = ($dsn, $user, $pass);
+
+ # reuse existing database handle if connection params match
+ my $connect = join(':', $dsn || '', $user || '', $pass || '');
+ return ''
+ if $self->{ _DBH } && $self->{ _DBH_CONNECT } eq $connect;
+
+ # otherwise disconnect any existing database handle that we opened
+ $self->{ _DBH }->disconnect()
+ if $self->{ _DBH } && $self->{ _DBH_CONNECT };
+
+ # don't need DBI to automatically print errors because all calls go
+ # via this plugin interface and we always check return values
+ $params->{ PrintError } = 0
+ unless defined $params->{ PrintError };
+
+ $self->{ _DBH } = DBI->connect_cached( $dsn, $user, $pass, $params )
+ || return $self->_throw("DBI connect failed: $DBI::errstr");
+
+ # store the connection parameters
+ $self->{ _DBH_CONNECT } = $connect;
+ }
+
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# disconnect()
+#
+# Disconnects the current active database connection.
+#------------------------------------------------------------------------
+
+sub disconnect {
+ my $self = shift;
+ $self->{ _DBH }->disconnect()
+ if $self->{ _DBH };
+ delete $self->{ _DBH };
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# tie( $table, $key )
+#
+# Return a hash tied to a table in the database, indexed by the specified
+# key.
+#------------------------------------------------------------------------
+
+sub tie {
+ my $self = shift;
+ my $params = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+ my ($table, $key, $klobs, $debug, %hash);
+
+ eval { require Tie::DBI };
+ $self->_throw("failed to load Tie::DBI module: $@") if $@;
+
+ $table = shift
+ || $params->{ table }
+ || $self->_throw('table not defined');
+
+ $key = shift
+ || $params->{ key }
+ || $self->_throw('key not defined');
+
+ # Achtung der Klobberman!
+ $klobs = $params->{ clobber };
+ $klobs = $params->{ CLOBBER } unless defined $klobs;
+
+ # going the extra mile to allow user to use UPPER or lower case or
+ # inherit internel debug flag set by connect()
+ $debug = $params->{ debug };
+ $debug = $params->{ DEBUG } unless defined $debug;
+ $debug = $self->{ _DEBUG } unless defined $debug;
+
+ tie %hash, 'Tie::DBI', {
+ %$params, # any other Tie::DBI options like DEBUG, WARN, etc
+ db => $self->{ _DBH } || $self->{ _DSN },
+ user => $self->{ _USER },
+ password => $self->{ _PASS },
+ table => $table,
+ key => $key,
+ CLOBBER => $klobs || 0,
+ DEBUG => $debug || 0,
+ };
+
+ return \%hash;
+}
+
+
+#------------------------------------------------------------------------
+# prepare($sql)
+#
+# Prepare a query and store the live statement handle internally for
+# subsequent execute() calls.
+#------------------------------------------------------------------------
+
+sub prepare {
+ my $self = shift;
+ my $sql = shift || return undef;
+
+ my $sth = $self->dbh->prepare($sql)
+ || return $self->_throw("DBI prepare failed: $DBI::errstr");
+
+ # create wrapper object around handle to return to template client
+ $sth = $QUERY->new($sth);
+ push(@{ $self->{ _STH } }, $sth);
+
+ return $sth;
+}
+
+
+#------------------------------------------------------------------------
+# execute()
+#
+# Calls execute() on the most recent statement created via prepare().
+#------------------------------------------------------------------------
+
+sub execute {
+ my $self = shift;
+
+ my $sth = $self->{ _STH }->[-1]
+ || return $self->_throw('no query prepared');
+
+ $sth->execute(@_);
+}
+
+
+#------------------------------------------------------------------------
+# query($sql, @params)
+#
+# Prepares and executes a SQL query.
+#------------------------------------------------------------------------
+
+sub query {
+ my $self = shift;
+ my $sql = shift;
+
+ $self->prepare($sql)->execute(@_);
+}
+
+
+#------------------------------------------------------------------------
+# do($sql, \%attr, @bind)
+#
+# Prepares and executes a SQL statement.
+#------------------------------------------------------------------------
+
+sub do {
+ my $self = shift;
+
+ return $self->dbh->do(@_)
+ || $self->_throw("DBI do failed: $DBI::errstr");
+}
+
+
+#------------------------------------------------------------------------
+# quote($value [, $data_type ])
+#
+# Returns a quoted string (correct for the connected database) from the
+# value passed in.
+#------------------------------------------------------------------------
+
+sub quote {
+ my $self = shift;
+ $self->dbh->quote(@_);
+}
+
+
+#------------------------------------------------------------------------
+# dbh()
+#
+# Internal method to retrieve the database handle belonging to the
+# instance or attempt to create a new one using connect.
+#------------------------------------------------------------------------
+
+sub dbh {
+ my $self = shift;
+
+ return $self->{ _DBH } || do {
+ $self->connect;
+ $self->{ _DBH };
+ };
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# Called automatically when the plugin object goes out of scope to
+# disconnect the database handle cleanly
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+ delete($self->{ _STH }); # first DESTROY any queries
+ $self->{ _DBH }->disconnect()
+ if $self->{ _DBH } && $self->{ _DBH_CONNECT };
+}
+
+
+#------------------------------------------------------------------------
+# _throw($error)
+#
+# Raise an error by throwing it via die() as a Template::Exception
+# object of type 'DBI'.
+#------------------------------------------------------------------------
+
+sub _throw {
+ my $self = shift;
+ my $error = shift || die "DBI throw() called without an error string\n";
+
+ # throw error as DBI exception
+ die (Template::Exception->new('DBI', $error));
+}
+
+
+#========================================================================
+# Template::Plugin::DBI::Query
+#========================================================================
+
+package Template::Plugin::DBI::Query;
+use vars qw( $DEBUG $ITERATOR );
+
+*DEBUG = \$Template::Plugin::DBI::DEBUG;
+*ITERATOR = \$Template::Plugin::DBI::ITERATOR;
+
+
+sub new {
+ my ($class, $sth) = @_;
+ bless \$sth, $class;
+}
+
+sub execute {
+ my $self = shift;
+
+ $$self->execute(@_)
+ || return Template::Plugin::DBI->_throw("execute failed: $DBI::errstr");
+
+ $ITERATOR->new($$self);
+}
+
+sub DESTROY {
+ my $self = shift;
+ $$self->finish();
+}
+
+
+#========================================================================
+# Template::Plugin::DBI::Iterator;
+#========================================================================
+
+package Template::Plugin::DBI::Iterator;
+
+use Template::Iterator;
+use base qw( Template::Iterator );
+use vars qw( $DEBUG );
+
+*DEBUG = \$Template::Plugin::DBI::DEBUG;
+
+
+sub new {
+ my ($class, $sth, $params) = @_;
+
+ my $rows = $sth->rows();
+
+ my $self = bless {
+ _STH => $sth,
+ SIZE => $rows,
+ MAX => $rows - 1,
+ }, $class;
+
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# get_first()
+#
+# Initialises iterator to read from statement handle. We maintain a
+# one-record lookahead buffer to allow us to detect if the current
+# record is the last in the series.
+#------------------------------------------------------------------------
+
+sub get_first {
+ my $self = shift;
+ $self->{ _STARTED } = 1;
+
+ # set some status variables into $self
+ @$self{ qw( PREV ITEM FIRST LAST COUNT INDEX ) }
+ = ( undef, undef, 2, 0, 0, -1 );
+
+ # support 'number' as an alias for 'count' for backwards compatability
+ $self->{ NUMBER } = 0;
+
+ print STDERR "get_first() called\n" if $DEBUG;
+
+ # get the first row
+ $self->_fetchrow();
+
+ print STDERR "get_first() calling get_next()\n" if $DEBUG;
+
+ return $self->get_next();
+}
+
+
+#------------------------------------------------------------------------
+# get_next()
+#
+# Called to read remaining result records from statement handle.
+#------------------------------------------------------------------------
+
+sub get_next {
+ my $self = shift;
+ my ($data, $fixup);
+
+ # increment the 'index' and 'count' counts
+ $self->{ INDEX }++;
+ $self->{ COUNT }++;
+ $self->{ NUMBER }++; # 'number' is old name for 'count'
+
+ # decrement the 'first-record' flag
+ $self->{ FIRST }-- if $self->{ FIRST };
+
+ # we should have a row already cache in NEXT
+ return (undef, Template::Constants::STATUS_DONE)
+ unless $data = $self->{ NEXT };
+
+ # set PREV to be current ITEM from last iteration
+ $self->{ PREV } = $self->{ ITEM };
+
+ # look ahead to the next row so that the rowcache is refilled
+ $self->_fetchrow();
+
+ $self->{ ITEM } = $data;
+ return ($data, Template::Constants::STATUS_OK);
+}
+
+
+sub get {
+ my $self = shift;
+ my ($data, $error);
+
+ ($data, $error) = $self->{ _STARTED }
+ ? $self->get_next() : $self->get_first();
+
+ return $data;
+}
+
+
+sub get_all {
+ my $self = shift;
+ my $sth = $self->{ _STH };
+ my $error;
+
+ my $data = $sth->fetchall_arrayref({});
+ $self->throw($error) if ($error = $sth->err());
+ unshift(@$data, $self->{ NEXT }) if $self->{ NEXT };
+ $self->{ LAST } = 1;
+ $self->{ NEXT } = undef;
+ $sth->finish();
+
+ return $data;
+}
+
+
+#------------------------------------------------------------------------
+# _fetchrow()
+#
+# Retrieve a record from the statement handle and store in row cache.
+#------------------------------------------------------------------------
+
+sub _fetchrow {
+ my $self = shift;
+ my $sth = $self->{ _STH };
+
+ my $data = $sth->fetchrow_hashref() || do {
+ $self->{ LAST } = 1;
+ $self->{ NEXT } = undef;
+ $sth->finish();
+ return;
+ };
+ $self->{ NEXT } = $data;
+ return;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::DBI - Template interface to the DBI module
+
+=head1 SYNOPSIS
+
+Making an implicit database connection:
+
+ # ...using positional arguments
+ [% USE DBI('dbi:driver:dbname', 'user', 'pass') %]
+
+ # ...using named parameters
+ [% USE DBI( database = 'dbi:driver:dbname',
+ username = 'user',
+ password = 'pass' )
+ %]
+
+ # ...using short named parameters (4 lzy ppl and bad typsits)
+ [% USE DBI( db = 'driver:dbname',
+ user = 'user',
+ pass = 'pass' )
+ %]
+
+ # ...or an existing DBI database handle
+ [% USE DBI( dbh = my_dbh_ref ) %]
+
+Making explicit database connections:
+
+ [% USE DBI %]
+
+ [% DBI.connect(db, user, pass) %]
+ ...
+
+ [% DBI.connect(new_db, new_user, new_pass) %]
+ ...
+
+ [% DBI.disconnect %] # final disconnect is optional
+
+Making an automagical database connection using DBI_DSN environment variable:
+
+ [% USE DBI %]
+
+Making database queries:
+
+ # single step query
+ [% FOREACH user = DBI.query('SELECT * FROM users') %]
+ [% user.uid %] blah blah [% user.name %] etc. etc.
+ [% END %]
+
+ # two stage prepare/execute
+ [% query = DBI.prepare('SELECT * FROM users WHERE uid = ?') %]
+
+ [% FOREACH user = query.execute('sam') %]
+ ...
+ [% END %]
+
+ [% FOREACH user = query.execute('abw') %]
+ ...
+ [% END %]
+
+Making non-SELECT statements:
+
+ [% IF DBI.do("DELETE FROM users WHERE uid = '$uid'") %]
+ The user '[% uid %]' was successfully deleted.
+ [% END %]
+
+Using named DBI connections:
+
+ [% USE one = DBI(...) %]
+ [% USE two = DBI(...) %]
+
+ [% FOREACH item = one.query("SELECT ...etc...") %]
+ ...
+ [% END %]
+
+ [% FOREACH item = two.query("SELECT ...etc...") %]
+ ...
+ [% END %]
+
+Tieing to a database table (via Tie::DBI):
+
+ [% people = DBI.tie('users', 'uid') %]
+
+ [% me = people.abw %] # => SELECT * FROM users WHERE uid='abw'
+
+ I am [% me.name %]
+
+ # clobber option allows table updates (see Tie::DBI)
+ [% people = DBI.tie('users', 'uid', clobber=1) %]
+
+ [% people.abw.name = 'not a number' %]
+
+ I am [% people.abw.name %] # I am a free man!
+
+=head1 DESCRIPTION
+
+This Template Toolkit plugin module provides an interface to the Perl
+DBI/DBD modules, allowing you to integrate SQL queries into your
+template documents. It also provides an interface via the Tie::DBI
+module (if installed on your system) so that you can access database
+records without having to embed any SQL in your templates.
+
+A DBI plugin object can be created as follows:
+
+ [% USE DBI %]
+
+This creates an uninitialised DBI object. You can then open a connection
+to a database using the connect() method.
+
+ [% DBI.connect('dbi:driver:dbname', 'user', 'pass') %]
+
+The DBI connection can be opened when the plugin is created by passing
+arguments to the constructor, called from the USE directive.
+
+ [% USE DBI('dbi:driver:dbname', 'user', 'pass') %]
+
+You can also use named parameters to provide the data source connection
+string, user name and password.
+
+ [% USE DBI(database => 'dbi:driver:dbname',
+ username => 'user',
+ password => 'pass') %]
+
+For backwards compatability with previous versions of this plugin, you can
+also spell 'database' as 'data_source'.
+
+ [% USE DBI(data_source => 'dbi:driver:dbname',
+ username => 'user',
+ password => 'pass') %]
+
+Lazy Template hackers may prefer to use 'db', 'dsn' or 'connect' as a
+shorthand form of the 'database' parameter, and 'user' and 'pass' as
+shorthand forms of 'username' and 'password', respectively. You can
+also drop the 'dbi:' prefix from the database connect string because
+the plugin will add it on for you automagically.
+
+ [% USE DBI(db => 'driver:dbname',
+ user => 'user',
+ pass => 'pass') %]
+
+Any additional DBI attributes can be specified as named parameters.
+The 'PrintError' attribute defaults to 0 unless explicitly set true.
+
+ [% USE DBI(db, user, pass, ChopBlanks=1) %]
+
+An alternate variable name can be provided for the plugin as per regular
+Template Toolkit syntax:
+
+ [% USE mydb = DBI('dbi:driver:dbname', 'user', 'pass') %]
+
+ [% FOREACH item = mydb.query('SELECT * FROM users') %]
+ ...
+ [% END %]
+
+You can also specify the DBI plugin name in lower case if you prefer:
+
+ [% USE dbi(dsn, user, pass) %]
+
+ [% FOREACH item = dbi.query('SELECT * FROM users') %]
+ ...
+ [% END %]
+
+The disconnect() method can be called to explicitly disconnect the
+current database, but this generally shouldn't be necessary as it is
+called automatically when the plugin goes out of scope. You can call
+connect() at any time to open a connection to another database. The
+previous connection will be closed automatically.
+
+Internally, the DBI connect_cached() method is used instead of the
+connect() method. This allows for connection caching in a server
+environment, such as when the Template Toolkit is used from an Apache
+mod_perl handler. In such a case, simply enable the mod_env module
+and put in a line such as:
+
+ SetEnv DBI_DSN "dbi:mysql:dbname;host=dbhost;
+ user=uname;password=pword"
+
+(NOTE: the string shown here is split across 2 lines for the sake of
+reasonable page formatting, but you should specify it all as one long
+string with no spaces or newlines).
+
+You can then use the DBI plugin without any parameters or the need
+to explicitly call connect().
+
+Once you've loaded a DBI plugin and opened a database connection using
+one of the techniques shown above, you can then make queries on the database
+using the familiar dotted notation:
+
+ [% FOREACH user = DBI.query('SELECT * FROM users') %]
+ [% user.uid %] blah blah [% user.name %] etc. etc.
+ [% END %]
+
+The query() method prepares a query and executes it all in one go.
+If you want to repeat a query with different parameters then you
+can use a separate prepare/execute cycle.
+
+ [% query = DBI.prepare('SELECT * FROM users WHERE uid = ?') %]
+
+ [% FOREACH user = query.execute('sam') %]
+ ...
+ [% END %]
+
+ [% FOREACH user = query.execute('abw') %]
+ ...
+ [% END %]
+
+The query() and execute() methods return an iterator object which
+manages the result set returned. You can save a reference to the
+iterator and access methods like size() to determine the number of
+rows returned by a query.
+
+ [% users = DBI.query('SELECT * FROM users') %]
+ [% users.size %] records returned
+
+or even
+
+ [% DBI.query('SELECT * FROM users').size %]
+
+When used within a FOREACH loop, the iterator is always aliased to the
+special C<loop> variable. This makes it possible to do things like this:
+
+ [% FOREACH user = DBI.query('SELECT * FROM users') %]
+ [% loop.count %]/[% loop.size %]: [% user.name %]
+ [% END %]
+
+to generate a result set of the form:
+
+ 1/3: Jerry Garcia
+ 2/3: Kurt Cobain
+ 3/3: Freddie Mercury
+
+See L<Template::Iterator> for further details on iterators and the
+methods that they implement.
+
+The DBI plugin also provides the do() method to execute non-SELECT
+statements like this:
+
+ [% IF DBI.do("DELETE FROM users WHERE uid = '$uid'") %]
+ The user '[% uid %]' was successfully deleted.
+ [% END %]
+
+The plugin also allows you to create a tie to a table in the database
+using the Tie::DBI module. Simply call the tie() method, passing the
+name of the table and the primary key as arguments.
+
+ [% people = DBI.tie('person', 'uid') %]
+
+You can then access records in the database table as if they were
+entries in the 'people' hash.
+
+ My name is [% people.abw.name %]
+
+IMPORTANT NOTE: the XS Stash (Template::Stash::XS) does not currently
+support access to tied hashes. If you are using the XS stash and having
+problems then you should try enabling the regular stash instead. You
+can do this by setting $Template::Config::STASH to 'Template::Stash'
+before instantiating the Template object.
+
+=head1 OBJECT METHODS
+
+=head2 connect($database, $username, $password)
+
+Establishes a database connection. This method accepts both positional
+and named parameter syntax. e.g.
+
+ [% DBI.connect( 'dbi:driver:dbname', 'timmy', 'sk8D00Dz' ) %]
+
+ [% DBI.connect( database = 'dbi:driver:dbname'
+ username = 'timmy'
+ password = 'sk8D00Dz' ) %]
+
+The connect method allows you to connect to a data source explicitly.
+It can also be used to reconnect an exisiting object to a different
+data source.
+
+If you already have a database handle then you can instruct the plugin
+to reuse it by passing it as the 'dbh' parameter.
+
+ [% DBI.connect( dbh = my_dbh_ref ) %]
+
+=head2 query($sql)
+
+This method submits an SQL query to the database and creates an iterator
+object to return the results. This may be used directly in a FOREACH
+directive as shown below. Data is automatically fetched a row at a time
+from the query result set as required for memory efficiency.
+
+ [% FOREACH user = DBI.query('SELECT * FROM users') %]
+ Each [% user.field %] can be printed here
+ [% END %]
+
+=head2 prepare($sql)
+
+Prepare a query for later execution. This returns a compiled query
+object (of the Template::Plugin::DBI::Query class) on which the
+execute() method can subsequently be called.
+
+ [% query = DBI.prepare('SELECT * FROM users WHERE id = ?') %]
+
+=head2 execute(@args)
+
+Execute a previously prepared query. This method should be called on
+the query object returned by the prepare() method. Returns an
+iterator object which can be used directly in a FOREACH directive.
+
+ [% query = DBI.prepare('SELECT * FROM users WHERE manager = ?') %]
+
+ [% FOREACH minion = query.execute('abw') %]
+ [% minion.name %]
+ [% END %]
+
+ [% FOREACH minion = query.execute('sam') %]
+ [% minion.name %]
+ [% END %]
+
+=head2 do($sql)
+
+The do() method executes a sql statement from which no records are
+returned. It will return true if the statement was successful
+
+ [% IF DBI.do("DELETE FROM users WHERE uid = 'sam'") %]
+ The user was successfully deleted.
+ [% END %]
+
+=head2 tie($table, $key, \%args)
+
+Returns a reference to a hash array tied to a table in the database,
+implemented using the Tie::DBI module. You should pass the name of
+the table and the key field as arguments.
+
+ [% people = DBI.tie('users', 'uid') %]
+
+Or if you prefer, you can use the 'table' and 'key' named parameters.
+
+ [% people = DBI.tie(table='users', key='uid') %]
+
+In this example, the Tie::DBI module will convert the accesses into
+the 'people' hash into SQL queries of the form:
+
+ SELECT * FROM users WHERE uid=?
+
+For example:
+
+ [% me = people.abw %]
+
+The record returned can then be accessed just like a normal hash.
+
+ I am [% me.name %]
+
+You can also do things like this to iterate through all the records
+in a table.
+
+ [% FOREACH uid = people.keys.sort;
+ person = people.$uid
+ %]
+ * [% person.id %] : [% person.name %]
+ [% END %]
+
+With the 'clobber' (or 'CLOBBER') option set you can update the record
+and have those changes automatically permeated back into the database.
+
+ [% people = DBI.tie('users', 'uid', clobber=1) %]
+
+ [% people.abw.name = 'not a number' %]
+
+ I am [% people.abw.name %] # I am a free man!
+
+And you can also add new records.
+
+ [% people.newguy = {
+ name = 'Nobby Newguy'
+ ...other fields...
+ }
+ %]
+
+See L<Tie::DBI> for further information on the 'CLOBBER' option.
+
+=head2 quote($value, $type)
+
+Calls the quote() method on the underlying DBI handle to quote the value
+specified in the appropriate manner for its type.
+
+=head2 dbh()
+
+Return the database handle currently in use by the plugin.
+
+=head2 disconnect()
+
+Disconnects the current database.
+
+=head1 AUTHORS
+
+The DBI plugin was originally written by Simon A Matthews, and
+distributed as a separate module. It was integrated into the Template
+Toolkit distribution for version 2.00 and includes contributions from
+Andy Wardley, Craig Barratt, Dave Hodgkinson and Rafael Kitover.
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 1999-2001 Simon Matthews. All Rights Reserved
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<DBI|DBI>, L<Tie::DBI|Tie::DBI>
+
diff --git a/lib/Template/Plugin/Datafile.pm b/lib/Template/Plugin/Datafile.pm
new file mode 100644
index 0000000..5cf53af
--- /dev/null
+++ b/lib/Template/Plugin/Datafile.pm
@@ -0,0 +1,198 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Datafile
+#
+# DESCRIPTION
+#
+# Template Toolkit Plugin which reads a datafile and constructs a
+# list object containing hashes representing records in the file.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Datafile.pm,v 2.66 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Datafile;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.66 $ =~ /(\d+)\.(\d+)/);
+
+sub new {
+ my ($class, $context, $filename, $params) = @_;
+ my ($delim, $line, @fields, @data, @results);
+ my $self = [ ];
+ local *FD;
+ local $/ = "\n";
+
+ $params ||= { };
+ $delim = $params->{'delim'} || ':';
+ $delim = quotemeta($delim);
+
+ return $class->fail("No filename specified")
+ unless $filename;
+
+ open(FD, $filename)
+ || return $class->fail("$filename: $!");
+
+ # first line of file should contain field definitions
+ while (! $line || $line =~ /^#/) {
+ $line = <FD>;
+ chomp $line;
+ $line =~ s/\r$//;
+ }
+
+ (@fields = split(/\s*$delim\s*/, $line))
+ || return $class->fail("first line of file must contain field names");
+
+ # read each line of the file
+ while (<FD>) {
+ chomp;
+ s/\r$//;
+
+ # ignore comments and blank lines
+ next if /^#/ || /^\s*$/;
+
+ # split line into fields
+ @data = split(/\s*$delim\s*/);
+
+ # create hash record to represent data
+ my %record;
+ @record{ @fields } = @data;
+
+ push(@$self, \%record);
+ }
+
+# return $self;
+ bless $self, $class;
+}
+
+
+sub as_list {
+ return $_[0];
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Datafile - Plugin to construct records from a simple data file
+
+=head1 SYNOPSIS
+
+ [% USE mydata = datafile('/path/to/datafile') %]
+ [% USE mydata = datafile('/path/to/datafile', delim = '|') %]
+
+ [% FOREACH record = mydata %]
+ [% record.this %] [% record.that %]
+ [% END %]
+
+=head1 DESCRIPTION
+
+This plugin provides a simple facility to construct a list of hash
+references, each of which represents a data record of known structure,
+from a data file.
+
+ [% USE datafile(filename) %]
+
+A absolute filename must be specified (for this initial implementation at
+least - in a future version it might also use the INCLUDE_PATH). An
+optional 'delim' parameter may also be provided to specify an alternate
+delimiter character.
+
+ [% USE userlist = datafile('/path/to/file/users') %]
+ [% USE things = datafile('items', delim = '|') %]
+
+The format of the file is intentionally simple. The first line
+defines the field names, delimited by colons with optional surrounding
+whitespace. Subsequent lines then defines records containing data
+items, also delimited by colons. e.g.
+
+ id : name : email : tel
+ abw : Andy Wardley : abw@cre.canon.co.uk : 555-1234
+ neilb : Neil Bowers : neilb@cre.canon.co.uk : 555-9876
+
+Each line is read, split into composite fields, and then used to
+initialise a hash array containing the field names as relevant keys.
+The plugin returns a blessed list reference containing the hash
+references in the order as defined in the file.
+
+ [% FOREACH user = userlist %]
+ [% user.id %]: [% user.name %]
+ [% END %]
+
+The first line of the file B<must> contain the field definitions.
+After the first line, blank lines will be ignored, along with comment
+line which start with a '#'.
+
+=head1 BUGS
+
+Should handle file names relative to INCLUDE_PATH.
+Doesn't permit use of ':' in a field. Some escaping mechanism is required.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.66, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Date.pm b/lib/Template/Plugin/Date.pm
new file mode 100644
index 0000000..7351686
--- /dev/null
+++ b/lib/Template/Plugin/Date.pm
@@ -0,0 +1,361 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Date
+#
+# DESCRIPTION
+#
+# Plugin to generate formatted date strings.
+#
+# AUTHORS
+# Thierry-Michel Barral <kktos@electron-libre.com>
+# Andy Wardley <abw@cre.canon.co.uk>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Date.pm,v 2.71 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Date;
+
+use strict;
+use vars qw( $VERSION $FORMAT @LOCALE_SUFFIX );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+use POSIX ();
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.71 $ =~ /(\d+)\.(\d+)/);
+$FORMAT = '%H:%M:%S %d-%b-%Y'; # default strftime() format
+@LOCALE_SUFFIX = qw( .ISO8859-1 .ISO_8859-15 .US-ASCII .UTF-8 );
+
+#------------------------------------------------------------------------
+# new(\%options)
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $context, $params) = @_;
+ bless {
+ $params ? %$params : ()
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# now()
+#
+# Call time() to return the current system time in seconds since the epoch.
+#------------------------------------------------------------------------
+
+sub now {
+ return time();
+}
+
+
+#------------------------------------------------------------------------
+# format()
+# format($time)
+# format($time, $format)
+# format($time, $format, $locale)
+# format($time, $format, $locale, $gmt_flag)
+# format(\%named_params);
+#
+# Returns a formatted time/date string for the specified time, $time,
+# (or the current system time if unspecified) using the $format, $locale,
+# and $gmt values specified as arguments or internal values set defined
+# at construction time). Specifying a Perl-true value for $gmt will
+# override the local time zone and force the output to be for GMT.
+# Any or all of the arguments may be specified as named parameters which
+# get passed as a hash array reference as the final argument.
+# ------------------------------------------------------------------------
+
+sub format {
+ my $self = shift;
+ my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { };
+ my $time = shift(@_) || $params->{ time } || $self->{ time }
+ || $self->now();
+ my $format = @_ ? shift(@_)
+ : ($params->{ format } || $self->{ format } || $FORMAT);
+ my $locale = @_ ? shift(@_)
+ : ($params->{ locale } || $self->{ locale });
+ my $gmt = @_ ? shift(@_)
+ : ($params->{ gmt } || $self->{ gmt });
+ my (@date, $datestr);
+
+ if ($time =~ /^\d+$/) {
+ # $time is now in seconds since epoch
+ if ($gmt) {
+ @date = (gmtime($time))[0..6];
+ }
+ else {
+ @date = (localtime($time))[0..6];
+ }
+ }
+ else {
+ # if $time is numeric, then we assume it's seconds since the epoch
+ # otherwise, we try to parse it as a 'H:M:S D:M:Y' string
+ @date = (split(/(?:\/| |:|-)/, $time))[2,1,0,3..5];
+ return (undef, Template::Exception->new('date',
+ "bad time/date string: expects 'h:m:s d:m:y' got: '$time'"))
+ unless @date >= 6 && defined $date[5];
+ $date[4] -= 1; # correct month number 1-12 to range 0-11
+ $date[5] -= 1900; # convert absolute year to years since 1900
+ $time = &POSIX::mktime(@date);
+ }
+
+ if ($locale) {
+ # format the date in a specific locale, saving and subsequently
+ # restoring the current locale.
+ my $old_locale = &POSIX::setlocale(&POSIX::LC_ALL);
+
+ # some systems expect locales to have a particular suffix
+ for my $suffix ('', @LOCALE_SUFFIX) {
+ my $try_locale = $locale.$suffix;
+ my $setlocale = &POSIX::setlocale(&POSIX::LC_ALL, $try_locale);
+ if (defined $setlocale && $try_locale eq $setlocale) {
+ $locale = $try_locale;
+ last;
+ }
+ }
+ $datestr = &POSIX::strftime($format, @date);
+ &POSIX::setlocale(&POSIX::LC_ALL, $old_locale);
+ }
+ else {
+ $datestr = &POSIX::strftime($format, @date);
+ }
+
+ return $datestr;
+}
+
+sub calc {
+ my $self = shift;
+ eval { require "Date/Calc.pm" };
+ $self->throw("failed to load Date::Calc: $@") if $@;
+ return Template::Plugin::Date::Calc->new('no context');
+}
+
+sub manip {
+ my $self = shift;
+ eval { require "Date/Manip.pm" };
+ $self->throw("failed to load Date::Manip: $@") if $@;
+ return Template::Plugin::Date::Manip->new('no context');
+}
+
+
+sub throw {
+ my $self = shift;
+ die (Template::Exception->new('date', join(', ', @_)));
+}
+
+
+package Template::Plugin::Date::Calc;
+use base qw( Template::Plugin );
+use vars qw( $AUTOLOAD );
+*throw = \&Template::Plugin::Date::throw;
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ my $sub = \&{"Date::Calc::$method"};
+ $self->throw("no such Date::Calc method: $method")
+ unless $sub;
+
+ &$sub(@_);
+}
+
+package Template::Plugin::Date::Manip;
+use base qw( Template::Plugin );
+use vars qw( $AUTOLOAD );
+*throw = \&Template::Plugin::Date::throw;
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ my $sub = \&{"Date::Manip::$method"};
+ $self->throw("no such Date::Manip method: $method")
+ unless $sub;
+
+ &$sub(@_);
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Date - Plugin to generate formatted date strings
+
+=head1 SYNOPSIS
+
+ [% USE date %]
+
+ # use current time and default format
+ [% date.format %]
+
+ # specify time as seconds since epoch or 'h:m:s d-m-y' string
+ [% date.format(960973980) %]
+ [% date.format('4:20:36 21/12/2000') %]
+
+ # specify format
+ [% date.format(mytime, '%H:%M:%S') %]
+
+ # specify locale
+ [% date.format(date.now, '%a %d %b %y', 'en_GB') %]
+
+ # named parameters
+ [% date.format(mytime, format = '%H:%M:%S') %]
+ [% date.format(locale = 'en_GB') %]
+ [% date.format(time = date.now,
+ format = '%H:%M:%S',
+ locale = 'en_GB) %]
+
+ # specify default format to plugin
+ [% USE date(format = '%H:%M:%S', locale = 'de_DE') %]
+
+ [% date.format %]
+ ...
+
+=head1 DESCRIPTION
+
+The Date plugin provides an easy way to generate formatted time and date
+strings by delegating to the POSIX strftime() routine.
+
+The plugin can be loaded via the familiar USE directive.
+
+ [% USE date %]
+
+This creates a plugin object with the default name of 'date'. An alternate
+name can be specified as such:
+
+ [% USE myname = date %]
+
+The plugin provides the format() method which accepts a time value, a
+format string and a locale name. All of these parameters are optional
+with the current system time, default format ('%H:%M:%S %d-%b-%Y') and
+current locale being used respectively, if undefined. Default values
+for the time, format and/or locale may be specified as named parameters
+in the USE directive.
+
+ [% USE date(format = '%a %d-%b-%Y', locale = 'fr_FR') %]
+
+When called without any parameters, the format() method returns a string
+representing the current system time, formatted by strftime() according
+to the default format and for the default locale (which may not be the
+current one, if locale is set in the USE directive).
+
+ [% date.format %]
+
+The plugin allows a time/date to be specified as seconds since the epoch,
+as is returned by time().
+
+ File last modified: [% date.format(filemod_time) %]
+
+The time/date can also be specified as a string of the form 'h:m:s d/m/y'.
+Any of the characters : / - or space may be used to delimit fields.
+
+ [% USE day = date(format => '%A', locale => 'en_GB') %]
+ [% day.format('4:20:00 9-13-2000') %]
+
+Output:
+
+ Tuesday
+
+A format string can also be passed to the format() method, and a locale
+specification may follow that.
+
+ [% date.format(filemod, '%d-%b-%Y') %]
+ [% date.format(filemod, '%d-%b-%Y', 'en_GB') %]
+
+A fourth parameter allows you to force output in GMT, in the case of
+seconds-since-the-epoch input:
+
+ [% date.format(filemod, '%d-%b-%Y', 'en_GB', 1) %]
+
+Note that in this case, if the local time is not GMT, then also specifying
+'%Z' (time zone) in the format parameter will lead to an extremely
+misleading result.
+
+Any or all of these parameters may be named. Positional parameters
+should always be in the order ($time, $format, $locale).
+
+ [% date.format(format => '%H:%M:%S') %]
+ [% date.format(time => filemod, format => '%H:%M:%S') %]
+ [% date.format(mytime, format => '%H:%M:%S') %]
+ [% date.format(mytime, format => '%H:%M:%S', locale => 'fr_FR') %]
+ [% date.format(mytime, format => '%H:%M:%S', gmt => 1) %]
+ ...etc...
+
+The now() method returns the current system time in seconds since the
+epoch.
+
+ [% date.format(date.now, '%A') %]
+
+The calc() method can be used to create an interface to the Date::Calc
+module (if installed on your system).
+
+ [% calc = date.calc %]
+ [% calc.Monday_of_Week(22, 2001).join('/') %]
+
+The manip() method can be used to create an interface to the Date::Manip
+module (if installed on your system).
+
+ [% manip = date.manip %]
+ [% manip.UnixDate("Noon Yesterday","%Y %b %d %H:%M") %]
+
+=head1 AUTHORS
+
+Thierry-Michel Barral E<lt>kktos@electron-libre.comE<gt> wrote the original
+plugin.
+
+Andy Wardley E<lt>abw@cre.canon.co.ukE<gt> provided some minor
+fixups/enhancements, a test script and documentation.
+
+Mark D. Mills E<lt>mark@hostile.orgE<gt> cloned Date::Manip from the
+cute Date::Calc sub-plugin.
+
+=head1 VERSION
+
+2.71, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<POSIX|POSIX>
+
diff --git a/lib/Template/Plugin/Directory.pm b/lib/Template/Plugin/Directory.pm
new file mode 100644
index 0000000..ec6247e
--- /dev/null
+++ b/lib/Template/Plugin/Directory.pm
@@ -0,0 +1,410 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Directory
+#
+# DESCRIPTION
+# Plugin for encapsulating information about a file system directory.
+#
+# AUTHORS
+# Michael Stevens <michael@etla.org>, with some mutilations from
+# Andy Wardley <abw@kfs.org>.
+#
+# COPYRIGHT
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Directory.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Directory;
+
+require 5.004;
+
+use strict;
+use Cwd;
+use File::Spec;
+use Template::Plugin::File;
+use vars qw( $VERSION );
+use base qw( Template::Plugin::File );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new(\%config)
+#
+# Constructor method.
+#------------------------------------------------------------------------
+
+sub new {
+ my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
+ my ($class, $context, $path) = @_;
+
+ return $class->throw('no directory specified')
+ unless defined $path and length $path;
+
+ my $self = $class->SUPER::new($context, $path, $config);
+ my ($dir, @files, $name, $item, $abs, $rel, $check);
+ $self->{ files } = [ ];
+ $self->{ dirs } = [ ];
+ $self->{ list } = [ ];
+ $self->{ _dir } = { };
+
+ # don't read directory if 'nostat' or 'noscan' set
+ return $self if $config->{ nostat } || $config->{ noscan };
+
+ $self->throw("$path: not a directory")
+ unless $self->{ isdir };
+
+ $self->scan($config);
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# scan(\%config)
+#
+# Scan directory for files and sub-directories.
+#------------------------------------------------------------------------
+
+sub scan {
+ my ($self, $config) = @_;
+ $config ||= { };
+ local *DH;
+ my ($dir, @files, $name, $abs, $rel, $item);
+
+ # set 'noscan' in config if recurse isn't set, to ensure Directories
+ # created don't try to scan deeper
+ $config->{ noscan } = 1 unless $config->{ recurse };
+
+ $dir = $self->{ abs };
+ opendir(DH, $dir) or return $self->throw("$dir: $!");
+
+ @files = readdir DH;
+ closedir(DH)
+ or return $self->throw("$dir close: $!");
+
+ my ($path, $files, $dirs, $list) = @$self{ qw( path files dirs list ) };
+ @$files = @$dirs = @$list = ();
+
+ foreach $name (sort @files) {
+ next if $name =~ /^\./;
+ $abs = File::Spec->catfile($dir, $name);
+ $rel = File::Spec->catfile($path, $name);
+
+ if (-d $abs) {
+ $item = Template::Plugin::Directory->new(undef, $rel, $config);
+ push(@$dirs, $item);
+ }
+ else {
+ $item = Template::Plugin::File->new(undef, $rel, $config);
+ push(@$files, $item);
+ }
+ push(@$list, $item);
+ $self->{ _dir }->{ $name } = $item;
+ }
+
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# file($filename)
+#
+# Fetch a named file from this directory.
+#------------------------------------------------------------------------
+
+sub file {
+ my ($self, $name) = @_;
+ return $self->{ _dir }->{ $name };
+}
+
+
+#------------------------------------------------------------------------
+# present($view)
+#
+# Present self to a Template::View
+#------------------------------------------------------------------------
+
+sub present {
+ my ($self, $view) = @_;
+ $view->view_directory($self);
+}
+
+
+#------------------------------------------------------------------------
+# content($view)
+#
+# Present directory content to a Template::View.
+#------------------------------------------------------------------------
+
+sub content {
+ my ($self, $view) = @_;
+ return $self->{ list } unless $view;
+ my $output = '';
+ foreach my $file (@{ $self->{ list } }) {
+ $output .= $file->present($view);
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# throw($msg)
+#
+# Throw a 'Directory' exception.
+#------------------------------------------------------------------------
+
+sub throw {
+ my ($self, $error) = @_;
+ die (Template::Exception->new('Directory', $error));
+}
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Directory - Plugin for generating directory listings
+
+=head1 SYNOPSIS
+
+ [% USE dir = Directory(dirpath) %]
+
+ # files returns list of regular files
+ [% FOREACH file = dir.files %]
+ [% file.name %] [% file.path %] ...
+ [% END %]
+
+ # dirs returns list of sub-directories
+ [% FOREACH subdir = dir.dirs %]
+ [% subdir.name %] [% subdir.path %] ...
+ [% END %]
+
+ # list returns both interleaved in order
+ [% FOREACH item = dir.list %]
+ [% IF item.isdir %]
+ Directory: [% item.name %]
+ [% ELSE
+ File: [% item.name %]
+ [% END %]
+ [% END %]
+
+ # define a VIEW to display dirs/files
+ [% VIEW myview %]
+ [% BLOCK file %]
+ File: [% item.name %]
+ [% END %]
+
+ [% BLOCK directory %]
+ Directory: [% item.name %]
+ [% item.content(myview) | indent -%]
+ [% END %]
+ [% END %]
+
+ # display directory content using view
+ [% myview.print(dir) %]
+
+=head1 DESCRIPTION
+
+This Template Toolkit plugin provides a simple interface to directory
+listings. It is derived from the Template::Plugin::File module and
+uses Template::Plugin::File object instances to represent files within
+a directory. Sub-directories within a directory are represented by
+further Template::Plugin::Directory instances.
+
+The constructor expects a directory name as an argument.
+
+ [% USE dir = Directory('/tmp') %]
+
+It then provides access to the files and sub-directories contained within
+the directory.
+
+ # regular files (not directories)
+ [% FOREACH file = dir.files %]
+ [% file.name %]
+ [% END %]
+
+ # directories only
+ [% FOREACH file = dir.dirs %]
+ [% file.name %]
+ [% END %]
+
+ # files and/or directories
+ [% FOREACH file = dir.list %]
+ [% file.name %] ([% file.isdir ? 'directory' : 'file' %])
+ [% END %]
+
+ [% USE Directory('foo/baz') %]
+
+The plugin constructor will throw a 'Directory' error if the specified
+path does not exist, is not a directory or fails to stat() (see
+L<Template::Plugin::File>). Otherwise, it will scan the directory and
+create lists named 'files' containing files, 'dirs' containing
+directories and 'list' containing both files and directories combined.
+The 'nostat' option can be set to disable all file/directory checks
+and directory scanning.
+
+Each file in the directory will be represented by a
+Template::Plugin::File object instance, and each directory by another
+Template::Plugin::Directory. If the 'recurse' flag is set, then those
+directories will contain further nested entries, and so on. With the
+'recurse' flag unset, as it is by default, then each is just a place
+marker for the directory and does not contain any further content
+unless its scan() method is explicitly called. The 'isdir' flag can
+be tested against files and/or directories, returning true if the item
+is a directory or false if it is a regular file.
+
+ [% FOREACH file = dir.list %]
+ [% IF file.isdir %]
+ * Directory: [% file.name %]
+ [% ELSE %]
+ * File: [% file.name %]
+ [% END %]
+ [% END %]
+
+This example shows how you might walk down a directory tree, displaying
+content as you go. With the recurse flag disabled, as is the default,
+we need to explicitly call the scan() method on each directory, to force
+it to lookup files and further sub-directories contained within.
+
+ [% USE dir = Directory(dirpath) %]
+ * [% dir.path %]
+ [% INCLUDE showdir %]
+
+ [% BLOCK showdir -%]
+ [% FOREACH file = dir.list -%]
+ [% IF file.isdir -%]
+ * [% file.name %]
+ [% file.scan -%]
+ [% INCLUDE showdir dir=file FILTER indent(4) -%]
+ [% ELSE -%]
+ - [% f.name %]
+ [% END -%]
+ [% END -%]
+ [% END %]
+
+This example is adapted (with some re-formatting for clarity) from
+a test in F<t/directry.t> which produces the following output:
+
+ * test/dir
+ - file1
+ - file2
+ * sub_one
+ - bar
+ - foo
+ * sub_two
+ - waz.html
+ - wiz.html
+ - xyzfile
+
+The 'recurse' flag can be set (disabled by default) to cause the
+constructor to automatically recurse down into all sub-directories,
+creating a new Template::Plugin::Directory object for each one and
+filling it with any further content. In this case there is no need
+to explicitly call the scan() method.
+
+ [% USE dir = Directory(dirpath, recurse=1) %]
+ ...
+
+ [% IF file.isdir -%]
+ * [% file.name %]
+ [% INCLUDE showdir dir=file FILTER indent(4) -%]
+ [% ELSE -%]
+ ...
+
+From version 2.01, the Template Toolkit provides support for views.
+A view can be defined as a VIEW ... END block and should contain
+BLOCK definitions for files ('file') and directories ('directory').
+
+ [% VIEW myview %]
+ [% BLOCK file %]
+ - [% item.name %]
+ [% END %]
+
+ [% BLOCK directory %]
+ * [% item.name %]
+ [% item.content(myview) FILTER indent %]
+ [% END %]
+ [% END %]
+
+Then the view print() method can be called, passing the
+Directory object as an argument.
+
+ [% USE dir = Directory(dirpath, recurse=1) %]
+ [% myview.print(dir) %]
+
+When a directory is presented to a view, either as [% myview.print(dir) %]
+or [% dir.present(view) %], then the 'directory' BLOCK within the 'myview'
+VIEW is processed, with the 'item' variable set to alias the Directory object.
+
+ [% BLOCK directory %]
+ * [% item.name %]
+ [% item.content(myview) FILTER indent %]
+ [% END %]
+
+The directory name is first printed and the content(view) method is
+then called to present each item within the directory to the view.
+Further directories will be mapped to the 'directory' block, and files
+will be mapped to the 'file' block.
+
+With the recurse option disabled, as it is by default, the 'directory'
+block should explicitly call a scan() on each directory.
+
+ [% VIEW myview %]
+ [% BLOCK file %]
+ - [% item.name %]
+ [% END %]
+
+ [% BLOCK directory %]
+ * [% item.name %]
+ [% item.scan %]
+ [% item.content(myview) FILTER indent %]
+ [% END %]
+ [% END %]
+
+ [% USE dir = Directory(dirpath) %]
+ [% myview.print(dir) %]
+
+=head1 TODO
+
+Might be nice to be able to specify accept/ignore options to catch
+a subset of files.
+
+=head1 AUTHORS
+
+Michael Stevens E<lt>michael@etla.orgE<gt> wrote the original Directory plugin
+on which this is based. Andy Wardley E<lt>abw@wardley.orgE<gt> split it into
+separate File and Directory plugins, added some extra code and documentation
+for VIEW support, and made a few other minor tweaks.
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::File|Template::Plugin::File>, L<Template::View|Template::View>
+
diff --git a/lib/Template/Plugin/Dumper.pm b/lib/Template/Plugin/Dumper.pm
new file mode 100644
index 0000000..5dbf1f6
--- /dev/null
+++ b/lib/Template/Plugin/Dumper.pm
@@ -0,0 +1,179 @@
+#==============================================================================
+#
+# Template::Plugin::Dumper
+#
+# DESCRIPTION
+#
+# A Template Plugin to provide a Template Interface to Data::Dumper
+#
+# AUTHOR
+# Simon Matthews <sam@knowledgepool.com>
+#
+# COPYRIGHT
+#
+# Copyright (C) 2000 Simon Matthews. All Rights Reserved
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------------
+#
+# $Id: Dumper.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#==============================================================================
+
+package Template::Plugin::Dumper;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use Data::Dumper;
+
+use vars qw( $VERSION $DEBUG @DUMPER_ARGS $AUTOLOAD );
+use base qw( Template::Plugin );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+@DUMPER_ARGS = qw( Indent Pad Varname Purity Useqq Terse Freezer
+ Toaster Deepcopy Quotekeys Bless Maxdepth );
+
+#==============================================================================
+# ----- CLASS METHODS -----
+#==============================================================================
+
+#------------------------------------------------------------------------
+# new($context, \@params)
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $context, $params) = @_;
+ my ($key, $val);
+ $params ||= { };
+
+
+ foreach my $arg (@DUMPER_ARGS) {
+ no strict 'refs';
+ if (defined ($val = $params->{ lc $arg })
+ or defined ($val = $params->{ $arg })) {
+ ${"Data\::Dumper\::$arg"} = $val;
+ }
+ }
+
+ bless {
+ _CONTEXT => $context,
+ }, $class;
+}
+
+sub dump {
+ my $self = shift;
+ my $content = Dumper @_;
+ return $content;
+}
+
+
+sub dump_html {
+ my $self = shift;
+ my $content = Dumper @_;
+ for ($content) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/\n/<br>\n/g;
+ }
+ return $content;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Dumper - Plugin interface to Data::Dumper
+
+=head1 SYNOPSIS
+
+ [% USE Dumper %]
+
+ [% Dumper.dump(variable) %]
+ [% Dumper.dump_html(variable) %]
+
+=head1 DESCRIPTION
+
+This is a very simple Template Toolkit Plugin Interface to the Data::Dumper
+module. A Dumper object will be instantiated via the following directive:
+
+ [% USE Dumper %]
+
+As a standard plugin, you can also specify its name in lower case:
+
+ [% USE dumper %]
+
+The Data::Dumper 'Pad', 'Indent' and 'Varname' options are supported
+as constructor arguments to affect the output generated. See L<Data::Dumper>
+for further details.
+
+ [% USE dumper(Indent=0, Pad="<br>") %]
+
+These options can also be specified in lower case.
+
+ [% USE dumper(indent=0, pad="<br>") %]
+
+=head1 METHODS
+
+There are two methods supported by the Dumper object. Each will
+output into the template the contents of the variables passed to the
+object method.
+
+=head2 dump()
+
+Generates a raw text dump of the data structure(s) passed
+
+ [% USE Dumper %]
+ [% Dumper.dump(myvar) %]
+ [% Dumper.dump(myvar, yourvar) %]
+
+=head2 dump_html()
+
+Generates a dump of the data structures, as per dump(), but with the
+characters E<lt>, E<gt> and E<amp> converted to their equivalent HTML
+entities and newlines converted to E<lt>brE<gt>.
+
+ [% USE Dumper %]
+ [% Dumper.dump_html(myvar) %]
+
+=head1 AUTHOR
+
+Simon Matthews E<lt>sam@knowledgepool.comE<gt>
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Simon Matthews All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Data::Dumper|Data::Dumper>
+
diff --git a/lib/Template/Plugin/File.pm b/lib/Template/Plugin/File.pm
new file mode 100644
index 0000000..d1d542e
--- /dev/null
+++ b/lib/Template/Plugin/File.pm
@@ -0,0 +1,416 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::File
+#
+# DESCRIPTION
+# Plugin for encapsulating information about a system file.
+#
+# AUTHOR
+# Originally written by Michael Stevens <michael@etla.org> as the
+# Directory plugin, then mutilated by Andy Wardley <abw@kfs.org>
+# into separate File and Directory plugins, with some additional
+# code for working with views, etc.
+#
+# COPYRIGHT
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: File.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::File;
+
+require 5.004;
+
+use strict;
+use Cwd;
+use File::Spec;
+use File::Basename;
+use Template::Plugin;
+
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use vars qw( @STAT_KEYS );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+@STAT_KEYS = qw( dev ino mode nlink uid gid rdev size
+ atime mtime ctime blksize blocks );
+
+
+#------------------------------------------------------------------------
+# new($context, $file, \%config)
+#
+# Create a new File object. Takes the pathname of the file as
+# the argument following the context and an optional
+# hash reference of configuration parameters.
+#------------------------------------------------------------------------
+
+sub new {
+ my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
+ my ($class, $context, $path) = @_;
+ my ($root, $home, @stat, $abs);
+
+ return $class->throw('no file specified')
+ unless defined $path and length $path;
+
+ # path, dir, name, root, home
+
+ if (File::Spec->file_name_is_absolute($path)) {
+ $root = '';
+ }
+ elsif (($root = $config->{ root })) {
+ # strip any trailing '/' from root
+ $root =~ s[/$][];
+ }
+ else {
+ $root = '';
+ }
+
+ my ($name, $dir, $ext) = fileparse($path, '\.\w+');
+ # fixup various items
+ $dir =~ s[/$][];
+ $dir = '' if $dir eq '.';
+ $name = $name . $ext;
+ $ext =~ s/^\.//g;
+ my @fields = File::Spec->splitdir($dir);
+ shift @fields if @fields && ! length $fields[0];
+ $home = join('/', ('..') x @fields);
+ $abs = File::Spec->catfile($root ? $root : (), $path);
+
+ my $self = {
+ path => $path,
+ name => $name,
+ root => $root,
+ home => $home,
+ dir => $dir,
+ ext => $ext,
+ abs => $abs,
+ user => '',
+ group => '',
+ isdir => '',
+ stat => defined $config->{ stat } ? $config->{ stat }
+ : ! $config->{ nostat },
+ map { ($_ => '') } @STAT_KEYS,
+ };
+
+ if ($self->{ stat }) {
+ (@stat = stat( $abs ))
+ || return $class->throw("$abs: $!");
+ @$self{ @STAT_KEYS } = @stat;
+ unless ($config->{ noid }) {
+ $self->{ user } = eval { getpwuid( $self->{ uid }) || $self->{ uid } };
+ $self->{ group } = eval { getgrgid( $self->{ gid }) || $self->{ gid } };
+ }
+ $self->{ isdir } = -d $abs;
+ }
+
+ bless $self, $class;
+}
+
+
+#-------------------------------------------------------------------------
+# rel($file)
+#
+# Generate a relative filename for some other file relative to this one.
+#------------------------------------------------------------------------
+
+sub rel {
+ my ($self, $path) = @_;
+ $path = $path->{ path } if ref $path eq ref $self; # assumes same root
+ return $path if $path =~ m[^/];
+ return $path unless $self->{ home };
+ return $self->{ home } . '/' . $path;
+}
+
+
+#------------------------------------------------------------------------
+# present($view)
+#
+# Present self to a Template::View.
+#------------------------------------------------------------------------
+
+sub present {
+ my ($self, $view) = @_;
+ $view->view_file($self);
+}
+
+
+sub throw {
+ my ($self, $error) = @_;
+ die (Template::Exception->new('File', $error));
+}
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::File - Plugin providing information about files
+
+=head1 SYNOPSIS
+
+ [% USE File(filepath) %]
+ [% File.path %] # full path
+ [% File.name %] # filename
+ [% File.dir %] # directory
+
+=head1 DESCRIPTION
+
+This plugin provides an abstraction of a file. It can be used to
+fetch details about files from the file system, or to represent abstract
+files (e.g. when creating an index page) that may or may not exist on
+a file system.
+
+A file name or path should be specified as a constructor argument. e.g.
+
+ [% USE File('foo.html') %]
+ [% USE File('foo/bar/baz.html') %]
+ [% USE File('/foo/bar/baz.html') %]
+
+The file should exist on the current file system (unless 'nostat'
+option set, see below) as an absolute file when specified with as
+leading '/' as per '/foo/bar/baz.html', or otherwise as one relative
+to the current working directory. The constructor performs a stat()
+on the file and makes the 13 elements returned available as the plugin
+items:
+
+ dev ino mode nlink uid gid rdev size
+ atime mtime ctime blksize blocks
+
+e.g.
+
+ [% USE File('/foo/bar/baz.html') %]
+
+ [% File.mtime %]
+ [% File.mode %]
+ ...
+
+In addition, the 'user' and 'group' items are set to contain the user
+and group names as returned by calls to getpwuid() and getgrgid() for
+the file 'uid' and 'gid' elements, respectively. On Win32 platforms
+on which getpwuid() and getgrid() are not available, these values are
+undefined.
+
+ [% USE File('/tmp/foo.html') %]
+ [% File.uid %] # e.g. 500
+ [% File.user %] # e.g. abw
+
+This user/group lookup can be disabled by setting the 'noid' option.
+
+ [% USE File('/tmp/foo.html', noid=1) %]
+ [% File.uid %] # e.g. 500
+ [% File.user %] # nothing
+
+The 'isdir' flag will be set if the file is a directory.
+
+ [% USE File('/tmp') %]
+ [% File.isdir %] # 1
+
+If the stat() on the file fails (e.g. file doesn't exists, bad
+permission, etc) then the constructor will throw a 'File' exception.
+This can be caught within a TRY...CATCH block.
+
+ [% TRY %]
+ [% USE File('/tmp/myfile') %]
+ File exists!
+ [% CATCH File %]
+ File error: [% error.info %]
+ [% END %]
+
+Note the capitalisation of the exception type, 'File' to indicate an
+error thrown by the 'File' plugin, to distinguish it from a regular
+'file' exception thrown by the Template Toolkit.
+
+Note that the 'File' plugin can also be referenced by the lower case
+name 'file'. However, exceptions are always thrown of the 'File'
+type, regardless of the capitalisation of the plugin named used.
+
+ [% USE file('foo.html') %]
+ [% file.mtime %]
+
+As with any other Template Toolkit plugin, an alternate name can be
+specified for the object created.
+
+ [% USE foo = file('foo.html') %]
+ [% foo.mtime %]
+
+The 'nostat' option can be specified to prevent the plugin constructor
+from performing a stat() on the file specified. In this case, the
+file does not have to exist in the file system, no attempt will be made
+to verify that it does, and no error will be thrown if it doesn't.
+The entries for the items usually returned by stat() will be set
+empty.
+
+ [% USE file('/some/where/over/the/rainbow.html', nostat=1)
+ [% file.mtime %] # nothing
+
+All File plugins, regardless of the nostat option, have set a number
+of items relating to the original path specified.
+
+=over 4
+
+=item path
+
+The full, original file path specified to the constructor.
+
+ [% USE file('/foo/bar.html') %]
+ [% file.path %] # /foo/bar.html
+
+=item name
+
+The name of the file without any leading directories.
+
+ [% USE file('/foo/bar.html') %]
+ [% file.name %] # bar.html
+
+=item dir
+
+The directory element of the path with the filename removed.
+
+ [% USE file('/foo/bar.html') %]
+ [% file.name %] # /foo
+
+=item ext
+
+The file extension, if any, appearing at the end of the path following
+a '.' (not included in the extension).
+
+ [% USE file('/foo/bar.html') %]
+ [% file.ext %] # html
+
+=item home
+
+This contains a string of the form '../..' to represent the upward path
+from a file to its root directory.
+
+ [% USE file('bar.html') %]
+ [% file.home %] # nothing
+
+ [% USE file('foo/bar.html') %]
+ [% file.home %] # ..
+
+ [% USE file('foo/bar/baz.html') %]
+ [% file.home %] # ../..
+
+=item root
+
+The 'root' item can be specified as a constructor argument, indicating
+a root directory in which the named file resides. This is otherwise
+set empty.
+
+ [% USE file('foo/bar.html', root='/tmp') %]
+ [% file.root %] # /tmp
+
+=item abs
+
+This returns the absolute file path by constructing a path from the
+'root' and 'path' options.
+
+ [% USE file('foo/bar.html', root='/tmp') %]
+ [% file.path %] # foo/bar.html
+ [% file.root %] # /tmp
+ [% file.abs %] # /tmp/foo/bar.html
+
+=back
+
+In addition, the following method is provided:
+
+=over 4
+
+=item rel(path)
+
+This returns a relative path from the current file to another path specified
+as an argument. It is constructed by appending the path to the 'home'
+item.
+
+ [% USE file('foo/bar/baz.html') %]
+ [% file.rel('wiz/waz.html') %] # ../../wiz/waz.html
+
+=back
+
+=head1 EXAMPLES
+
+ [% USE file('/foo/bar/baz.html') %]
+
+ [% file.path %] # /foo/bar/baz.html
+ [% file.dir %] # /foo/bar
+ [% file.name %] # baz.html
+ [% file.home %] # ../..
+ [% file.root %] # ''
+ [% file.abspath %] # /foo/bar/baz.html
+ [% file.ext %] # html
+ [% file.mtime %] # 987654321
+ [% file.atime %] # 987654321
+ [% file.uid %] # 500
+ [% file.user %] # abw
+
+ [% USE file('foo.html') %]
+
+ [% file.path %] # foo.html
+ [% file.dir %] # ''
+ [% file.name %] # foo.html
+ [% file.root %] # ''
+ [% file.home %] # ''
+ [% file.abspath %] # foo.html
+
+ [% USE file('foo/bar/baz.html') %]
+
+ [% file.path %] # foo/bar/baz.html
+ [% file.dir %] # foo/bar
+ [% file.name %] # baz.html
+ [% file.root %] # ''
+ [% file.home %] # ../..
+ [% file.abspath %] # foo/bar/baz.html
+
+ [% USE file('foo/bar/baz.html', root='/tmp') %]
+
+ [% file.path %] # foo/bar/baz.html
+ [% file.dir %] # foo/bar
+ [% file.name %] # baz.html
+ [% file.root %] # /tmp
+ [% file.home %] # ../..
+ [% file.abspath %] # /tmp/foo/bar/baz.html
+
+ # calculate other file paths relative to this file and its root
+ [% USE file('foo/bar/baz.html', root => '/tmp/tt2') %]
+ [% file.path('baz/qux.html') %] # ../../baz/qux.html
+ [% file.dir('wiz/woz.html') %] # ../../wiz/woz.html
+
+
+=head1 AUTHORS
+
+Michael Stevens E<lt>michael@etla.orgE<gt> wrote the original Directory plugin
+on which this is based. Andy Wardley E<lt>abw@wardley.orgE<gt> split it into
+separate File and Directory plugins, added some extra code and documentation
+for VIEW support, and made a few other minor tweaks.
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::Directory|Template::Plugin::Directory>, L<Template::View|Template::View>
+
diff --git a/lib/Template/Plugin/Filter.pm b/lib/Template/Plugin/Filter.pm
new file mode 100644
index 0000000..38da7ba
--- /dev/null
+++ b/lib/Template/Plugin/Filter.pm
@@ -0,0 +1,436 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Filter
+#
+# DESCRIPTION
+# Template Toolkit module implementing a base class plugin
+# object which acts like a filter and can be used with the
+# FILTER directive.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Filter.pm,v 1.30 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Filter;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION $DYNAMIC );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.30 $ =~ /(\d+)\.(\d+)/);
+$DYNAMIC = 0 unless defined $DYNAMIC;
+
+
+sub new {
+ my ($class, $context, @args) = @_;
+ my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { };
+
+ # look for $DYNAMIC
+ my $dynamic;
+ {
+ no strict 'refs';
+ $dynamic = ${"$class\::DYNAMIC"};
+ }
+ $dynamic = $DYNAMIC unless defined $dynamic;
+
+ my $self = bless {
+ _CONTEXT => $context,
+ _DYNAMIC => $dynamic,
+ _ARGS => \@args,
+ _CONFIG => $config,
+ }, $class;
+
+ return $self->init($config)
+ || $class->error($self->error());
+}
+
+
+sub init {
+ my ($self, $config) = @_;
+ return $self;
+}
+
+
+sub factory {
+ my $self = shift;
+
+ if ($self->{ _DYNAMIC }) {
+ return $self->{ _DYNAMIC_FILTER } ||= [ sub {
+ my ($context, @args) = @_;
+ my $config = ref $args[-1] eq 'HASH' ? pop(@args) : { };
+
+ return sub {
+ $self->filter(shift, \@args, $config);
+ };
+ }, 1 ];
+ }
+ else {
+ return $self->{ _STATIC_FILTER } ||= sub {
+ $self->filter(shift);
+ };
+ }
+}
+
+
+sub filter {
+ my ($self, $text, $args, $config) = @_;
+ return $text;
+}
+
+
+sub merge_config {
+ my ($self, $newcfg) = @_;
+ my $owncfg = $self->{ _CONFIG };
+ return $owncfg unless $newcfg;
+ return { %$owncfg, %$newcfg };
+}
+
+
+sub merge_args {
+ my ($self, $newargs) = @_;
+ my $ownargs = $self->{ _ARGS };
+ return $ownargs unless $newargs;
+ return [ @$ownargs, @$newargs ];
+}
+
+
+sub install_filter {
+ my ($self, $name) = @_;
+ $self->{ _CONTEXT }->define_filter( $name => $self->factory() );
+ return $self;
+}
+
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Filter - Base class for plugin filters
+
+=head1 SYNOPSIS
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ # ...mungify $text...
+
+ return $text;
+ }
+
+ # now load it...
+ [% USE MyFilter %]
+
+ # ...and use the returned object as a filter
+ [% FILTER $MyFilter %]
+ ...
+ [% END %]
+
+=head1 DESCRIPTION
+
+This module implements a base class for plugin filters. It hides
+the underlying complexity involved in creating and using filters
+that get defined and made available by loading a plugin.
+
+To use the module, simply create your own plugin module that is
+inherited from the Template::Plugin::Filter class.
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+Then simply define your filter() method. When called, you get
+passed a reference to your plugin object ($self) and the text
+to be filtered.
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ # ...mungify $text...
+
+ return $text;
+ }
+
+To use your custom plugin, you have to make sure that the Template
+Toolkit knows about your plugin namespace.
+
+ my $tt2 = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+Or for individual plugins you can do it like this:
+
+ my $tt2 = Template->new({
+ PLUGINS => {
+ MyFilter => 'MyOrg::Template::Plugin::MyFilter',
+ },
+ });
+
+Then you USE your plugin in the normal way.
+
+ [% USE MyFilter %]
+
+The object returned is stored in the variable of the same name,
+'MyFilter'. When you come to use it as a FILTER, you should add
+a dollar prefix. This indicates that you want to use the filter
+stored in the variable 'MyFilter' rather than the filter named
+'MyFilter', which is an entirely different thing (see later for
+information on defining filters by name).
+
+ [% FILTER $MyFilter %]
+ ...text to be filtered...
+ [% END %]
+
+You can, of course, assign it to a different variable.
+
+ [% USE blat = MyFilter %]
+
+ [% FILTER $blat %]
+ ...text to be filtered...
+ [% END %]
+
+Any configuration parameters passed to the plugin constructor from the
+USE directive are stored internally in the object for inspection by
+the filter() method (or indeed any other method). Positional
+arguments are stored as a reference to a list in the _ARGS item while
+named configuration parameters are stored as a reference to a hash
+array in the _CONFIG item.
+
+For example, loading a plugin as shown here:
+
+ [% USE blat = MyFilter 'foo' 'bar' baz = 'blam' %]
+
+would allow the filter() method to do something like this:
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ my $args = $self->{ _ARGS }; # [ 'foo', 'bar' ]
+ my $conf = $self->{ _CONFIG }; # { baz => 'blam' }
+
+ # ...munge $text...
+
+ return $text;
+ }
+
+By default, plugins derived from this module will create static
+filters. A static filter is created once when the plugin gets
+loaded via the USE directive and re-used for all subsequent
+FILTER operations. That means that any argument specified with
+the FILTER directive are ignored.
+
+Dynamic filters, on the other hand, are re-created each time
+they are used by a FILTER directive. This allows them to act
+on any parameters passed from the FILTER directive and modify
+their behaviour accordingly.
+
+There are two ways to create a dynamic filter. The first is to
+define a $DYNAMIC class variable set to a true value.
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+ use vars qw( $DYNAMIC );
+
+ $DYNAMIC = 1;
+
+The other way is to set the internal _DYNAMIC value within the init()
+method which gets called by the new() constructor.
+
+ sub init {
+ my $self = shift;
+ $self->{ _DYNAMIC } = 1;
+ return $self;
+ }
+
+When this is set to a true value, the plugin will automatically
+create a dynamic filter. The outcome is that the filter() method
+will now also get passed a reference to an array of postional
+arguments and a reference to a hash array of named parameters.
+
+So, using a plugin filter like this:
+
+ [% FILTER $blat 'foo' 'bar' baz = 'blam' %]
+
+would allow the filter() method to work like this:
+
+ sub filter {
+ my ($self, $text, $args, $conf) = @_;
+
+ # $args = [ 'foo', 'bar' ]
+ # $conf = { baz => 'blam' }
+
+ }
+
+In this case can pass parameters to both the USE and FILTER directives,
+so your filter() method should probably take that into account.
+
+ [% USE MyFilter 'foo' wiz => 'waz' %]
+
+ [% FILTER $MyFilter 'bar' biz => 'baz' %]
+ ...
+ [% END %]
+
+You can use the merge_args() and merge_config() methods to do a quick
+and easy job of merging the local (e.g. FILTER) parameters with the
+internal (e.g. USE) values and returning new sets of conglomerated
+data.
+
+ sub filter {
+ my ($self, $text, $args, $conf) = @_;
+
+ $args = $self->merge_args($args);
+ $conf = $self->merge_config($conf);
+
+ # $args = [ 'foo', 'bar' ]
+ # $conf = { wiz => 'waz', biz => 'baz' }
+ ...
+ }
+
+You can also have your plugin install itself as a named filter by
+calling the install_filter() method from the init() method. You
+should provide a name for the filter, something that you might
+like to make a configuration option.
+
+ sub init {
+ my $self = shift;
+ my $name = $self->{ _CONFIG }->{ name } || 'myfilter';
+ $self->install_filter($name);
+ return $self;
+ }
+
+This allows the plugin filter to be used as follows:
+
+ [% USE MyFilter %]
+
+ [% FILTER myfilter %]
+ ...
+ [% END %]
+
+or
+
+ [% USE MyFilter name = 'swipe' %]
+
+ [% FILTER swipe %]
+ ...
+ [% END %]
+
+Alternately, you can allow a filter name to be specified as the
+first positional argument.
+
+ sub init {
+ my $self = shift;
+ my $name = $self->{ _ARGS }->[0] || 'myfilter';
+ $self->install_filter($name);
+ return $self;
+ }
+
+ [% USE MyFilter 'swipe' %]
+
+ [% FILTER swipe %]
+ ...
+ [% END %]
+
+=head1 EXAMPLE
+
+Here's a complete example of a plugin filter module.
+
+ package My::Template::Plugin::Change;
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+ sub init {
+ my $self = shift;
+
+ $self->{ _DYNAMIC } = 1;
+
+ # first arg can specify filter name
+ $self->install_filter($self->{ _ARGS }->[0] || 'change');
+
+ return $self;
+ }
+
+
+ sub filter {
+ my ($self, $text, $args, $config) = @_;
+
+ $config = $self->merge_config($config);
+ my $regex = join('|', keys %$config);
+
+ $text =~ s/($regex)/$config->{ $1 }/ge;
+
+ return $text;
+ }
+
+ 1;
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.30, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Filters|Template::Filters>, L<Template::Manual::Filters|Template::Manual::Filters>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Format.pm b/lib/Template/Plugin/Format.pm
new file mode 100644
index 0000000..bba55d2
--- /dev/null
+++ b/lib/Template/Plugin/Format.pm
@@ -0,0 +1,124 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Format
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit Plugin which creates formatting functions.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Format.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Format;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+
+sub new {
+ my ($class, $context, $format) = @_;;
+ return defined $format
+ ? make_formatter($format)
+ : \&make_formatter;
+}
+
+
+sub make_formatter {
+ my $format = shift;
+ $format = '%s' unless defined $format;
+ return sub {
+ my @args = @_;
+ push(@args, '') unless @args;
+ return sprintf($format, @args);
+ }
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Format - Plugin to create formatting functions
+
+=head1 SYNOPSIS
+
+ [% USE format %]
+ [% commented = format('# %s') %]
+ [% commented('The cat sat on the mat') %]
+
+ [% USE bold = format('<b>%s</b>') %]
+ [% bold('Hello') %]
+
+=head1 DESCRIPTION
+
+The format plugin constructs sub-routines which format text according to
+a printf()-like format string.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Constants.pm b/lib/Template/Plugin/GD/Constants.pm
new file mode 100644
index 0000000..6fc8e7c
--- /dev/null
+++ b/lib/Template/Plugin/GD/Constants.pm
@@ -0,0 +1,138 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Constants
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD constants
+# in the GD.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Constants.pm,v 1.55 2004/01/13 16:20:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Constants;
+
+require 5.004;
+
+use strict;
+use GD qw(/^gd/ /^GD/);
+use Template::Plugin;
+use base qw( Template::Plugin );
+use vars qw( @ISA $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ my $self = { };
+ bless $self, $class;
+
+ #
+ # GD has exported various gd* and GD_* contstants. Find them.
+ #
+ foreach my $v ( keys(%Template::Plugin::GD::Constants::) ) {
+ $self->{$v} = eval($v) if ( $v =~ /^gd/ || $v =~ /^GD_/ );
+ }
+ return $self;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Constants - Interface to GD module constants
+
+=head1 SYNOPSIS
+
+ [% USE gdc = GD.Constants %]
+
+ # --> the constants gdc.gdBrushed, gdc.gdSmallFont, gdc.GD_CMP_IMAGE
+ # are now available
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE gdc = GD.Constants;
+ USE im = GD.Image(200,100);
+ black = im.colorAllocate(0 ,0, 0);
+ red = im.colorAllocate(255,0, 0);
+ r = im.string(gdc.gdLargeFont, 10, 10, "Large Red Text", red);
+ im.png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Constants plugin provides access to the various GD module's
+constants (such as gdBrushed, gdSmallFont, gdTransparent, GD_CMP_IMAGE
+etc). When GD.pm is used in perl it exports various contstants
+into the caller's namespace. This plugin makes those exported
+constants available as template variables.
+
+See L<Template::Plugin::GD::Image> and L<GD> for further examples and
+details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+Lincoln D. Stein wrote the GD.pm interface to the GD library.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+The GD.pm interface is copyright 1995-2000, Lincoln D. Stein.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Image|Template::Plugin::GD::Image>, L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon>, L<GD|GD>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/area.pm b/lib/Template/Plugin/GD/Graph/area.pm
new file mode 100644
index 0000000..d09d024
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/area.pm
@@ -0,0 +1,148 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::area
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::area
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: area.pm,v 1.57 2004/01/13 16:20:51 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::area;
+
+require 5.004;
+
+use strict;
+use GD::Graph::area;
+use Template::Plugin;
+use base qw( GD::Graph::area Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::area - Create area graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.area(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 5, 12, 24, 33, 19, 8, 6, 15, 21],
+ [ -1, -2, -5, -6, -3, 1.5, 1, 1.3, 2]
+ ];
+
+ USE my_graph = GD.Graph.area();
+ my_graph.set(
+ two_axes => 1,
+ zero_axis => 1,
+ transparent => 0,
+ );
+ my_graph.set_legend('left axis', 'right axis' );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.area plugin provides an interface to the GD::Graph::area
+class defined by the GD::Graph module. It allows one or more (x,y) data
+sets to be plotted as lines with the area between the line and x-axis
+shaded, in addition to axes and legends.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/bars.pm b/lib/Template/Plugin/GD/Graph/bars.pm
new file mode 100644
index 0000000..9bc08c5
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/bars.pm
@@ -0,0 +1,191 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::bars
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::bars
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: bars.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::bars;
+
+require 5.004;
+
+use strict;
+use GD::Graph::bars;
+use Template::Plugin;
+use base qw( GD::Graph::bars Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::bars - Create bar graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.bars(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
+ ];
+
+ USE my_graph = GD.Graph.bars();
+
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A Simple Bar Chart',
+ y_max_value => 8,
+ y_tick_number => 8,
+ y_label_skip => 2,
+
+ # shadows
+ bar_spacing => 8,
+ shadow_depth => 4,
+ shadowclr => 'dred',
+
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 5, 12, 24, 33, 19, 8, 6, 15, 21],
+ [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
+ ];
+
+ USE my_graph = GD.Graph.bars();
+
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'Two data sets',
+
+ # shadows
+ bar_spacing => 8,
+ shadow_depth => 4,
+ shadowclr => 'dred',
+
+ long_ticks => 1,
+ y_max_value => 40,
+ y_tick_number => 8,
+ y_label_skip => 2,
+ bar_spacing => 3,
+
+ accent_treshold => 200,
+
+ transparent => 0,
+ );
+ my_graph.set_legend( 'Data set 1', 'Data set 2' );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.bars plugin provides an interface to the GD::Graph::bars
+class defined by the GD::Graph module. It allows one or more (x,y) data
+sets to be plotted with each point represented by a bar, in addition
+to axes and legends.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/bars3d.pm b/lib/Template/Plugin/GD/Graph/bars3d.pm
new file mode 100644
index 0000000..79a930b
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/bars3d.pm
@@ -0,0 +1,166 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::bars3d
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::bars3d
+# package in the GD::Graph3D.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: bars3d.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::bars3d;
+
+require 5.004;
+
+use strict;
+use GD::Graph::bars3d;
+use Template::Plugin;
+use base qw( GD::Graph::bars3d Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::bars3d - Create 3D bar graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.bars3d(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec", ],
+ [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7],
+ [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8],
+ [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2],
+ ];
+
+ USE my_graph = GD.Graph.bars3d();
+
+ my_graph.set(
+ x_label => 'Month',
+ y_label => 'Measure of success',
+ title => 'A 3d Bar Chart',
+
+ y_max_value => 8,
+ y_min_value => -8,
+ y_tick_number => 16,
+ y_label_skip => 2,
+ box_axis => 0,
+ line_width => 3,
+ zero_axis_only => 1,
+ x_label_position => 1,
+ y_label_position => 1,
+
+ x_label_skip => 3,
+ x_tick_offset => 2,
+
+ transparent => 0,
+ );
+ my_graph.set_legend("Us", "Them", "Others");
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.bars3d plugin provides an interface to the GD::Graph::bars3d
+class defined by the GD::Graph3d module. It allows one or more (x,y) data
+sets to be plotted as y versus x bars with a 3-dimensional appearance,
+together with axes and legends.
+
+See L<GD::Graph3d> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph3d module was written by Jeremy Wadsack.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph3d is copyright (C) 1999,2000 Wadsack-Allen. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/lines.pm b/lib/Template/Plugin/GD/Graph/lines.pm
new file mode 100644
index 0000000..678cc64
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/lines.pm
@@ -0,0 +1,178 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::lines
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::lines
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: lines.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::lines;
+
+require 5.004;
+
+use strict;
+use GD::Graph::lines;
+use Template::Plugin;
+use base qw( GD::Graph::lines Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::lines - Create line graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.lines(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE g = GD.Graph.lines(300,200);
+ x = [1, 2, 3, 4];
+ y = [5, 4, 2, 3];
+ g.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'Title'
+ );
+ g.plot([x, y]).png | stdout(1);
+ END;
+ -%]
+
+ [% FILTER null;
+ data = [
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec", ],
+ [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7],
+ [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8],
+ [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2],
+ ];
+
+ USE my_graph = GD.Graph.lines();
+
+ my_graph.set(
+ x_label => 'Month',
+ y_label => 'Measure of success',
+ title => 'A Simple Line Graph',
+
+ y_max_value => 8,
+ y_min_value => -8,
+ y_tick_number => 16,
+ y_label_skip => 2,
+ box_axis => 0,
+ line_width => 3,
+ zero_axis_only => 1,
+ x_label_position => 1,
+ y_label_position => 1,
+
+ x_label_skip => 3,
+ x_tick_offset => 2,
+
+ transparent => 0,
+ );
+ my_graph.set_legend("Us", "Them", "Others");
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.lines plugin provides an interface to the GD::Graph::lines
+class defined by the GD::Graph module. It allows one or more (x,y) data
+sets to be plotted as y versus x lines with axes and legends.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/lines3d.pm b/lib/Template/Plugin/GD/Graph/lines3d.pm
new file mode 100644
index 0000000..1f12715
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/lines3d.pm
@@ -0,0 +1,166 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::lines3d
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::lines3d
+# package in the GD::Graph3D.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: lines3d.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::lines3d;
+
+require 5.004;
+
+use strict;
+use GD::Graph::lines3d;
+use Template::Plugin;
+use base qw( GD::Graph::lines3d Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::lines3d - Create 3D line graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.lines3d(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec", ],
+ [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7],
+ [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8],
+ [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2],
+ ];
+
+ USE my_graph = GD.Graph.lines3d();
+
+ my_graph.set(
+ x_label => 'Month',
+ y_label => 'Measure of success',
+ title => 'A 3d Line Graph',
+
+ y_max_value => 8,
+ y_min_value => -8,
+ y_tick_number => 16,
+ y_label_skip => 2,
+ box_axis => 0,
+ line_width => 3,
+ zero_axis_only => 1,
+ x_label_position => 1,
+ y_label_position => 1,
+
+ x_label_skip => 3,
+ x_tick_offset => 2,
+
+ transparent => 0,
+ );
+ my_graph.set_legend("Us", "Them", "Others");
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.lines3d plugin provides an interface to the GD::Graph::lines3d
+class defined by the GD::Graph3d module. It allows one or more (x,y) data
+sets to be plotted as y versus x lines with a 3-dimensional appearance,
+together with axes and legends.
+
+See L<GD::Graph3d> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph3d module was written by Jeremy Wadsack.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph3d is copyright (C) 1999,2000 Wadsack-Allen. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/linespoints.pm b/lib/Template/Plugin/GD/Graph/linespoints.pm
new file mode 100644
index 0000000..8dc48d9
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/linespoints.pm
@@ -0,0 +1,158 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::linespoints
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::linespoints
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: linespoints.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::linespoints;
+
+require 5.004;
+
+use strict;
+use GD::Graph::linespoints;
+use Template::Plugin;
+use base qw( GD::Graph::linespoints Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::linespoints - Create line/point graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.linespoints(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [50, 52, 53, 54, 55, 56, 57, 58, 59],
+ [60, 61, 61, 63, 68, 66, 65, 61, 58],
+ [70, 72, 71, 74, 78, 73, 75, 71, 68],
+ ];
+
+ USE my_graph = GD.Graph.linespoints;
+
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A Lines and Points Graph',
+ y_max_value => 80,
+ y_tick_number => 6,
+ y_label_skip => 2,
+ y_long_ticks => 1,
+ x_tick_length => 2,
+ markers => [ 1, 5 ],
+ skip_undef => 1,
+ transparent => 0,
+ );
+ my_graph.set_legend('data set 1', 'data set 2', 'data set 3');
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.linespoints plugin provides an interface to the
+GD::Graph::linespoints class defined by the GD::Graph module. It allows
+one or more (x,y) data sets to be plotted as y versus x lines, plus
+symbols placed at each point, in addition to axes and legends.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/mixed.pm b/lib/Template/Plugin/GD/Graph/mixed.pm
new file mode 100644
index 0000000..10dd533
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/mixed.pm
@@ -0,0 +1,176 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::mixed
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::mixed
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: mixed.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::mixed;
+
+require 5.004;
+
+use strict;
+use GD::Graph::mixed;
+use Template::Plugin;
+use base qw( GD::Graph::mixed Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::mixed - Create mixed graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.mixed(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 1, 2, 5, 6, 3, 1.5, -1, -3, -4],
+ [ -4, -3, 1, 1, -3, -1.5, -2, -1, 0],
+ [ 9, 8, 9, 8.4, 7.1, 7.5, 8, 3, -3],
+ [ 0.1, 0.2, 0.5, 0.4, 0.3, 0.5, 0.1, 0, 0.4],
+ [ -0.1, 2, 5, 4, -3, 2.5, 3.2, 4, -4],
+ ];
+
+ USE my_graph = GD.Graph.mixed();
+
+ my_graph.set(
+ types => ['lines', 'lines', 'points', 'area', 'linespoints'],
+ default_type => 'points',
+ );
+
+ my_graph.set(
+
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A Mixed Type Graph',
+
+ y_max_value => 10,
+ y_min_value => -5,
+ y_tick_number => 3,
+ y_label_skip => 0,
+ x_plot_values => 0,
+ y_plot_values => 0,
+
+ long_ticks => 1,
+ x_ticks => 0,
+
+ legend_marker_width => 24,
+ line_width => 3,
+ marker_size => 5,
+
+ bar_spacing => 8,
+
+ transparent => 0,
+ );
+
+ my_graph.set_legend('one', 'two', 'three', 'four', 'five', 'six');
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.mixed plugin provides an interface to the GD::Graph::mixed
+class defined by the GD::Graph module. It allows one or more (x,y) data
+sets to be plotted with various styles (lines, points, bars, areas etc).
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/pie.pm b/lib/Template/Plugin/GD/Graph/pie.pm
new file mode 100644
index 0000000..e72e26c
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/pie.pm
@@ -0,0 +1,141 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::pie
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::pie
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: pie.pm,v 1.55 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::pie;
+
+require 5.004;
+
+use strict;
+use GD::Graph::pie;
+use Template::Plugin;
+use base qw( GD::Graph::pie Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::pie - Create pie charts with legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.pie(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th"],
+ [ 4, 2, 3, 4, 3, 3.5]
+ ];
+
+ USE my_graph = GD.Graph.pie( 250, 200 );
+
+ my_graph.set(
+ title => 'A Pie Chart',
+ label => 'Label',
+ axislabelclr => 'black',
+ pie_height => 36,
+
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.pie plugin provides an interface to the GD::Graph::pie
+class defined by the GD::Graph module. It allows an (x,y) data set to
+be plotted as a pie chart. The x values are typically strings.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/pie3d.pm b/lib/Template/Plugin/GD/Graph/pie3d.pm
new file mode 100644
index 0000000..5f677e0
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/pie3d.pm
@@ -0,0 +1,145 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::pie3d
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::pie3d
+# package in the GD::Graph3D.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: pie3d.pm,v 1.55 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::pie3d;
+
+require 5.004;
+
+use strict;
+use GD::Graph::pie3d;
+use Template::Plugin;
+use base qw( GD::Graph::pie3d Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::pie3d - Create 3D pie charts with legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.pie3d(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th"],
+ [ 4, 2, 3, 4, 3, 3.5]
+ ];
+
+ USE my_graph = GD.Graph.pie3d( 250, 200 );
+
+ my_graph.set(
+ title => 'A Pie Chart',
+ label => 'Label',
+ axislabelclr => 'black',
+ pie_height => 36,
+
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.pie3d plugin provides an interface to the GD::Graph::pie3d
+class defined by the GD::Graph module. It allows an (x,y) data set to
+be plotted as a 3d pie chart. The x values are typically strings.
+
+Note that GD::Graph::pie already produces a 3d effect, so GD::Graph::pie3d
+is just a wrapper around GD::Graph::pie. Similarly, the plugin
+GD.Graph.pie3d is effectively the same as the plugin GD.Graph.pie.
+
+See L<GD::Graph3d> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph3d module was written by Jeremy Wadsack. The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph3d is copyright (c) 1999,2000 Wadsack-Allen. All Rights Reserved. GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Graph/points.pm b/lib/Template/Plugin/GD/Graph/points.pm
new file mode 100644
index 0000000..97acf51
--- /dev/null
+++ b/lib/Template/Plugin/GD/Graph/points.pm
@@ -0,0 +1,155 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Graph::points
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Graph::points
+# package in the GD::Graph.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: points.pm,v 1.57 2004/01/13 16:20:56 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Graph::points;
+
+require 5.004;
+
+use strict;
+use GD::Graph::points;
+use Template::Plugin;
+use base qw( GD::Graph::points Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return $class->SUPER::new(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+
+sub set_legend
+{
+ my $self = shift;
+
+ $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Graph::points - Create point graphs with axes and legends
+
+=head1 SYNOPSIS
+
+ [% USE g = GD.Graph.points(x_size, y_size); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 5, 12, 24, 33, 19, 8, 6, 15, 21],
+ [ 1, 2, 5, 6, 3, 1.5, 2, 3, 4],
+ ];
+ USE my_graph = GD.Graph.points();
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A Points Graph',
+ y_max_value => 40,
+ y_tick_number => 8,
+ y_label_skip => 2,
+ legend_placement => 'RC',
+ long_ticks => 1,
+ marker_size => 6,
+ markers => [ 1, 7, 5 ],
+
+ transparent => 0,
+ );
+ my_graph.set_legend('one', 'two');
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Graph.points plugin provides an interface to the GD::Graph::points
+class defined by the GD::Graph module. It allows one or more (x,y) data
+sets to be plotted as points, in addition to axes and legends.
+
+See L<GD::Graph> for more details.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Graph module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.57, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Graph is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Image.pm b/lib/Template/Plugin/GD/Image.pm
new file mode 100644
index 0000000..46a06d7
--- /dev/null
+++ b/lib/Template/Plugin/GD/Image.pm
@@ -0,0 +1,184 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Image
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Image
+# class in the GD.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Image.pm,v 1.55 2004/01/13 16:20:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Image;
+
+require 5.004;
+
+use strict;
+use GD;
+use Template::Plugin;
+use base qw( GD Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return new GD::Image(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Image - Interface to GD Graphics Library
+
+=head1 SYNOPSIS
+
+ [% USE im = GD.Image(x_size, y_size) %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE gdc = GD.Constants;
+ USE im = GD.Image(200,100);
+ black = im.colorAllocate(0 ,0, 0);
+ red = im.colorAllocate(255,0, 0);
+ r = im.string(gdc.gdLargeFont, 10, 10, "Large Red Text", red);
+ im.png | stdout(1);
+ END;
+ -%]
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ # allocate some colors
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+ # And fill it with red
+ im.fill(50,50,red);
+ # Output binary image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ USE c = GD.Constants;
+ USE poly = GD.Polygon;
+
+ # allocate some colors
+ white = im.colorAllocate(255,255,255);
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0,255);
+ green = im.colorAllocate(0, 255,0);
+
+ # make the background transparent and interlaced
+ im.transparent(white);
+ im.interlaced('true');
+
+ # Put a black frame around the picture
+ im.rectangle(0,0,99,99,black);
+
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+
+ # And fill it with red
+ im.fill(50,50,red);
+
+ # Draw a blue triangle
+ poly.addPt(50,0);
+ poly.addPt(99,99);
+ poly.addPt(0,99);
+ im.filledPolygon(poly, blue);
+
+ # Output binary image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Image plugin provides an interface to GD.pm's GD::Image class.
+The GD::Image class is the main interface to GD.pm.
+
+It is very important that no extraneous template output appear before or
+after the image. Since some methods return values that would otherwise
+appear in the output, it is recommended that GD.Image code be wrapped in
+a null filter. The methods that produce the final output (eg, png, jpeg,
+gd etc) can then explicitly make their output appear by using the
+stdout filter, with a non-zero argument to force binary mode (required
+for non-modern operating systems).
+
+See L<GD> for a complete description of the GD library and all the
+methods that can be called via the GD.Image plugin.
+See L<Template::Plugin::GD::Constants> for a plugin that allows you
+access to GD.pm's constants.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+Lincoln D. Stein wrote the GD.pm interface to the GD library.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+The GD.pm interface is copyright 1995-2000, Lincoln D. Stein.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon>, L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants>, L<GD|GD>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Polygon.pm b/lib/Template/Plugin/GD/Polygon.pm
new file mode 100644
index 0000000..0d1d5c6
--- /dev/null
+++ b/lib/Template/Plugin/GD/Polygon.pm
@@ -0,0 +1,155 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Polygon
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Polygon
+# class in the GD.pm module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Polygon.pm,v 1.55 2004/01/13 16:20:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Polygon;
+
+require 5.004;
+
+use strict;
+use GD;
+use Template::Plugin;
+use base qw( Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ return new GD::Polygon(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Polygon - Interface to GD module Polygon class
+
+=head1 SYNOPSIS
+
+ [% USE poly = GD.Polygon;
+ poly.addPt(50,0);
+ poly.addPt(99,99);
+ %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ USE c = GD.Constants;
+
+ # allocate some colors
+ white = im.colorAllocate(255,255,255);
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0,255);
+ green = im.colorAllocate(0, 255,0);
+
+ # make the background transparent and interlaced
+ im.transparent(white);
+ im.interlaced('true');
+
+ # Put a black frame around the picture
+ im.rectangle(0,0,99,99,black);
+
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+
+ # And fill it with red
+ im.fill(50,50,red);
+
+ # Draw a blue triangle by defining a polygon
+ USE poly = GD.Polygon;
+ poly.addPt(50,0);
+ poly.addPt(99,99);
+ poly.addPt(0,99);
+ im.filledPolygon(poly, blue);
+
+ # Output binary image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Polygon plugin provides an interface to GD.pm's GD::Polygon class.
+
+See L<GD> for a complete description of the GD library and all the
+methods that can be called via the GD.Polygon plugin.
+See L<Template::Plugin::GD::Image> for the main interface to the
+GD functions.
+See L<Template::Plugin::GD::Constants> for a plugin that allows you
+access to GD.pm's constants.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+Lincoln D. Stein wrote the GD.pm interface to the GD library.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+The GD.pm interface is copyright 1995-2000, Lincoln D. Stein.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Image|Template::Plugin::GD::Image>, L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants>, L<GD|GD>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Text.pm b/lib/Template/Plugin/GD/Text.pm
new file mode 100644
index 0000000..f18b2e0
--- /dev/null
+++ b/lib/Template/Plugin/GD/Text.pm
@@ -0,0 +1,140 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Text
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Text
+# module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Text.pm,v 1.55 2004/01/13 16:20:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Text;
+
+require 5.004;
+
+use strict;
+use GD::Text;
+use Template::Plugin;
+use base qw( GD::Text Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ return new GD::Text(@_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Text - Text utilities for use with GD
+
+=head1 SYNOPSIS
+
+ [% USE gd_text = GD.Text %]
+
+=head1 EXAMPLES
+
+ [%
+ USE gd_c = GD.Constants;
+ USE t = GD.Text;
+ x = t.set_text('Some text');
+ r = t.get('width', 'height', 'char_up', 'char_down');
+ r.join(":"); "\n"; # returns 54:13:13:0.
+ -%]
+
+ [%
+ USE gd_c = GD.Constants;
+ USE t = GD.Text(text => 'FooBar Banana', font => gd_c.gdGiantFont);
+ t.get('width'); "\n"; # returns 117.
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Text plugin provides an interface to the GD::Text module.
+It allows attributes of strings such as width and height in pixels
+to be computed.
+
+See L<GD::Text> for more details. See
+L<Template::Plugin::GD::Text::Align> and
+L<Template::Plugin::GD::Text::Wrap> for plugins that
+allow you to render aligned or wrapped text in GD images.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Text module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Text is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap>, L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align>, L<GD|GD>, L<GD::Text|GD::Text>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Text/Align.pm b/lib/Template/Plugin/GD/Text/Align.pm
new file mode 100644
index 0000000..8b79069
--- /dev/null
+++ b/lib/Template/Plugin/GD/Text/Align.pm
@@ -0,0 +1,147 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Text::Align
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Text::Align
+# module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Align.pm,v 1.55 2004/01/13 16:21:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Text::Align;
+
+require 5.004;
+
+use strict;
+use GD::Text::Align;
+use Template::Plugin;
+use base qw( GD::Text::Align Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ my $gd = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ return $class->SUPER::new($gd, @_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Text::Align - Draw aligned strings in GD images
+
+=head1 SYNOPSIS
+
+ [% USE align = GD.Text.Align(gd_image); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ USE gdc = GD.Constants;
+ # allocate some colors
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+
+ USE a = GD.Text.Align(im);
+ a.set_font(gdc.gdLargeFont);
+ a.set_text("Hello");
+ a.set(colour => red, halign => "center");
+ a.draw(50,70,0);
+
+ # Output image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Text.Align plugin provides an interface to the GD::Text::Align
+module. It allows text to be drawn in GD images with various alignments
+and orientations.
+
+See L<GD::Text::Align> for more details. See
+L<Template::Plugin::GD::Text::Wrap> for a plugin
+that allow you to render wrapped text in GD images.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Text module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Text is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text|Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap>, L<GD|GD>, L<GD::Text::Align|GD::Text::Align>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/GD/Text/Wrap.pm b/lib/Template/Plugin/GD/Text/Wrap.pm
new file mode 100644
index 0000000..0438599
--- /dev/null
+++ b/lib/Template/Plugin/GD/Text/Wrap.pm
@@ -0,0 +1,183 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::GD::Text::Wrap
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the GD::Text::Wrap
+# module.
+#
+# AUTHOR
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Craig Barratt. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Wrap.pm,v 1.55 2004/01/13 16:21:46 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::GD::Text::Wrap;
+
+require 5.004;
+
+use strict;
+use GD::Text::Wrap;
+use Template::Plugin;
+use base qw( GD::Text::Wrap Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/);
+
+sub new
+{
+ my $class = shift;
+ my $context = shift;
+ my $gd = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ return $class->SUPER::new($gd, @_);
+}
+
+sub set
+{
+ my $self = shift;
+
+ push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" );
+ $self->SUPER::set(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::GD::Text::Wrap - Break and wrap strings in GD images
+
+=head1 SYNOPSIS
+
+ [% USE align = GD.Text.Wrap(gd_image); %]
+
+=head1 EXAMPLES
+
+ [% FILTER null;
+ USE gd = GD.Image(200,400);
+ USE gdc = GD.Constants;
+ black = gd.colorAllocate(0, 0, 0);
+ green = gd.colorAllocate(0, 255, 0);
+ txt = "This is some long text. " | repeat(10);
+ USE wrapbox = GD.Text.Wrap(gd,
+ line_space => 4,
+ color => green,
+ text => txt,
+ );
+ wrapbox.set_font(gdc.gdMediumBoldFont);
+ wrapbox.set(align => 'center', width => 160);
+ wrapbox.draw(20, 20);
+ gd.png | stdout(1);
+ END;
+ -%]
+
+ [% txt = BLOCK -%]
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
+ sed diam nonummy nibh euismod tincidunt ut laoreet dolore
+ magna aliquam erat volutpat.
+ [% END -%]
+ [% FILTER null;
+ #
+ # This example follows the example in GD::Text::Wrap, except
+ # we create a second image that is a copy just enough of the
+ # first image to hold the final text, plus a border.
+ #
+ USE gd = GD.Image(400,400);
+ USE gdc = GD.Constants;
+ green = gd.colorAllocate(0, 255, 0);
+ blue = gd.colorAllocate(0, 0, 255);
+ USE wrapbox = GD.Text.Wrap(gd,
+ line_space => 4,
+ color => green,
+ text => txt,
+ );
+ wrapbox.set_font(gdc.gdMediumBoldFont);
+ wrapbox.set(align => 'center', width => 140);
+ rect = wrapbox.get_bounds(5, 5);
+ x0 = rect.0;
+ y0 = rect.1;
+ x1 = rect.2 + 9;
+ y1 = rect.3 + 9;
+ gd.filledRectangle(0, 0, x1, y1, blue);
+ gd.rectangle(0, 0, x1, y1, green);
+ wrapbox.draw(x0, y0);
+ nx = x1 + 1;
+ ny = y1 + 1;
+ USE gd2 = GD.Image(nx, ny);
+ gd2.copy(gd, 0, 0, 0, 0, x1, y1);
+ gd2.png | stdout(1);
+ END;
+ -%]
+
+=head1 DESCRIPTION
+
+The GD.Text.Wrap plugin provides an interface to the GD::Text::Wrap
+module. It allows multiples line of text to be drawn in GD images with
+various wrapping and alignment.
+
+See L<GD::Text::Wrap> for more details. See
+L<Template::Plugin::GD::Text::Align> for a plugin
+that allow you to draw text with various alignment
+and orientation.
+
+=head1 AUTHOR
+
+Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+
+The GD::Text module was written by Martien Verbruggen.
+
+
+=head1 VERSION
+
+1.55, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt>
+
+GD::Text is copyright 1999 Martien Verbruggen.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align>, L<GD|GD>, L<GD::Text::Wrap|GD::Text::Wrap>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/HTML.pm b/lib/Template/Plugin/HTML.pm
new file mode 100644
index 0000000..5cb63e0
--- /dev/null
+++ b/lib/Template/Plugin/HTML.pm
@@ -0,0 +1,197 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::HTML
+#
+# DESCRIPTION
+#
+# Template Toolkit plugin providing useful functionality for generating
+# HTML.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: HTML.pm,v 2.56 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::HTML;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.56 $ =~ /(\d+)\.(\d+)/);
+
+sub new {
+ my ($class, $context, @args) = @_;
+ my $hash = ref $args[-1] eq 'HASH' ? pop @args : { };
+ bless {
+ _SORTED => $hash->{ sorted } || 0,
+ }, $class;
+}
+
+sub element {
+ my ($self, $name, $attr) = @_;
+ ($name, $attr) = %$name if ref $name eq 'HASH';
+ return '' unless defined $name and length $name;
+ $attr = $self->attributes($attr);
+ $attr = " $attr" if $attr;
+ return "<$name$attr>";
+}
+
+sub attributes {
+ my ($self, $hash) = @_;
+ return '' unless UNIVERSAL::isa($hash, 'HASH');
+
+ my @keys = keys %$hash;
+ @keys = sort @keys if $self->{ _SORTED };
+
+ join(' ', map {
+ "$_=\"" . $self->escape( $hash->{ $_ } ) . '"';
+ } @keys);
+}
+
+sub escape {
+ my ($self, $text) = @_;
+ for ($text) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/"/&quot;/g;
+ }
+ $text;
+}
+
+sub url {
+ my ($self, $text) = @_;
+ return undef unless defined $text;
+ $text =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+ return $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::HTML - Plugin to create HTML elements
+
+=head1 SYNOPSIS
+
+ [% USE HTML %]
+
+ [% HTML.escape("if (a < b && c > d) ..." %]
+
+ [% HTML.element(table => { border => 1, cellpadding => 2 }) %]
+
+ [% HTML.attributes(border => 1, cellpadding => 2) %]
+
+=head1 DESCRIPTION
+
+The HTML plugin is very new and very basic, implementing a few useful
+methods for generating HTML. It is likely to be extended in the future
+or integrated with a larger project to generate HTML elements in a generic
+way (as discussed recently on the mod_perl mailing list).
+
+=head1 METHODS
+
+=head2 escape(text)
+
+Returns the source text with any HTML reserved characters such as
+E<lt>, E<gt>, etc., correctly esacped to their entity equivalents.
+
+=head2 attributes(hash)
+
+Returns the elements of the hash array passed by reference correctly
+formatted (e.g. values quoted and correctly escaped) as attributes for
+an HTML element.
+
+=head2 element(type, attributes)
+
+Generates an HTML element of the specified type and with the attributes
+provided as an optional hash array reference as the second argument or
+as named arguments.
+
+ [% HTML.element(table => { border => 1, cellpadding => 2 }) %]
+ [% HTML.element('table', border=1, cellpadding=2) %]
+ [% HTML.element(table => attribs) %]
+
+=head1 DEBUGGING
+
+The HTML plugin accepts a 'sorted' option as a constructor argument
+which, when set to any true value, causes the attributes generated by
+the attributes() method (either directly or via element()) to be
+returned in sorted order. Order of attributes isn't important in
+HTML, but this is provided mainly for the purposes of debugging where
+it is useful to have attributes generated in a deterministic order
+rather than whatever order the hash happened to feel like returning
+the keys in.
+
+ [% USE HTML(sorted=1) %]
+ [% HTML.element( foo => { charlie => 1, bravo => 2, alpha => 3 } ) %]
+
+generates:
+
+ <foo alpha="3" bravo="2" charlie="1">
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.56, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Image.pm b/lib/Template/Plugin/Image.pm
new file mode 100644
index 0000000..4eb509c
--- /dev/null
+++ b/lib/Template/Plugin/Image.pm
@@ -0,0 +1,425 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Image
+#
+# DESCRIPTION
+# Plugin for encapsulating information about an image.
+#
+# AUTHOR
+# Andy Wardley <abw@wardley.org>
+#
+# COPYRIGHT
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Image.pm,v 1.13 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Image;
+
+require 5.004;
+
+use strict;
+use Template::Exception;
+use Template::Plugin;
+use File::Spec;
+#use Image::Info;
+#use Image::Size;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION $AUTOLOAD );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.13 $ =~ /(\d+)\.(\d+)/);
+
+BEGIN {
+ if (eval { require Image::Info; }) {
+ *img_info = \&Image::Info::image_info;
+ }
+ elsif (eval { require Image::Size; }) {
+ *img_info = sub {
+ my $file = shift;
+ my @stuff = Image::Size::imgsize($file);
+ return { "width" => $stuff[0],
+ "height" => $stuff[1],
+ "error" =>
+ # imgsize returns either a three letter file type
+ # or an error message as third value
+ (defined($stuff[2]) && length($stuff[2]) > 3
+ ? $stuff[2]
+ : undef),
+ };
+ }
+ }
+ else {
+ die(Template::Exception->new("image",
+ "Couldn't load Image::Info or Image::Size: $@"));
+ }
+
+}
+
+#------------------------------------------------------------------------
+# new($context, $name, \%config)
+#
+# Create a new Image object. Takes the pathname of the file as
+# the argument following the context and an optional
+# hash reference of configuration parameters.
+#------------------------------------------------------------------------
+
+sub new {
+ my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
+ my ($class, $context, $name) = @_;
+ my ($root, $file, $type);
+
+ # name can be a positional or named argument
+ $name = $config->{ name } unless defined $name;
+
+ return $class->throw('no image file specified')
+ unless defined $name and length $name;
+
+ # name can be specified as an absolute path or relative
+ # to a root directory
+
+ if ($root = $config->{ root }) {
+ $file = File::Spec->catfile($root, $name);
+ }
+ else {
+ $file = $name;
+ }
+
+ # Make a note of whether we are using Image::Size or
+ # Image::Info -- at least for the test suite
+ $type = $INC{"Image/Size.pm"} ? "Image::Size" : "Image::Info";
+
+ # do we want to check to see if file exists?
+
+ bless {
+ name => $name,
+ file => $file,
+ root => $root,
+ type => $type,
+ }, $class;
+}
+
+#------------------------------------------------------------------------
+# init()
+#
+# Calls image_info on $self->{ file }
+#------------------------------------------------------------------------
+
+sub init {
+ my $self = shift;
+ return $self if $self->{ size };
+
+ my $image = img_info($self->{ file });
+ return $self->throw($image->{ error }) if defined $image->{ error };
+
+ @$self{ keys %$image } = values %$image;
+ $self->{ size } = [ $image->{ width }, $image->{ height } ];
+
+ $self->{ modtime } = (stat $self->{ file })[10];
+
+ return $self;
+}
+
+#------------------------------------------------------------------------
+# attr()
+#
+# Return the width and height as HTML/XML attributes.
+#------------------------------------------------------------------------
+
+sub attr {
+ my $self = shift;
+ my $size = $self->size();
+ return "width=\"$size->[0]\" height=\"$size->[1]\"";
+}
+
+#------------------------------------------------------------------------
+# modtime()
+#
+# Return last modification time as a time_t:
+#
+# [% date.format(image.modtime, "%Y/%m/%d") %]
+#------------------------------------------------------------------------
+
+sub modtime {
+ my $self = shift;
+ $self->init;
+ return $self->{ modtime };
+}
+
+#------------------------------------------------------------------------
+# tag(\%options)
+#
+# Return an XHTML img tag.
+#------------------------------------------------------------------------
+
+sub tag {
+ my $self = shift;
+ my $options = ref $_[0] eq 'HASH' ? shift : { @_ };
+
+ my $tag = "<img src=\"$self->{ name }\" " . $self->attr();
+
+ if (%$options) {
+ while (my ($key, $val) = each %$options) {
+ $tag .= " $key=\"$val\"";
+ }
+ }
+
+ $tag .= ' />';
+
+ return $tag;
+}
+
+
+sub throw {
+ my ($self, $error) = @_;
+ die (Template::Exception->new('Image', $error));
+}
+
+sub AUTOLOAD {
+ my $self = shift;
+ (my $a = $AUTOLOAD) =~ s/.*:://;
+
+ $self->init;
+ return $self->{ $a };
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Image - Plugin access to image sizes
+
+=head1 SYNOPSIS
+
+ [% USE Image(filename) %]
+ [% Image.width %]
+ [% Image.height %]
+ [% Image.size.join(', ') %]
+ [% Image.attr %]
+ [% Image.tag %]
+
+=head1 DESCRIPTION
+
+This plugin provides an interface to the Image::Info or Image::Size
+modules for determining the size of image files.
+
+You can specify the plugin name as either 'Image' or 'image'. The
+plugin object created will then have the same name. The file name of
+the image should be specified as a positional or named argument.
+
+ [% # all these are valid, take your pick %]
+ [% USE Image('foo.gif') %]
+ [% USE image('bar.gif') %]
+ [% USE Image 'ping.gif' %]
+ [% USE image(name='baz.gif') %]
+ [% USE Image name='pong.gif' %]
+
+You can also provide an alternate name for an Image plugin object.
+
+ [% USE img1 = image 'foo.gif' %]
+ [% USE img2 = image 'bar.gif' %]
+
+The 'width' and 'height' methods return the width and height of the
+image, respectively. The 'size' method returns a reference to a 2
+element list containing the width and height.
+
+ [% USE image 'foo.gif' %]
+ width: [% image.width %]
+ height: [% image.height %]
+ size: [% image.size.join(', ') %]
+
+The 'attr' method returns the height and width as HTML/XML attributes.
+
+ [% USE image 'foo.gif' %]
+ [% image.attr %]
+
+Typical output:
+
+ width="60" height="20"
+
+The 'tag' method returns a complete XHTML tag referencing the image.
+
+ [% USE image 'foo.gif' %]
+ [% image.tag %]
+
+Typical output:
+
+ <img src="foo.gif" width="60" height="20" />
+
+You can provide any additional attributes that should be added to the
+XHTML tag.
+
+
+ [% USE image 'foo.gif' %]
+ [% image.tag(border=0, class="logo") %]
+
+Typical output:
+
+ <img src="foo.gif" width="60" height="20" border="0" class="logo" />
+
+The 'modtime' method returns the ctime of the file in question, suitable
+for use with date.format:
+
+ [% USE image 'foo.gif' %]
+ [% USE date %]
+ [% date.format(image.modtime, "%B, %e %Y") %]
+
+=head1 CATCHING ERRORS
+
+If the image file cannot be found then the above methods will throw an
+'Image' error. You can enclose calls to these methods in a
+TRY...CATCH block to catch any potential errors.
+
+ [% TRY;
+ image.width;
+ CATCH;
+ error; # print error
+ END
+ %]
+
+=head1 USING Image::Info
+
+At run time, the plugin tries to load Image::Info in preference to
+Image::Size. If Image::Info is found, then some additional methods are
+available, in addition to 'size', 'width', 'height', 'attr', and 'tag'.
+These additional methods are named after the elements that Image::Info
+retrieves from the image itself; see L<Image::Info> for more details
+-- the types of methods available depend on the type of image.
+These additional methods will always include the following:
+
+=over 4
+
+=item file_media_type
+
+This is the MIME type that is appropriate for the given file format.
+The corresponding value is a string like: "image/png" or "image/jpeg".
+
+=item file_ext
+
+The is the suggested file name extention for a file of the given
+file format. The value is a 3 letter, lowercase string like
+"png", "jpg".
+
+
+=item color_type
+
+The value is a short string describing what kind of values the pixels
+encode. The value can be one of the following:
+
+ Gray
+ GrayA
+ RGB
+ RGBA
+ CMYK
+ YCbCr
+ CIELab
+
+These names can also be prefixed by "Indexed-" if the image is
+composed of indexes into a palette. Of these, only "Indexed-RGB" is
+likely to occur.
+
+(It is similar to the TIFF field PhotometricInterpretation, but this
+name was found to be too long, so we used the PNG inpired term
+instead.)
+
+=item resolution
+
+The value of this field normally gives the physical size of the image
+on screen or paper. When the unit specifier is missing then this field
+denotes the squareness of pixels in the image.
+
+The syntax of this field is:
+
+ <res> <unit>
+ <xres> "/" <yres> <unit>
+ <xres> "/" <yres>
+
+The E<lt>resE<gt>, E<lt>xresE<gt> and E<lt>yresE<gt> fields are
+numbers. The E<lt>unitE<gt> is a string like C<dpi>, C<dpm> or
+C<dpcm> (denoting "dots per inch/cm/meter).
+
+=item SamplesPerPixel
+
+This says how many channels there are in the image. For some image
+formats this number might be higher than the number implied from the
+C<color_type>.
+
+=item BitsPerSample
+
+This says how many bits are used to encode each of samples. The value
+is a reference to an array containing numbers. The number of elements
+in the array should be the same as C<SamplesPerPixel>.
+
+=item Comment
+
+Textual comments found in the file. The value is a reference to an
+array if there are multiple comments found.
+
+=item Interlace
+
+If the image is interlaced, then this tell which interlace method is
+used.
+
+=item Compression
+
+This tell which compression algorithm is used.
+
+=item Gamma
+
+A number.
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.13, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Iterator.pm b/lib/Template/Plugin/Iterator.pm
new file mode 100644
index 0000000..0f33b2f
--- /dev/null
+++ b/lib/Template/Plugin/Iterator.pm
@@ -0,0 +1,118 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Iterator
+#
+# DESCRIPTION
+#
+# Plugin to create a Template::Iterator from a list of items and optional
+# configuration parameters.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Iterator.pm,v 2.62 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Iterator;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+use Template::Iterator;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+
+#------------------------------------------------------------------------
+# new($context, \@data, \%args)
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ Template::Iterator->new(@_);
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Iterator - Plugin to create iterators (Template::Iterator)
+
+=head1 SYNOPSIS
+
+ [% USE iterator(list, args) %]
+
+ [% FOREACH item = iterator %]
+ [% '<ul>' IF iterator.first %]
+ <li>[% item %]
+ [% '</ul>' IF iterator.last %]
+ [% END %]
+
+=head1 DESCRIPTION
+
+The iterator plugin provides a way to create a Template::Iterator object
+to iterate over a data set. An iterator is implicitly automatically by the
+FOREACH directive. This plugin allows the iterator to be explicitly created
+with a given name.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::Iterator|Template::Iterator>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Pod.pm b/lib/Template/Plugin/Pod.pm
new file mode 100644
index 0000000..e5f82c2
--- /dev/null
+++ b/lib/Template/Plugin/Pod.pm
@@ -0,0 +1,116 @@
+#==============================================================================
+#
+# Template::Plugin::Pod
+#
+# DESCRIPTION
+# Pod parser and object model.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Pod.pm,v 2.62 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Pod;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+
+use Pod::POM;
+
+#------------------------------------------------------------------------
+# new($context, \%config)
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+
+ Pod::POM->new(@_);
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Pod - Plugin interface to Pod::POM (Pod Object Model)
+
+=head1 SYNOPSIS
+
+ [% USE Pod(podfile) %]
+
+ [% FOREACH head1 = Pod.head1;
+ FOREACH head2 = head1/head2;
+ ...
+ END;
+ END
+ %]
+
+=head1 DESCRIPTION
+
+This plugin is an interface to the Pod::POM module.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Pod::POM|Pod::POM>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Procedural.pm b/lib/Template/Plugin/Procedural.pm
new file mode 100644
index 0000000..8601225
--- /dev/null
+++ b/lib/Template/Plugin/Procedural.pm
@@ -0,0 +1,170 @@
+#==============================================================================
+#
+# Template::Plugin::Procedural
+#
+# DESCRIPTION
+#
+# A Template Plugin to provide a Template Interface to Data::Dumper
+#
+# AUTHOR
+# Mark Fowler <mark@twoshortplanks.com>
+#
+# COPYRIGHT
+#
+# Copyright (C) 2002 Mark Fowler. All Rights Reserved
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------------
+#
+# $Id: Procedural.pm,v 1.11 2004/01/13 16:20:38 abw Exp $
+#
+#==============================================================================
+
+package Template::Plugin::Procedural;
+
+require 5.004;
+
+use strict;
+
+use vars qw( $VERSION $DEBUG $AUTOLOAD );
+use base qw( Template::Plugin );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.11 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+#------------------------------------------------------------------------
+# load
+#------------------------------------------------------------------------
+
+sub load
+{
+ my ($class, $context) = @_;
+
+ # create a proxy namespace that will be used for objects
+ my $proxy = "Template::Plugin::" . $class;
+
+ # okay, in our proxy create the autoload routine that will
+ # call the right method in the real class
+ no strict "refs";
+ *{ $proxy . "::AUTOLOAD" } =
+ sub
+ {
+ # work out what the method is called
+ $AUTOLOAD =~ s!^.*::!!;
+
+ print STDERR "Calling '$AUTOLOAD' in '$class'\n"
+ if $DEBUG;
+
+ # look up the sub for that method (but in a OO way)
+ my $uboat = $class->can($AUTOLOAD);
+
+ # if it existed call it as a subroutine, not as a method
+ if ($uboat)
+ {
+ shift @_;
+ return $uboat->(@_);
+ }
+
+ print STDERR "Eeek, no such method '$AUTOLOAD'\n"
+ if $DEBUG;
+
+ return "";
+ };
+
+ # create a simple new method that simply returns a blessed
+ # scalar as the object.
+ *{ $proxy . "::new" } =
+ sub
+ {
+ my $this;
+ return bless \$this, $_[0];
+ };
+
+ return $proxy;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Procedural - Base class for procedural plugins
+
+=head1 SYNOPSIS
+
+ package Template::Plugin::LWPSimple;
+ use base qw(Template::Plugin::Procedural);
+ use LWP::Simple; # exports 'get'
+ 1;
+
+ [% USE LWPSimple %]
+ [% LWPSimple.get("http://www.tt2.org/") %]
+
+=head1 DESCRIPTION
+
+B<Template::Plugin::Procedural> is a base class for Template Toolkit
+plugins that causes defined subroutines to be called directly rather
+than as a method. Essentially this means that subroutines will not
+receive the class name or object as its first argument.
+
+This is most useful when creating plugins for modules that normally
+work by exporting subroutines that do not expect such additional
+arguments.
+
+Despite the fact that subroutines will not be called in an OO manner,
+inheritance still function as normal. A class that uses
+B<Template::Plugin::Procedural> can be subclassed and both subroutines
+defined in the subclass and subroutines defined in the original class
+will be available to the Template Toolkit and will be called without
+the class/object argument.
+
+=head1 AUTHOR
+
+Mark Fowler E<lt>mark@twoshortplanks.comE<gt>
+
+L<http://www.twoshortplanks.com|http://www.twoshortplanks.com>
+
+
+
+
+=head1 VERSION
+
+1.11, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2002 Mark Fowler E<lt>mark@twoshortplanks.comE<gt>
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/String.pm b/lib/Template/Plugin/String.pm
new file mode 100644
index 0000000..34dd007
--- /dev/null
+++ b/lib/Template/Plugin/String.pm
@@ -0,0 +1,796 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::String
+#
+# DESCRIPTION
+# Template Toolkit plugin to implement a basic String object.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: String.pm,v 2.33 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::String;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use Template::Exception;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION $ERROR);
+use overload q|""| => "text",
+ fallback => 1;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.33 $ =~ /(\d+)\.(\d+)/);
+$ERROR = '';
+
+*centre = \*center;
+*append = \*push;
+*prepend = \*unshift;
+
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, @args) = @_;
+ my $context = ref $class ? undef : shift(@args);
+ my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { };
+
+ $class = ref($class) || $class;
+
+ my $text = defined $config->{ text }
+ ? $config->{ text }
+ : (@args ? shift(@args) : '');
+
+# print STDERR "text: [$text]\n";
+# print STDERR "class: [$class]\n";
+
+ my $self = bless {
+ text => $text,
+ filters => [ ],
+ _CONTEXT => $context,
+ }, $class;
+
+ my $filter = $config->{ filter } || $config->{ filters };
+
+ # install any output filters specified as 'filter' or 'filters' option
+ $self->output_filter($filter)
+ if $filter;
+
+ return $self;
+}
+
+
+sub text {
+ my $self = shift;
+ return $self->{ text } unless @{ $self->{ filters } };
+
+ my $text = $self->{ text };
+ my $context = $self->{ _CONTEXT };
+
+ foreach my $dispatch (@{ $self->{ filters } }) {
+ my ($name, $args) = @$dispatch;
+ my $code = $context->filter($name, $args)
+ || $self->throw($context->error());
+ $text = &$code($text);
+ }
+ return $text;
+}
+
+
+sub copy {
+ my $self = shift;
+ $self->new($self->{ text });
+}
+
+
+sub throw {
+ my $self = shift;
+
+ die (Template::Exception->new('String', join('', @_)));
+}
+
+
+#------------------------------------------------------------------------
+# output_filter($filter)
+#
+# Install automatic output filter(s) for the string. $filter can a list:
+# [ 'name1', 'name2' => [ ..args.. ], name4 => { ..args.. } ] or a hash
+# { name1 => '', name2 => [ args ], name3 => { args } }
+#------------------------------------------------------------------------
+
+sub output_filter {
+ my ($self, $filter) = @_;
+ my ($name, $args, $dispatch);
+ my $filters = $self->{ filters };
+ my $count = 0;
+
+ if (ref $filter eq 'HASH') {
+ $filter = [ %$filter ];
+ }
+ elsif (ref $filter ne 'ARRAY') {
+ $filter = [ split(/\s*\W+\s*/, $filter) ];
+ }
+
+ while (@$filter) {
+ $name = shift @$filter;
+
+ # args may follow as a reference (or empty string, e.g. { foo => '' }
+ if (@$filter && (ref($filter->[0]) || ! length $filter->[0])) {
+ $args = shift @$filter;
+ if ($args) {
+ $args = [ $args ] unless ref $args eq 'ARRAY';
+ }
+ else {
+ $args = [ ];
+ }
+ }
+ else {
+ $args = [ ];
+ }
+
+# $self->DEBUG("adding output filter $name(@$args)\n");
+
+ push(@$filters, [ $name, $args ]);
+ $count++;
+ }
+
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+
+sub push {
+ my $self = shift;
+ $self->{ text } .= join('', @_);
+ return $self;
+}
+
+
+sub unshift {
+ my $self = shift;
+ $self->{ text } = join('', @_) . $self->{ text };
+ return $self;
+}
+
+
+sub pop {
+ my $self = shift;
+ my $strip = shift || return $self;
+ $self->{ text } =~ s/$strip$//;
+ return $self;
+}
+
+
+sub shift {
+ my $self = shift;
+ my $strip = shift || return $self;
+ $self->{ text } =~ s/^$strip//;
+ return $self;
+}
+
+#------------------------------------------------------------------------
+
+sub center {
+ my ($self, $width) = @_;
+ my $text = $self->{ text };
+ my $len = length $text;
+ $width ||= 0;
+
+ if ($len < $width) {
+ my $lpad = int(($width - $len) / 2);
+ my $rpad = $width - $len - $lpad;
+ $self->{ text } = (' ' x $lpad) . $self->{ text } . (' ' x $rpad);
+ }
+
+ return $self;
+}
+
+
+sub left {
+ my ($self, $width) = @_;
+ my $len = length $self->{ text };
+ $width ||= 0;
+
+ $self->{ text } .= (' ' x ($width - $len))
+ if $width > $len;
+
+ return $self;
+}
+
+
+sub right {
+ my ($self, $width) = @_;
+ my $len = length $self->{ text };
+ $width ||= 0;
+
+ $self->{ text } = (' ' x ($width - $len)) . $self->{ text }
+ if $width > $len;
+
+ return $self;
+}
+
+
+sub format {
+ my ($self, $format) = @_;
+ $format = '%s' unless defined $format;
+ $self->{ text } = sprintf($format, $self->{ text });
+ return $self;
+}
+
+
+sub filter {
+ my ($self, $name, @args) = @_;
+
+ my $context = $self->{ _CONTEXT };
+
+ my $code = $context->filter($name, \@args)
+ || $self->throw($context->error());
+ return &$code($self->{ text });
+}
+
+
+#------------------------------------------------------------------------
+
+sub upper {
+ my $self = CORE::shift;
+ $self->{ text } = uc $self->{ text };
+ return $self;
+}
+
+
+sub lower {
+ my $self = CORE::shift;
+ $self->{ text } = lc $self->{ text };
+ return $self;
+}
+
+
+sub capital {
+ my $self = CORE::shift;
+ $self->{ text } =~ s/^(.)/\U$1/;
+ return $self;
+}
+
+#------------------------------------------------------------------------
+
+sub chop {
+ my $self = CORE::shift;
+ chop $self->{ text };
+ return $self;
+}
+
+
+sub chomp {
+ my $self = CORE::shift;
+ chomp $self->{ text };
+ return $self;
+}
+
+
+sub trim {
+ my $self = CORE::shift;
+ for ($self->{ text }) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ return $self;
+}
+
+
+sub collapse {
+ my $self = CORE::shift;
+ for ($self->{ text }) {
+ s/^\s+//;
+ s/\s+$//;
+ s/\s+/ /g
+ }
+ return $self;
+
+}
+
+#------------------------------------------------------------------------
+
+sub length {
+ my $self = CORE::shift;
+ return length $self->{ text };
+}
+
+
+sub truncate {
+ my ($self, $length, $suffix) = @_;
+ return $self unless defined $length;
+ $suffix ||= '';
+ return $self if CORE::length $self->{ text } <= $length;
+ $self->{ text } = substr($self->{ text }, 0,
+ $length - CORE::length($suffix)) . $suffix;
+ return $self;
+}
+
+
+sub repeat {
+ my ($self, $n) = @_;
+ return $self unless defined $n;
+ $self->{ text } = $self->{ text } x $n;
+ return $self;
+}
+
+
+sub replace {
+ my ($self, $search, $replace) = @_;
+ return $self unless defined $search;
+ $replace = '' unless defined $replace;
+ $self->{ text } =~ s/$search/$replace/g;
+ return $self;
+}
+
+
+sub remove {
+ my ($self, $search) = @_;
+ $search = '' unless defined $search;
+ $self->{ text } =~ s/$search//g;
+ return $self;
+}
+
+
+sub split {
+ my $self = CORE::shift;
+ my $split = CORE::shift;
+ my $limit = CORE::shift || 0;
+ $split = '\s+' unless defined $split;
+ return [ split($split, $self->{ text }, $limit) ];
+}
+
+
+sub search {
+ my ($self, $pattern) = @_;
+ return $self->{ text } =~ /$pattern/;
+}
+
+
+sub equals {
+ my ($self, $comparison) = @_;
+ return $self->{ text } eq $comparison;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::String - Object oriented interface for string manipulation
+
+=head1 SYNOPSIS
+
+ # create String objects via USE directive
+ [% USE String %]
+ [% USE String 'initial text' %]
+ [% USE String text => 'initial text' %]
+
+ # or from an existing String via new()
+ [% newstring = String.new %]
+ [% newstring = String.new('newstring text') %]
+ [% newstring = String.new( text => 'newstring text' ) %]
+
+ # or from an existing String via copy()
+ [% newstring = String.copy %]
+
+ # append text to string
+ [% String.append('text to append') %]
+
+ # format left, right or center/centre padded
+ [% String.left(20) %]
+ [% String.right(20) %]
+ [% String.center(20) %] # American spelling
+ [% String.centre(20) %] # European spelling
+
+ # and various other methods...
+
+=head1 DESCRIPTION
+
+This module implements a String class for doing stringy things to
+text in an object-oriented way.
+
+You can create a String object via the USE directive, adding any
+initial text value as an argument or as the named parameter 'text'.
+
+ [% USE String %]
+ [% USE String 'initial text' %]
+ [% USE String text='initial text' %]
+
+The object created will be referenced as 'String' by default, but you
+can provide a different variable name for the object to be assigned
+to:
+
+ [% USE greeting = String 'Hello World' %]
+
+Once you've got a String object, you can use it as a prototype to
+create other String objects with the new() method.
+
+ [% USE String %]
+ [% greeting = String.new('Hello World') %]
+
+The new() method also accepts an initial text string as an argument
+or the named parameter 'text'.
+
+ [% greeting = String.new( text => 'Hello World' ) %]
+
+You can also call copy() to create a new String as a copy of the
+original.
+
+ [% greet2 = greeting.copy %]
+
+The String object has a text() method to return the content of the
+string.
+
+ [% greeting.text %]
+
+However, it is sufficient to simply print the string and let the
+overloaded stringification operator call the text() method
+automatically for you.
+
+ [% greeting %]
+
+Thus, you can treat String objects pretty much like any regular piece
+of text, interpolating it into other strings, for example:
+
+ [% msg = "It printed '$greeting' and then dumped core\n" %]
+
+You also have the benefit of numerous other methods for manipulating
+the string.
+
+ [% msg.append("PS Don't eat the yellow snow") %]
+
+Note that all methods operate on and mutate the contents of the string
+itself. If you want to operate on a copy of the string then simply
+take a copy first:
+
+ [% msg.copy.append("PS Don't eat the yellow snow") %]
+
+These methods return a reference to the String object itself. This
+allows you to chain multiple methods together.
+
+ [% msg.copy.append('foo').right(72) %]
+
+It also means that in the above examples, the String is returned which
+causes the text() method to be called, which results in the new value of
+the string being printed. To suppress printing of the string, you can
+use the CALL directive.
+
+ [% foo = String.new('foo') %]
+
+ [% foo.append('bar') %] # prints "foobar"
+
+ [% CALL foo.append('bar') %] # nothing
+
+=head1 METHODS
+
+=head2 Construction Methods
+
+The following methods are used to create new String objects.
+
+=over 4
+
+=item new()
+
+Creates a new string using an initial value passed as a positional
+argument or the named parameter 'text'.
+
+ [% USE String %]
+ [% msg = String.new('Hello World') %]
+ [% msg = String.new( text => 'Hello World' ) %]
+
+=item copy()
+
+Creates a new String object which contains a copy of the original string.
+
+ [% msg2 = msg.copy %]
+
+=back
+
+=head2 Inspection Methods
+
+These methods are used to inspect the string content or other parameters
+relevant to the string.
+
+=over 4
+
+=item text()
+
+Returns the internal text value of the string. The stringification
+operator is overloaded to call this method. Thus the following are
+equivalent:
+
+ [% msg.text %]
+ [% msg %]
+
+=item length()
+
+Returns the length of the string.
+
+ [% USE String("foo") %]
+
+ [% String.length %] # => 3
+
+=item search($pattern)
+
+Searches the string for the regular expression specified in $pattern
+returning true if found or false otherwise.
+
+ [% item = String.new('foo bar baz wiz waz woz') %]
+
+ [% item.search('wiz') ? 'WIZZY! :-)' : 'not wizzy :-(' %]
+
+=item split($pattern, $limit)
+
+Splits the string based on the delimiter $pattern and optional $limit.
+Delegates to Perl's internal split() so the parameters are exactly the same.
+
+ [% FOREACH item.split %]
+ ...
+ [% END %]
+
+ [% FOREACH item.split('baz|waz') %]
+ ...
+ [% END %]
+
+=back
+
+=head2 Mutation Methods
+
+These methods modify the internal value of the string. For example:
+
+ [% USE str=String('foobar') %]
+
+ [% str.append('.html') %] # str => 'foobar.html'
+
+The value of the String 'str' is now 'foobar.html'. If you don't want
+to modify the string then simply take a copy first.
+
+ [% str.copy.append('.html') %]
+
+These methods all return a reference to the String object itself. This
+has two important benefits. The first is that when used as above, the
+String object 'str' returned by the append() method will be stringified
+with a call to its text() method. This will return the newly modified
+string content. In other words, a directive like:
+
+ [% str.append('.html') %]
+
+will update the string and also print the new value. If you just want
+to update the string but not print the new value then use CALL.
+
+ [% CALL str.append('.html') %]
+
+The other benefit of these methods returning a reference to the String
+is that you can chain as many different method calls together as you
+like. For example:
+
+ [% String.append('.html').trim.format(href) %]
+
+Here are the methods:
+
+=over 4
+
+=item push($suffix, ...) / append($suffix, ...)
+
+Appends all arguments to the end of the string. The
+append() method is provided as an alias for push().
+
+ [% msg.push('foo', 'bar') %]
+ [% msg.append('foo', 'bar') %]
+
+=item pop($suffix)
+
+Removes the suffix passed as an argument from the end of the String.
+
+ [% USE String 'foo bar' %]
+ [% String.pop(' bar') %] # => 'foo'
+
+=item unshift($prefix, ...) / prepend($prefix, ...)
+
+Prepends all arguments to the beginning of the string. The
+prepend() method is provided as an alias for unshift().
+
+ [% msg.unshift('foo ', 'bar ') %]
+ [% msg.prepend('foo ', 'bar ') %]
+
+=item shift($prefix)
+
+Removes the prefix passed as an argument from the start of the String.
+
+ [% USE String 'foo bar' %]
+ [% String.shift('foo ') %] # => 'bar'
+
+=item left($pad)
+
+If the length of the string is less than $pad then the string is left
+formatted and padded with spaces to $pad length.
+
+ [% msg.left(20) %]
+
+=item right($pad)
+
+As per left() but right padding the String to a length of $pad.
+
+ [% msg.right(20) %]
+
+=item center($pad) / centre($pad)
+
+As per left() and right() but formatting the String to be centered within
+a space padded string of length $pad. The centre() method is provided as
+an alias for center() to keep Yanks and Limeys happy.
+
+ [% msg.center(20) %] # American spelling
+ [% msg.centre(20) %] # European spelling
+
+=item format($format)
+
+Apply a format in the style of sprintf() to the string.
+
+ [% USE String("world") %]
+ [% String.format("Hello %s\n") %] # => "Hello World\n"
+
+=item upper()
+
+Converts the string to upper case.
+
+ [% USE String("foo") %]
+
+ [% String.upper %] # => 'FOO'
+
+=item lower()
+
+Converts the string to lower case
+
+ [% USE String("FOO") %]
+
+ [% String.lower %] # => 'foo'
+
+=item capital()
+
+Converts the first character of the string to upper case.
+
+ [% USE String("foo") %]
+
+ [% String.capital %] # => 'Foo'
+
+The remainder of the string is left untouched. To force the string to
+be all lower case with only the first letter capitalised, you can do
+something like this:
+
+ [% USE String("FOO") %]
+
+ [% String.lower.capital %] # => 'Foo'
+
+=item chop()
+
+Removes the last character from the string.
+
+ [% USE String("foop") %]
+
+ [% String.chop %] # => 'foo'
+
+=item chomp()
+
+Removes the trailing newline from the string.
+
+ [% USE String("foo\n") %]
+
+ [% String.chomp %] # => 'foo'
+
+=item trim()
+
+Removes all leading and trailing whitespace from the string
+
+ [% USE String(" foo \n\n ") %]
+
+ [% String.trim %] # => 'foo'
+
+=item collapse()
+
+Removes all leading and trailing whitespace and collapses any sequences
+of multiple whitespace to a single space.
+
+ [% USE String(" \n\r \t foo \n \n bar \n") %]
+
+ [% String.collapse %] # => "foo bar"
+
+=item truncate($length, $suffix)
+
+Truncates the string to $length characters.
+
+ [% USE String('long string') %]
+ [% String.truncate(4) %] # => 'long'
+
+If $suffix is specified then it will be appended to the truncated
+string. In this case, the string will be further shortened by the
+length of the suffix to ensure that the newly constructed string
+complete with suffix is exactly $length characters long.
+
+ [% USE msg = String('Hello World') %]
+ [% msg.truncate(8, '...') %] # => 'Hello...'
+
+=item replace($search, $replace)
+
+Replaces all occurences of $search in the string with $replace.
+
+ [% USE String('foo bar foo baz') %]
+ [% String.replace('foo', 'wiz') %] # => 'wiz bar wiz baz'
+
+=item remove($search)
+
+Remove all occurences of $search in the string.
+
+ [% USE String('foo bar foo baz') %]
+ [% String.remove('foo ') %] # => 'bar baz'
+
+=item repeat($count)
+
+Repeats the string $count times.
+
+ [% USE String('foo ') %]
+ [% String.repeat(3) %] # => 'foo foo foo '
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.33, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Table.pm b/lib/Template/Plugin/Table.pm
new file mode 100644
index 0000000..c1fd79a
--- /dev/null
+++ b/lib/Template/Plugin/Table.pm
@@ -0,0 +1,464 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Table
+#
+# DESCRIPTION
+#
+# Plugin to order a linear data set into a virtual 2-dimensional table
+# from which row and column permutations can be fetched.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Table.pm,v 2.64 2004/01/13 16:20:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Table;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA $VERSION $AUTOLOAD );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($context, \@data, \%args)
+#
+# This constructor method initialises the object to iterate through
+# the data set passed by reference to a list as the first parameter.
+# It calculates the shape of the permutation table based on the ROWS
+# or COLS parameters specified in the $args hash reference. The
+# OVERLAP parameter may be provided to specify the number of common
+# items that should be shared between subseqent columns.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $context, $data, $params) = @_;
+ my ($size, $rows, $cols, $coloff, $overlap, $error);
+
+ # if the data item is a reference to a Template::Iterator object,
+ # or subclass thereof, we call its get_all() method to extract all
+ # the data it contains
+ if (UNIVERSAL::isa($data, 'Template::Iterator')) {
+ ($data, $error) = $data->get_all();
+ return $class->error("iterator failed to provide data for table: ",
+ $error)
+ if $error;
+ }
+
+ return $class->error('invalid table data, expecting a list')
+ unless ref $data eq 'ARRAY';
+
+ $params ||= { };
+ return $class->error('invalid table parameters, expecting a hash')
+ unless ref $params eq 'HASH';
+
+ # ensure keys are folded to upper case
+ @$params{ map { uc } keys %$params } = values %$params;
+
+ $size = scalar @$data;
+ $overlap = $params->{ OVERLAP } || 0;
+
+ # calculate number of columns based on a specified number of rows
+ if ($rows = $params->{ ROWS }) {
+ if ($size < $rows) {
+ $rows = $size; # pad?
+ $cols = 1;
+ $coloff = 0;
+ }
+ else {
+ $coloff = $rows - $overlap;
+ $cols = int ($size / $coloff)
+ + ($size % $coloff > $overlap ? 1 : 0)
+ }
+ }
+ # calculate number of rows based on a specified number of columns
+ elsif ($cols = $params->{ COLS }) {
+ if ($size < $cols) {
+ $cols = $size;
+ $rows = 1;
+ $coloff = 1;
+ }
+ else {
+ $coloff = int ($size / $cols)
+ + ($size % $cols > $overlap ? 1 : 0);
+ $rows = $coloff + $overlap;
+ }
+ }
+ else {
+ $rows = $size;
+ $cols = 1;
+ $coloff = 0;
+ }
+
+ bless {
+ _DATA => $data,
+ _SIZE => $size,
+ _NROWS => $rows,
+ _NCOLS => $cols,
+ _COLOFF => $coloff,
+ _OVERLAP => $overlap,
+ _PAD => defined $params->{ PAD } ? $params->{ PAD } : 1,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# row($n)
+#
+# Returns a reference to a list containing the items in the row whose
+# number is specified by parameter. If the row number is undefined,
+# it calls rows() to return a list of all rows.
+#------------------------------------------------------------------------
+
+sub row {
+ my ($self, $row) = @_;
+ my ($data, $cols, $offset, $size, $pad)
+ = @$self{ qw( _DATA _NCOLS _COLOFF _SIZE _PAD) };
+ my @set;
+
+ # return all rows if row number not specified
+ return $self->rows()
+ unless defined $row;
+
+ return () if $row >= $self->{ _NROWS } || $row < 0;
+
+ my $index = $row;
+
+ for (my $c = 0; $c < $cols; $c++) {
+ push(@set, $index < $size
+ ? $data->[$index]
+ : ($pad ? undef : ()));
+ $index += $offset;
+ }
+ return \@set;
+}
+
+
+#------------------------------------------------------------------------
+# col($n)
+#
+# Returns a reference to a list containing the items in the column whose
+# number is specified by parameter. If the column number is undefined,
+# it calls cols() to return a list of all columns.
+#------------------------------------------------------------------------
+
+sub col {
+ my ($self, $col) = @_;
+ my ($data, $size) = @$self{ qw( _DATA _SIZE ) };
+ my ($start, $end);
+ my $blanks = 0;
+
+ # return all cols if row number not specified
+ return $self->cols()
+ unless defined $col;
+
+ return () if $col >= $self->{ _NCOLS } || $col < 0;
+
+ $start = $self->{ _COLOFF } * $col;
+ $end = $start + $self->{ _NROWS } - 1;
+ $end = $start if $end < $start;
+ if ($end >= $size) {
+ $blanks = ($end - $size) + 1;
+ $end = $size - 1;
+ }
+ return () if $start >= $size;
+ return [ @$data[$start..$end],
+ $self->{ _PAD } ? ((undef) x $blanks) : () ];
+}
+
+
+#------------------------------------------------------------------------
+# rows()
+#
+# Returns all rows as a reference to a list of rows.
+#------------------------------------------------------------------------
+
+sub rows {
+ my $self = shift;
+ return [ map { $self->row($_) } (0..$self->{ _NROWS }-1) ];
+}
+
+
+#------------------------------------------------------------------------
+# cols()
+#
+# Returns all rows as a reference to a list of rows.
+#------------------------------------------------------------------------
+
+sub cols {
+ my $self = shift;
+ return [ map { $self->col($_) } (0..$self->{ _NCOLS }-1) ];
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides read access to various internal data members.
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ if ($item =~ /^data|size|nrows|ncols|overlap|pad$/) {
+ return $self->{ $item };
+ }
+ else {
+ return (undef, "no such table method: $item");
+ }
+}
+
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Table - Plugin to present data in a table
+
+=head1 SYNOPSIS
+
+ [% USE table(list, rows=n, cols=n, overlap=n, pad=0) %]
+
+ [% FOREACH item = table.row(n) %]
+ [% item %]
+ [% END %]
+
+ [% FOREACH item = table.col(n) %]
+ [% item %]
+ [% END %]
+
+ [% FOREACH row = table.rows %]
+ [% FOREACH item = row %]
+ [% item %]
+ [% END %]
+ [% END %]
+
+ [% FOREACH col = table.cols %]
+ [% col.first %] - [% col.last %] ([% col.size %] entries)
+ [% END %]
+
+=head1 DESCRIPTION
+
+The Table plugin allows you to format a list of data items into a
+virtual table. When you create a Table plugin via the USE directive,
+simply pass a list reference as the first parameter and then specify
+a fixed number of rows or columns.
+
+ [% USE Table(list, rows=5) %]
+ [% USE table(list, cols=5) %]
+
+The 'Table' plugin name can also be specified in lower case as shown
+in the second example above. You can also specify an alternative variable
+name for the plugin as per regular Template Toolkit syntax.
+
+ [% USE mydata = table(list, rows=5) %]
+
+The plugin then presents a table based view on the data set. The data
+isn't actually reorganised in any way but is available via the row(),
+col(), rows() and cols() as if formatted into a simple two dimensional
+table of n rows x n columns. Thus, if our sample 'alphabet' list
+contained the letters 'a' to 'z', the above USE directives would
+create plugins that represented the following views of the alphabet.
+
+ [% USE table(alphabet, ... %]
+
+ rows=5 cols=5
+ a f k p u z a g m s y
+ b g l q v b h n t z
+ c h m r w c i o u
+ d i n s x d j p v
+ e j o t y e k q w
+ f l r x
+
+We can request a particular row or column using the row() and col()
+methods.
+
+ [% USE table(alphabet, rows=5) %]
+ [% FOREACH item = table.row(0) %]
+ # [% item %] set to each of [ a f k p u z ] in turn
+ [% END %]
+
+ [% FOREACH item = table.col(2) %]
+ # [% item %] set to each of [ m n o p q r ] in turn
+ [% END %]
+
+Data in rows is returned from left to right, columns from top to
+bottom. The first row/column is 0. By default, rows or columns that
+contain empty values will be padded with the undefined value to fill
+it to the same size as all other rows or columns. For example, the
+last row (row 4) in the first example would contain the values [ e j o
+t y undef ]. The Template Toolkit will safely accept these undefined
+values and print a empty string. You can also use the IF directive to
+test if the value is set.
+
+ [% FOREACH item = table.row(4) %]
+ [% IF item %]
+ Item: [% item %]
+ [% END %]
+ [% END %]
+
+You can explicitly disable the 'pad' option when creating the plugin to
+returned shortened rows/columns where the data is empty.
+
+ [% USE table(alphabet, cols=5, pad=0) %]
+ [% FOREACH item = table.col(4) %]
+ # [% item %] set to each of 'y z'
+ [% END %]
+
+The rows() method returns all rows/columns in the table as a reference
+to a list of rows (themselves list references). The row() methods
+when called without any arguments calls rows() to return all rows in
+the table.
+
+Ditto for cols() and col().
+
+ [% USE table(alphabet, cols=5) %]
+ [% FOREACH row = table.rows %]
+ [% FOREACH item = row %]
+ [% item %]
+ [% END %]
+ [% END %]
+
+The Template Toolkit provides the first(), last() and size() methods
+that can be called on list references to return the first/last entry
+or the number of entried. The following example shows how we might
+use this to provide an alphabetical index split into 3 even parts.
+
+ [% USE table(alphabet, cols=3, pad=0) %]
+ [% FOREACH group = table.col %]
+ [ [% group.first %] - [% group.last %] ([% group.size %] letters) ]
+ [% END %]
+
+This produces the following output:
+
+ [ a - i (9 letters) ]
+ [ j - r (9 letters) ]
+ [ s - z (8 letters) ]
+
+We can also use the general purpose join() list method which joins
+the items of the list using the connecting string specified.
+
+ [% USE table(alphabet, cols=5) %]
+ [% FOREACH row = table.rows %]
+ [% row.join(' - ') %]
+ [% END %]
+
+Data in the table is ordered downwards rather than across but can easily
+be transformed on output. For example, to format our data in 5 columns
+with data ordered across rather than down, we specify 'rows=5' to order
+the data as such:
+
+ a f . .
+ b g .
+ c h
+ d i
+ e j
+
+and then iterate down through each column (a-e, f-j, etc.) printing
+the data across.
+
+ a b c d e
+ f g h i j
+ . .
+ .
+
+Example code to do so would be much like the following:
+
+ [% USE table(alphabet, rows=3) %]
+ [% FOREACH cols = table.cols %]
+ [% FOREACH item = cols %]
+ [% item %]
+ [% END %]
+ [% END %]
+
+ a b c
+ d e f
+ g h i
+ j . .
+ .
+
+In addition to a list reference, the Table plugin constructor may be
+passed a reference to a Template::Iterator object or subclass thereof.
+The get_all() method is first called on the iterator to return all
+remaining items. These are then available via the usual Table interface.
+
+ [% USE DBI(dsn,user,pass) -%]
+
+ # query() returns an iterator
+ [% results = DBI.query('SELECT * FROM alphabet ORDER BY letter') %]
+
+ # pass into Table plugin
+ [% USE table(results, rows=8 overlap=1 pad=0) -%]
+
+ [% FOREACH row = table.cols -%]
+ [% row.first.letter %] - [% row.last.letter %]:
+ [% row.join(', ') %]
+ [% END %]
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/URL.pm b/lib/Template/Plugin/URL.pm
new file mode 100644
index 0000000..c2246b7
--- /dev/null
+++ b/lib/Template/Plugin/URL.pm
@@ -0,0 +1,236 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::URL
+#
+# DESCRIPTION
+#
+# Template Toolkit Plugin for constructing URL's from a base stem
+# and adaptable parameters.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: URL.pm,v 2.64 2004/01/13 16:20:39 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::URL;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA $VERSION );
+use Template::Plugin;
+
+@ISA = qw( Template::Plugin );
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($context, $baseurl, \%url_params)
+#
+# Constructor method which returns a sub-routine closure for constructing
+# complex URL's from a base part and hash of additional parameters.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $context, $base, $args) = @_;
+ $args ||= { };
+
+ return sub {
+ my $newbase = shift unless ref $_[0] eq 'HASH';
+ my $newargs = shift || { };
+ my $combo = { %$args, %$newargs };
+ my $urlargs = join('&amp;',
+# map { "$_=" . escape($combo->{ $_ }) }
+ map { args($_, $combo->{ $_ }) }
+ grep { defined $combo->{ $_ } }
+ sort keys %$combo);
+
+ my $query = $newbase || $base || '';
+ $query .= '?' if length $query && length $urlargs;
+ $query .= $urlargs if length $urlargs;
+
+ return $query
+ }
+}
+
+
+sub args {
+ my ($key, $val) = @_;
+ $key = escape($key);
+ return map {
+ "$key=" . escape($_);
+ } ref $val eq 'ARRAY' ? @$val : $val;
+
+}
+
+#------------------------------------------------------------------------
+# escape($url)
+#
+# URL-encode data. Borrowed with minor modifications from CGI.pm.
+# Kudos to Lincold Stein.
+#------------------------------------------------------------------------
+
+sub escape {
+ my $toencode = shift;
+ return undef unless defined($toencode);
+ $toencode=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+ return $toencode;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::URL - Plugin to construct complex URLs
+
+=head1 SYNOPSIS
+
+ [% USE url('/cgi-bin/foo.pl') %]
+
+ [% url(debug = 1, id = 123) %]
+ # ==> /cgi/bin/foo.pl?debug=1&amp;id=123
+
+
+ [% USE mycgi = url('/cgi-bin/bar.pl', mode='browse', debug=1) %]
+
+ [% mycgi %]
+ # ==> /cgi/bin/bar.pl?mode=browse&amp;debug=1
+
+ [% mycgi(mode='submit') %]
+ # ==> /cgi/bin/bar.pl?mode=submit&amp;debug=1
+
+ [% mycgi(debug='d2 p0', id='D4-2k[4]') %]
+ # ==> /cgi-bin/bar.pl?mode=browse&amp;debug=d2%20p0&amp;id=D4-2k%5B4%5D
+
+
+=head1 DESCRIPTION
+
+The URL plugin can be used to construct complex URLs from a base stem
+and a hash array of additional query parameters.
+
+The constructor should be passed a base URL and optionally, a hash array
+reference of default parameters and values. Used from with a Template
+Documents, this would look something like the following:
+
+ [% USE url('http://www.somewhere.com/cgi-bin/foo.pl') %]
+ [% USE url('/cgi-bin/bar.pl', mode='browse') %]
+ [% USE url('/cgi-bin/baz.pl', mode='browse', debug=1) %]
+
+When the plugin is then called without any arguments, the default base
+and parameters are returned as a formatted query string.
+
+ [% url %]
+
+For the above three examples, these will produce the following outputs:
+
+ http://www.somewhere.com/cgi-bin/foo.pl
+ /cgi-bin/bar.pl?mode=browse
+ /cgi-bin/baz.pl?mode=browse&amp;debug=1
+
+Additional parameters may be also be specified:
+
+ [% url(mode='submit', id='wiz') %]
+
+Which, for the same three examples, produces:
+
+ http://www.somewhere.com/cgi-bin/foo.pl?mode=submit&amp;id=wiz
+ /cgi-bin/bar.pl?mode=browse&amp;id=wiz
+ /cgi-bin/baz.pl?mode=browse&amp;debug=1&amp;id=wiz
+
+A new base URL may also be specified as the first option:
+
+ [% url('/cgi-bin/waz.pl', test=1) %]
+
+producing
+
+ /cgi-bin/waz.pl?test=1
+ /cgi-bin/waz.pl?mode=browse&amp;test=1
+ /cgi-bin/waz.pl?mode=browse&amp;debug=1&amp;test=1
+
+
+The ordering of the parameters is non-deterministic due to fact that
+Perl's hashes themselves are unordered. This isn't a problem as the
+ordering of CGI parameters is insignificant (to the best of my knowledge).
+All values will be properly escaped thanks to some code borrowed from
+Lincoln Stein's CGI.pm. e.g.
+
+ [% USE url('/cgi-bin/woz.pl') %]
+ [% url(name="Elrich von Benjy d'Weiro") %]
+
+Here the spaces and "'" character are escaped in the output:
+
+ /cgi-bin/woz.pl?name=Elrich%20von%20Benjy%20d%27Weiro
+
+Alternate name may be provided for the plugin at construction time
+as per regular Template Toolkit syntax.
+
+ [% USE mycgi = url('cgi-bin/min.pl') %]
+
+ [% mycgi(debug=1) %]
+
+Note that in the following line, additional parameters are seperated
+by '&amp;', while common usage on the Web is to just use '&'. '&amp;'
+is actually the Right Way to do it. See this URL for more information:
+http://ppewww.ph.gla.ac.uk/~flavell/www/formgetbyurl.html
+
+ /cgi-bin/waz.pl?mode=browse&amp;debug=1&amp;test=1
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/View.pm b/lib/Template/Plugin/View.pm
new file mode 100644
index 0000000..c22ba16
--- /dev/null
+++ b/lib/Template/Plugin/View.pm
@@ -0,0 +1,127 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::View
+#
+# DESCRIPTION
+# A user-definable view based on templates. Similar to the concept of
+# a "Skin".
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: View.pm,v 2.63 2004/01/13 16:20:39 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::View;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/);
+
+use Template::View;
+
+#------------------------------------------------------------------------
+# new($context, \%config)
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ my $view = Template::View->new($context, @_)
+ || return $class->error($Template::View::ERROR);
+ $view->seal();
+ return $view;
+}
+
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::View - Plugin to create views (Template::View)
+
+=head1 SYNOPSIS
+
+ [% USE view(
+ prefix = 'splash/' # template prefix/suffix
+ suffix = '.tt2'
+ bgcol = '#ffffff' # and any other variables you
+ style = 'Fancy HTML' # care to define as view metadata,
+ items = [ foo, bar.baz ] # including complex data and
+ foo = bar ? baz : x.y.z # expressions
+ %]
+
+ [% view.title %] # access view metadata
+
+ [% view.header(title = 'Foo!') %] # view "methods" process blocks or
+ [% view.footer %] # templates with prefix/suffix added
+
+=head1 DESCRIPTION
+
+This plugin module creates Template::View objects. Views are an
+experimental feature and are subject to change in the near future.
+In the mean time, please consult L<Template::View> for further info.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.63, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Template::View|Template::View>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/Wrap.pm b/lib/Template/Plugin/Wrap.pm
new file mode 100644
index 0000000..96c600a
--- /dev/null
+++ b/lib/Template/Plugin/Wrap.pm
@@ -0,0 +1,162 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Wrap
+#
+# DESCRIPTION
+# Plugin for wrapping text via the Text::Wrap module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Wrap.pm,v 2.63 2004/01/13 16:20:40 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Wrap;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+use Text::Wrap;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/);
+
+sub new {
+ my ($class, $context, $format) = @_;;
+ $context->define_filter('wrap', [ \&wrap_filter_factory => 1 ]);
+ return \&tt_wrap;
+}
+
+sub tt_wrap {
+ my $text = shift;
+ my $width = shift || 72;
+ my $itab = shift;
+ my $ntab = shift;
+ $itab = '' unless defined $itab;
+ $ntab = '' unless defined $ntab;
+ $Text::Wrap::columns = $width;
+ Text::Wrap::wrap($itab, $ntab, $text);
+}
+
+sub wrap_filter_factory {
+ my ($context, @args) = @_;
+ return sub {
+ my $text = shift;
+ tt_wrap($text, @args);
+ }
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Wrap - Plugin interface to Text::Wrap
+
+=head1 SYNOPSIS
+
+ [% USE wrap %]
+
+ # call wrap subroutine
+ [% wrap(mytext, width, initial_tab, subsequent_tab) %]
+
+ # or use wrap FILTER
+ [% mytext FILTER wrap(width, initital_tab, subsequent_tab) %]
+
+=head1 DESCRIPTION
+
+This plugin provides an interface to the Text::Wrap module which
+provides simple paragraph formatting.
+
+It defines a 'wrap' subroutine which can be called, passing the input
+text and further optional parameters to specify the page width (default:
+72), and tab characters for the first and subsequent lines (no defaults).
+
+ [% USE wrap %]
+
+ [% text = BLOCK %]
+ First, attach the transmutex multiplier to the cross-wired
+ quantum homogeniser.
+ [% END %]
+
+ [% wrap(text, 40, '* ', ' ') %]
+
+Output:
+
+ * First, attach the transmutex
+ multiplier to the cross-wired quantum
+ homogeniser.
+
+It also registers a 'wrap' filter which accepts the same three optional
+arguments but takes the input text directly via the filter input.
+
+ [% FILTER bullet = wrap(40, '* ', ' ') -%]
+ First, attach the transmutex multiplier to the cross-wired quantum
+ homogeniser.
+ [%- END %]
+
+ [% FILTER bullet -%]
+ Then remodulate the shield to match the harmonic frequency, taking
+ care to correct the phase difference.
+ [% END %]
+
+Output:
+
+ * First, attach the transmutex
+ multiplier to the cross-wired quantum
+ homogeniser.
+
+ * Then remodulate the shield to match
+ the harmonic frequency, taking
+ care to correct the phase difference.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@wardley.orgE<gt>
+
+The Text::Wrap module was written by David Muir Sharnoff
+E<lt>muir@idiom.comE<gt> with help from Tim Pierce and many
+others.
+
+=head1 VERSION
+
+2.63, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<Text::Wrap|Text::Wrap>
+
diff --git a/lib/Template/Plugin/XML/DOM.pm b/lib/Template/Plugin/XML/DOM.pm
new file mode 100644
index 0000000..30bac3b
--- /dev/null
+++ b/lib/Template/Plugin/XML/DOM.pm
@@ -0,0 +1,841 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::XML::DOM
+#
+# DESCRIPTION
+#
+# Simple Template Toolkit plugin interfacing to the XML::DOM.pm module.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>
+# Simon Matthews <sam@knowledgepool.com>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley, Simon Matthews. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: DOM.pm,v 2.54 2004/01/13 16:21:50 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::XML::DOM;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use XML::DOM;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION $DEBUG );
+
+$VERSION = 2.6;
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#------------------------------------------------------------------------
+# new($context, \%config)
+#
+# Constructor method for XML::DOM plugin. Creates an XML::DOM::Parser
+# object and initialise plugin configuration.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+
+ my $parser ||= XML::DOM::Parser->new(%$args)
+ || return $class->_throw("failed to create XML::DOM::Parser\n");
+
+ # we've had to deprecate the old usage because it broke things big time
+ # with DOM trees never getting cleaned up.
+ return $class->_throw("XML::DOM usage has changed - you must now call parse()\n")
+ if @_;
+
+ bless {
+ _PARSER => $parser,
+ _DOCS => [ ],
+ _CONTEXT => $context,
+ _PREFIX => $args->{ prefix } || '',
+ _SUFFIX => $args->{ suffix } || '',
+ _DEFAULT => $args->{ default } || '',
+ _VERBOSE => $args->{ verbose } || 0,
+ _NOSPACE => $args->{ nospace } || 0,
+ _DEEP => $args->{ deep } || 0,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# parse($content, \%named_params)
+#
+# Parses an XML stream, provided as the first positional argument (assumed
+# to be a filename unless it contains a '<' character) or specified in
+# the named parameter hash as one of 'text', 'xml' (same as text), 'file'
+# or 'filename'.
+#------------------------------------------------------------------------
+
+sub parse {
+ my $self = shift;
+ my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+ my $parser = $self->{ _PARSER };
+ my ($content, $about, $method, $doc);
+
+ # determine the input source from a positional parameter (may be a
+ # filename or XML text if it contains a '<' character) or by using
+ # named parameters which may specify one of 'file', 'filename', 'text'
+ # or 'xml'
+
+ if ($content = shift) {
+ if ($content =~ /\</) {
+ $about = 'xml text';
+ $method = 'parse';
+ }
+ else {
+ $about = "xml file $content";
+ $method = 'parsefile';
+ }
+ }
+ elsif ($content = $args->{ text } || $args->{ xml }) {
+ $about = 'xml text';
+ $method = 'parse';
+ }
+ elsif ($content = $args->{ file } || $args->{ filename }) {
+ $about = "xml file $content";
+ $method = 'parsefile';
+ }
+ else {
+ return $self->_throw('no filename or xml text specified');
+ }
+
+ # parse the input source using the appropriate method determined above
+ eval { $doc = $parser->$method($content) } and not $@
+ or return $self->_throw("failed to parse $about: $@");
+
+ # update XML::DOM::Document _UserData to contain config details
+ $doc->[ XML::DOM::Node::_UserData ] = {
+ map { ( $_ => $self->{ $_ } ) }
+ qw( _CONTEXT _PREFIX _SUFFIX _VERBOSE _NOSPACE _DEEP _DEFAULT ),
+ };
+
+ # keep track of all DOM docs for subsequent dispose()
+# print STDERR "DEBUG: $self adding doc: $doc\n"
+# if $DEBUG;
+
+ push(@{ $self->{ _DOCS } }, $doc);
+
+ return $doc;
+}
+
+
+#------------------------------------------------------------------------
+# _throw($errmsg)
+#
+# Raised a Template::Exception of type XML.DOM via die().
+#------------------------------------------------------------------------
+
+sub _throw {
+ my ($self, $error) = @_;
+ die (Template::Exception->new('XML.DOM', $error));
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# Cleanup method which calls dispose() on any and all DOM documents
+# created by this object. Also breaks any circular references that
+# may exist with the context object.
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+
+ # call dispose() on each document produced by this parser
+ foreach my $doc (@{ $self->{ _DOCS } }) {
+# print STDERR "DEBUG: $self destroying $doc\n"
+# if $DEBUG;
+ if (ref $doc) {
+# print STDERR "disposing of $doc\n";
+ undef $doc->[ XML::DOM::Node::_UserData ]->{ _CONTEXT };
+ $doc->dispose();
+ }
+ }
+ delete $self->{ _CONTEXT };
+ delete $self->{ _PARSER };
+}
+
+
+
+#========================================================================
+package XML::DOM::Node;
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# present($view)
+#
+# Method to present node via a view (supercedes all that messy toTemplate
+# stuff below).
+#------------------------------------------------------------------------
+
+sub present {
+ my ($self, $view) = @_;
+
+ if ($self->getNodeType() == XML::DOM::ELEMENT_NODE) {
+ # it's an element
+ $view->view($self->getTagName(), $self);
+ }
+ else {
+ my $text = $self->toString();
+ $view->view('text', $text);
+ }
+}
+
+sub content {
+ my ($self, $view) = @_;
+ my $output = '';
+ foreach my $node (@{ $self->getChildNodes }) {
+ $output .= $node->present($view);
+
+# abw test passing args, Aug 2001
+# $output .= $view->print($node);
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# toTemplate($prefix, $suffix, \%named_params)
+#
+# Process the current node as a template.
+#------------------------------------------------------------------------
+
+sub toTemplate {
+ my $self = shift;
+ _template_node($self, $self->_args(@_));
+}
+
+
+#------------------------------------------------------------------------
+# childrenToTemplate($prefix, $suffix, \%named_params)
+#
+# Process all the current node's children as templates.
+#------------------------------------------------------------------------
+
+sub childrenToTemplate {
+ my $self = shift;
+ _template_kids($self, $self->_args(@_));
+}
+
+
+#------------------------------------------------------------------------
+# allChildrenToTemplate($prefix, $suffix, \%named_params)
+#
+# Process all the current node's children, and their children, and
+# their children, etc., etc., as templates. Same effect as calling the
+# childrenToTemplate() method with the 'deep' option set.
+#------------------------------------------------------------------------
+
+sub allChildrenToTemplate {
+ my $self = shift;
+ my $args = $self->_args(@_);
+ $args->{ deep } = 1;
+ _template_kids($self, $args);
+}
+
+
+#------------------------------------------------------------------------
+# _args($prefix, $suffix, \%name_params)
+#
+# Reads the optional positional parameters, $prefix and $suffix, and
+# also examines any named parameters hash to construct a set of
+# current configuration parameters. Where not specified directly, the
+# object defaults are used.
+#------------------------------------------------------------------------
+
+sub _args {
+ my $self = shift;
+ my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+ my $doc = $self->getOwnerDocument() || $self;
+ my $data = $doc->[ XML::DOM::Node::_UserData ];
+
+ return {
+ prefix => @_ ? shift : $args->{ prefix } || $data->{ _PREFIX },
+ suffix => @_ ? shift : $args->{ suffix } || $data->{ _SUFFIX },
+ verbose => $args->{ verbose } || $data->{ _VERBOSE },
+ nospace => $args->{ nospace } || $data->{ _NOSPACE },
+ deep => $args->{ deep } || $data->{ _DEEP },
+ default => $args->{ default } || $data->{ _DEFAULT },
+ context => $data->{ _CONTEXT },
+ };
+}
+
+
+
+#------------------------------------------------------------------------
+# _template_node($node, $args, $vars)
+#
+# Process a template for the current DOM node where the template name
+# is taken from the node TagName, with any specified 'prefix' and/or
+# 'suffix' applied. The 'default' argument can also be provided to
+# specify a default template to be used when a specific template can't
+# be found. The $args parameter referenced a hash array through which
+# these configuration items are passed (see _args()). The current DOM
+# node is made available to the template as the variable 'node', along
+# with any other variables passed in the optional $vars hash reference.
+# To permit the 'children' and 'prune' callbacks to be raised as node
+# methods (see _template_kids() below), these items, if defined in the
+# $vars hash, are copied into the node object where its AUTOLOAD method
+# can find them.
+#------------------------------------------------------------------------
+
+sub _template_node {
+ my $node = shift || die "no XML::DOM::Node reference\n";
+ my $args = shift || die "no XML::DOM args passed to _template_node\n";
+ my $vars = shift || { };
+ my $context = $args->{ context } || die "no context in XML::DOM args\n";
+ my $template;
+ my $output = '';
+
+ # if this is not an element then it is text so output it
+ unless ($node->getNodeType() == XML::DOM::ELEMENT_NODE ) {
+ if ($args->{ verbose }) {
+ $output = $node->toString();
+ $output =~ s/\s+$// if $args->{ nospace };
+ }
+ }
+ else {
+ my $element = ( $args->{ prefix } || '' )
+ . $node->getTagName()
+ . ( $args->{ suffix } || '' );
+
+ # locate a template by name built from prefix, tagname and suffix
+ # or fall back on any default template specified
+ eval { $template = $context->template($element) };
+ eval { $template = $context->template($args->{ default }) }
+ if $@ && $args->{ default };
+ $template = $element unless $template;
+
+ # copy 'children' and 'prune' callbacks into node object (see AUTOLOAD)
+ my $doc = $node->getOwnerDocument() || $node;
+ my $data = $doc->[ XML::DOM::Node::_UserData ];
+
+ $data->{ _TT_CHILDREN } = $vars->{ children };
+ $data->{ _TT_PRUNE } = $vars->{ prune };
+
+ # add node reference to existing vars hash
+ $vars->{ node } = $node;
+
+ $output = $context->include($template, $vars);
+
+ # break any circular references
+ delete $vars->{ node };
+ delete $data->{ _TT_CHILDREN };
+ delete $data->{ _TT_PRUNE };
+ }
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# _template_kids($node, $args)
+#
+# Process all the children of the current node as templates, via calls
+# to _template_node(). If the 'deep' argument is set, then the process
+# will continue recursively. In this case, the node template is first
+# processed, followed by any children of that node (i.e. depth first,
+# parent before). A closure called 'children' is created and added
+# to the Stash variables passed to _template_node(). This can be called
+# from the parent template to process all child nodes at the current point.
+# This then "prunes" the tree preventing the children from being processed
+# after the parent template. A 'prune' callback is also added to prune
+# the tree without processing the children. Note that _template_node()
+# copies these callbacks into each parent node, allowing them to be called
+# as [% node.
+#------------------------------------------------------------------------
+
+sub _template_kids {
+ my $node = shift || die "no XML::DOM::Node reference\n";
+ my $args = shift || die "no XML::DOM args passed to _template_kids\n";
+ my $context = $args->{ context } || die "no context in XML::DOM args\n";
+ my $output = '';
+
+ foreach my $kid ( $node->getChildNodes() ) {
+ # define some callbacks to allow template to call [% content %]
+ # or [% prune %]. They are also inserted into each node reference
+ # so they can be called as [% node.content %] and [% node.prune %]
+ my $prune = 0;
+ my $vars = { };
+ $vars->{ children } = sub {
+ $prune = 1;
+ _template_kids($kid, $args);
+ };
+ $vars->{ prune } = sub {
+ $prune = 1;
+ return '';
+ };
+
+ $output .= _template_node($kid, $args, $vars);
+ $output .= _template_kids($kid, $args)
+ if $args->{ deep } && ! $prune;
+ }
+ return $output;
+}
+
+
+#========================================================================
+package XML::DOM::Element;
+#========================================================================
+
+use vars qw( $AUTOLOAD );
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+ my $attrib;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ my $doc = $self->getOwnerDocument() || $self;
+ my $data = $doc->[ XML::DOM::Node::_UserData ];
+
+ # call 'content' or 'prune' callbacks, if defined (see _template_node())
+ return &$attrib()
+ if ($method =~ /^children|prune$/)
+ && defined($attrib = $data->{ "_TT_\U$method" })
+ && ref $attrib eq 'CODE';
+
+ return $attrib
+ if defined ($attrib = $self->getAttribute($method));
+
+ return '';
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::XML::DOM - Plugin interface to XML::DOM
+
+=head1 SYNOPSIS
+
+ # load plugin
+ [% USE dom = XML.DOM %]
+
+ # also provide XML::Parser options
+ [% USE dom = XML.DOM(ProtocolEncoding =E<gt> 'ISO-8859-1') %]
+
+ # parse an XML file
+ [% doc = dom.parse(filename) %]
+ [% doc = dom.parse(file => filename) %]
+
+ # parse XML text
+ [% doc = dom.parse(xmltext) %]
+ [% doc = dom.parse(text => xmltext) %]
+
+ # call any XML::DOM methods on document/element nodes
+ [% FOREACH node = doc.getElementsByTagName('report') %]
+ * [% node.getAttribute('title') %] # or just '[% node.title %]'
+ [% END %]
+
+ # define VIEW to present node(s)
+ [% VIEW report notfound='xmlstring' %]
+ # handler block for a <report>...</report> element
+ [% BLOCK report %]
+ [% item.content(view) %]
+ [% END %]
+
+ # handler block for a <section title="...">...</section> element
+ [% BLOCK section %]
+ <h1>[% item.title %]</h1>
+ [% item.content(view) %]
+ [% END %]
+
+ # default template block converts item to string representation
+ [% BLOCK xmlstring; item.toString; END %]
+
+ # block to generate simple text
+ [% BLOCK text; item; END %]
+ [% END %]
+
+ # now present node (and children) via view
+ [% report.print(node) %]
+
+ # or print node content via view
+ [% node.content(report) %]
+
+ # following methods are soon to be deprecated in favour of views
+ [% node.toTemplate %]
+ [% node.childrenToTemplate %]
+ [% node.allChildrenToTemplate %]
+
+=head1 PRE-REQUISITES
+
+This plugin requires that the XML::Parser (2.19 or later) and XML::DOM
+(1.27 or later) modules be installed. These are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML
+
+Note that the XML::DOM module is now distributed as part of the
+'libxml-enno' bundle.
+
+=head1 DESCRIPTION
+
+This is a Template Toolkit plugin interfacing to the XML::DOM module.
+The plugin loads the XML::DOM module and creates an XML::DOM::Parser
+object which is stored internally. The parse() method can then be
+called on the plugin to parse an XML stream into a DOM document.
+
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse('/tmp/myxmlfile') %]
+
+NOTE: earlier versions of this XML::DOM plugin expected a filename to
+be passed as an argument to the constructor. This is no longer
+supported due to the fact that it caused a serious memory leak. We
+apologise for the inconvenience but must insist that you change your
+templates as shown:
+
+ # OLD STYLE: now fails with a warning
+ [% USE dom = XML.DOM('tmp/myxmlfile') %]
+
+ # NEW STYLE: do this instead
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse('tmp/myxmlfile') %]
+
+The root of the problem lies in XML::DOM creating massive circular
+references in the object models it constructs. The dispose() method
+must be called on each document to release the memory that it would
+otherwise hold indefinately. The XML::DOM plugin object (i.e. 'dom'
+in these examples) acts as a sentinel for the documents it creates
+('doc' and any others). When the plugin object goes out of scope at
+the end of the current template, it will automatically call dispose()
+on any documents that it has created. Note that if you dispose of the
+the plugin object before the end of the block (i.e. by assigning a
+new value to the 'dom' variable) then the documents will also be
+disposed at that point and should not be used thereafter.
+
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse('/tmp/myfile') %]
+ [% dom = 'new value' %] # releases XML.DOM plugin and calls
+ # dispose() on 'doc', so don't use it!
+
+Any template processing parameters (see toTemplate() method and
+friends, below) can be specified with the constructor and will be used
+to define defaults for the object.
+
+ [% USE dom = XML.DOM(prefix => 'theme1/') %]
+
+The plugin constructor will also accept configuration options destined
+for the XML::Parser object:
+
+ [% USE dom = XML.DOM(ProtocolEncoding => 'ISO-8859-1') %]
+
+=head1 METHODS
+
+=head2 parse()
+
+The parse() method accepts a positional parameter which contains a filename
+or XML string. It is assumed to be a filename unless it contains a E<lt>
+character.
+
+ [% xmlfile = '/tmp/foo.xml' %]
+ [% doc = dom.parse(xmlfile) %]
+
+ [% xmltext = BLOCK %]
+ <xml>
+ <blah><etc/></blah>
+ ...
+ </xml>
+ [% END %]
+ [% doc = dom.parse(xmltext) %]
+
+The named parameters 'file' (or 'filename') and 'text' (or 'xml') can also
+be used:
+
+ [% doc = dom.parse(file = xmlfile) %]
+ [% doc = dom.parse(text = xmltext) %]
+
+The parse() method returns an instance of the XML::DOM::Document object
+representing the parsed document in DOM form. You can then call any
+XML::DOM methods on the document node and other nodes that its methods
+may return. See L<XML::DOM> for full details.
+
+ [% FOREACH node = doc.getElementsByTagName('CODEBASE') %]
+ * [% node.getAttribute('href') %]
+ [% END %]
+
+This plugin also provides an AUTOLOAD method for XML::DOM::Node which
+calls getAttribute() for any undefined methods. Thus, you can use the
+short form of
+
+ [% node.attrib %]
+
+in place of
+
+ [% node.getAttribute('attrib') %]
+
+=head2 toTemplate()
+
+B<NOTE: This method will soon be deprecated in favour of the VIEW based
+approach desribed below.>
+
+This method will process a template for the current node on which it is
+called. The template name is constructed from the node TagName with any
+optional 'prefix' and/or 'suffix' options applied. A 'default' template
+can be named to be used when the specific template cannot be found. The
+node object is available to the template as the 'node' variable.
+
+Thus, for this XML fragment:
+
+ <page title="Hello World!">
+ ...
+ </page>
+
+and this template definition:
+
+ [% BLOCK page %]
+ Page: [% node.title %]
+ [% END %]
+
+the output of calling toTemplate() on the E<lt>pageE<gt> node would be:
+
+ Page: Hello World!
+
+=head2 childrenToTemplate()
+
+B<NOTE: This method will soon be deprecated in favour of the VIEW based
+approach desribed below.>
+
+Effectively calls toTemplate() for the current node and then for each of
+the node's children. By default, the parent template is processed first,
+followed by each of the children. The 'children' closure can be called
+from within the parent template to have them processed and output
+at that point. This then suppresses the children from being processed
+after the parent template.
+
+Thus, for this XML fragment:
+
+ <foo>
+ <bar id="1"/>
+ <bar id="2"/>
+ </foo>
+
+and these template definitions:
+
+ [% BLOCK foo %]
+ start of foo
+ end of foo
+ [% END %]
+
+ [% BLOCK bar %]
+ bar [% node.id %]
+ [% END %]
+
+the output of calling childrenToTemplate() on the parent E<lt>fooE<gt> node
+would be:
+
+ start of foo
+ end of foo
+ bar 1
+ bar 2
+
+Adding a call to [% children %] in the 'foo' template:
+
+ [% BLOCK foo %]
+ start of foo
+ [% children %]
+ end of foo
+ [% END %]
+
+then creates output as:
+
+ start of foo
+ bar 1
+ bar 2
+ end of foo
+
+The 'children' closure can also be called as a method of the node, if you
+prefer:
+
+ [% BLOCK foo %]
+ start of foo
+ [% node.children %]
+ end of foo
+ [% END %]
+
+The 'prune' closure is also defined and can be called as [% prune %] or
+[% node.prune %]. It prunes the currrent node, preventing any descendants
+from being further processed.
+
+ [% BLOCK anynode %]
+ [% node.toString; node.prune %]
+ [% END %]
+
+=head2 allChildrenToTemplate()
+
+B<NOTE: This method will soon be deprecated in favour of the VIEW based
+approach desribed below.>
+
+Similar to childrenToTemplate() but processing all descendants (i.e. children
+of children and so on) recursively. This is identical to calling the
+childrenToTemplate() method with the 'deep' flag set to any true value.
+
+=head1 PRESENTING DOM NODES USING VIEWS
+
+You can define a VIEW to present all or part of a DOM tree by automatically
+mapping elements onto templates. Consider a source document like the
+following:
+
+ <report>
+ <section title="Introduction">
+ <p>
+ Blah blah.
+ <ul>
+ <li>Item 1</li>
+ <li>item 2</li>
+ </ul>
+ </p>
+ </section>
+ <section title="The Gory Details">
+ ...
+ </section>
+ </report>
+
+We can load it up via the XML::DOM plugin and fetch the node for the
+E<lt>reportE<gt> element.
+
+ [% USE dom = XML.DOM;
+ doc = dom.parse(file => filename);
+ report = doc.getElementsByTagName('report')
+ %]
+
+We can then define a VIEW as follows to present this document fragment in
+a particular way. The L<Template::Manual::Views> documentation
+contains further details on the VIEW directive and various configuration
+options it supports.
+
+ [% VIEW report_view notfound='xmlstring' %]
+ # handler block for a <report>...</report> element
+ [% BLOCK report %]
+ [% item.content(view) %]
+ [% END %]
+
+ # handler block for a <section title="...">...</section> element
+ [% BLOCK section %]
+ <h1>[% item.title %]</h1>
+ [% item.content(view) %]
+ [% END %]
+
+ # default template block converts item to string representation
+ [% BLOCK xmlstring; item.toString; END %]
+
+ # block to generate simple text
+ [% BLOCK text; item; END %]
+ [% END %]
+
+Each BLOCK defined within the VIEW represents a presentation style for
+a particular element or elements. The current node is available via the
+'item' variable. Elements that contain other content can generate it
+according to the current view by calling [% item.content(view) %].
+Elements that don't have a specific template defined are mapped to the
+'xmlstring' template via the 'notfound' parameter specified in the VIEW
+header. This replicates the node as an XML string, effectively allowing
+general XML/XHTML markup to be passed through unmodified.
+
+To present the report node via the view, we simply call:
+
+ [% report_view.print(report) %]
+
+The output from the above example would look something like this:
+
+ <h1>Introduction</h1>
+ <p>
+ Blah blah.
+ <ul>
+ <li>Item 1</li>
+ <li>item 2</li>
+ </ul>
+ </p>
+
+ <h1>The Gory Details</h1>
+ ...
+
+To print just the content of the report node (i.e. don't process the
+'report' template for the report node), you can call:
+
+ [% report.content(report_view) %]
+
+=head1 AUTHORS
+
+This plugin module was written by Andy Wardley E<lt>abw@wardley.orgE<gt>
+and Simon Matthews E<lt>sam@knowledgepool.comE<gt>.
+
+The XML::DOM module is by Enno Derksen E<lt>enno@att.comE<gt> and Clark
+Cooper E<lt>coopercl@sch.ge.comE<gt>. It extends the the XML::Parser
+module, also by Clark Cooper which itself is built on James Clark's expat
+library.
+
+=head1 VERSION
+
+2.6, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 HISTORY
+
+Version 2.5 : updated for use with version 1.27 of the XML::DOM module.
+
+=over 4
+
+=item *
+
+XML::DOM 1.27 now uses array references as the underlying data type
+for DOM nodes instead of hash array references. User data is now
+bound to the _UserData node entry instead of being forced directly
+into the node hash.
+
+=back
+
+=head1 BUGS
+
+The childrenToTemplate() and allChildrenToTemplate() methods can easily
+slip into deep recursion.
+
+The 'verbose' and 'nospace' options are not documented. They may
+change in the near future.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000-2001 Andy Wardley, Simon Matthews. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<XML::DOM|XML::DOM>, L<XML::Parser|XML::Parser>
+
diff --git a/lib/Template/Plugin/XML/RSS.pm b/lib/Template/Plugin/XML/RSS.pm
new file mode 100644
index 0000000..32da7d8
--- /dev/null
+++ b/lib/Template/Plugin/XML/RSS.pm
@@ -0,0 +1,194 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::XML::RSS
+#
+# DESCRIPTION
+#
+# Template Toolkit plugin which interfaces to Jonathan Eisenzopf's XML::RSS
+# module. RSS is the Rich Site Summary format.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: RSS.pm,v 2.64 2004/01/13 16:21:50 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::XML::RSS;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use base qw( Template::Plugin );
+use Template::Plugin;
+use XML::RSS;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+
+sub load {
+ return $_[0];
+}
+
+sub new {
+ my ($class, $context, $filename) = @_;
+
+ return $class->fail('No filename specified')
+ unless $filename;
+
+ my $rss = XML::RSS->new
+ or return $class->fail('failed to create XML::RSS');
+
+ # Attempt to determine if $filename is an XML string or
+ # a filename. Based on code from the XML.XPath plugin.
+ eval {
+ if ($filename =~ /\</) {
+ $rss->parse($filename);
+ }
+ else {
+ $rss->parsefile($filename)
+ }
+ } and not $@
+ or return $class->fail("failed to parse $filename: $@");
+
+ return $rss;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::XML::RSS - Plugin interface to XML::RSS
+
+=head1 SYNOPSIS
+
+ [% USE news = XML.RSS($filename) %]
+
+ [% FOREACH item = news.items %]
+ [% item.title %]
+ [% item.link %]
+ [% END %]
+
+=head1 PRE-REQUISITES
+
+This plugin requires that the XML::Parser and XML::RSS modules be
+installed. These are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML
+
+=head1 DESCRIPTION
+
+This Template Toolkit plugin provides a simple interface to the
+XML::RSS module.
+
+ [% USE news = XML.RSS('mysite.rdf') %]
+
+It creates an XML::RSS object, which is then used to parse the RSS
+file specified as a parameter in the USE directive. A reference to
+the XML::RSS object is then returned.
+
+An RSS (Rich Site Summary) file is typically used to store short news
+'headlines' describing different links within a site. This example is
+extracted from http://slashdot.org/slashdot.rdf.
+
+ <?xml version="1.0"?><rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://my.netscape.com/rdf/simple/0.9/">
+
+ <channel>
+ <title>Slashdot:News for Nerds. Stuff that Matters.</title>
+ <link>http://slashdot.org</link>
+ <description>News for Nerds. Stuff that Matters</description>
+ </channel>
+
+ <image>
+ <title>Slashdot</title>
+ <url>http://slashdot.org/images/slashdotlg.gif</url>
+ <link>http://slashdot.org</link>
+ </image>
+
+ <item>
+ <title>DVD CCA Battle Continues Next Week</title>
+ <link>http://slashdot.org/article.pl?sid=00/01/12/2051208</link>
+ </item>
+
+ <item>
+ <title>Matrox to fund DRI Development</title>
+ <link>http://slashdot.org/article.pl?sid=00/01/13/0718219</link>
+ </item>
+
+ <item>
+ <title>Mike Shaver Leaving Netscape</title>
+ <link>http://slashdot.org/article.pl?sid=00/01/13/0711258</link>
+ </item>
+
+ </rdf:RDF>
+
+The attributes of the channel and image elements can be retrieved directly
+from the plugin object using the familiar dotted compound notation:
+
+ [% news.channel.title %]
+ [% news.channel.link %]
+ [% news.channel.etc... %]
+
+ [% news.image.title %]
+ [% news.image.url %]
+ [% news.image.link %]
+ [% news.image.etc... %]
+
+The list of news items can be retrieved using the 'items' method:
+
+ [% FOREACH item = news.items %]
+ [% item.title %]
+ [% item.link %]
+ [% END %]
+
+=head1 AUTHORS
+
+This plugin was written by Andy Wardley E<lt>abw@wardley.orgE<gt>,
+inspired by an article in Web Techniques by Randal Schwartz
+E<lt>merlyn@stonehenge.comE<gt>.
+
+The XML::RSS module, which implements all of the functionality that
+this plugin delegates to, was written by Jonathan Eisenzopf
+E<lt>eisen@pobox.comE<gt>.
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<XML::RSS|XML::RSS>, L<XML::Parser|XML::Parser>
+
diff --git a/lib/Template/Plugin/XML/Simple.pm b/lib/Template/Plugin/XML/Simple.pm
new file mode 100644
index 0000000..aaa4479
--- /dev/null
+++ b/lib/Template/Plugin/XML/Simple.pm
@@ -0,0 +1,124 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::XML::Simple
+#
+# DESCRIPTION
+# Template Toolkit plugin interfacing to the XML::Simple.pm module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Simple.pm,v 2.63 2004/01/13 16:21:50 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::XML::Simple;
+
+require 5.004;
+
+use strict;
+use Template::Plugin;
+use XML::Simple;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($context, $file_or_text, \%config)
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ my $input = shift;
+ my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+
+ XMLin($input, %$args);
+}
+
+
+
+#------------------------------------------------------------------------
+# _throw($errmsg)
+#
+# Raise a Template::Exception of type XML.Simple via die().
+#------------------------------------------------------------------------
+
+sub _throw {
+ my ($self, $error) = @_;
+ die (Template::Exception->new('XML.Simple', $error));
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::XML::Simple - Plugin interface to XML::Simple
+
+=head1 SYNOPSIS
+
+ # load plugin and specify XML file to parse
+ [% USE xml = XML.Simple(xml_file_or_text) %]
+
+=head1 DESCRIPTION
+
+This is a Template Toolkit plugin interfacing to the XML::Simple module.
+
+=head1 PRE-REQUISITES
+
+This plugin requires that the XML::Parser and XML::Simple modules be
+installed. These are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML
+
+=head1 AUTHORS
+
+This plugin wrapper module was written by Andy Wardley
+E<lt>abw@wardley.orgE<gt>.
+
+The XML::Simple module which implements all the core functionality
+was written by Grant McLean E<lt>grantm@web.co.nzE<gt>.
+
+=head1 VERSION
+
+2.63, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<XML::Simple|XML::Simple>, L<XML::Parser|XML::Parser>
+
diff --git a/lib/Template/Plugin/XML/Style.pm b/lib/Template/Plugin/XML/Style.pm
new file mode 100644
index 0000000..7613f2f
--- /dev/null
+++ b/lib/Template/Plugin/XML/Style.pm
@@ -0,0 +1,357 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::XML::Style
+#
+# DESCRIPTION
+# Template Toolkit plugin which performs some basic munging of XML
+# to perform simple stylesheet like transformations.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2001 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Style.pm,v 2.34 2004/01/13 16:21:50 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::XML::Style;
+
+require 5.004;
+
+use strict;
+use Template::Plugin::Filter;
+
+use base qw( Template::Plugin::Filter );
+use vars qw( $VERSION $DYNAMIC $FILTER_NAME );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.34 $ =~ /(\d+)\.(\d+)/);
+$DYNAMIC = 1;
+$FILTER_NAME = 'xmlstyle';
+
+
+#------------------------------------------------------------------------
+# new($context, \%config)
+#------------------------------------------------------------------------
+
+sub init {
+ my $self = shift;
+ my $name = $self->{ _ARGS }->[0] || $FILTER_NAME;
+ $self->install_filter($name);
+ return $self;
+}
+
+
+sub filter {
+ my ($self, $text, $args, $config) = @_;
+
+ # munge start tags
+ $text =~ s/ < ([\w\.\:]+) ( \s+ [^>]+ )? >
+ / $self->start_tag($1, $2, $config)
+ /gsex;
+
+ # munge end tags
+ $text =~ s/ < \/ ([\w\.\:]+) >
+ / $self->end_tag($1, $config)
+ /gsex;
+
+ return $text;
+
+}
+
+
+sub start_tag {
+ my ($self, $elem, $textattr, $config) = @_;
+ $textattr ||= '';
+ my ($pre, $post);
+
+ # look for an element match in the stylesheet
+ my $match = $config->{ $elem }
+ || $self->{ _CONFIG }->{ $elem }
+ || return "<$elem$textattr>";
+
+ # merge element attributes into copy of stylesheet attributes
+ my $attr = { %{ $match->{ attributes } || { } } };
+ while ($textattr =~ / \s* ([\w\.\:]+) = " ([^"]+) " /gsx ) {
+ $attr->{ $1 } = $2;
+ }
+ $textattr = join(' ', map { "$_=\"$attr->{$_}\"" } keys %$attr);
+ $textattr = " $textattr" if $textattr;
+
+ $elem = $match->{ element } || $elem;
+ $pre = $match->{ pre_start } || '';
+ $post = $match->{ post_start } || '';
+
+ return "$pre<$elem$textattr>$post";
+}
+
+
+sub end_tag {
+ my ($self, $elem, $config) = @_;
+ my ($pre, $post);
+
+ # look for an element match in the stylesheet
+ my $match = $config->{ $elem }
+ || $self->{ _CONFIG }->{ $elem }
+ || return "</$elem>";
+
+ $elem = $match->{ element } || $elem;
+ $pre = $match->{ pre_end } || '';
+ $post = $match->{ post_end } || '';
+
+ return "$pre</$elem>$post";
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::XML::Style - Simple XML stylesheet transfomations
+
+=head1 SYNOPSIS
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ cellspacing = 1
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle %]
+ <table>
+ <tr>
+ <td>Foo</td> <td>Bar</td> <td>Baz</td>
+ </tr>
+ </table>
+ [% END %]
+
+=head1 DESCRIPTION
+
+This plugin defines a filter for performing simple stylesheet based
+transformations of XML text.
+
+Named parameters are used to define those XML elements which require
+transformation. These may be specified with the USE directive when
+the plugin is loaded and/or with the FILTER directive when the plugin
+is used.
+
+This example shows how the default attributes C<border="0"> and
+C<cellpadding="4"> can be added to E<lt>tableE<gt> elements.
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle %]
+ <table>
+ ...
+ </table>
+ [% END %]
+
+This produces the output:
+
+ <table border="0" cellpadding="4">
+ ...
+ </table>
+
+Parameters specified within the USE directive are applied automatically each
+time the C<xmlstyle> FILTER is used. Additional parameters passed to the
+FILTER directive apply for only that block.
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle
+ tr = {
+ attributes = {
+ valign="top"
+ }
+ }
+ %]
+ <table>
+ <tr>
+ ...
+ </tr>
+ </table>
+ [% END %]
+
+Of course, you may prefer to define your stylesheet structures once and
+simply reference them by name. Passing a hash reference of named parameters
+is just the same as specifying the named parameters as far as the Template
+Toolkit is concerned.
+
+ [% style_one = {
+ table = { ... }
+ tr = { ... }
+ }
+ style_two = {
+ table = { ... }
+ td = { ... }
+ }
+ style_three = {
+ th = { ... }
+ tv = { ... }
+ }
+ %]
+
+ [% USE xmlstyle style_one %]
+
+ [% FILTER xmlstyle style_two %]
+ # style_one and style_two applied here
+ [% END %]
+
+ [% FILTER xmlstyle style_three %]
+ # style_one and style_three applied here
+ [% END %]
+
+Any attributes defined within the source tags will override those specified
+in the style sheet.
+
+ [% USE xmlstyle
+ div = { attributes = { align = 'left' } }
+ %]
+
+
+ [% FILTER xmlstyle %]
+ <div>foo</div>
+ <div align="right">bar</div>
+ [% END %]
+
+The output produced is:
+
+ <div align="left">foo</div>
+ <div align="right">bar</div>
+
+The filter can also be used to change the element from one type to another.
+
+ [% FILTER xmlstyle
+ th = {
+ element = 'td'
+ attributes = { bgcolor='red' }
+ }
+ %]
+ <tr>
+ <th>Heading</th>
+ </tr>
+ <tr>
+ <td>Value</td>
+ </tr>
+ [% END %]
+
+The output here is as follows. Notice how the end tag C<E<lt>/thE<gt>> is
+changed to C<E<lt>/tdE<gt>> as well as the start tag.
+
+ <tr>
+ <td bgcolor="red">Heading</td>
+ </tr>
+ <tr>
+ <td>Value</td>
+ </tr>
+
+You can also define text to be added immediately before or after the
+start or end tags. For example:
+
+ [% FILTER xmlstyle
+ table = {
+ pre_start = '<div align="center">'
+ post_end = '</div>'
+ }
+ th = {
+ element = 'td'
+ attributes = { bgcolor='red' }
+ post_start = '<b>'
+ pre_end = '</b>'
+ }
+ %]
+ <table>
+ <tr>
+ <th>Heading</th>
+ </tr>
+ <tr>
+ <td>Value</td>
+ </tr>
+ </table>
+ [% END %]
+
+The output produced is:
+
+ <div align="center">
+ <table>
+ <tr>
+ <td bgcolor="red"><b>Heading</b></td>
+ </tr>
+ <tr>
+ <td>Value</td>
+ </tr>
+ </table>
+ </div>
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.34, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Plugin/XML/XPath.pm b/lib/Template/Plugin/XML/XPath.pm
new file mode 100644
index 0000000..adf9292
--- /dev/null
+++ b/lib/Template/Plugin/XML/XPath.pm
@@ -0,0 +1,284 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::XML::XPath
+#
+# DESCRIPTION
+#
+# Template Toolkit plugin interfacing to the XML::XPath.pm module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: XPath.pm,v 2.69 2004/01/13 16:21:50 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::XML::XPath;
+
+require 5.004;
+
+use strict;
+use Template::Exception;
+use Template::Plugin;
+use XML::XPath;
+
+use base qw( Template::Plugin );
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($context, \%config)
+#
+# Constructor method for XML::XPath plugin. Creates an XML::XPath
+# object and initialises plugin configuration.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $context = shift;
+ my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
+ my ($content, $about);
+
+ # determine the input source from a positional parameter (may be a
+ # filename or XML text if it contains a '<' character) or by using
+ # named parameters which may specify one of 'file', 'filename', 'text'
+ # or 'xml'
+
+ if ($content = shift) {
+ if ($content =~ /\</) {
+ $about = 'xml text';
+ $args->{ xml } = $content;
+ }
+ else {
+ $about = "xml file $content";
+ $args->{ filename } = $content;
+ }
+ }
+ elsif ($content = $args->{ text } || $args->{ xml }) {
+ $about = 'xml text';
+ $args->{ xml } = $content;
+ }
+ elsif ($content = $args->{ file } || $args->{ filename }) {
+ $about = "xml file $content";
+ $args->{ filename } = $content;
+ }
+ else {
+ return $class->_throw('no filename or xml text specified');
+ }
+
+ return XML::XPath->new(%$args)
+ or $class->_throw("failed to create XML::XPath::Parser\n");
+}
+
+
+
+#------------------------------------------------------------------------
+# _throw($errmsg)
+#
+# Raise a Template::Exception of type XML.XPath via die().
+#------------------------------------------------------------------------
+
+sub _throw {
+ my ($self, $error) = @_;
+# print STDERR "about to throw $error\n";
+ die (Template::Exception->new('XML.XPath', $error));
+}
+
+
+#========================================================================
+package XML::XPath::Node::Element;
+#========================================================================
+
+#------------------------------------------------------------------------
+# present($view)
+#
+# Method to present an element node via a view.
+#------------------------------------------------------------------------
+
+sub present {
+ my ($self, $view) = @_;
+ $view->view($self->getName(), $self);
+}
+
+sub content {
+ my ($self, $view) = @_;
+ my $output = '';
+ foreach my $node (@{ $self->getChildNodes }) {
+ $output .= $node->present($view);
+ }
+ return $output;
+}
+
+#----------------------------------------------------------------------
+# starttag(), endtag()
+#
+# Methods to output the start & end tag, e.g. <foo bar="baz"> & </foo>
+#----------------------------------------------------------------------
+
+sub starttag {
+ my ($self) = @_;
+ my $output = "<". $self->getName();
+ foreach my $attr ($self->getAttributes())
+ {
+ $output .= $attr->toString();
+ }
+ $output .= ">";
+ return $output;
+}
+
+sub endtag {
+ my ($self) = @_;
+ return "</". $self->getName() . ">";
+}
+
+#========================================================================
+package XML::XPath::Node::Text;
+#========================================================================
+
+#------------------------------------------------------------------------
+# present($view)
+#
+# Method to present a text node via a view.
+#------------------------------------------------------------------------
+
+sub present {
+ my ($self, $view) = @_;
+ $view->view('text', $self->string_value);
+}
+
+
+#========================================================================
+package XML::XPath::Node::Comment;
+#========================================================================
+
+sub present { return ''; }
+sub starttag { return ''; }
+sub endtag { return ''; }
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::XML::XPath - Plugin interface to XML::XPath
+
+=head1 SYNOPSIS
+
+ # load plugin and specify XML file to parse
+ [% USE xpath = XML.XPath(xmlfile) %]
+ [% USE xpath = XML.XPath(file => xmlfile) %]
+ [% USE xpath = XML.XPath(filename => xmlfile) %]
+
+ # load plugin and specify XML text to parse
+ [% USE xpath = XML.XPath(xmltext) %]
+ [% USE xpath = XML.XPath(xml => xmltext) %]
+ [% USE xpath = XML.XPath(text => xmltext) %]
+
+ # then call any XPath methods (see XML::XPath docs)
+ [% FOREACH page = xpath.findnodes('/html/body/page') %]
+ [% page.getAttribute('title') %]
+ [% END %]
+
+ # define VIEW to present node(s)
+ [% VIEW repview notfound='xmlstring' %]
+ # handler block for a <report>...</report> element
+ [% BLOCK report %]
+ [% item.content(view) %]
+ [% END %]
+
+ # handler block for a <section title="...">...</section> element
+ [% BLOCK section %]
+ <h1>[% item.getAttribute('title') | html %]</h1>
+ [% item.content(view) %]
+ [% END %]
+
+ # default template block passes tags through and renders
+ # out the children recursivly
+ [% BLOCK xmlstring;
+ item.starttag; item.content(view); item.endtag;
+ END %]
+
+ # block to generate simple text
+ [% BLOCK text; item | html; END %]
+ [% END %]
+
+ # now present node (and children) via view
+ [% repview.print(page) %]
+
+ # or print node content via view
+ [% page.content(repview) %]
+
+=head1 PRE-REQUISITES
+
+This plugin requires that the XML::Parser and XML::XPath modules be
+installed. These are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML
+
+=head1 DESCRIPTION
+
+This is a Template Toolkit plugin interfacing to the XML::XPath module.
+
+All methods implemented by the XML::XPath modules are available. In
+addition, the XML::XPath::Node::Element module implements
+present($view) and content($view) methods method for seamless
+integration with Template Toolkit VIEWs. The XML::XPath::Node::Text
+module is also adorned with a present($view) method which presents
+itself via the view using the 'text' template.
+
+To aid the reconstruction of XML, methods starttag and endtag are
+added to XML::XPath::Node::Element which return the start and
+end tag for that element. This means that you can easily do:
+
+ [% item.starttag %][% item.content(view) %][% item.endtag %]
+
+To render out the start tag, followed by the content rendered in the
+view "view", followed by the end tag.
+
+=head1 AUTHORS
+
+This plugin module was written by Andy Wardley E<lt>abw@wardley.orgE<gt>.
+
+The XML::XPath module is by Matt Sergeant E<lt>matt@sergeant.orgE<gt>.
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<XML::XPath|XML::XPath>, L<XML::Parser|XML::Parser>
+
diff --git a/lib/Template/Plugins.pm b/lib/Template/Plugins.pm
new file mode 100644
index 0000000..1904efc
--- /dev/null
+++ b/lib/Template/Plugins.pm
@@ -0,0 +1,1041 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugins
+#
+# DESCRIPTION
+# Plugin provider which handles the loading of plugin modules and
+# instantiation of plugin objects.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Plugins.pm,v 2.70 2004/01/13 16:19:15 abw Exp $
+#
+#============================================================================
+
+package Template::Plugins;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $STD_PLUGINS );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.70 $ =~ /(\d+)\.(\d+)/);
+
+$STD_PLUGINS = {
+ 'autoformat' => 'Template::Plugin::Autoformat',
+ 'cgi' => 'Template::Plugin::CGI',
+ 'datafile' => 'Template::Plugin::Datafile',
+ 'date' => 'Template::Plugin::Date',
+ 'debug' => 'Template::Plugin::Debug',
+ 'directory' => 'Template::Plugin::Directory',
+ 'dbi' => 'Template::Plugin::DBI',
+ 'dumper' => 'Template::Plugin::Dumper',
+ 'file' => 'Template::Plugin::File',
+ 'format' => 'Template::Plugin::Format',
+ 'html' => 'Template::Plugin::HTML',
+ 'image' => 'Template::Plugin::Image',
+ 'iterator' => 'Template::Plugin::Iterator',
+ 'pod' => 'Template::Plugin::Pod',
+ 'table' => 'Template::Plugin::Table',
+ 'url' => 'Template::Plugin::URL',
+ 'view' => 'Template::Plugin::View',
+ 'wrap' => 'Template::Plugin::Wrap',
+ 'xmlstyle' => 'Template::Plugin::XML::Style',
+};
+
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name, \@args, $context)
+#
+# General purpose method for requesting instantiation of a plugin
+# object. The name of the plugin is passed as the first parameter.
+# The internal FACTORY lookup table is consulted to retrieve the
+# appropriate factory object or class name. If undefined, the _load()
+# method is called to attempt to load the module and return a factory
+# class/object which is then cached for subsequent use. A reference
+# to the calling context should be passed as the third parameter.
+# This is passed to the _load() class method. The new() method is
+# then called against the factory class name or prototype object to
+# instantiate a new plugin object, passing any arguments specified by
+# list reference as the second parameter. e.g. where $factory is the
+# class name 'MyClass', the new() method is called as a class method,
+# $factory->new(...), equivalent to MyClass->new(...) . Where
+# $factory is a prototype object, the new() method is called as an
+# object method, $myobject->new(...). This latter approach allows
+# plugins to act as Singletons, cache shared data, etc.
+#
+# Returns a reference to a plugin, (undef, STATUS_DECLINE) to decline
+# the request or ($error, STATUS_ERROR) on error.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name, $args, $context) = @_;
+ my ($factory, $plugin, $error);
+
+ $self->debug("fetch($name, ",
+ defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ',
+ defined $context ? $context : '<no context>',
+ ')') if $self->{ DEBUG };
+
+ # NOTE:
+ # the $context ref gets passed as the first parameter to all regular
+ # plugins, but not to those loaded via LOAD_PERL; to hack around
+ # this until we have a better implementation, we pass the $args
+ # reference to _load() and let it unshift the first args in the
+ # LOAD_PERL case
+
+ $args ||= [ ];
+ unshift @$args, $context;
+
+ $factory = $self->{ FACTORY }->{ $name } ||= do {
+ ($factory, $error) = $self->_load($name, $context);
+ return ($factory, $error) if $error; ## RETURN
+ $factory;
+ };
+
+ # call the new() method on the factory object or class name
+ eval {
+ if (ref $factory eq 'CODE') {
+ defined( $plugin = &$factory(@$args) )
+ || die "$name plugin failed\n";
+ }
+ else {
+ defined( $plugin = $factory->new(@$args) )
+ || die "$name plugin failed: ", $factory->error(), "\n";
+ }
+ };
+ if ($error = $@) {
+# chomp $error;
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+
+ return $plugin;
+}
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Private initialisation method.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+ my ($pbase, $plugins, $factory) =
+ @$params{ qw( PLUGIN_BASE PLUGINS PLUGIN_FACTORY ) };
+
+ $plugins ||= { };
+ if (ref $pbase ne 'ARRAY') {
+ $pbase = $pbase ? [ $pbase ] : [ ];
+ }
+ push(@$pbase, 'Template::Plugin');
+
+ $self->{ PLUGIN_BASE } = $pbase;
+ $self->{ PLUGINS } = { %$STD_PLUGINS, %$plugins };
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ LOAD_PERL } = $params->{ LOAD_PERL } || 0;
+ $self->{ FACTORY } = $factory || { };
+ $self->{ DEBUG } = ( $params->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_PLUGINS;
+
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# _load($name, $context)
+#
+# Private method which attempts to load a plugin module and determine the
+# correct factory name or object by calling the load() class method in
+# the loaded module.
+#------------------------------------------------------------------------
+
+sub _load {
+ my ($self, $name, $context) = @_;
+ my ($factory, $module, $base, $pkg, $file, $ok, $error);
+
+ if ($module = $self->{ PLUGINS }->{ $name }) {
+ # plugin module name is explicitly stated in PLUGIN_NAME
+ $pkg = $module;
+ ($file = $module) =~ s|::|/|g;
+ $file =~ s|::|/|g;
+ $self->debug("loading $module.pm (PLUGIN_NAME)")
+ if $self->{ DEBUG };
+ $ok = eval { require "$file.pm" };
+ $error = $@;
+ }
+ else {
+ # try each of the PLUGIN_BASE values to build module name
+ ($module = $name) =~ s/\./::/g;
+
+ foreach $base (@{ $self->{ PLUGIN_BASE } }) {
+ $pkg = $base . '::' . $module;
+ ($file = $pkg) =~ s|::|/|g;
+
+ $self->debug("loading $file.pm (PLUGIN_BASE)")
+ if $self->{ DEBUG };
+
+ $ok = eval { require "$file.pm" };
+ last unless $@;
+
+ $error .= "$@\n"
+ unless ($@ =~ /^Can\'t locate $file\.pm/);
+ }
+ }
+
+ if ($ok) {
+ $self->debug("calling $pkg->load()") if $self->{ DEBUG };
+
+ $factory = eval { $pkg->load($context) };
+ $error = '';
+ if ($@ || ! $factory) {
+ $error = $@ || 'load() returned a false value';
+ }
+ }
+ elsif ($self->{ LOAD_PERL }) {
+ # fallback - is it a regular Perl module?
+ ($file = $module) =~ s|::|/|g;
+ eval { require "$file.pm" };
+ if ($@) {
+ $error = $@;
+ }
+ else {
+ # this is a regular Perl module so the new() constructor
+ # isn't expecting a $context reference as the first argument;
+ # so we construct a closure which removes it before calling
+ # $module->new(@_);
+ $factory = sub {
+ shift;
+ $module->new(@_);
+ };
+ $error = '';
+ }
+ }
+
+ if ($factory) {
+ $self->debug("$name => $factory") if $self->{ DEBUG };
+ return $factory;
+ }
+ elsif ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DECLINED);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which constructs and returns text representing the current
+# state of the object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Plugins] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( TOLERANT LOAD_PERL )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+
+ local $" = ', ';
+ my $fkeys = join(", ", keys %{$self->{ FACTORY }});
+ my $plugins = $self->{ PLUGINS };
+ $plugins = join('', map {
+ sprintf(" $format", $_, $plugins->{ $_ });
+ } keys %$plugins);
+ $plugins = "{\n$plugins }";
+
+ $output .= sprintf($format, 'PLUGIN_BASE', "[ @{ $self->{ PLUGIN_BASE } } ]");
+ $output .= sprintf($format, 'PLUGINS', $plugins);
+ $output .= sprintf($format, 'FACTORY', $fkeys);
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugins - Plugin provider module
+
+=head1 SYNOPSIS
+
+ use Template::Plugins;
+
+ $plugin_provider = Template::Plugins->new(\%options);
+
+ ($plugin, $error) = $plugin_provider->fetch($name, @args);
+
+=head1 DESCRIPTION
+
+The Template::Plugins module defines a provider class which can be used
+to load and instantiate Template Toolkit plugin modules.
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+Constructor method which instantiates and returns a reference to a
+Template::Plugins object. A reference to a hash array of configuration
+items may be passed as a parameter. These are described below.
+
+Note that the Template.pm front-end module creates a Template::Plugins
+provider, passing all configuration items. Thus, the examples shown
+below in the form:
+
+ $plugprov = Template::Plugins->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+can also be used via the Template module as:
+
+ $ttengine = Template->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+as well as the more explicit form of:
+
+ $plugprov = Template::Plugins->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+ $ttengine = Template->new({
+ LOAD_PLUGINS => [ $plugprov ],
+ });
+
+=head2 fetch($name, @args)
+
+Called to request that a plugin of a given name be provided. The relevant
+module is first loaded (if necessary) and the load() class method called
+to return the factory class name (usually the same package name) or a
+factory object (a prototype). The new() method is then called as a
+class or object method against the factory, passing all remaining
+parameters.
+
+Returns a reference to a new plugin object or ($error, STATUS_ERROR)
+on error. May also return (undef, STATUS_DECLINED) to decline to
+serve the request. If TOLERANT is set then all errors will be
+returned as declines.
+
+=head1 CONFIGURATION OPTIONS
+
+The following list details the configuration options that can be provided
+to the Template::Plugins new() constructor.
+
+=over 4
+
+
+
+
+=item PLUGINS
+
+The PLUGINS options can be used to provide a reference to a hash array
+that maps plugin names to Perl module names. A number of standard
+plugins are defined (e.g. 'table', 'cgi', 'dbi', etc.) which map to
+their corresponding Template::Plugin::* counterparts. These can be
+redefined by values in the PLUGINS hash.
+
+ my $plugins = Template::Plugins->new({
+ PLUGINS => {
+ cgi => 'MyOrg::Template::Plugin::CGI',
+ foo => 'MyOrg::Template::Plugin::Foo',
+ bar => 'MyOrg::Template::Plugin::Bar',
+ },
+ });
+
+The USE directive is used to create plugin objects and does so by
+calling the plugin() method on the current Template::Context object.
+If the plugin name is defined in the PLUGINS hash then the
+corresponding Perl module is loaded via require(). The context then
+calls the load() class method which should return the class name
+(default and general case) or a prototype object against which the
+new() method can be called to instantiate individual plugin objects.
+
+If the plugin name is not defined in the PLUGINS hash then the PLUGIN_BASE
+and/or LOAD_PERL options come into effect.
+
+
+
+
+
+=item PLUGIN_BASE
+
+If a plugin is not defined in the PLUGINS hash then the PLUGIN_BASE is used
+to attempt to construct a correct Perl module name which can be successfully
+loaded.
+
+The PLUGIN_BASE can be specified as a single value or as a reference
+to an array of multiple values. The default PLUGIN_BASE value,
+'Template::Plugin', is always added the the end of the PLUGIN_BASE
+list (a single value is first converted to a list). Each value should
+contain a Perl package name to which the requested plugin name is
+appended.
+
+example 1:
+
+ my $plugins = Template::Plugins->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+example 2:
+
+ my $plugins = Template::Plugins->new({
+ PLUGIN_BASE => [ 'MyOrg::Template::Plugin',
+ 'YourOrg::Template::Plugin' ],
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or YourOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+
+
+
+
+
+=item LOAD_PERL
+
+If a plugin cannot be loaded using the PLUGINS or PLUGIN_BASE
+approaches then the provider can make a final attempt to load the
+module without prepending any prefix to the module path. This allows
+regular Perl modules (i.e. those that don't reside in the
+Template::Plugin or some other such namespace) to be loaded and used
+as plugins.
+
+By default, the LOAD_PERL option is set to 0 and no attempt will be made
+to load any Perl modules that aren't named explicitly in the PLUGINS
+hash or reside in a package as named by one of the PLUGIN_BASE
+components.
+
+Plugins loaded using the PLUGINS or PLUGIN_BASE receive a reference to
+the current context object as the first argument to the new()
+constructor. Modules loaded using LOAD_PERL are assumed to not
+conform to the plugin interface. They must provide a new() class
+method for instantiating objects but it will not receive a reference
+to the context as the first argument. Plugin modules should provide a
+load() class method (or inherit the default one from the
+Template::Plugin base class) which is called the first time the plugin
+is loaded. Regular Perl modules need not. In all other respects,
+regular Perl objects and Template Toolkit plugins are identical.
+
+If a particular Perl module does not conform to the common, but not
+unilateral, new() constructor convention then a simple plugin wrapper
+can be written to interface to it.
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Plugins module by setting it to include the DEBUG_PLUGINS
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS,
+ });
+
+
+
+
+=back
+
+
+
+=head1 TEMPLATE TOOLKIT PLUGINS
+
+The following plugin modules are distributed with the Template
+Toolkit. Some of the plugins interface to external modules (detailed
+below) which should be downloaded from any CPAN site and installed
+before using the plugin.
+
+=head2 Autoformat
+
+The Autoformat plugin is an interface to Damian Conway's Text::Autoformat
+Perl module which provides advanced text wrapping and formatting. See
+L<Template::Plugin::Autoformat> and L<Text::Autoformat> for further
+details.
+
+ [% USE autoformat(left=10, right=20) %]
+ [% autoformat(mytext) %] # call autoformat sub
+ [% mytext FILTER autoformat %] # or use autoformat filter
+
+The Text::Autoformat module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 CGI
+
+The CGI plugin is a wrapper around Lincoln Stein's
+E<lt>lstein@genome.wi.mit.eduE<gt> CGI.pm module. The plugin is
+distributed with the Template Toolkit (see L<Template::Plugin::CGI>)
+and the CGI module itself is distributed with recent versions Perl,
+or is available from CPAN.
+
+ [% USE CGI %]
+ [% CGI.param('param_name') %]
+ [% CGI.start_form %]
+ [% CGI.popup_menu( Name => 'color',
+ Values => [ 'Green', 'Brown' ] ) %]
+ [% CGI.end_form %]
+
+=head2 Datafile
+
+Provides an interface to data stored in a plain text file in a simple
+delimited format. The first line in the file specifies field names
+which should be delimiter by any non-word character sequence.
+Subsequent lines define data using the same delimiter as int he first
+line. Blank lines and comments (lines starting '#') are ignored. See
+L<Template::Plugin::Datafile> for further details.
+
+/tmp/mydata:
+
+ # define names for each field
+ id : email : name : tel
+ # here's the data
+ fred : fred@here.com : Fred Smith : 555-1234
+ bill : bill@here.com : Bill White : 555-5678
+
+example:
+
+ [% USE userlist = datafile('/tmp/mydata') %]
+
+ [% FOREACH user = userlist %]
+ [% user.name %] ([% user.id %])
+ [% END %]
+
+=head2 Date
+
+The Date plugin provides an easy way to generate formatted time and date
+strings by delegating to the POSIX strftime() routine. See
+L<Template::Plugin::Date> and L<POSIX> for further details.
+
+ [% USE date %]
+ [% date.format %] # current time/date
+
+ File last modified: [% date.format(template.modtime) %]
+
+=head2 Directory
+
+The Directory plugin provides a simple interface to a directory and
+the files within it. See L<Template::Plugin::Directory> for further
+details.
+
+ [% USE dir = Directory('/tmp') %]
+ [% FOREACH file = dir.files %]
+ # all the plain files in the directory
+ [% END %]
+ [% FOREACH file = dir.dirs %]
+ # all the sub-directories
+ [% END %]
+
+=head2 DBI
+
+The DBI plugin, developed by Simon Matthews
+E<lt>sam@knowledgepool.comE<gt>, brings the full power of Tim Bunce's
+E<lt>Tim.Bunce@ig.co.ukE<gt> database interface module (DBI) to your
+templates. See L<Template::Plugin::DBI> and L<DBI> for further details.
+
+ [% USE DBI('dbi:driver:database', 'user', 'pass') %]
+
+ [% FOREACH user = DBI.query( 'SELECT * FROM users' ) %]
+ [% user.id %] [% user.name %]
+ [% END %]
+
+The DBI and relevant DBD modules are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/DBI/
+
+=head2 Dumper
+
+The Dumper plugin provides an interface to the Data::Dumper module. See
+L<Template::Plugin::Dumper> and L<Data::Dumper> for futher details.
+
+ [% USE dumper(indent=0, pad="<br>") %]
+ [% dumper.dump(myvar, yourvar) %]
+
+=head2 File
+
+The File plugin provides a general abstraction for files and can be
+used to fetch information about specific files within a filesystem.
+See L<Template::Plugin::File> for further details.
+
+ [% USE File('/tmp/foo.html') %]
+ [% File.name %] # foo.html
+ [% File.dir %] # /tmp
+ [% File.mtime %] # modification time
+
+=head2 Filter
+
+This module implements a base class plugin which can be subclassed
+to easily create your own modules that define and install new filters.
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ # ...mungify $text...
+
+ return $text;
+ }
+
+ # now load it...
+ [% USE MyFilter %]
+
+ # ...and use the returned object as a filter
+ [% FILTER $MyFilter %]
+ ...
+ [% END %]
+
+See L<Template::Plugin::Filter> for further details.
+
+=head2 Format
+
+The Format plugin provides a simple way to format text according to a
+printf()-like format. See L<Template::Plugin::Format> for further
+details.
+
+ [% USE bold = format('<b>%s</b>') %]
+ [% bold('Hello') %]
+
+=head2 GD::Image, GD::Polygon, GD::Constants
+
+These plugins provide access to the GD graphics library via Lincoln
+D. Stein's GD.pm interface. These plugins allow PNG, JPEG and other
+graphical formats to be generated.
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ # allocate some colors
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+ # And fill it with red
+ im.fill(50,50,red);
+ # Output image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Image> for further details.
+
+=head2 GD::Text, GD::Text::Align, GD::Text::Wrap
+
+These plugins provide access to Martien Verbruggen's GD::Text,
+GD::Text::Align and GD::Text::Wrap modules. These plugins allow the
+layout, alignment and wrapping of text when drawing text in GD images.
+
+ [% FILTER null;
+ USE gd = GD.Image(200,400);
+ USE gdc = GD.Constants;
+ black = gd.colorAllocate(0, 0, 0);
+ green = gd.colorAllocate(0, 255, 0);
+ txt = "This is some long text. " | repeat(10);
+ USE wrapbox = GD.Text.Wrap(gd,
+ line_space => 4,
+ color => green,
+ text => txt,
+ );
+ wrapbox.set_font(gdc.gdMediumBoldFont);
+ wrapbox.set(align => 'center', width => 160);
+ wrapbox.draw(20, 20);
+ gd.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Align>
+and L<Template::Plugin::GD::Text::Wrap> for further details.
+
+=head2 GD::Graph::lines, GD::Graph::bars, GD::Graph::points, GD::Graph::linespoin
+ts, GD::Graph::area, GD::Graph::mixed, GD::Graph::pie
+
+These plugins provide access to Martien Verbruggen's GD::Graph module
+that allows graphs, plots and charts to be created. These plugins allow
+graphs, plots and charts to be generated in PNG, JPEG and other
+graphical formats.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th"],
+ [ 4, 2, 3, 4, 3, 3.5]
+ ];
+ USE my_graph = GD.Graph.pie(250, 200);
+ my_graph.set(
+ title => 'A Pie Chart',
+ label => 'Label',
+ axislabelclr => 'black',
+ pie_height => 36,
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines>,
+L<Template::Plugin::GD::Graph::bars>,
+L<Template::Plugin::GD::Graph::points>,
+L<Template::Plugin::GD::Graph::linespoints>,
+L<Template::Plugin::GD::Graph::area>,
+L<Template::Plugin::GD::Graph::mixed>,
+L<Template::Plugin::GD::Graph::pie>, and
+L<GD::Graph>,
+for more details.
+
+=head2 GD::Graph::bars3d, GD::Graph::lines3d, GD::Graph::pie3d
+
+These plugins provide access to Jeremy Wadsack's GD::Graph3d
+module. This allows 3D bar charts and 3D lines plots to
+be generated.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
+ ];
+ USE my_graph = GD.Graph.bars3d();
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A 3d Bar Chart',
+ y_max_value => 8,
+ y_tick_number => 8,
+ y_label_skip => 2,
+ # shadows
+ bar_spacing => 8,
+ shadow_depth => 4,
+ shadowclr => 'dred',
+ transparent => 0,
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines3d>,
+L<Template::Plugin::GD::Graph::bars3d>, and
+L<Template::Plugin::GD::Graph::pie3d>
+for more details.
+
+=head2 HTML
+
+The HTML plugin is very new and very basic, implementing a few useful
+methods for generating HTML. It is likely to be extended in the future
+or integrated with a larger project to generate HTML elements in a generic
+way (as discussed recently on the mod_perl mailing list).
+
+ [% USE HTML %]
+ [% HTML.escape("if (a < b && c > d) ..." %]
+ [% HTML.attributes(border => 1, cellpadding => 2) %]
+ [% HTML.element(table => { border => 1, cellpadding => 2 }) %]
+
+See L<Template::Plugin::HTML> for further details.
+
+=head2 Iterator
+
+The Iterator plugin provides a way to create a Template::Iterator
+object to iterate over a data set. An iterator is created
+automatically by the FOREACH directive and is aliased to the 'loop'
+variable. This plugin allows an iterator to be explicitly created
+with a given name, or the default plugin name, 'iterator'. See
+L<Template::Plugin::Iterator> for further details.
+
+ [% USE iterator(list, args) %]
+
+ [% FOREACH item = iterator %]
+ [% '<ul>' IF iterator.first %]
+ <li>[% item %]
+ [% '</ul>' IF iterator.last %]
+ [% END %]
+
+=head2 Pod
+
+This plugin provides an interface to the L<Pod::POM|Pod::POM> module
+which parses POD documents into an internal object model which can
+then be traversed and presented through the Template Toolkit.
+
+ [% USE Pod(podfile) %]
+
+ [% FOREACH head1 = Pod.head1;
+ FOREACH head2 = head1/head2;
+ ...
+ END;
+ END
+ %]
+
+=head2 String
+
+The String plugin implements an object-oriented interface for
+manipulating strings. See L<Template::Plugin::String> for further
+details.
+
+ [% USE String 'Hello' %]
+ [% String.append(' World') %]
+
+ [% msg = String.new('Another string') %]
+ [% msg.replace('string', 'text') %]
+
+ The string "[% msg %]" is [% msg.length %] characters long.
+
+=head2 Table
+
+The Table plugin allows you to format a list of data items into a
+virtual table by specifying a fixed number of rows or columns, with
+an optional overlap. See L<Template::Plugin::Table> for further
+details.
+
+ [% USE table(list, rows=10, overlap=1) %]
+
+ [% FOREACH item = table.col(3) %]
+ [% item %]
+ [% END %]
+
+=head2 URL
+
+The URL plugin provides a simple way of contructing URLs from a base
+part and a variable set of parameters. See L<Template::Plugin::URL>
+for further details.
+
+ [% USE mycgi = url('/cgi-bin/bar.pl', debug=1) %]
+
+ [% mycgi %]
+ # ==> /cgi/bin/bar.pl?debug=1
+
+ [% mycgi(mode='submit') %]
+ # ==> /cgi/bin/bar.pl?mode=submit&debug=1
+
+=head2 Wrap
+
+The Wrap plugin uses the Text::Wrap module by David Muir Sharnoff
+E<lt>muir@idiom.comE<gt> (with help from Tim Pierce and many many others)
+to provide simple paragraph formatting. See L<Template::Plugin::Wrap>
+and L<Text::Wrap> for further details.
+
+ [% USE wrap %]
+ [% wrap(mytext, 40, '* ', ' ') %] # use wrap sub
+ [% mytext FILTER wrap(40) -%] # or wrap FILTER
+
+The Text::Wrap module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 XML::DOM
+
+The XML::DOM plugin gives access to the XML Document Object Module via
+Clark Cooper E<lt>cooper@sch.ge.comE<gt> and Enno Derksen's
+E<lt>enno@att.comE<gt> XML::DOM module. See L<Template::Plugin::XML::DOM>
+and L<XML::DOM> for further details.
+
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse(filename) %]
+
+ [% FOREACH node = doc.getElementsByTagName('CODEBASE') %]
+ * [% node.getAttribute('href') %]
+ [% END %]
+
+The plugin requires the XML::DOM module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::RSS
+
+The XML::RSS plugin is a simple interface to Jonathan Eisenzopf's
+E<lt>eisen@pobox.comE<gt> XML::RSS module. A RSS (Rich Site Summary)
+file is typically used to store short news 'headlines' describing
+different links within a site. This plugin allows you to parse RSS
+files and format the contents accordingly using templates.
+See L<Template::Plugin::XML::RSS> and L<XML::RSS> for further details.
+
+ [% USE news = XML.RSS(filename) %]
+
+ [% FOREACH item = news.items %]
+ <a href="[% item.link %]">[% item.title %]</a>
+ [% END %]
+
+The XML::RSS module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::Simple
+
+This plugin implements an interface to the L<XML::Simple|XML::Simple>
+module.
+
+ [% USE xml = XML.Simple(xml_file_or_text) %]
+
+ [% xml.head.title %]
+
+See L<Template::Plugin::XML::Simple> for further details.
+
+=head2 XML::Style
+
+This plugin defines a filter for performing simple stylesheet based
+transformations of XML text.
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ cellspacing = 1
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle %]
+ <table>
+ <tr>
+ <td>Foo</td> <td>Bar</td> <td>Baz</td>
+ </tr>
+ </table>
+ [% END %]
+
+See L<Template::Plugin::XML::Style> for further details.
+
+=head2 XML::XPath
+
+The XML::XPath plugin provides an interface to Matt Sergeant's
+E<lt>matt@sergeant.orgE<gt> XML::XPath module. See
+L<Template::Plugin::XML::XPath> and L<XML::XPath> for further details.
+
+ [% USE xpath = XML.XPath(xmlfile) %]
+ [% FOREACH page = xpath.findnodes('/html/body/page') %]
+ [% page.getAttribute('title') %]
+ [% END %]
+
+The plugin requires the XML::XPath module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+
+
+
+=head1 BUGS / ISSUES
+
+=over 4
+
+=item *
+
+It might be worthwhile being able to distinguish between absolute
+module names and those which should be applied relative to PLUGIN_BASE
+directories. For example, use 'MyNamespace::MyModule' to denote
+absolute module names (e.g. LOAD_PERL), and 'MyNamespace.MyModule' to
+denote relative to PLUGIN_BASE.
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.70, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Plugin|Template::Plugin>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Provider.pm b/lib/Template/Provider.pm
new file mode 100644
index 0000000..0826a18
--- /dev/null
+++ b/lib/Template/Provider.pm
@@ -0,0 +1,1449 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Provider
+#
+# DESCRIPTION
+# This module implements a class which handles the loading, compiling
+# and caching of templates. Multiple Template::Provider objects can
+# be stacked and queried in turn to effect a Chain-of-Command between
+# them. A provider will attempt to return the requested template,
+# an error (STATUS_ERROR) or decline to provide the template
+# (STATUS_DECLINE), allowing subsequent providers to attempt to
+# deliver it. See 'Design Patterns' for further details.
+#
+# AUTHOR
+# Andy Wardley <abw@wardley.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# TODO:
+# * optional provider prefix (e.g. 'http:')
+# * fold ABSOLUTE and RELATIVE test cases into one regex?
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Provider.pm,v 2.79 2004/01/13 16:19:16 abw Exp $
+#
+#============================================================================
+
+package Template::Provider;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR $DOCUMENT $STAT_TTL $MAX_DIRS );
+use base qw( Template::Base );
+use Template::Config;
+use Template::Constants;
+use Template::Document;
+use File::Basename;
+use File::Spec;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.79 $ =~ /(\d+)\.(\d+)/);
+
+# name of document class
+$DOCUMENT = 'Template::Document' unless defined $DOCUMENT;
+
+# maximum time between performing stat() on file to check staleness
+$STAT_TTL = 1 unless defined $STAT_TTL;
+
+# maximum number of directories in an INCLUDE_PATH, to prevent runaways
+$MAX_DIRS = 64 unless defined $MAX_DIRS;
+
+use constant PREV => 0;
+use constant NAME => 1;
+use constant DATA => 2;
+use constant LOAD => 3;
+use constant NEXT => 4;
+use constant STAT => 5;
+
+$DEBUG = 0 unless defined $DEBUG;
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name)
+#
+# Returns a compiled template for the name specified by parameter.
+# The template is returned from the internal cache if it exists, or
+# loaded and then subsequently cached. The ABSOLUTE and RELATIVE
+# configuration flags determine if absolute (e.g. '/something...')
+# and/or relative (e.g. './something') paths should be honoured. The
+# INCLUDE_PATH is otherwise used to find the named file. $name may
+# also be a reference to a text string containing the template text,
+# or a file handle from which the content is read. The compiled
+# template is not cached in these latter cases given that there is no
+# filename to cache under. A subsequent call to store($name,
+# $compiled) can be made to cache the compiled template for future
+# fetch() calls, if necessary.
+#
+# Returns a compiled template or (undef, STATUS_DECLINED) if the
+# template could not be found. On error (e.g. the file was found
+# but couldn't be read or parsed), the pair ($error, STATUS_ERROR)
+# is returned. The TOLERANT configuration option can be set to
+# downgrade any errors to STATUS_DECLINE.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name) = @_;
+ my ($data, $error);
+
+ if (ref $name) {
+ # $name can be a reference to a scalar, GLOB or file handle
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data)
+ unless $error;
+ $data = $data->{ data }
+ unless $error;
+ }
+ elsif (File::Spec->file_name_is_absolute($name)) {
+ # absolute paths (starting '/') allowed if ABSOLUTE set
+ ($data, $error) = $self->{ ABSOLUTE }
+ ? $self->_fetch($name)
+ : $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ("$name: absolute paths are not allowed (set ABSOLUTE option)",
+ Template::Constants::STATUS_ERROR);
+ }
+ elsif ($name =~ m[^\.+/]) {
+ # anything starting "./" is relative to cwd, allowed if RELATIVE set
+ ($data, $error) = $self->{ RELATIVE }
+ ? $self->_fetch($name)
+ : $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ("$name: relative paths are not allowed (set RELATIVE option)",
+ Template::Constants::STATUS_ERROR);
+ }
+ else {
+ # otherwise, it's a file name relative to INCLUDE_PATH
+ ($data, $error) = $self->{ INCLUDE_PATH }
+ ? $self->_fetch_path($name)
+ : (undef, Template::Constants::STATUS_DECLINED);
+ }
+
+# $self->_dump_cache()
+# if $DEBUG > 1;
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# store($name, $data)
+#
+# Store a compiled template ($data) in the cached as $name.
+#------------------------------------------------------------------------
+
+sub store {
+ my ($self, $name, $data) = @_;
+ $self->_store($name, {
+ data => $data,
+ load => 0,
+ });
+}
+
+
+#------------------------------------------------------------------------
+# load($name)
+#
+# Load a template without parsing/compiling it, suitable for use with
+# the INSERT directive. There's some duplication with fetch() and at
+# some point this could be reworked to integrate them a little closer.
+#------------------------------------------------------------------------
+
+sub load {
+ my ($self, $name) = @_;
+ my ($data, $error);
+ my $path = $name;
+
+ if (File::Spec->file_name_is_absolute($name)) {
+ # absolute paths (starting '/') allowed if ABSOLUTE set
+ $error = "$name: absolute paths are not allowed (set ABSOLUTE option)"
+ unless $self->{ ABSOLUTE };
+ }
+ elsif ($name =~ m[^\.+/]) {
+ # anything starting "./" is relative to cwd, allowed if RELATIVE set
+ $error = "$name: relative paths are not allowed (set RELATIVE option)"
+ unless $self->{ RELATIVE };
+ }
+ else {
+ INCPATH: {
+ # otherwise, it's a file name relative to INCLUDE_PATH
+ my $paths = $self->paths()
+ || return ($self->error(), Template::Constants::STATUS_ERROR);
+
+ foreach my $dir (@$paths) {
+ $path = "$dir/$name";
+ last INCPATH
+ if -f $path;
+ }
+ undef $path; # not found
+ }
+ }
+
+ if (defined $path && ! $error) {
+ local $/ = undef; # slurp files in one go
+ local *FH;
+ if (open(FH, $path)) {
+ $data = <FH>;
+ close(FH);
+ }
+ else {
+ $error = "$name: $!";
+ }
+ }
+
+ if ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+ elsif (! defined $path) {
+ return (undef, Template::Constants::STATUS_DECLINED);
+ }
+ else {
+ return ($data, Template::Constants::STATUS_OK);
+ }
+}
+
+
+
+#------------------------------------------------------------------------
+# include_path(\@newpath)
+#
+# Accessor method for the INCLUDE_PATH setting. If called with an
+# argument, this method will replace the existing INCLUDE_PATH with
+# the new value.
+#------------------------------------------------------------------------
+
+sub include_path {
+ my ($self, $path) = @_;
+ $self->{ INCLUDE_PATH } = $path if $path;
+ return $self->{ INCLUDE_PATH };
+}
+
+
+#------------------------------------------------------------------------
+# paths()
+#
+# Evaluates the INCLUDE_PATH list, ignoring any blank entries, and
+# calling and subroutine or object references to return dynamically
+# generated path lists. Returns a reference to a new list of paths
+# or undef on error.
+#------------------------------------------------------------------------
+
+sub paths {
+ my $self = shift;
+ my @ipaths = @{ $self->{ INCLUDE_PATH } };
+ my (@opaths, $dpaths, $dir);
+ my $count = $MAX_DIRS;
+
+ while (@ipaths && --$count) {
+ $dir = shift @ipaths || next;
+
+ # $dir can be a sub or object ref which returns a reference
+ # to a dynamically generated list of search paths.
+
+ if (ref $dir eq 'CODE') {
+ eval { $dpaths = &$dir() };
+ if ($@) {
+ chomp $@;
+ return $self->error($@);
+ }
+ unshift(@ipaths, @$dpaths);
+ next;
+ }
+ elsif (UNIVERSAL::can($dir, 'paths')) {
+ $dpaths = $dir->paths()
+ || return $self->error($dir->error());
+ unshift(@ipaths, @$dpaths);
+ next;
+ }
+ else {
+ push(@opaths, $dir);
+ }
+ }
+ return $self->error("INCLUDE_PATH exceeds $MAX_DIRS directories")
+ if @ipaths;
+
+ return \@opaths;
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# The provider cache is implemented as a doubly linked list which Perl
+# cannot free by itself due to the circular references between NEXT <=>
+# PREV items. This cleanup method walks the list deleting all the NEXT/PREV
+# references, allowing the proper cleanup to occur and memory to be
+# repooled.
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+ my ($slot, $next);
+
+ $slot = $self->{ HEAD };
+ while ($slot) {
+ $next = $slot->[ NEXT ];
+ undef $slot->[ PREV ];
+ undef $slot->[ NEXT ];
+ $slot = $next;
+ }
+ undef $self->{ HEAD };
+ undef $self->{ TAIL };
+}
+
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init()
+#
+# Initialise the cache.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+ my $size = $params->{ CACHE_SIZE };
+ my $path = $params->{ INCLUDE_PATH } || '.';
+ my $cdir = $params->{ COMPILE_DIR } || '';
+ my $dlim = $params->{ DELIMITER };
+ my $debug;
+
+ # tweak delim to ignore C:/
+ unless (defined $dlim) {
+ $dlim = ($^O eq 'MSWin32') ? ':(?!\\/)' : ':';
+ }
+
+ # coerce INCLUDE_PATH to an array ref, if not already so
+ $path = [ split(/$dlim/, $path) ]
+ unless ref $path eq 'ARRAY';
+
+ # don't allow a CACHE_SIZE 1 because it breaks things and the
+ # additional checking isn't worth it
+ $size = 2
+ if defined $size && ($size == 1 || $size < 0);
+
+ if (defined ($debug = $params->{ DEBUG })) {
+ $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PROVIDER
+ | Template::Constants::DEBUG_FLAGS );
+ }
+ else {
+ $self->{ DEBUG } = $DEBUG;
+ }
+
+ if ($self->{ DEBUG }) {
+ local $" = ', ';
+ $self->debug("creating cache of ",
+ defined $size ? $size : 'unlimited',
+ " slots for [ @$path ]");
+ }
+
+ # create COMPILE_DIR and sub-directories representing each INCLUDE_PATH
+ # element in which to store compiled files
+ if ($cdir) {
+
+# Stas' hack
+# # this is a hack to solve the problem with INCLUDE_PATH using
+# # relative dirs
+# my $segments = 0;
+# for (@$path) {
+# my $c = 0;
+# $c++ while m|\.\.|g;
+# $segments = $c if $c > $segments;
+# }
+# $cdir .= "/".join "/",('hack') x $segments if $segments;
+#
+
+ require File::Path;
+ foreach my $dir (@$path) {
+ next if ref $dir;
+ my $wdir = $dir;
+ $wdir =~ s[:][]g if $^O eq 'MSWin32';
+ $wdir =~ /(.*)/; # untaint
+ &File::Path::mkpath(File::Spec->catfile($cdir, $1));
+ }
+ }
+
+ $self->{ LOOKUP } = { };
+ $self->{ SLOTS } = 0;
+ $self->{ SIZE } = $size;
+ $self->{ INCLUDE_PATH } = $path;
+ $self->{ DELIMITER } = $dlim;
+ $self->{ COMPILE_DIR } = $cdir;
+ $self->{ COMPILE_EXT } = $params->{ COMPILE_EXT } || '';
+ $self->{ ABSOLUTE } = $params->{ ABSOLUTE } || 0;
+ $self->{ RELATIVE } = $params->{ RELATIVE } || 0;
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ DOCUMENT } = $params->{ DOCUMENT } || $DOCUMENT;
+ $self->{ PARSER } = $params->{ PARSER };
+ $self->{ DEFAULT } = $params->{ DEFAULT };
+# $self->{ PREFIX } = $params->{ PREFIX };
+ $self->{ PARAMS } = $params;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _fetch($name)
+#
+# Fetch a file from cache or disk by specification of an absolute or
+# relative filename. No search of the INCLUDE_PATH is made. If the
+# file is found and loaded, it is compiled and cached.
+#------------------------------------------------------------------------
+
+sub _fetch {
+ my ($self, $name) = @_;
+ my $size = $self->{ SIZE };
+ my ($slot, $data, $error);
+
+ $self->debug("_fetch($name)") if $self->{ DEBUG };
+
+ my $compiled = $self->_compiled_filename($name);
+
+ if (defined $size && ! $size) {
+ # caching disabled so load and compile but don't cache
+ if ($compiled && -f $compiled
+ && (stat($name))[9] <= (stat($compiled))[9]) {
+ $data = $self->_load_compiled($compiled);
+ $error = $self->error() unless $data;
+ }
+ else {
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $data->{ data }
+ unless $error;
+ }
+ }
+ elsif ($slot = $self->{ LOOKUP }->{ $name }) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ }
+ else {
+ # nothing in cache so try to load, compile and cache
+ if ($compiled && -f $compiled
+ && (stat($name))[9] <= (stat($compiled))[9]) {
+ $data = $self->_load_compiled($compiled);
+ $error = $self->error() unless $data;
+ $self->store($name, $data) unless $error;
+ }
+ else {
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $self->_store($name, $data)
+ unless $error;
+ }
+ }
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _fetch_path($name)
+#
+# Fetch a file from cache or disk by specification of an absolute cache
+# name (e.g. 'header') or filename relative to one of the INCLUDE_PATH
+# directories. If the file isn't already cached and can be found and
+# loaded, it is compiled and cached under the full filename.
+#------------------------------------------------------------------------
+
+sub _fetch_path {
+ my ($self, $name) = @_;
+ my ($size, $compext, $compdir) =
+ @$self{ qw( SIZE COMPILE_EXT COMPILE_DIR ) };
+ my ($dir, $paths, $path, $compiled, $slot, $data, $error);
+ local *FH;
+
+ $self->debug("_fetch_path($name)") if $self->{ DEBUG };
+
+ # caching is enabled if $size is defined and non-zero or undefined
+ my $caching = (! defined $size || $size);
+
+ INCLUDE: {
+
+ # the template may have been stored using a non-filename name
+ if ($caching && ($slot = $self->{ LOOKUP }->{ $name })) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ last INCLUDE;
+ }
+
+ $paths = $self->paths() || do {
+ $error = Template::Constants::STATUS_ERROR;
+ $data = $self->error();
+ last INCLUDE;
+ };
+
+ # search the INCLUDE_PATH for the file, in cache or on disk
+ foreach $dir (@$paths) {
+ $path = File::Spec->catfile($dir, $name);
+
+ $self->debug("searching path: $path\n") if $self->{ DEBUG };
+
+ if ($caching && ($slot = $self->{ LOOKUP }->{ $path })) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ last INCLUDE;
+ }
+ elsif (-f $path) {
+ $compiled = $self->_compiled_filename($path)
+ if $compext || $compdir;
+
+ if ($compiled && -f $compiled
+ && (stat($path))[9] <= (stat($compiled))[9]) {
+ if ($data = $self->_load_compiled($compiled)) {
+ # store in cache
+ $data = $self->store($path, $data);
+ $error = Template::Constants::STATUS_OK;
+ last INCLUDE;
+ }
+ else {
+ warn($self->error(), "\n");
+ }
+ }
+ # $compiled is set if an attempt to write the compiled
+ # template to disk should be made
+
+ ($data, $error) = $self->_load($path, $name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $self->_store($path, $data)
+ unless $error || ! $caching;
+ $data = $data->{ data } if ! $caching;
+ # all done if $error is OK or ERROR
+ last INCLUDE if ! $error
+ || $error == Template::Constants::STATUS_ERROR;
+ }
+ }
+ # template not found, so look for a DEFAULT template
+ my $default;
+ if (defined ($default = $self->{ DEFAULT }) && $name ne $default) {
+ $name = $default;
+ redo INCLUDE;
+ }
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ } # INCLUDE
+
+ return ($data, $error);
+}
+
+
+
+sub _compiled_filename {
+ my ($self, $file) = @_;
+ my ($compext, $compdir) = @$self{ qw( COMPILE_EXT COMPILE_DIR ) };
+ my ($path, $compiled);
+
+ return undef
+ unless $compext || $compdir;
+
+ $path = $file;
+ $path =~ /^(.+)$/s or die "invalid filename: $path";
+ $path =~ s[:][]g if $^O eq 'MSWin32';
+
+ $compiled = "$path$compext";
+ $compiled = File::Spec->catfile($compdir, $compiled) if length $compdir;
+
+ return $compiled;
+}
+
+
+sub _load_compiled {
+ my ($self, $file) = @_;
+ my $compiled;
+
+ # load compiled template via require(); we zap any
+ # %INC entry to ensure it is reloaded (we don't
+ # want 1 returned by require() to say it's in memory)
+ delete $INC{ $file };
+ eval { $compiled = require $file; };
+ return $@
+ ? $self->error("compiled template $compiled: $@")
+ : $compiled;
+}
+
+
+
+#------------------------------------------------------------------------
+# _load($name, $alias)
+#
+# Load template text from a string ($name = scalar ref), GLOB or file
+# handle ($name = ref), or from an absolute filename ($name = scalar).
+# Returns a hash array containing the following items:
+# name filename or $alias, if provided, or 'input text', etc.
+# text template text
+# time modification time of file, or current time for handles/strings
+# load time file was loaded (now!)
+#
+# On error, returns ($error, STATUS_ERROR), or (undef, STATUS_DECLINED)
+# if TOLERANT is set.
+#------------------------------------------------------------------------
+
+sub _load {
+ my ($self, $name, $alias) = @_;
+ my ($data, $error);
+ my $tolerant = $self->{ TOLERANT };
+ my $now = time;
+ local $/ = undef; # slurp files in one go
+ local *FH;
+
+ $alias = $name unless defined $alias or ref $name;
+
+ $self->debug("_load($name, ", defined $alias ? $alias : '<no alias>',
+ ')') if $self->{ DEBUG };
+
+ LOAD: {
+ if (ref $name eq 'SCALAR') {
+ # $name can be a SCALAR reference to the input text...
+ $data = {
+ name => defined $alias ? $alias : 'input text',
+ text => $$name,
+ time => $now,
+ load => 0,
+ };
+ }
+ elsif (ref $name) {
+ # ...or a GLOB or file handle...
+ my $text = <$name>;
+ $data = {
+ name => defined $alias ? $alias : 'input file handle',
+ text => $text,
+ time => $now,
+ load => 0,
+ };
+ }
+ elsif (-f $name) {
+ if (open(FH, $name)) {
+ my $text = <FH>;
+ $data = {
+ name => $alias,
+ path => $name,
+ text => $text,
+ time => (stat $name)[9],
+ load => $now,
+ };
+ }
+ elsif ($tolerant) {
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ }
+ else {
+ $data = "$alias: $!";
+ $error = Template::Constants::STATUS_ERROR;
+ }
+ }
+ else {
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ }
+ }
+
+ $data->{ path } = $data->{ name }
+ if $data and ! defined $data->{ path };
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _refresh(\@slot)
+#
+# Private method called to mark a cache slot as most recently used.
+# A reference to the slot array should be passed by parameter. The
+# slot is relocated to the head of the linked list. If the file from
+# which the data was loaded has been upated since it was compiled, then
+# it is re-loaded from disk and re-compiled.
+#------------------------------------------------------------------------
+
+sub _refresh {
+ my ($self, $slot) = @_;
+ my ($head, $file, $data, $error);
+
+
+ $self->debug("_refresh([ ",
+ join(', ', map { defined $_ ? $_ : '<undef>' } @$slot),
+ '])') if $self->{ DEBUG };
+
+ # if it's more than $STAT_TTL seconds since we last performed a
+ # stat() on the file then we need to do it again and see if the file
+ # time has changed
+ if ( (time - $slot->[ STAT ]) > $STAT_TTL && stat $slot->[ NAME ] ) {
+ $slot->[ STAT ] = time;
+
+ if ( (stat(_))[9] != $slot->[ LOAD ]) {
+
+ $self->debug("refreshing cache file ", $slot->[ NAME ])
+ if $self->{ DEBUG };
+
+ ($data, $error) = $self->_load($slot->[ NAME ],
+ $slot->[ DATA ]->{ name });
+ ($data, $error) = $self->_compile($data)
+ unless $error;
+
+ unless ($error) {
+ $slot->[ DATA ] = $data->{ data };
+ $slot->[ LOAD ] = $data->{ time };
+ }
+ }
+ }
+
+ unless( $self->{ HEAD } == $slot ) {
+ # remove existing slot from usage chain...
+ if ($slot->[ PREV ]) {
+ $slot->[ PREV ]->[ NEXT ] = $slot->[ NEXT ];
+ }
+ else {
+ $self->{ HEAD } = $slot->[ NEXT ];
+ }
+ if ($slot->[ NEXT ]) {
+ $slot->[ NEXT ]->[ PREV ] = $slot->[ PREV ];
+ }
+ else {
+ $self->{ TAIL } = $slot->[ PREV ];
+ }
+
+ # ..and add to start of list
+ $head = $self->{ HEAD };
+ $head->[ PREV ] = $slot if $head;
+ $slot->[ PREV ] = undef;
+ $slot->[ NEXT ] = $head;
+ $self->{ HEAD } = $slot;
+ }
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _store($name, $data)
+#
+# Private method called to add a data item to the cache. If the cache
+# size limit has been reached then the oldest entry at the tail of the
+# list is removed and its slot relocated to the head of the list and
+# reused for the new data item. If the cache is under the size limit,
+# or if no size limit is defined, then the item is added to the head
+# of the list.
+#------------------------------------------------------------------------
+
+sub _store {
+ my ($self, $name, $data, $compfile) = @_;
+ my $size = $self->{ SIZE };
+ my ($slot, $head);
+
+ # extract the load time and compiled template from the data
+# my $load = $data->{ load };
+ my $load = (stat($name))[9];
+ $data = $data->{ data };
+
+ $self->debug("_store($name, $data)") if $self->{ DEBUG };
+
+ if (defined $size && $self->{ SLOTS } >= $size) {
+ # cache has reached size limit, so reuse oldest entry
+
+ $self->debug("reusing oldest cache entry (size limit reached: $size)\nslots: $self->{ SLOTS }") if $self->{ DEBUG };
+
+ # remove entry from tail of list
+ $slot = $self->{ TAIL };
+ $slot->[ PREV ]->[ NEXT ] = undef;
+ $self->{ TAIL } = $slot->[ PREV ];
+
+ # remove name lookup for old node
+ delete $self->{ LOOKUP }->{ $slot->[ NAME ] };
+
+ # add modified node to head of list
+ $head = $self->{ HEAD };
+ $head->[ PREV ] = $slot if $head;
+ @$slot = ( undef, $name, $data, $load, $head, time );
+ $self->{ HEAD } = $slot;
+
+ # add name lookup for new node
+ $self->{ LOOKUP }->{ $name } = $slot;
+ }
+ else {
+ # cache is under size limit, or none is defined
+
+ $self->debug("adding new cache entry") if $self->{ DEBUG };
+
+ # add new node to head of list
+ $head = $self->{ HEAD };
+ $slot = [ undef, $name, $data, $load, $head, time ];
+ $head->[ PREV ] = $slot if $head;
+ $self->{ HEAD } = $slot;
+ $self->{ TAIL } = $slot unless $self->{ TAIL };
+
+ # add lookup from name to slot and increment nslots
+ $self->{ LOOKUP }->{ $name } = $slot;
+ $self->{ SLOTS }++;
+ }
+
+ return $data;
+}
+
+
+#------------------------------------------------------------------------
+# _compile($data)
+#
+# Private method called to parse the template text and compile it into
+# a runtime form. Creates and delegates a Template::Parser object to
+# handle the compilation, or uses a reference passed in PARSER. On
+# success, the compiled template is stored in the 'data' item of the
+# $data hash and returned. On error, ($error, STATUS_ERROR) is returned,
+# or (undef, STATUS_DECLINED) if the TOLERANT flag is set.
+# The optional $compiled parameter may be passed to specify
+# the name of a compiled template file to which the generated Perl
+# code should be written. Errors are (for now...) silently
+# ignored, assuming that failures to open a file for writing are
+# intentional (e.g directory write permission).
+#------------------------------------------------------------------------
+
+sub _compile {
+ my ($self, $data, $compfile) = @_;
+ my $text = $data->{ text };
+ my ($parsedoc, $error);
+
+ $self->debug("_compile($data, ",
+ defined $compfile ? $compfile : '<no compfile>', ')')
+ if $self->{ DEBUG };
+
+ my $parser = $self->{ PARSER }
+ ||= Template::Config->parser($self->{ PARAMS })
+ || return (Template::Config->error(), Template::Constants::STATUS_ERROR);
+
+ # discard the template text - we don't need it any more
+ delete $data->{ text };
+
+ # call parser to compile template into Perl code
+ if ($parsedoc = $parser->parse($text, $data)) {
+
+ $parsedoc->{ METADATA } = {
+ 'name' => $data->{ name },
+ 'modtime' => $data->{ time },
+ %{ $parsedoc->{ METADATA } },
+ };
+
+ # write the Perl code to the file $compfile, if defined
+ if ($compfile) {
+ my $basedir = &File::Basename::dirname($compfile);
+ $basedir =~ /(.*)/;
+ $basedir = $1;
+ &File::Path::mkpath($basedir) unless -d $basedir;
+
+ my $docclass = $self->{ DOCUMENT };
+ $error = 'cache failed to write '
+ . &File::Basename::basename($compfile)
+ . ': ' . $docclass->error()
+ unless $docclass->write_perl_file($compfile, $parsedoc);
+
+ # set atime and mtime of newly compiled file, don't bother
+ # if time is undef
+ if (!defined($error) && defined $data->{ time }) {
+ my ($cfile) = $compfile =~ /^(.+)$/s or do {
+ return("invalid filename: $compfile",
+ Template::Constants::STATUS_ERROR);
+ };
+
+ my ($ctime) = $data->{ time } =~ /^(\d+)$/;
+ unless ($ctime || $ctime eq 0) {
+ return("invalid time: $ctime",
+ Template::Constants::STATUS_ERROR);
+ }
+ utime($ctime, $ctime, $cfile);
+ }
+ }
+
+ unless ($error) {
+ return $data ## RETURN ##
+ if $data->{ data } = $DOCUMENT->new($parsedoc);
+ $error = $Template::Document::ERROR;
+ }
+ }
+ else {
+ $error = Template::Exception->new( 'parse', "$data->{ name } " .
+ $parser->error() );
+ }
+
+ # return STATUS_ERROR, or STATUS_DECLINED if we're being tolerant
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR)
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal object
+# state.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $size = $self->{ SIZE };
+ my $parser = $self->{ PARSER };
+ $parser = $parser ? $parser->_dump() : '<no parser>';
+ $parser =~ s/\n/\n /gm;
+ $size = 'unlimited' unless defined $size;
+
+ my $output = "[Template::Provider] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ $output .= sprintf($format, 'INCLUDE_PATH',
+ '[ ' . join(', ', @{ $self->{ INCLUDE_PATH } }) . ' ]');
+ $output .= sprintf($format, 'CACHE_SIZE', $size);
+
+ foreach $key (qw( ABSOLUTE RELATIVE TOLERANT DELIMITER
+ COMPILE_EXT COMPILE_DIR )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+ $output .= sprintf($format, 'PARSER', $parser);
+
+
+ local $" = ', ';
+ my $lookup = $self->{ LOOKUP };
+ $lookup = join('', map {
+ sprintf(" $format", $_, defined $lookup->{ $_ }
+ ? ('[ ' . join(', ', map { defined $_ ? $_ : '<undef>' }
+ @{ $lookup->{ $_ } }) . ' ]') : '<undef>');
+ } sort keys %$lookup);
+ $lookup = "{\n$lookup }";
+
+ $output .= sprintf($format, LOOKUP => $lookup);
+
+ $output .= '}';
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# _dump_cache()
+#
+# Debug method which prints the current state of the cache to STDERR.
+#------------------------------------------------------------------------
+
+sub _dump_cache {
+ my $self = shift;
+ my ($node, $lut, $count);
+
+ $count = 0;
+ if ($node = $self->{ HEAD }) {
+ while ($node) {
+ $lut->{ $node } = $count++;
+ $node = $node->[ NEXT ];
+ }
+ $node = $self->{ HEAD };
+ print STDERR "CACHE STATE:\n";
+ print STDERR " HEAD: ", $self->{ HEAD }->[ NAME ], "\n";
+ print STDERR " TAIL: ", $self->{ TAIL }->[ NAME ], "\n";
+ while ($node) {
+ my ($prev, $name, $data, $load, $next) = @$node;
+# $name = '...' . substr($name, -10) if length $name > 10;
+ $prev = $prev ? "#$lut->{ $prev }<-": '<undef>';
+ $next = $next ? "->#$lut->{ $next }": '<undef>';
+ print STDERR " #$lut->{ $node } : [ $prev, $name, $data, $load, $next ]\n";
+ $node = $node->[ NEXT ];
+ }
+ }
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Provider - Provider module for loading/compiling templates
+
+=head1 SYNOPSIS
+
+ $provider = Template::Provider->new(\%options);
+
+ ($template, $error) = $provider->fetch($name);
+
+=head1 DESCRIPTION
+
+The Template::Provider is used to load, parse, compile and cache template
+documents. This object may be sub-classed to provide more specific
+facilities for loading, or otherwise providing access to templates.
+
+The Template::Context objects maintain a list of Template::Provider
+objects which are polled in turn (via fetch()) to return a requested
+template. Each may return a compiled template, raise an error, or
+decline to serve the reqest, giving subsequent providers a chance to
+do so.
+
+This is the "Chain of Responsiblity" pattern. See 'Design Patterns' for
+further information.
+
+This documentation needs work.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%options)
+
+Constructor method which instantiates and returns a new Template::Provider
+object. The optional parameter may be a hash reference containing any of
+the following items:
+
+=over 4
+
+
+
+
+=item INCLUDE_PATH
+
+The INCLUDE_PATH is used to specify one or more directories in which
+template files are located. When a template is requested that isn't
+defined locally as a BLOCK, each of the INCLUDE_PATH directories is
+searched in turn to locate the template file. Multiple directories
+can be specified as a reference to a list or as a single string where
+each directory is delimited by ':'.
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => '/usr/local/templates',
+ });
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => '/usr/local/templates:/tmp/my/templates',
+ });
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ '/tmp/my/templates' ],
+ });
+
+On Win32 systems, a little extra magic is invoked, ignoring delimiters
+that have ':' followed by a '/' or '\'. This avoids confusion when using
+directory names like 'C:\Blah Blah'.
+
+When specified as a list, the INCLUDE_PATH path can contain elements
+which dynamically generate a list of INCLUDE_PATH directories. These
+generator elements can be specified as a reference to a subroutine or
+an object which implements a paths() method.
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ \&incpath_generator,
+ My::IncPath::Generator->new( ... ) ],
+ });
+
+Each time a template is requested and the INCLUDE_PATH examined, the
+subroutine or object method will be called. A reference to a list of
+directories should be returned. Generator subroutines should report
+errors using die(). Generator objects should return undef and make an
+error available via its error() method.
+
+For example:
+
+ sub incpath_generator {
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ die "cannot generate INCLUDE_PATH...\n";
+ }
+ }
+
+or:
+
+ package My::IncPath::Generator;
+
+ # Template::Base (or Class::Base) provides error() method
+ use Template::Base;
+ use base qw( Template::Base );
+
+ sub paths {
+ my $self = shift;
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ return $self->error("cannot generate INCLUDE_PATH...\n");
+ }
+ }
+
+ 1;
+
+
+
+
+
+=item DELIMITER
+
+Used to provide an alternative delimiter character sequence for
+separating paths specified in the INCLUDE_PATH. The default
+value for DELIMITER is ':'.
+
+ # tolerate Silly Billy's file system conventions
+ my $provider = Template::Provider->new({
+ DELIMITER => '; ',
+ INCLUDE_PATH => 'C:/HERE/NOW; D:/THERE/THEN',
+ });
+
+ # better solution: install Linux! :-)
+
+On Win32 systems, the default delimiter is a little more intelligent,
+splitting paths only on ':' characters that aren't followed by a '/'.
+This means that the following should work as planned, splitting the
+INCLUDE_PATH into 2 separate directories, C:/foo and C:/bar.
+
+ # on Win32 only
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => 'C:/Foo:C:/Bar'
+ });
+
+However, if you're using Win32 then it's recommended that you
+explicitly set the DELIMITER character to something else (e.g. ';')
+rather than rely on this subtle magic.
+
+
+
+
+=item ABSOLUTE
+
+The ABSOLUTE flag is used to indicate if templates specified with
+absolute filenames (e.g. '/foo/bar') should be processed. It is
+disabled by default and any attempt to load a template by such a
+name will cause a 'file' exception to be raised.
+
+ my $provider = Template::Provider->new({
+ ABSOLUTE => 1,
+ });
+
+ # this is why it's disabled by default
+ [% INSERT /etc/passwd %]
+
+On Win32 systems, the regular expression for matching absolute
+pathnames is tweaked slightly to also detect filenames that start
+with a driver letter and colon, such as:
+
+ C:/Foo/Bar
+
+
+
+
+
+
+=item RELATIVE
+
+The RELATIVE flag is used to indicate if templates specified with
+filenames relative to the current directory (e.g. './foo/bar' or
+'../../some/where/else') should be loaded. It is also disabled by
+default, and will raise a 'file' error if such template names are
+encountered.
+
+ my $provider = Template::Provider->new({
+ RELATIVE => 1,
+ });
+
+ [% INCLUDE ../logs/error.log %]
+
+
+
+
+
+=item DEFAULT
+
+The DEFAULT option can be used to specify a default template which should
+be used whenever a specified template can't be found in the INCLUDE_PATH.
+
+ my $provider = Template::Provider->new({
+ DEFAULT => 'notfound.html',
+ });
+
+If a non-existant template is requested through the Template process()
+method, or by an INCLUDE, PROCESS or WRAPPER directive, then the
+DEFAULT template will instead be processed, if defined. Note that the
+DEFAULT template is not used when templates are specified with
+absolute or relative filenames, or as a reference to a input file
+handle or text string.
+
+
+
+
+
+=item CACHE_SIZE
+
+The Template::Provider module caches compiled templates to avoid the need
+to re-parse template files or blocks each time they are used. The CACHE_SIZE
+option is used to limit the number of compiled templates that the module
+should cache.
+
+By default, the CACHE_SIZE is undefined and all compiled templates are
+cached. When set to any positive value, the cache will be limited to
+storing no more than that number of compiled templates. When a new
+template is loaded and compiled and the cache is full (i.e. the number
+of entries == CACHE_SIZE), the least recently used compiled template
+is discarded to make room for the new one.
+
+The CACHE_SIZE can be set to 0 to disable caching altogether.
+
+ my $provider = Template::Provider->new({
+ CACHE_SIZE => 64, # only cache 64 compiled templates
+ });
+
+ my $provider = Template::Provider->new({
+ CACHE_SIZE => 0, # don't cache any compiled templates
+ });
+
+
+
+
+
+
+=item COMPILE_EXT
+
+From version 2 onwards, the Template Toolkit has the ability to
+compile templates to Perl code and save them to disk for subsequent
+use (i.e. cache persistence). The COMPILE_EXT option may be
+provided to specify a filename extension for compiled template files.
+It is undefined by default and no attempt will be made to read or write
+any compiled template files.
+
+ my $provider = Template::Provider->new({
+ COMPILE_EXT => '.ttc',
+ });
+
+If COMPILE_EXT is defined (and COMPILE_DIR isn't, see below) then compiled
+template files with the COMPILE_EXT extension will be written to the same
+directory from which the source template files were loaded.
+
+Compiling and subsequent reuse of templates happens automatically
+whenever the COMPILE_EXT or COMPILE_DIR options are set. The Template
+Toolkit will automatically reload and reuse compiled files when it
+finds them on disk. If the corresponding source file has been modified
+since the compiled version as written, then it will load and re-compile
+the source and write a new compiled version to disk.
+
+This form of cache persistence offers significant benefits in terms of
+time and resources required to reload templates. Compiled templates can
+be reloaded by a simple call to Perl's require(), leaving Perl to handle
+all the parsing and compilation. This is a Good Thing.
+
+=item COMPILE_DIR
+
+The COMPILE_DIR option is used to specify an alternate directory root
+under which compiled template files should be saved.
+
+ my $provider = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ });
+
+The COMPILE_EXT option may also be specified to have a consistent file
+extension added to these files.
+
+ my $provider1 = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc1',
+ });
+
+ my $provider2 = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc2',
+ });
+
+
+When COMPILE_EXT is undefined, the compiled template files have the
+same name as the original template files, but reside in a different
+directory tree.
+
+Each directory in the INCLUDE_PATH is replicated in full beneath the
+COMPILE_DIR directory. This example:
+
+ my $provider = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ INCLUDE_PATH => '/home/abw/templates:/usr/share/templates',
+ });
+
+would create the following directory structure:
+
+ /tmp/ttc/home/abw/templates/
+ /tmp/ttc/usr/share/templates/
+
+Files loaded from different INCLUDE_PATH directories will have their
+compiled forms save in the relevant COMPILE_DIR directory.
+
+On Win32 platforms a filename may by prefixed by a drive letter and
+colon. e.g.
+
+ C:/My Templates/header
+
+The colon will be silently stripped from the filename when it is added
+to the COMPILE_DIR value(s) to prevent illegal filename being generated.
+Any colon in COMPILE_DIR elements will be left intact. For example:
+
+ # Win32 only
+ my $provider = Template::Provider->new({
+ DELIMITER => ';',
+ COMPILE_DIR => 'C:/TT2/Cache',
+ INCLUDE_PATH => 'C:/TT2/Templates;D:/My Templates',
+ });
+
+This would create the following cache directories:
+
+ C:/TT2/Cache/C/TT2/Templates
+ C:/TT2/Cache/D/My Templates
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+
+
+=item PARSER
+
+The Template::Parser module implements a parser object for compiling
+templates into Perl code which can then be executed. A default object
+of this class is created automatically and then used by the
+Template::Provider whenever a template is loaded and requires
+compilation. The PARSER option can be used to provide a reference to
+an alternate parser object.
+
+ my $provider = Template::Provider->new({
+ PARSER => MyOrg::Template::Parser->new({ ... }),
+ });
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Provider module by setting it to include the DEBUG_PROVIDER
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_PROVIDER,
+ });
+
+
+
+=back
+
+=head2 fetch($name)
+
+Returns a compiled template for the name specified. If the template
+cannot be found then (undef, STATUS_DECLINED) is returned. If an error
+occurs (e.g. read error, parse error) then ($error, STATUS_ERROR) is
+returned, where $error is the error message generated. If the TOLERANT
+flag is set the the method returns (undef, STATUS_DECLINED) instead of
+returning an error.
+
+=head2 store($name, $template)
+
+Stores the compiled template, $template, in the cache under the name,
+$name. Susbequent calls to fetch($name) will return this template in
+preference to any disk-based file.
+
+=head2 include_path(\@newpath))
+
+Accessor method for the INCLUDE_PATH setting. If called with an
+argument, this method will replace the existing INCLUDE_PATH with
+the new value.
+
+=head2 paths()
+
+This method generates a copy of the INCLUDE_PATH list. Any elements in the
+list which are dynamic generators (e.g. references to subroutines or objects
+implementing a paths() method) will be called and the list of directories
+returned merged into the output list.
+
+It is possible to provide a generator which returns itself, thus sending
+this method into an infinite loop. To detect and prevent this from happening,
+the C<$MAX_DIRS> package variable, set to 64 by default, limits the maximum
+number of paths that can be added to, or generated for the output list. If
+this number is exceeded then the method will immediately return an error
+reporting as much.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.79, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Parser|Template::Parser>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Service.pm b/lib/Template/Service.pm
new file mode 100644
index 0000000..fb5590a
--- /dev/null
+++ b/lib/Template/Service.pm
@@ -0,0 +1,775 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Service
+#
+# DESCRIPTION
+# Module implementing a template processing service which wraps a
+# template within PRE_PROCESS and POST_PROCESS templates and offers
+# ERROR recovery.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Service.pm,v 2.75 2004/01/13 16:19:16 abw Exp $
+#
+#============================================================================
+
+package Template::Service;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR );
+use base qw( Template::Base );
+use Template::Base;
+use Template::Config;
+use Template::Exception;
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.75 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# process($template, \%params)
+#
+# Process a template within a service framework. A service may encompass
+# PRE_PROCESS and POST_PROCESS templates and an ERROR hash which names
+# templates to be substituted for the main template document in case of
+# error. Each service invocation begins by resetting the state of the
+# context object via a call to reset(). The AUTO_RESET option may be set
+# to 0 (default: 1) to bypass this step.
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $template, $params) = @_;
+ my $context = $self->{ CONTEXT };
+ my ($name, $output, $procout, $error);
+ $output = '';
+
+ $self->debug("process($template, ",
+ defined $params ? $params : '<no params>',
+ ')') if $self->{ DEBUG };
+
+ $context->reset()
+ if $self->{ AUTO_RESET };
+
+ # pre-request compiled template from context so that we can alias it
+ # in the stash for pre-processed templates to reference
+ eval { $template = $context->template($template) };
+ return $self->error($@)
+ if $@;
+
+ # localise the variable stash with any parameters passed
+ # and set the 'template' variable
+ $params ||= { };
+ $params->{ template } = $template
+ unless ref $template eq 'CODE';
+ $context->localise($params);
+
+ SERVICE: {
+ # PRE_PROCESS
+ eval {
+ foreach $name (@{ $self->{ PRE_PROCESS } }) {
+ $self->debug("PRE_PROCESS: $name") if $self->{ DEBUG };
+ $output .= $context->process($name);
+ }
+ };
+ last SERVICE if ($error = $@);
+
+ # PROCESS
+ eval {
+ foreach $name (@{ $self->{ PROCESS } || [ $template ] }) {
+ $self->debug("PROCESS: $name") if $self->{ DEBUG };
+ $procout .= $context->process($name);
+ }
+ };
+ if ($error = $@) {
+ last SERVICE
+ unless defined ($procout = $self->_recover(\$error));
+ }
+
+ if (defined $procout) {
+ # WRAPPER
+ eval {
+ foreach $name (reverse @{ $self->{ WRAPPER } }) {
+ $self->debug("WRAPPER: $name") if $self->{ DEBUG };
+ $procout = $context->process($name, { content => $procout });
+ }
+ };
+ last SERVICE if ($error = $@);
+ $output .= $procout;
+ }
+
+ # POST_PROCESS
+ eval {
+ foreach $name (@{ $self->{ POST_PROCESS } }) {
+ $self->debug("POST_PROCESS: $name") if $self->{ DEBUG };
+ $output .= $context->process($name);
+ }
+ };
+ last SERVICE if ($error = $@);
+ }
+
+ $context->delocalise();
+ delete $params->{ template };
+
+ if ($error) {
+# $error = $error->as_string if ref $error;
+ return $self->error($error);
+ }
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# context()
+#
+# Returns the internal CONTEXT reference.
+#------------------------------------------------------------------------
+
+sub context {
+ return $_[0]->{ CONTEXT };
+}
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+sub _init {
+ my ($self, $config) = @_;
+ my ($item, $data, $context, $block, $blocks);
+ my $delim = $config->{ DELIMITER };
+ $delim = ':' unless defined $delim;
+
+ # coerce PRE_PROCESS, PROCESS and POST_PROCESS to arrays if necessary,
+ # by splitting on non-word characters
+ foreach $item (qw( PRE_PROCESS PROCESS POST_PROCESS WRAPPER )) {
+ $data = $config->{ $item };
+ $self->{ $item } = [ ], next unless (defined $data);
+ $data = [ split($delim, $data || '') ]
+ unless ref $data eq 'ARRAY';
+ $self->{ $item } = $data;
+ }
+ # unset PROCESS option unless explicitly specified in config
+ $self->{ PROCESS } = undef
+ unless defined $config->{ PROCESS };
+
+ $self->{ ERROR } = $config->{ ERROR } || $config->{ ERRORS };
+ $self->{ AUTO_RESET } = defined $config->{ AUTO_RESET }
+ ? $config->{ AUTO_RESET } : 1;
+ $self->{ DEBUG } = ( $config->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_SERVICE;
+
+ $context = $self->{ CONTEXT } = $config->{ CONTEXT }
+ || Template::Config->context($config)
+ || return $self->error(Template::Config->error);
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _recover(\$exception)
+#
+# Examines the internal ERROR hash array to find a handler suitable
+# for the exception object passed by reference. Selecting the handler
+# is done by delegation to the exception's select_handler() method,
+# passing the set of handler keys as arguments. A 'default' handler
+# may also be provided. The handler value represents the name of a
+# template which should be processed.
+#------------------------------------------------------------------------
+
+sub _recover {
+ my ($self, $error) = @_;
+ my $context = $self->{ CONTEXT };
+ my ($hkey, $handler, $output);
+
+ # there shouldn't ever be a non-exception object received at this
+ # point... unless a module like CGI::Carp messes around with the
+ # DIE handler.
+ return undef
+ unless (ref $$error);
+
+ # a 'stop' exception is thrown by [% STOP %] - we return the output
+ # buffer stored in the exception object
+ return $$error->text()
+ if $$error->type() eq 'stop';
+
+ my $handlers = $self->{ ERROR }
+ || return undef; ## RETURN
+
+ if (ref $handlers eq 'HASH') {
+ if ($hkey = $$error->select_handler(keys %$handlers)) {
+ $handler = $handlers->{ $hkey };
+ $self->debug("using error handler for $hkey") if $self->{ DEBUG };
+ }
+ elsif ($handler = $handlers->{ default }) {
+ # use default handler
+ $self->debug("using default error handler") if $self->{ DEBUG };
+ }
+ else {
+ return undef; ## RETURN
+ }
+ }
+ else {
+ $handler = $handlers;
+ $self->debug("using default error handler") if $self->{ DEBUG };
+ }
+
+ eval { $handler = $context->template($handler) };
+ if ($@) {
+ $$error = $@;
+ return undef; ## RETURN
+ };
+
+ $context->stash->set('error', $$error);
+ eval {
+ $output .= $context->process($handler);
+ };
+ if ($@) {
+ $$error = $@;
+ return undef; ## RETURN
+ }
+
+ return $output;
+}
+
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which return a string representing the internal object
+# state.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $context = $self->{ CONTEXT }->_dump();
+ $context =~ s/\n/\n /gm;
+
+ my $error = $self->{ ERROR };
+ $error = join('',
+ "{\n",
+ (map { " $_ => $error->{ $_ }\n" }
+ keys %$error),
+ "}\n")
+ if ref $error;
+
+ local $" = ', ';
+ return <<EOF;
+$self
+PRE_PROCESS => [ @{ $self->{ PRE_PROCESS } } ]
+POST_PROCESS => [ @{ $self->{ POST_PROCESS } } ]
+ERROR => $error
+CONTEXT => $context
+EOF
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Service - General purpose template processing service
+
+=head1 SYNOPSIS
+
+ use Template::Service;
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => [ 'config', 'header' ],
+ POST_PROCESS => 'footer',
+ ERROR => {
+ user => 'user/index.html',
+ dbi => 'error/database',
+ default => 'error/default',
+ },
+ });
+
+ my $output = $service->process($template_name, \%replace)
+ || die $service->error(), "\n";
+
+=head1 DESCRIPTION
+
+The Template::Service module implements an object class for providing
+a consistent template processing service.
+
+Standard header (PRE_PROCESS) and footer (POST_PROCESS) templates may
+be specified which are prepended and appended to all templates
+processed by the service (but not any other templates or blocks
+INCLUDEd or PROCESSed from within). An ERROR hash may be specified
+which redirects the service to an alternate template file in the case
+of uncaught exceptions being thrown. This allows errors to be
+automatically handled by the service and a guaranteed valid response
+to be generated regardless of any processing problems encountered.
+
+A default Template::Service object is created by the Template module.
+Any Template::Service options may be passed to the Template new()
+constructor method and will be forwarded to the Template::Service
+constructor.
+
+ use Template;
+
+ my $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+Similarly, the Template::Service constructor will forward all configuration
+parameters onto other default objects (e.g. Template::Context) that it may
+need to instantiate.
+
+A Template::Service object (or subclass/derivative) can be explicitly
+instantiated and passed to the Template new() constructor method as
+the SERVICE item.
+
+ use Template;
+ use Template::Service;
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $template = Template->new({
+ SERVICE => $service,
+ });
+
+The Template::Service module can be sub-classed to create custom service
+handlers.
+
+ use Template;
+ use MyOrg::Template::Service;
+
+ my $service = MyOrg::Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ COOL_OPTION => 'enabled in spades',
+ });
+
+ my $template = Template->new({
+ SERVICE => $service,
+ });
+
+The Template module uses the Template::Config service() factory method
+to create a default service object when required. The
+$Template::Config::SERVICE package variable may be set to specify an
+alternate service module. This will be loaded automatically and its
+new() constructor method called by the service() factory method when
+a default service object is required. Thus the previous example could
+be written as:
+
+ use Template;
+
+ $Template::Config::SERVICE = 'MyOrg::Template::Service';
+
+ my $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ COOL_OPTION => 'enabled in spades',
+ });
+
+=head1 METHODS
+
+=head2 new(\%config)
+
+The new() constructor method is called to instantiate a Template::Service
+object. Configuration parameters may be specified as a HASH reference or
+as a list of (name =E<gt> value) pairs.
+
+ my $service1 = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $service2 = Template::Service->new( ERROR => 'error.html' );
+
+The new() method returns a Template::Service object (or sub-class) or
+undef on error. In the latter case, a relevant error message can be
+retrieved by the error() class method or directly from the
+$Template::Service::ERROR package variable.
+
+ my $service = Template::Service->new(\%config)
+ || die Template::Service->error();
+
+ my $service = Template::Service->new(\%config)
+ || die $Template::Service::ERROR;
+
+The following configuration items may be specified:
+
+=over 4
+
+
+
+
+=item PRE_PROCESS, POST_PROCESS
+
+These values may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed immediately
+before and/or after each template. These do not get added to
+templates processed into a document via directives such as INCLUDE,
+PROCESS, WRAPPER etc.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ };
+
+Multiple templates may be specified as a reference to a list. Each is
+processed in the order defined.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => [ 'config', 'header' ],
+ POST_PROCESS => 'footer',
+ };
+
+Alternately, multiple template may be specified as a single string,
+delimited by ':'. This delimiter string can be changed via the
+DELIMITER option.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'config:header',
+ POST_PROCESS => 'footer',
+ };
+
+The PRE_PROCESS and POST_PROCESS templates are evaluated in the same
+variable context as the main document and may define or update
+variables for subsequent use.
+
+config:
+
+ [% # set some site-wide variables
+ bgcolor = '#ffffff'
+ version = 2.718
+ %]
+
+header:
+
+ [% DEFAULT title = 'My Funky Web Site' %]
+ <html>
+ <head>
+ <title>[% title %]</title>
+ </head>
+ <body bgcolor="[% bgcolor %]">
+
+footer:
+
+ <hr>
+ Version [% version %]
+ </body>
+ </html>
+
+The Template::Document object representing the main template being processed
+is available within PRE_PROCESS and POST_PROCESS templates as the 'template'
+variable. Metadata items defined via the META directive may be accessed
+accordingly.
+
+ $service->process('mydoc.html', $vars);
+
+mydoc.html:
+
+ [% META title = 'My Document Title' %]
+ blah blah blah
+ ...
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title></head>
+ <body bgcolor="[% bgcolor %]">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=item PROCESS
+
+The PROCESS option may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed instead of the
+main template passed to the Template::Service process() method. This can
+be used to apply consistent wrappers around all templates, similar to
+the use of PRE_PROCESS and POST_PROCESS templates.
+
+ my $service = Template::Service->new({
+ PROCESS => 'content',
+ };
+
+ # processes 'content' instead of 'foo.html'
+ $service->process('foo.html');
+
+A reference to the original template is available in the 'template'
+variable. Metadata items can be inspected and the template can be
+processed by specifying it as a variable reference (i.e. prefixed by
+'$') to an INCLUDE, PROCESS or WRAPPER directive.
+
+content:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title>
+ </head>
+
+ <body>
+ [% PROCESS $template %]
+ <hr>
+ &copy; Copyright [% template.copyright %]
+ </body>
+ </html>
+
+foo.html:
+
+ [% META
+ title = 'The Foo Page'
+ author = 'Fred Foo'
+ copyright = '2000 Fred Foo'
+ %]
+ <h1>[% template.title %]</h1>
+ Welcome to the Foo Page, blah blah blah
+
+output:
+
+ <html>
+ <head>
+ <title>The Foo Page</title>
+ </head>
+
+ <body>
+ <h1>The Foo Page</h1>
+ Welcome to the Foo Page, blah blah blah
+ <hr>
+ &copy; Copyright 2000 Fred Foo
+ </body>
+ </html>
+
+
+
+
+
+
+
+=item ERROR
+
+The ERROR (or ERRORS if you prefer) configuration item can be used to
+name a single template or specify a hash array mapping exception types
+to templates which should be used for error handling. If an uncaught
+exception is raised from within a template then the appropriate error
+template will instead be processed.
+
+If specified as a single value then that template will be processed
+for all uncaught exceptions.
+
+ my $service = Template::Service->new({
+ ERROR => 'error.html'
+ });
+
+If the ERROR item is a hash reference the keys are assumed to be
+exception types and the relevant template for a given exception will
+be selected. A 'default' template may be provided for the general
+case. Note that 'ERROR' can be pluralised to 'ERRORS' if you find
+it more appropriate in this case.
+
+ my $service = Template::Service->new({
+ ERRORS => {
+ user => 'user/index.html',
+ dbi => 'error/database',
+ default => 'error/default',
+ },
+ });
+
+In this example, any 'user' exceptions thrown will cause the
+'user/index.html' template to be processed, 'dbi' errors are handled
+by 'error/database' and all others by the 'error/default' template.
+Any PRE_PROCESS and/or POST_PROCESS templates will also be applied
+to these error templates.
+
+Note that exception types are hierarchical and a 'foo' handler will
+catch all 'foo.*' errors (e.g. foo.bar, foo.bar.baz) if a more
+specific handler isn't defined. Be sure to quote any exception types
+that contain periods to prevent Perl concatenating them into a single
+string (i.e. C<user.passwd> is parsed as 'user'.'passwd').
+
+ my $service = Template::Service->new({
+ ERROR => {
+ 'user.login' => 'user/login.html',
+ 'user.passwd' => 'user/badpasswd.html',
+ 'user' => 'user/index.html',
+ 'default' => 'error/default',
+ },
+ });
+
+In this example, any template processed by the $service object, or
+other templates or code called from within, can raise a 'user.login'
+exception and have the service redirect to the 'user/login.html'
+template. Similarly, a 'user.passwd' exception has a specific
+handling template, 'user/badpasswd.html', while all other 'user' or
+'user.*' exceptions cause a redirection to the 'user/index.html' page.
+All other exception types are handled by 'error/default'.
+
+
+Exceptions can be raised in a template using the THROW directive,
+
+ [% THROW user.login 'no user id: please login' %]
+
+or by calling the throw() method on the current Template::Context object,
+
+ $context->throw('user.passwd', 'Incorrect Password');
+ $context->throw('Incorrect Password'); # type 'undef'
+
+or from Perl code by calling die() with a Template::Exception object,
+
+ die (Template::Exception->new('user.denied', 'Invalid User ID'));
+
+or by simply calling die() with an error string. This is
+automagically caught and converted to an exception of 'undef'
+type which can then be handled in the usual way.
+
+ die "I'm sorry Dave, I can't do that";
+
+
+
+
+
+
+
+=item AUTO_RESET
+
+The AUTO_RESET option is set by default and causes the local BLOCKS
+cache for the Template::Context object to be reset on each call to the
+Template process() method. This ensures that any BLOCKs defined
+within a template will only persist until that template is finished
+processing. This prevents BLOCKs defined in one processing request
+from interfering with other independent requests subsequently
+processed by the same context object.
+
+The BLOCKS item may be used to specify a default set of block definitions
+for the Template::Context object. Subsequent BLOCK definitions in templates
+will over-ride these but they will be reinstated on each reset if AUTO_RESET
+is enabled (default), or if the Template::Context reset() method is called.
+
+
+
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Service module by setting it to include the DEBUG_SERVICE
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_SERVICE,
+ });
+
+
+
+
+=back
+
+=head2 process($input, \%replace)
+
+The process() method is called to process a template specified as the first
+parameter, $input. This may be a file name, file handle (e.g. GLOB or IO::Handle)
+or a reference to a text string containing the template text. An additional
+hash reference may be passed containing template variable definitions.
+
+The method processes the template, adding any PRE_PROCESS or POST_PROCESS
+templates defined, and returns the output text. An uncaught exception thrown
+by the template will be handled by a relevant ERROR handler if defined.
+Errors that occur in the PRE_PROCESS or POST_PROCESS templates, or those that
+occur in the main input template and aren't handled, cause the method to
+return undef to indicate failure. The appropriate error message can be
+retrieved via the error() method.
+
+ $service->process('myfile.html', { title => 'My Test File' })
+ || die $service->error();
+
+
+=head2 context()
+
+Returns a reference to the internal context object which is, by default, an
+instance of the Template::Context class.
+
+=head2 error()
+
+Returns the most recent error message.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.79, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Stash.pm b/lib/Template/Stash.pm
new file mode 100644
index 0000000..64553fe
--- /dev/null
+++ b/lib/Template/Stash.pm
@@ -0,0 +1,1040 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash
+#
+# DESCRIPTION
+# Definition of an object class which stores and manages access to
+# variables for the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@wardley.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Stash.pm,v 2.85 2004/01/30 17:51:01 abw Exp $
+#
+#============================================================================
+
+package Template::Stash;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.85 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# -- PACKAGE VARIABLES AND SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# Definitions of various pseudo-methods. ROOT_OPS are merged into all
+# new Template::Stash objects, and are thus default global functions.
+# SCALAR_OPS are methods that can be called on a scalar, and ditto
+# respectively for LIST_OPS and HASH_OPS
+#------------------------------------------------------------------------
+
+$ROOT_OPS = {
+ 'inc' => sub { local $^W = 0; my $item = shift; ++$item },
+ 'dec' => sub { local $^W = 0; my $item = shift; --$item },
+# import => \&hash_import,
+ defined $ROOT_OPS ? %$ROOT_OPS : (),
+};
+
+$SCALAR_OPS = {
+ 'item' => sub { $_[0] },
+ 'list' => sub { [ $_[0] ] },
+ 'hash' => sub { { value => $_[0] } },
+ 'length' => sub { length $_[0] },
+ 'size' => sub { return 1 },
+ 'defined' => sub { return 1 },
+ 'repeat' => sub {
+ my ($str, $count) = @_;
+ $str = '' unless defined $str;
+ return '' unless $count;
+ $count ||= 1;
+ return $str x $count;
+ },
+ 'search' => sub {
+ my ($str, $pattern) = @_;
+ return $str unless defined $str and defined $pattern;
+ return $str =~ /$pattern/;
+ },
+ 'replace' => sub {
+ my ($str, $search, $replace) = @_;
+ $replace = '' unless defined $replace;
+ return $str unless defined $str and defined $search;
+ $str =~ s/$search/$replace/g;
+# print STDERR "s [ $search ] [ $replace ] g\n";
+# eval "\$str =~ s$search$replaceg";
+ return $str;
+ },
+ 'match' => sub {
+ my ($str, $search) = @_;
+ return $str unless defined $str and defined $search;
+ my @matches = ($str =~ /$search/);
+ return @matches ? \@matches : '';
+ },
+ 'split' => sub {
+ my ($str, $split, @args) = @_;
+ $str = '' unless defined $str;
+ return [ defined $split ? split($split, $str, @args)
+ : split(' ', $str, @args) ];
+ },
+ 'chunk' => sub {
+ my ($string, $size) = @_;
+ my @list;
+ $size ||= 1;
+ if ($size < 0) {
+ # sexeger! It's faster to reverse the string, search
+ # it from the front and then reverse the output than to
+ # search it from the end, believe it nor not!
+ $string = reverse $string;
+ $size = -$size;
+ unshift(@list, scalar reverse $1)
+ while ($string =~ /((.{$size})|(.+))/g);
+ }
+ else {
+ push(@list, $1) while ($string =~ /((.{$size})|(.+))/g);
+ }
+ return \@list;
+ },
+
+
+ defined $SCALAR_OPS ? %$SCALAR_OPS : (),
+};
+
+$HASH_OPS = {
+ 'item' => sub {
+ my ($hash, $item) = @_;
+ $item = '' unless defined $item;
+ return if $item =~ /^[_.]/;
+ $hash->{ $item };
+ },
+ 'hash' => sub { $_[0] },
+ 'size' => sub { scalar keys %{$_[0]} },
+ 'keys' => sub { [ keys %{ $_[0] } ] },
+ 'values' => sub { [ values %{ $_[0] } ] },
+ 'each' => sub { [ %{ $_[0] } ] },
+ 'list' => sub {
+ my ($hash, $what) = @_; $what ||= '';
+ return ($what eq 'keys') ? [ keys %$hash ]
+ : ($what eq 'values') ? [ values %$hash ]
+ : ($what eq 'each') ? [ %$hash ]
+ : [ map { { key => $_ , value => $hash->{ $_ } } }
+ keys %$hash ];
+ },
+ 'exists' => sub { exists $_[0]->{ $_[1] } },
+ 'defined' => sub { defined $_[0]->{ $_[1] } },
+ 'import' => \&hash_import,
+ 'sort' => sub {
+ my ($hash) = @_;
+ [ sort { lc $hash->{$a} cmp lc $hash->{$b} } (keys %$hash) ];
+ },
+ 'nsort' => sub {
+ my ($hash) = @_;
+ [ sort { $hash->{$a} <=> $hash->{$b} } (keys %$hash) ];
+ },
+ defined $HASH_OPS ? %$HASH_OPS : (),
+};
+
+$LIST_OPS = {
+ 'item' => sub { $_[0]->[ $_[1] || 0 ] },
+ 'list' => sub { $_[0] },
+ 'hash' => sub { my $list = shift; my $n = 0;
+ return { map { ($n++, $_) } @$list }; },
+ 'push' => sub { my $list = shift; push(@$list, shift); return '' },
+ 'pop' => sub { my $list = shift; pop(@$list) },
+ 'unshift' => sub { my $list = shift; unshift(@$list, shift); return '' },
+ 'shift' => sub { my $list = shift; shift(@$list) },
+ 'max' => sub { local $^W = 0; my $list = shift; $#$list; },
+ 'size' => sub { local $^W = 0; my $list = shift; $#$list + 1; },
+ 'first' => sub {
+ my $list = shift;
+ return $list->[0] unless @_;
+ return [ @$list[0..$_[0]-1] ];
+ },
+ 'last' => sub {
+ my $list = shift;
+ return $list->[-1] unless @_;
+ return [ @$list[-$_[0]..-1] ];
+ },
+ 'reverse' => sub { my $list = shift; [ reverse @$list ] },
+ 'grep' => sub {
+ my ($list, $pattern) = @_;
+ $pattern ||= '';
+ return [ grep /$pattern/, @$list ];
+ },
+ 'join' => sub {
+ my ($list, $joint) = @_;
+ join(defined $joint ? $joint : ' ',
+ map { defined $_ ? $_ : '' } @$list)
+ },
+ 'sort' => sub {
+ $^W = 0;
+ my ($list, $field) = @_;
+ return $list unless @$list > 1; # no need to sort 1 item lists
+ return $field # Schwartzian Transform
+ ? map { $_->[0] } # for case insensitivity
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_, lc(ref($_) eq 'HASH'
+ ? $_->{ $field } :
+ UNIVERSAL::can($_, $field)
+ ? $_->$field() : $_) ] }
+ @$list
+ : map { $_->[0] }
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_, lc $_ ] }
+ @$list
+ },
+ 'nsort' => sub {
+ my ($list, $field) = @_;
+ return $list unless $#$list; # no need to sort 1 item lists
+ return $field # Schwartzian Transform
+ ? map { $_->[0] } # for case insensitivity
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, lc(ref($_) eq 'HASH'
+ ? $_->{ $field } :
+ UNIVERSAL::can($_, $field)
+ ? $_->$field() : $_) ] }
+ @$list
+ : map { $_->[0] }
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, lc $_ ] }
+ @$list
+ },
+ 'unique' => sub { my %u; [ grep { ++$u{$_} == 1 } @{$_[0]} ] },
+ 'merge' => sub {
+ my $list = shift;
+ return [ @$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_ ];
+ },
+ 'slice' => sub {
+ my ($list, $from, $to) = @_;
+ $from ||= 0;
+ $to = $#$list unless defined $to;
+ return [ @$list[$from..$to] ];
+ },
+ 'splice' => sub {
+ my ($list, $offset, $length, @replace) = @_;
+ if (@replace) {
+ # @replace can contain a list of multiple replace items, or
+ # be a single reference to a list
+ @replace = @{ $replace[0] }
+ if @replace == 1 && ref $replace[0] eq 'ARRAY';
+ return [ splice @$list, $offset, $length, @replace ];
+ }
+ elsif (defined $length) {
+ return [ splice @$list, $offset, $length ];
+ }
+ elsif (defined $offset) {
+ return [ splice @$list, $offset ];
+ }
+ else {
+ return [ splice(@$list) ];
+ }
+ },
+
+ defined $LIST_OPS ? %$LIST_OPS : (),
+};
+
+sub hash_import {
+ my ($hash, $imp) = @_;
+ $imp = {} unless ref $imp eq 'HASH';
+ @$hash{ keys %$imp } = values %$imp;
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# define_vmethod($type, $name, \&sub)
+#
+# Defines a virtual method of type $type (SCALAR, HASH, or LIST), with
+# name $name, that invokes &sub when called. It is expected that &sub
+# be able to handle the type that it will be called upon.
+#------------------------------------------------------------------------
+
+sub define_vmethod {
+ my ($class, $type, $name, $sub) = @_;
+ my $op;
+ $type = lc $type;
+
+ if ($type =~ /^scalar|item$/) {
+ $op = $SCALAR_OPS;
+ }
+ elsif ($type eq 'hash') {
+ $op = $HASH_OPS;
+ }
+ elsif ($type =~ /^list|array$/) {
+ $op = $LIST_OPS;
+ }
+ else {
+ die "invalid vmethod type: $type\n";
+ }
+
+ $op->{ $name } = $sub;
+
+ return 1;
+}
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# Constructor method which creates a new Template::Stash object.
+# An optional hash reference may be passed containing variable
+# definitions that will be used to initialise the stash.
+#
+# Returns a reference to a newly created Template::Stash.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ };
+
+ my $self = {
+ global => { },
+ %$params,
+ %$ROOT_OPS,
+ '_PARENT' => undef,
+ };
+
+ bless $self, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# clone(\%params)
+#
+# Creates a copy of the current stash object to effect localisation
+# of variables. The new stash is blessed into the same class as the
+# parent (which may be a derived class) and has a '_PARENT' member added
+# which contains a reference to the parent stash that created it
+# ($self). This member is used in a successive declone() method call to
+# return the reference to the parent.
+#
+# A parameter may be provided which should reference a hash of
+# variable/values which should be defined in the new stash. The
+# update() method is called to define these new variables in the cloned
+# stash.
+#
+# Returns a reference to a cloned Template::Stash.
+#------------------------------------------------------------------------
+
+sub clone {
+ my ($self, $params) = @_;
+ $params ||= { };
+
+ # look out for magical 'import' argument which imports another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ delete $params->{ import };
+ }
+ else {
+ undef $import;
+ }
+
+ my $clone = bless {
+ %$self, # copy all parent members
+ %$params, # copy all new data
+ '_PARENT' => $self, # link to parent
+ }, ref $self;
+
+ # perform hash import if defined
+ &{ $HASH_OPS->{ import }}($clone, $import)
+ if defined $import;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# declone($export)
+#
+# Returns a reference to the PARENT stash. When called in the following
+# manner:
+# $stash = $stash->declone();
+# the reference count on the current stash will drop to 0 and be "freed"
+# and the caller will be left with a reference to the parent. This
+# contains the state of the stash before it was cloned.
+#------------------------------------------------------------------------
+
+sub declone {
+ my $self = shift;
+ $self->{ _PARENT } || $self;
+}
+
+
+#------------------------------------------------------------------------
+# get($ident)
+#
+# Returns the value for an variable stored in the stash. The variable
+# may be specified as a simple string, e.g. 'foo', or as an array
+# reference representing compound variables. In the latter case, each
+# pair of successive elements in the list represent a node in the
+# compound variable. The first is the variable name, the second a
+# list reference of arguments or 0 if undefined. So, the compound
+# variable [% foo.bar('foo').baz %] would be represented as the list
+# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the
+# identifier or an empty string if undefined. Errors are thrown via
+# die().
+#------------------------------------------------------------------------
+
+sub get {
+ my ($self, $ident, $args) = @_;
+ my ($root, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) {
+ my $size = $#$ident;
+
+ # if $ident is a list reference, then we evaluate each item in the
+ # identifier against the previous result, using the root stash
+ # ($self) as the first implicit 'result'...
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1]);
+ last unless defined $result;
+ $root = $result;
+ }
+ }
+ else {
+ $result = $self->_dotop($root, $ident, $args);
+ }
+
+ return defined $result ? $result : $self->undefined($ident, $args);
+}
+
+
+#------------------------------------------------------------------------
+# set($ident, $value, $default)
+#
+# Updates the value for a variable in the stash. The first parameter
+# should be the variable name or array, as per get(). The second
+# parameter should be the intended value for the variable. The third,
+# optional parameter is a flag which may be set to indicate 'default'
+# mode. When set true, the variable will only be updated if it is
+# currently undefined or has a false value. The magical 'IMPORT'
+# variable identifier may be used to indicate that $value is a hash
+# reference whose values should be imported. Returns the value set,
+# or an empty string if not set (e.g. default mode). In the case of
+# IMPORT, returns the number of items imported from the hash.
+#------------------------------------------------------------------------
+
+sub set {
+ my ($self, $ident, $value, $default) = @_;
+ my ($root, $result, $error);
+
+ $root = $self;
+
+ ELEMENT: {
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) }
+ split(/\./, $ident) ])) {
+
+ # a compound identifier may contain multiple elements (e.g.
+ # foo.bar.baz) and we must first resolve all but the last,
+ # using _dotop() with the $lvalue flag set which will create
+ # intermediate hashes if necessary...
+ my $size = $#$ident;
+ foreach (my $i = 0; $i < $size - 2; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 1);
+ last ELEMENT unless defined $result;
+ $root = $result;
+ }
+
+ # then we call _assign() to assign the value to the last element
+ $result = $self->_assign($root, @$ident[$size-1, $size],
+ $value, $default);
+ }
+ else {
+ $result = $self->_assign($root, $ident, 0, $value, $default);
+ }
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# getref($ident)
+#
+# Returns a "reference" to a particular item. This is represented as a
+# closure which will return the actual stash item when called.
+# WARNING: still experimental!
+#------------------------------------------------------------------------
+
+sub getref {
+ my ($self, $ident, $args) = @_;
+ my ($root, $item, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY') {
+ my $size = $#$ident;
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ ($item, $args) = @$ident[$i, $i + 1];
+ last if $i >= $size - 2; # don't evaluate last node
+ last unless defined
+ ($root = $self->_dotop($root, $item, $args));
+ }
+ }
+ else {
+ $item = $ident;
+ }
+
+ if (defined $root) {
+ return sub { my @args = (@{$args||[]}, @_);
+ $self->_dotop($root, $item, \@args);
+ }
+ }
+ else {
+ return sub { '' };
+ }
+}
+
+
+
+
+#------------------------------------------------------------------------
+# update(\%params)
+#
+# Update multiple variables en masse. No magic is performed. Simple
+# variable names only.
+#------------------------------------------------------------------------
+
+sub update {
+ my ($self, $params) = @_;
+
+ # look out for magical 'import' argument to import another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ @$self{ keys %$import } = values %$import;
+ delete $params->{ import };
+ }
+
+ @$self{ keys %$params } = values %$params;
+}
+
+
+#------------------------------------------------------------------------
+# undefined($ident, $args)
+#
+# Method called when a get() returns an undefined value. Can be redefined
+# in a subclass to implement alternate handling.
+#------------------------------------------------------------------------
+
+sub undefined {
+ my ($self, $ident, $args);
+ return '';
+}
+
+
+#========================================================================
+# ----- PRIVATE OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dotop($root, $item, \@args, $lvalue)
+#
+# This is the core 'dot' operation method which evaluates elements of
+# variables against their root. All variables have an implicit root
+# which is the stash object itself (a hash). Thus, a non-compound
+# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is
+# '(stash.)foo.bar'. The first parameter is a reference to the current
+# root, initially the stash itself. The second parameter contains the
+# name of the variable element, e.g. 'foo'. The third optional
+# parameter is a reference to a list of any parenthesised arguments
+# specified for the variable, which are passed to sub-routines, object
+# methods, etc. The final parameter is an optional flag to indicate
+# if this variable is being evaluated on the left side of an assignment
+# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will
+# be created (e.g. bar) if necessary.
+#
+# Returns the result of evaluating the item against the root, having
+# performed any variable "magic". The value returned can then be used
+# as the root of the next _dotop() in a compound sequence. Returns
+# undef if the variable is undefined.
+#------------------------------------------------------------------------
+
+sub _dotop {
+ my ($self, $root, $item, $args, $lvalue) = @_;
+ my $rootref = ref $root;
+ my $atroot = ($root eq $self);
+ my ($value, @result);
+
+ $args ||= [ ];
+ $lvalue ||= 0;
+
+# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n"
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to access a private member, starting _ or .
+ return undef
+ unless defined($root) and defined($item) and $item !~ /^[\._]/;
+
+ if ($atroot || $rootref eq 'HASH') {
+ # if $root is a regular HASH or a Template::Stash kinda HASH (the
+ # *real* root of everything). We first lookup the named key
+ # in the hash, or create an empty hash in its place if undefined
+ # and the $lvalue flag is set. Otherwise, we check the HASH_OPS
+ # pseudo-methods table, calling the code if found, or return undef.
+
+ if (defined($value = $root->{ $item })) {
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ elsif ($lvalue) {
+ # we create an intermediate hash if this is an lvalue
+ return $root->{ $item } = { }; ## RETURN
+ }
+ # ugly hack: only allow import vmeth to be called on root stash
+ elsif (($value = $HASH_OPS->{ $item })
+ && ! $atroot || $item eq 'import') {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ( ref $item eq 'ARRAY' ) {
+ # hash slice
+ return [@$root{@$item}]; ## RETURN
+ }
+ }
+ elsif ($rootref eq 'ARRAY') {
+ # if root is an ARRAY then we check for a LIST_OPS pseudo-method
+ # (except for l-values for which it doesn't make any sense)
+ # or return the numerical index into the array, or undef
+
+ if (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ($item =~ /^-?\d+$/) {
+ $value = $root->[$item];
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ elsif ( ref $item eq 'ARRAY' ) {
+ # array slice
+ return [@$root[@$item]]; ## RETURN
+ }
+ }
+
+ # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL')
+ # doesn't appear to work with CGI, returning true for the first call
+ # and false for all subsequent calls.
+
+ elsif (ref($root) && UNIVERSAL::can($root, 'can')) {
+
+ # if $root is a blessed reference (i.e. inherits from the
+ # UNIVERSAL object base class) then we call the item as a method.
+ # If that fails then we try to fallback on HASH behaviour if
+ # possible.
+ eval { @result = $root->$item(@$args); };
+
+ if ($@) {
+ # temporary hack - required to propogate errors thrown
+ # by views; if $@ is a ref (e.g. Template::Exception
+ # object then we assume it's a real error that needs
+ # real throwing
+
+ die $@ if ref($@) || ($@ !~ /Can't locate object method/);
+
+ # failed to call object method, so try some fallbacks
+# patch from Stephen Howard
+# -- remove from here... --
+ if (UNIVERSAL::isa($root, 'HASH')
+ && defined($value = $root->{ $item })) {
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args);
+ }
+# -- and replace with this... --
+# if (UNIVERSAL::isa($root, 'HASH') ) {
+# if( defined($value = $root->{ $item })) {
+# return $value unless ref $value eq 'CODE'; ## RETURN
+# @result = &$value(@$args);
+# }
+# elsif ($value = $HASH_OPS->{ $item }) {
+# @result = &$value($root, @$args);
+# }
+# }
+# -- remove from here... --
+ elsif (UNIVERSAL::isa($root, 'ARRAY')
+ && ($value = $LIST_OPS->{ $item })) {
+ @result = &$value($root, @$args);
+ }
+# -- and replace with this... --
+# elsif (UNIVERSAL::isa($root, 'ARRAY') ) {
+# if( $value = $LIST_OPS->{ $item }) {
+# @result = &$value($root, @$args);
+# }
+# elsif( $item =~ /^-?\d+$/ ) {
+# $value = $root->[$item];
+# return $value unless ref $value eq 'CODE'; ## RETURN
+# @result = &$value(@$args); ## @result
+# }
+# elsif ( ref $item eq 'ARRAY' ) {
+# # array slice
+# return [@$root[@$item]]; ## RETURN
+# }
+# }
+# -- end --
+ elsif ($value = $SCALAR_OPS->{ $item }) {
+ @result = &$value($root, @$args);
+ }
+ elsif ($value = $LIST_OPS->{ $item }) {
+ @result = &$value([$root], @$args);
+ }
+ elsif ($self->{ _DEBUG }) {
+ @result = (undef, $@);
+ }
+ }
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+ # at this point, it doesn't look like we've got a reference to
+ # anything we know about, so we try the SCALAR_OPS pseudo-methods
+ # table (but not for l-values)
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ # last-ditch: can we promote a scalar to a one-element
+ # list and apply a LIST_OPS virtual method?
+ @result = &$value([$root], @$args);
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "don't know how to access [ $root ].$item\n"; ## DIE
+ }
+ else {
+ @result = ();
+ }
+
+ # fold multiple return items into a list unless first item is undef
+ if (defined $result[0]) {
+ return ## RETURN
+ scalar @result > 1 ? [ @result ] : $result[0];
+ }
+ elsif (defined $result[1]) {
+ die $result[1]; ## DIE
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "$item is undefined\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _assign($root, $item, \@args, $value, $default)
+#
+# Similar to _dotop() above, but assigns a value to the given variable
+# instead of simply returning it. The first three parameters are the
+# root item, the item and arguments, as per _dotop(), followed by the
+# value to which the variable should be set and an optional $default
+# flag. If set true, the variable will only be set if currently false
+# (undefined/zero)
+#------------------------------------------------------------------------
+
+sub _assign {
+ my ($self, $root, $item, $args, $value, $default) = @_;
+ my $rootref = ref $root;
+ my $atroot = ($root eq $self);
+ my $result;
+ $args ||= [ ];
+ $default ||= 0;
+
+# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n",
+# "value=$value, default=$default)\n")
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to update a private member, starting _ or .
+ return undef ## RETURN
+ unless $root and defined $item and $item !~ /^[\._]/;
+
+ if ($rootref eq 'HASH' || $atroot) {
+# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
+# # import hash entries into root hash
+# @$root{ keys %$value } = values %$value;
+# return ''; ## RETURN
+# }
+ # if the root is a hash we set the named key
+ return ($root->{ $item } = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) {
+ # or set a list item by index number
+ return ($root->[$item] = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) {
+ # try to call the item as a method of an object
+
+ return $root->$item(@$args, $value) ## RETURN
+ unless $default && $root->$item();
+
+# 2 issues:
+# - method call should be wrapped in eval { }
+# - fallback on hash methods if object method not found
+#
+# eval { $result = $root->$item(@$args, $value); };
+#
+# if ($@) {
+# die $@ if ref($@) || ($@ !~ /Can't locate object method/);
+#
+# # failed to call object method, so try some fallbacks
+# if (UNIVERSAL::isa($root, 'HASH') && exists $root->{ $item }) {
+# $result = ($root->{ $item } = $value)
+# unless $default && $root->{ $item };
+# }
+# }
+# return $result; ## RETURN
+
+ }
+ else {
+ die "don't know how to assign to [$root].[$item]\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object. The method calls itself recursively to dump sub-hashes.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ return "[Template::Stash] " . $self->_dump_frame(2);
+}
+
+sub _dump_frame {
+ my ($self, $indent) = @_;
+ $indent ||= 1;
+ my $buffer = ' ';
+ my $pad = $buffer x $indent;
+ my $text = "{\n";
+ local $" = ', ';
+
+ my ($key, $value);
+
+ return $text . "...excessive recursion, terminating\n"
+ if $indent > 32;
+
+ foreach $key (keys %$self) {
+ $value = $self->{ $key };
+ $value = '<undef>' unless defined $value;
+ next if $key =~ /^\./;
+ if (ref($value) eq 'ARRAY') {
+ $value = '[ ' . join(', ', map { defined $_ ? $_ : '<undef>' }
+ @$value) . ' ]';
+ }
+ elsif (ref $value eq 'HASH') {
+ $value = _dump_frame($value, $indent + 1);
+ }
+
+ $text .= sprintf("$pad%-16s => $value\n", $key);
+ }
+ $text .= $buffer x ($indent - 1) . '}';
+ return $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash - Magical storage for template variables
+
+=head1 SYNOPSIS
+
+ use Template::Stash;
+
+ my $stash = Template::Stash->new(\%vars);
+
+ # get variable values
+ $value = $stash->get($variable);
+ $value = $stash->get(\@compound);
+
+ # set variable value
+ $stash->set($variable, $value);
+ $stash->set(\@compound, $value);
+
+ # default variable value
+ $stash->set($variable, $value, 1);
+ $stash->set(\@compound, $value, 1);
+
+ # set variable values en masse
+ $stash->update(\%new_vars)
+
+ # methods for (de-)localising variables
+ $stash = $stash->clone(\%new_vars);
+ $stash = $stash->declone();
+
+=head1 DESCRIPTION
+
+The Template::Stash module defines an object class which is used to store
+variable values for the runtime use of the template processor. Variable
+values are stored internally in a hash reference (which itself is blessed
+to create the object) and are accessible via the get() and set() methods.
+
+Variables may reference hash arrays, lists, subroutines and objects
+as well as simple values. The stash automatically performs the right
+magic when dealing with variables, calling code or object methods,
+indexing into lists, hashes, etc.
+
+The stash has clone() and declone() methods which are used by the
+template processor to make temporary copies of the stash for
+localising changes made to variables.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%params)
+
+The new() constructor method creates and returns a reference to a new
+Template::Stash object.
+
+ my $stash = Template::Stash->new();
+
+A hash reference may be passed to provide variables and values which
+should be used to initialise the stash.
+
+ my $stash = Template::Stash->new({ var1 => 'value1',
+ var2 => 'value2' });
+
+=head2 get($variable)
+
+The get() method retrieves the variable named by the first parameter.
+
+ $value = $stash->get('var1');
+
+Dotted compound variables can be retrieved by specifying the variable
+elements by reference to a list. Each node in the variable occupies
+two entries in the list. The first gives the name of the variable
+element, the second is a reference to a list of arguments for that
+element, or 0 if none.
+
+ [% foo.bar(10).baz(20) %]
+
+ $stash->get([ 'foo', 0, 'bar', [ 10 ], 'baz', [ 20 ] ]);
+
+=head2 set($variable, $value, $default)
+
+The set() method sets the variable name in the first parameter to the
+value specified in the second.
+
+ $stash->set('var1', 'value1');
+
+If the third parameter evaluates to a true value, the variable is
+set only if it did not have a true value before.
+
+ $stash->set('var2', 'default_value', 1);
+
+Dotted compound variables may be specified as per get() above.
+
+ [% foo.bar = 30 %]
+
+ $stash->set([ 'foo', 0, 'bar', 0 ], 30);
+
+The magical variable 'IMPORT' can be specified whose corresponding
+value should be a hash reference. The contents of the hash array are
+copied (i.e. imported) into the current namespace.
+
+ # foo.bar = baz, foo.wiz = waz
+ $stash->set('foo', { 'bar' => 'baz', 'wiz' => 'waz' });
+
+ # import 'foo' into main namespace: foo = baz, wiz = waz
+ $stash->set('IMPORT', $stash->get('foo'));
+
+=head2 clone(\%params)
+
+The clone() method creates and returns a new Template::Stash object which
+represents a localised copy of the parent stash. Variables can be
+freely updated in the cloned stash and when declone() is called, the
+original stash is returned with all its members intact and in the
+same state as they were before clone() was called.
+
+For convenience, a hash of parameters may be passed into clone() which
+is used to update any simple variable (i.e. those that don't contain any
+namespace elements like 'foo' and 'bar' but not 'foo.bar') variables while
+cloning the stash. For adding and updating complex variables, the set()
+method should be used after calling clone(). This will correctly resolve
+and/or create any necessary namespace hashes.
+
+A cloned stash maintains a reference to the stash that it was copied
+from in its '_PARENT' member.
+
+=head2 declone()
+
+The declone() method returns the '_PARENT' reference and can be used to
+restore the state of a stash as described above.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.85, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Stash/Context.pm b/lib/Template/Stash/Context.pm
new file mode 100644
index 0000000..975dc79
--- /dev/null
+++ b/lib/Template/Stash/Context.pm
@@ -0,0 +1,791 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash::Context
+#
+# DESCRIPTION
+# This is an alternate stash object which includes a patch from
+# Craig Barratt to implement various new virtual methods to allow
+# dotted template variable to denote if object methods and subroutines
+# should be called in scalar or list context. It adds a little overhead
+# to each stash call and I'm a little wary of doing that. So for now,
+# it's implemented as a separate stash module which will allow us to
+# test it out, benchmark it and switch it in or out as we require.
+#
+# This is what Craig has to say about it:
+#
+# Here's a better set of features for the core. Attached is a new version
+# of Stash.pm (based on TT2.02) that:
+#
+# - supports the special op "scalar" that forces scalar context on
+# function calls, eg:
+#
+# cgi.param("foo").scalar
+#
+# calls cgi.param("foo") in scalar context (unlike my wimpy
+# scalar op from last night). Array context is the default.
+#
+# With non-function operands, scalar behaves like the perl
+# version (eg: no-op for scalar, size for arrays, etc).
+#
+# - supports the special op "ref" that behaves like the perl ref.
+# If applied to a function the function is not called. Eg:
+#
+# cgi.param("foo").ref
+#
+# does *not* call cgi.param and evaluates to "CODE". Similarly,
+# HASH.ref, ARRAY.ref return what you expect.
+#
+# - adds a new scalar and list op called "array" that is a no-op for
+# arrays and promotes scalars to one-element arrays.
+#
+# - allows scalar ops to be applied to arrays and hashes in place,
+# eg: ARRAY.repeat(3) repeats each element in place.
+#
+# - allows list ops to be applied to scalars by promoting the scalars
+# to one-element arrays (like an implicit "array"). So you can
+# do things like SCALAR.size, SCALAR.join and get a useful result.
+#
+# This also means you can now use x.0 to safely get the first element
+# whether x is an array or scalar.
+#
+# The new Stash.pm passes the TT2.02 test suite. But I haven't tested the
+# new features very much. One nagging implementation problem is that the
+# "scalar" and "ref" ops have higher precedence than user variable names.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Context.pm,v 1.58 2004/01/13 16:21:52 abw Exp $
+#
+#============================================================================
+
+package Template::Stash::Context;
+
+require 5.004;
+
+use strict;
+use Template::Stash;
+use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.58 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# -- PACKAGE VARIABLES AND SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# copy virtual methods from those in the regular Template::Stash
+#------------------------------------------------------------------------
+
+$ROOT_OPS = {
+ %$Template::Stash::ROOT_OPS,
+ defined $ROOT_OPS ? %$ROOT_OPS : (),
+};
+
+$SCALAR_OPS = {
+ %$Template::Stash::SCALAR_OPS,
+ 'array' => sub { return [$_[0]] },
+ defined $SCALAR_OPS ? %$SCALAR_OPS : (),
+};
+
+$LIST_OPS = {
+ %$Template::Stash::LIST_OPS,
+ 'array' => sub { return $_[0] },
+ defined $LIST_OPS ? %$LIST_OPS : (),
+};
+
+$HASH_OPS = {
+ %$Template::Stash::HASH_OPS,
+ defined $HASH_OPS ? %$HASH_OPS : (),
+};
+
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# Constructor method which creates a new Template::Stash object.
+# An optional hash reference may be passed containing variable
+# definitions that will be used to initialise the stash.
+#
+# Returns a reference to a newly created Template::Stash.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ };
+
+ my $self = {
+ global => { },
+ %$params,
+ %$ROOT_OPS,
+ '_PARENT' => undef,
+ };
+
+ bless $self, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# clone(\%params)
+#
+# Creates a copy of the current stash object to effect localisation
+# of variables. The new stash is blessed into the same class as the
+# parent (which may be a derived class) and has a '_PARENT' member added
+# which contains a reference to the parent stash that created it
+# ($self). This member is used in a successive declone() method call to
+# return the reference to the parent.
+#
+# A parameter may be provided which should reference a hash of
+# variable/values which should be defined in the new stash. The
+# update() method is called to define these new variables in the cloned
+# stash.
+#
+# Returns a reference to a cloned Template::Stash.
+#------------------------------------------------------------------------
+
+sub clone {
+ my ($self, $params) = @_;
+ $params ||= { };
+
+ # look out for magical 'import' argument which imports another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ delete $params->{ import };
+ }
+ else {
+ undef $import;
+ }
+
+ my $clone = bless {
+ %$self, # copy all parent members
+ %$params, # copy all new data
+ '_PARENT' => $self, # link to parent
+ }, ref $self;
+
+ # perform hash import if defined
+ &{ $HASH_OPS->{ import }}($clone, $import)
+ if defined $import;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# declone($export)
+#
+# Returns a reference to the PARENT stash. When called in the following
+# manner:
+# $stash = $stash->declone();
+# the reference count on the current stash will drop to 0 and be "freed"
+# and the caller will be left with a reference to the parent. This
+# contains the state of the stash before it was cloned.
+#------------------------------------------------------------------------
+
+sub declone {
+ my $self = shift;
+ $self->{ _PARENT } || $self;
+}
+
+
+#------------------------------------------------------------------------
+# get($ident)
+#
+# Returns the value for an variable stored in the stash. The variable
+# may be specified as a simple string, e.g. 'foo', or as an array
+# reference representing compound variables. In the latter case, each
+# pair of successive elements in the list represent a node in the
+# compound variable. The first is the variable name, the second a
+# list reference of arguments or 0 if undefined. So, the compound
+# variable [% foo.bar('foo').baz %] would be represented as the list
+# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the
+# identifier or an empty string if undefined. Errors are thrown via
+# die().
+#------------------------------------------------------------------------
+
+sub get {
+ my ($self, $ident, $args) = @_;
+ my ($root, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) {
+ my $size = $#$ident;
+
+ # if $ident is a list reference, then we evaluate each item in the
+ # identifier against the previous result, using the root stash
+ # ($self) as the first implicit 'result'...
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ if ( $i + 2 <= $size && ($ident->[$i+2] eq "scalar"
+ || $ident->[$i+2] eq "ref") ) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 0,
+ $ident->[$i+2]);
+ $i += 2;
+ } else {
+ $result = $self->_dotop($root, @$ident[$i, $i+1]);
+ }
+ last unless defined $result;
+ $root = $result;
+ }
+ }
+ else {
+ $result = $self->_dotop($root, $ident, $args);
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# set($ident, $value, $default)
+#
+# Updates the value for a variable in the stash. The first parameter
+# should be the variable name or array, as per get(). The second
+# parameter should be the intended value for the variable. The third,
+# optional parameter is a flag which may be set to indicate 'default'
+# mode. When set true, the variable will only be updated if it is
+# currently undefined or has a false value. The magical 'IMPORT'
+# variable identifier may be used to indicate that $value is a hash
+# reference whose values should be imported. Returns the value set,
+# or an empty string if not set (e.g. default mode). In the case of
+# IMPORT, returns the number of items imported from the hash.
+#------------------------------------------------------------------------
+
+sub set {
+ my ($self, $ident, $value, $default) = @_;
+ my ($root, $result, $error);
+
+ $root = $self;
+
+ ELEMENT: {
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) }
+ split(/\./, $ident) ])) {
+
+ # a compound identifier may contain multiple elements (e.g.
+ # foo.bar.baz) and we must first resolve all but the last,
+ # using _dotop() with the $lvalue flag set which will create
+ # intermediate hashes if necessary...
+ my $size = $#$ident;
+ foreach (my $i = 0; $i < $size - 2; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 1);
+ last ELEMENT unless defined $result;
+ $root = $result;
+ }
+
+ # then we call _assign() to assign the value to the last element
+ $result = $self->_assign($root, @$ident[$size-1, $size],
+ $value, $default);
+ }
+ else {
+ $result = $self->_assign($root, $ident, 0, $value, $default);
+ }
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# getref($ident)
+#
+# Returns a "reference" to a particular item. This is represented as a
+# closure which will return the actual stash item when called.
+# WARNING: still experimental!
+#------------------------------------------------------------------------
+
+sub getref {
+ my ($self, $ident, $args) = @_;
+ my ($root, $item, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY') {
+ my $size = $#$ident;
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ ($item, $args) = @$ident[$i, $i + 1];
+ last if $i >= $size - 2; # don't evaluate last node
+ last unless defined
+ ($root = $self->_dotop($root, $item, $args));
+ }
+ }
+ else {
+ $item = $ident;
+ }
+
+ if (defined $root) {
+ return sub { my @args = (@{$args||[]}, @_);
+ $self->_dotop($root, $item, \@args);
+ }
+ }
+ else {
+ return sub { '' };
+ }
+}
+
+
+
+
+#------------------------------------------------------------------------
+# update(\%params)
+#
+# Update multiple variables en masse. No magic is performed. Simple
+# variable names only.
+#------------------------------------------------------------------------
+
+sub update {
+ my ($self, $params) = @_;
+
+ # look out for magical 'import' argument to import another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ @$self{ keys %$import } = values %$import;
+ delete $params->{ import };
+ }
+
+ @$self{ keys %$params } = values %$params;
+}
+
+
+#========================================================================
+# ----- PRIVATE OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dotop($root, $item, \@args, $lvalue, $nextItem)
+#
+# This is the core 'dot' operation method which evaluates elements of
+# variables against their root. All variables have an implicit root
+# which is the stash object itself (a hash). Thus, a non-compound
+# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is
+# '(stash.)foo.bar'. The first parameter is a reference to the current
+# root, initially the stash itself. The second parameter contains the
+# name of the variable element, e.g. 'foo'. The third optional
+# parameter is a reference to a list of any parenthesised arguments
+# specified for the variable, which are passed to sub-routines, object
+# methods, etc. The final parameter is an optional flag to indicate
+# if this variable is being evaluated on the left side of an assignment
+# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will
+# be created (e.g. bar) if necessary.
+#
+# Returns the result of evaluating the item against the root, having
+# performed any variable "magic". The value returned can then be used
+# as the root of the next _dotop() in a compound sequence. Returns
+# undef if the variable is undefined.
+#------------------------------------------------------------------------
+
+sub _dotop {
+ my ($self, $root, $item, $args, $lvalue, $nextItem) = @_;
+ my $rootref = ref $root;
+ my ($value, @result, $ret, $retVal);
+ $nextItem ||= "";
+ my $scalarContext = 1 if ( $nextItem eq "scalar" );
+ my $returnRef = 1 if ( $nextItem eq "ref" );
+
+ $args ||= [ ];
+ $lvalue ||= 0;
+
+# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n"
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to access a private member, starting _ or .
+ return undef
+ unless defined($root) and defined($item) and $item !~ /^[\._]/;
+
+ if (ref(\$root) eq "SCALAR" && !$lvalue &&
+ (($value = $LIST_OPS->{ $item }) || $item =~ /^-?\d+$/) ) {
+ #
+ # Promote scalar to one element list, to be processed below.
+ #
+ $rootref = 'ARRAY';
+ $root = [$root];
+ }
+ if ($rootref eq __PACKAGE__ || $rootref eq 'HASH') {
+
+ # if $root is a regular HASH or a Template::Stash kinda HASH (the
+ # *real* root of everything). We first lookup the named key
+ # in the hash, or create an empty hash in its place if undefined
+ # and the $lvalue flag is set. Otherwise, we check the HASH_OPS
+ # pseudo-methods table, calling the code if found, or return undef.
+
+ if (defined($value = $root->{ $item })) {
+ ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+ $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif ($lvalue) {
+ # we create an intermediate hash if this is an lvalue
+ return $root->{ $item } = { }; ## RETURN
+ }
+ elsif ($value = $HASH_OPS->{ $item }) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (ref $item eq 'ARRAY') {
+ # hash slice
+ return [@$root{@$item}]; ## RETURN
+ }
+ elsif ($value = $SCALAR_OPS->{ $item }) {
+ #
+ # Apply scalar ops to every hash element, in place.
+ #
+ foreach my $key ( keys %$root ) {
+ $root->{$key} = &$value($root->{$key}, @$args);
+ }
+ }
+ }
+ elsif ($rootref eq 'ARRAY') {
+
+ # if root is an ARRAY then we check for a LIST_OPS pseudo-method
+ # (except for l-values for which it doesn't make any sense)
+ # or return the numerical index into the array, or undef
+
+ if (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+ #
+ # Apply scalar ops to every array element, in place.
+ #
+ for ( my $i = 0 ; $i < @$root ; $i++ ) {
+ $root->[$i] = &$value($root->[$i], @$args); ## @result
+ }
+ }
+ elsif ($item =~ /^-?\d+$/) {
+ $value = $root->[$item];
+ ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+ $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif (ref $item eq 'ARRAY' ) {
+ # array slice
+ return [@$root[@$item]]; ## RETURN
+ }
+ }
+
+ # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL')
+ # doesn't appear to work with CGI, returning true for the first call
+ # and false for all subsequent calls.
+
+ elsif (ref($root) && UNIVERSAL::can($root, 'can')) {
+
+ # if $root is a blessed reference (i.e. inherits from the
+ # UNIVERSAL object base class) then we call the item as a method.
+ # If that fails then we try to fallback on HASH behaviour if
+ # possible.
+ return ref $root->can($item) if ( $returnRef ); ## RETURN
+ eval {
+ @result = $scalarContext ? scalar $root->$item(@$args)
+ : $root->$item(@$args); ## @result
+ };
+
+ if ($@) {
+ # failed to call object method, so try some fallbacks
+ if (UNIVERSAL::isa($root, 'HASH')
+ && defined($value = $root->{ $item })) {
+ ($ret, $retVal, @result) = _dotop_return($value, $args,
+ $returnRef, $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif (UNIVERSAL::isa($root, 'ARRAY')
+ && ($value = $LIST_OPS->{ $item })) {
+ @result = &$value($root, @$args);
+ }
+ else {
+ @result = (undef, $@);
+ }
+ }
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+
+ # at this point, it doesn't look like we've got a reference to
+ # anything we know about, so we try the SCALAR_OPS pseudo-methods
+ # table (but not for l-values)
+
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "don't know how to access [ $root ].$item\n"; ## DIE
+ }
+ else {
+ @result = ();
+ }
+
+ # fold multiple return items into a list unless first item is undef
+ if (defined $result[0]) {
+ return ref(@result > 1 ? [ @result ] : $result[0])
+ if ( $returnRef ); ## RETURN
+ if ( $scalarContext ) {
+ return scalar @result if ( @result > 1 ); ## RETURN
+ return scalar(@{$result[0]}) if ( ref $result[0] eq "ARRAY" );
+ return scalar(%{$result[0]}) if ( ref $result[0] eq "HASH" );
+ return $result[0]; ## RETURN
+ } else {
+ return @result > 1 ? [ @result ] : $result[0]; ## RETURN
+ }
+ }
+ elsif (defined $result[1]) {
+ die $result[1]; ## DIE
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "$item is undefined\n"; ## DIE
+ }
+
+ return undef;
+}
+
+#------------------------------------------------------------------------
+# ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+# $scalarContext);
+#
+# Handle the various return processing for _dotop
+#------------------------------------------------------------------------
+sub _dotop_return
+{
+ my($value, $args, $returnRef, $scalarContext) = @_;
+ my(@result);
+
+ return (1, ref $value) if ( $returnRef ); ## RETURN
+ if ( $scalarContext ) {
+ return (1, scalar(@$value)) if ref $value eq 'ARRAY'; ## RETURN
+ return (1, scalar(%$value)) if ref $value eq 'HASH'; ## RETURN
+ return (1, scalar($value)) unless ref $value eq 'CODE'; ## RETURN;
+ @result = scalar &$value(@$args) ## @result;
+ } else {
+ return (1, $value) unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ return (0, undef, @result);
+}
+
+
+#------------------------------------------------------------------------
+# _assign($root, $item, \@args, $value, $default)
+#
+# Similar to _dotop() above, but assigns a value to the given variable
+# instead of simply returning it. The first three parameters are the
+# root item, the item and arguments, as per _dotop(), followed by the
+# value to which the variable should be set and an optional $default
+# flag. If set true, the variable will only be set if currently false
+# (undefined/zero)
+#------------------------------------------------------------------------
+
+sub _assign {
+ my ($self, $root, $item, $args, $value, $default) = @_;
+ my $rootref = ref $root;
+ my $result;
+ $args ||= [ ];
+ $default ||= 0;
+
+# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n",
+# "value=$value, default=$default)\n")
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to update a private member, starting _ or .
+ return undef ## RETURN
+ unless $root and defined $item and $item !~ /^[\._]/;
+
+ if ($rootref eq 'HASH' || $rootref eq __PACKAGE__) {
+# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
+# # import hash entries into root hash
+# @$root{ keys %$value } = values %$value;
+# return ''; ## RETURN
+# }
+ # if the root is a hash we set the named key
+ return ($root->{ $item } = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) {
+ # or set a list item by index number
+ return ($root->[$item] = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) {
+ # try to call the item as a method of an object
+ return $root->$item(@$args, $value); ## RETURN
+ }
+ else {
+ die "don't know how to assign to [$root].[$item]\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object. The method calls itself recursively to dump sub-hashes.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $indent = shift || 1;
+ my $buffer = ' ';
+ my $pad = $buffer x $indent;
+ my $text = '';
+ local $" = ', ';
+
+ my ($key, $value);
+
+
+ return $text . "...excessive recursion, terminating\n"
+ if $indent > 32;
+
+ foreach $key (keys %$self) {
+
+ $value = $self->{ $key };
+ $value = '<undef>' unless defined $value;
+
+ if (ref($value) eq 'ARRAY') {
+ $value = "$value [@$value]";
+ }
+ $text .= sprintf("$pad%-8s => $value\n", $key);
+ next if $key =~ /^\./;
+ if (UNIVERSAL::isa($value, 'HASH')) {
+ $text .= _dump($value, $indent + 1);
+ }
+ }
+ $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash::Context - Experimetal stash allowing list/scalar context definition
+
+=head1 SYNOPSIS
+
+ use Template;
+ use Template::Stash::Context;
+
+ my $stash = Template::Stash::Context->new(\%vars);
+ my $tt2 = Template->new({ STASH => $stash });
+
+=head1 DESCRIPTION
+
+This is an alternate stash object which includes a patch from
+Craig Barratt to implement various new virtual methods to allow
+dotted template variable to denote if object methods and subroutines
+should be called in scalar or list context. It adds a little overhead
+to each stash call and I'm a little wary of applying that to the core
+default stash without investigating the effects first. So for now,
+it's implemented as a separate stash module which will allow us to
+test it out, benchmark it and switch it in or out as we require.
+
+This is what Craig has to say about it:
+
+Here's a better set of features for the core. Attached is a new version
+of Stash.pm (based on TT2.02) that:
+
+* supports the special op "scalar" that forces scalar context on
+function calls, eg:
+
+ cgi.param("foo").scalar
+
+calls cgi.param("foo") in scalar context (unlike my wimpy
+scalar op from last night). Array context is the default.
+
+With non-function operands, scalar behaves like the perl
+version (eg: no-op for scalar, size for arrays, etc).
+
+* supports the special op "ref" that behaves like the perl ref.
+If applied to a function the function is not called. Eg:
+
+ cgi.param("foo").ref
+
+does *not* call cgi.param and evaluates to "CODE". Similarly,
+HASH.ref, ARRAY.ref return what you expect.
+
+* adds a new scalar and list op called "array" that is a no-op for
+arrays and promotes scalars to one-element arrays.
+
+* allows scalar ops to be applied to arrays and hashes in place,
+eg: ARRAY.repeat(3) repeats each element in place.
+
+* allows list ops to be applied to scalars by promoting the scalars
+to one-element arrays (like an implicit "array"). So you can
+do things like SCALAR.size, SCALAR.join and get a useful result.
+
+This also means you can now use x.0 to safely get the first element
+whether x is an array or scalar.
+
+The new Stash.pm passes the TT2.02 test suite. But I haven't tested the
+new features very much. One nagging implementation problem is that the
+"scalar" and "ref" ops have higher precedence than user variable names.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.58, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Stash|Template::Stash>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Stash/XS.pm b/lib/Template/Stash/XS.pm
new file mode 100644
index 0000000..2cfb543
--- /dev/null
+++ b/lib/Template/Stash/XS.pm
@@ -0,0 +1,176 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash::XS
+#
+# DESCRIPTION
+#
+# Perl bootstrap for XS module. Inherits methods from
+# Template::Stash when not implemented in the XS module.
+#
+#========================================================================
+
+package Template::Stash::XS;
+
+use Template;
+use Template::Stash;
+
+BEGIN {
+ require DynaLoader;
+ @Template::Stash::XS::ISA = qw( DynaLoader Template::Stash );
+
+ eval {
+ bootstrap Template::Stash::XS $Template::VERSION;
+ };
+ if ($@) {
+ die "Couldn't load Template::Stash::XS $Template::VERSION:\n\n$@\n";
+ }
+}
+
+
+sub DESTROY {
+ # no op
+ 1;
+}
+
+
+# catch missing method calls here so perl doesn't barf
+# trying to load *.al files
+sub AUTOLOAD {
+ my ($self, @args) = @_;
+ my @c = caller(0);
+ my $auto = $AUTOLOAD;
+
+ $auto =~ s/.*:://;
+ $self =~ s/=.*//;
+
+ die "Can't locate object method \"$auto\"" .
+ " via package \"$self\" at $c[1] line $c[2]\n";
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash::XS - Experimetal high-speed stash written in XS
+
+=head1 SYNOPSIS
+
+ use Template;
+ use Template::Stash::XS;
+
+ my $stash = Template::Stash::XS->new(\%vars);
+ my $tt2 = Template->new({ STASH => $stash });
+
+=head1 DESCRIPTION
+
+This module loads the XS version of Template::Stash::XS. It should
+behave very much like the old one, but run about twice as fast.
+See the synopsis above for usage information.
+
+Only a few methods (such as get and set) have been implemented in XS.
+The others are inherited from Template::Stash.
+
+=head1 NOTE
+
+To always use the XS version of Stash, modify the Template/Config.pm
+module near line 45:
+
+ $STASH = 'Template::Stash::XS';
+
+If you make this change, then there is no need to explicitly create
+an instance of Template::Stash::XS as seen in the SYNOPSIS above. Just
+use Template as normal.
+
+Alternatively, in your code add this line before creating a Template
+object:
+
+ $Template::Config::STASH = 'Template::Stash::XS';
+
+To use the original, pure-perl version restore this line in
+Template/Config.pm:
+
+ $STASH = 'Template::Stash';
+
+Or in your code:
+
+ $Template::Config::STASH = 'Template::Stash';
+
+You can elect to have this performed once for you at installation
+time by answering 'y' or 'n' to the question that asks if you want
+to make the XS Stash the default.
+
+=head1 BUGS
+
+Please report bugs to the Template Toolkit mailing list
+templates@template-toolkit.org
+
+As of version 2.05 of the Template Toolkit, use of the XS Stash is
+known to have 2 potentially troublesome side effects. The first
+problem is that accesses to tied hashes (e.g. Apache::Session) may not
+work as expected. This should be fixed in an imminent release. If
+you are using tied hashes then it is suggested that you use the
+regular Stash by default, or write a thin wrapper around your tied
+hashes to enable the XS Stash to access items via regular method
+calls.
+
+The second potential problem is that enabling the XS Stash causes all
+the Template Toolkit modules to be installed in an architecture
+dependant library, e.g. in
+
+ /usr/lib/perl5/site_perl/5.6.0/i386-linux/Template
+
+instead of
+
+ /usr/lib/perl5/site_perl/5.6.0/Template
+
+At the time of writing, we're not sure why this is happening but it's
+likely that this is either a bug or intentional feature in the Perl
+ExtUtils::MakeMaker module. As far as I know, Perl always checks the
+architecture dependant directories before the architecture independant
+ones. Therefore, a newer version of the Template Toolkit installed
+with the XS Stash enabled should be used by Perl in preference to any
+existing version using the regular stash. However, if you install a
+future version of the Template Toolkit with the XS Stash disabled, you
+may find that Perl continues to use the older version with XS Stash
+enabled in preference.
+
+=head1 AUTHORS
+
+Andy Wardley E<lt>abw@tt2.orgE<gt>
+
+Doug Steinwand E<lt>dsteinwand@citysearch.comE<gt>
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=head1 SEE ALSO
+
+L<Template::Stash|Template::Stash>
+
diff --git a/lib/Template/Test.pm b/lib/Template/Test.pm
new file mode 100644
index 0000000..9413d68
--- /dev/null
+++ b/lib/Template/Test.pm
@@ -0,0 +1,711 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Test
+#
+# DESCRIPTION
+# Module defining a test harness which processes template input and
+# then compares the output against pre-define expected output.
+# Generates test output compatible with Test::Harness. This was
+# originally the t/texpect.pl script.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Test.pm,v 2.69 2004/01/13 16:19:16 abw Exp $
+#
+#============================================================================
+
+package Template::Test;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
+ $VERSION $DEBUG $EXTRA $PRESERVE $REASON $NO_FLUSH
+ $loaded %callsign);
+use Template qw( :template );
+use Exporter;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0;
+@ISA = qw( Exporter );
+@EXPORT = qw( ntests ok is match flush skip_all test_expect callsign banner );
+@EXPORT_OK = ( 'assert' );
+%EXPORT_TAGS = ( all => [ @EXPORT_OK, @EXPORT ] );
+$| = 1;
+
+$REASON = 'not applicable on this platform';
+$NO_FLUSH = 0;
+$EXTRA = 0; # any extra tests to come after test_expect()
+$PRESERVE = 0 # don't mangle newlines in output/expect
+ unless defined $PRESERVE;
+
+# always set binmode on Win32 machines so that any output generated
+# is true to what we expect
+$Template::BINMODE = ($^O eq 'MSWin32') ? 1 : 0;
+
+my @results = ();
+my ($ntests, $ok_count);
+*is = \&match;
+
+END {
+ # ensure flush() is called to print any cached results
+ flush();
+}
+
+
+#------------------------------------------------------------------------
+# ntests($n)
+#
+# Declare how many (more) tests are expected to come. If ok() is called
+# before ntests() then the results are cached instead of being printed
+# to STDOUT. When ntests() is called, the total number of tests
+# (including any cached) is known and the "1..$ntests" line can be
+# printed along with the cached results. After that, calls to ok()
+# generated printed output immediately.
+#------------------------------------------------------------------------
+
+sub ntests {
+ $ntests = shift;
+ # add any pre-declared extra tests, or pre-stored test @results, to
+ # the grand total of tests
+ $ntests += $EXTRA + scalar @results;
+ $ok_count = 1;
+ print $ntests ? "1..$ntests\n" : "1..$ntests # skipped: $REASON\n";
+ # flush cached results
+ foreach my $pre_test (@results) {
+ ok(@$pre_test);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# ok($truth, $msg)
+#
+# Tests the value passed for truth and generates an "ok $n" or "not ok $n"
+# line accordingly. If ntests() hasn't been called then we cached
+# results for later, instead.
+#------------------------------------------------------------------------
+
+sub ok {
+ my ($ok, $msg) = @_;
+
+ # cache results if ntests() not yet called
+ unless ($ok_count) {
+ push(@results, [ $ok, $msg ]);
+ return $ok;
+ }
+
+ $msg = defined $msg ? " - $msg" : '';
+ if ($ok) {
+ print "ok ", $ok_count++, "$msg\n";
+ }
+ else {
+ print STDERR "FAILED $ok_count: $msg\n" if defined $msg;
+ print "not ok ", $ok_count++, "$msg\n";
+ }
+}
+
+
+
+#------------------------------------------------------------------------
+# assert($truth, $error)
+#
+# Test value for truth, die if false.
+#------------------------------------------------------------------------
+
+sub assert {
+ my ($ok, $err) = @_;
+ return ok(1) if $ok;
+
+ # failed
+ my ($pkg, $file, $line) = caller();
+ $err ||= "assert failed";
+ $err .= " at $file line $line\n";
+ ok(0);
+ die $err;
+}
+
+#------------------------------------------------------------------------
+# match( $result, $expect )
+#------------------------------------------------------------------------
+
+sub match {
+ my ($result, $expect, $msg) = @_;
+ my $count = $ok_count ? $ok_count : scalar @results + 1;
+
+ # force stringification of $result to avoid 'no eq method' overload errors
+ $result = "$result" if ref $result;
+
+ if ($result eq $expect) {
+ return ok(1, $msg);
+ }
+ else {
+ print STDERR "FAILED $count:\n expect: [$expect]\n result: [$result]\n";
+ return ok(0, $msg);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# flush()
+#
+# Flush any tests results.
+#------------------------------------------------------------------------
+
+sub flush {
+ ntests(0)
+ unless $ok_count || $NO_FLUSH;
+}
+
+
+#------------------------------------------------------------------------
+# skip_all($reason)
+#
+# Skip all tests, setting $REASON to contain any message passed. Calls
+# exit(0) which triggers flush() which generates a "1..0 # $REASON"
+# string to keep to test harness happy.
+#------------------------------------------------------------------------
+
+sub skip_all {
+ $REASON = join('', @_);
+ exit(0);
+}
+
+
+#------------------------------------------------------------------------
+# test_expect($input, $template, \%replace)
+#
+# This is the main testing sub-routine. The $input parameter should be a
+# text string or a filehandle reference (e.g. GLOB or IO::Handle) from
+# which the input text can be read. The input should contain a number
+# of tests which are split up and processed individually, comparing the
+# generated output against the expected output. Tests should be defined
+# as follows:
+#
+# -- test --
+# test input
+# -- expect --
+# expected output
+#
+# -- test --
+# etc...
+#
+# The number of tests is determined and ntests() is called to generate
+# the "0..$n" line compatible with Test::Harness. Each test input is
+# then processed by the Template object passed as the second parameter,
+# $template. This may also be a hash reference containing configuration
+# which are used to instantiate a Template object, or may be left
+# undefined in which case a default Template object will be instantiated.
+# The third parameter, also optional, may be a reference to a hash array
+# defining template variables. This is passed to the template process()
+# method.
+#------------------------------------------------------------------------
+
+sub test_expect {
+ my ($src, $tproc, $params) = @_;
+ my ($input, @tests);
+ my ($output, $expect, $match);
+ my $count = 0;
+ my $ttprocs;
+
+ # read input text
+ eval {
+ local $/ = undef;
+ $input = ref $src ? <$src> : $src;
+ };
+ if ($@) {
+ ntests(1); ok(0);
+ warn "Cannot read input text from $src\n";
+ return undef;
+ }
+
+ # remove any comment lines
+ $input =~ s/^#.*?\n//gm;
+
+ # remove anything before '-- start --' and/or after '-- stop --'
+ $input = $' if $input =~ /\s*--\s*start\s*--\s*/;
+ $input = $` if $input =~ /\s*--\s*stop\s*--\s*/;
+
+ @tests = split(/^\s*--\s*test\s*--\s*\n/im, $input);
+
+ # if the first line of the file was '--test--' (optional) then the
+ # first test will be empty and can be discarded
+ shift(@tests) if $tests[0] =~ /^\s*$/;
+
+ ntests(3 + scalar(@tests) * 2);
+
+ # first test is that Template loaded OK, which it did
+ ok(1, 'running test_expect()');
+
+ # optional second param may contain a Template reference or a HASH ref
+ # of constructor options, or may be undefined
+ if (ref($tproc) eq 'HASH') {
+ # create Template object using hash of config items
+ $tproc = Template->new($tproc)
+ || die Template->error(), "\n";
+ }
+ elsif (ref($tproc) eq 'ARRAY') {
+ # list of [ name => $tproc, name => $tproc ], use first $tproc
+ $ttprocs = { @$tproc };
+ $tproc = $tproc->[1];
+ }
+ elsif (! ref $tproc) {
+ $tproc = Template->new()
+ || die Template->error(), "\n";
+ }
+ # otherwise, we assume it's a Template reference
+
+ # test: template processor created OK
+ ok($tproc, 'template processor is engaged');
+
+ # third test is that the input read ok, which it did
+ ok(1, 'input read and split into ' . scalar @tests . ' tests');
+
+ # the remaining tests are defined in @tests...
+ foreach $input (@tests) {
+ $count++;
+ my $name = '';
+
+ if ($input =~ s/^\s*-- name:? (.*?) --\s*\n//im) {
+ $name = $1;
+ }
+ else {
+ $name = "template text $count";
+ }
+
+ # split input by a line like "-- expect --"
+ ($input, $expect) =
+ split(/^\s*--\s*expect\s*--\s*\n/im, $input);
+ $expect = ''
+ unless defined $expect;
+
+ $output = '';
+
+ # input text may be prefixed with "-- use name --" to indicate a
+ # Template object in the $ttproc hash which we should use
+ if ($input =~ s/^\s*--\s*use\s+(\S+)\s*--\s*\n//im) {
+ my $ttname = $1;
+ my $ttlookup;
+ if ($ttlookup = $ttprocs->{ $ttname }) {
+ $tproc = $ttlookup;
+ }
+ else {
+ warn "no such template object to use: $ttname\n";
+ }
+ }
+
+ # process input text
+ $tproc->process(\$input, $params, \$output) || do {
+ warn "Template process failed: ", $tproc->error(), "\n";
+ # report failure and automatically fail the expect match
+ ok(0, "$name process FAILED: " . subtext($input));
+ ok(0, '(obviously did not match expected)');
+ next;
+ };
+
+ # processed OK
+ ok(1, "$name processed OK: " . subtext($input));
+
+ # another hack: if the '-- expect --' section starts with
+ # '-- process --' then we process the expected output
+ # before comparing it with the generated output. This is
+ # slightly twisted but it makes it possible to run tests
+ # where the expected output isn't static. See t/date.t for
+ # an example.
+
+ if ($expect =~ s/^\s*--+\s*process\s*--+\s*\n//im) {
+ my $out;
+ $tproc->process(\$expect, $params, \$out) || do {
+ warn("Template process failed (expect): ",
+ $tproc->error(), "\n");
+ # report failure and automatically fail the expect match
+ ok(0, "failed to process expected output ["
+ . subtext($expect) . ']');
+ next;
+ };
+ $expect = $out;
+ };
+
+ # strip any trailing blank lines from expected and real output
+ foreach ($expect, $output) {
+ s/\n*\Z//mg;
+ }
+
+ $match = ($expect eq $output) ? 1 : 0;
+ if (! $match || $DEBUG) {
+ print "MATCH FAILED\n"
+ unless $match;
+
+ my ($copyi, $copye, $copyo) = ($input, $expect, $output);
+ unless ($PRESERVE) {
+ foreach ($copyi, $copye, $copyo) {
+ s/\n/\\n/g;
+ }
+ }
+ printf(" input: [%s]\nexpect: [%s]\noutput: [%s]\n",
+ $copyi, $copye, $copyo);
+ }
+
+ ok($match, $match ? "$name matched expected" : "$name did not match expected");
+ };
+}
+
+#------------------------------------------------------------------------
+# callsign()
+#
+# Returns a hash array mapping lower a..z to their phonetic alphabet
+# equivalent.
+#------------------------------------------------------------------------
+
+sub callsign {
+ my %callsign;
+ @callsign{ 'a'..'z' } = qw(
+ alpha bravo charlie delta echo foxtrot golf hotel india
+ juliet kilo lima mike november oscar papa quebec romeo
+ sierra tango umbrella victor whisky x-ray yankee zulu );
+ return \%callsign;
+}
+
+
+#------------------------------------------------------------------------
+# banner($text)
+#
+# Prints a banner with the specified text if $DEBUG is set.
+#------------------------------------------------------------------------
+
+sub banner {
+ return unless $DEBUG;
+ my $text = join('', @_);
+ my $count = $ok_count ? $ok_count - 1 : scalar @results;
+ print "-" x 72, "\n$text ($count tests completed)\n", "-" x 72, "\n";
+}
+
+
+sub subtext {
+ my $text = shift;
+ $text =~ s/\s*$//sg;
+ $text = substr($text, 0, 32) . '...' if length $text > 32;
+ $text =~ s/\n/\\n/g;
+ return $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Test - Module for automating TT2 test scripts
+
+=head1 SYNOPSIS
+
+ use Template::Test;
+
+ $Template::Test::DEBUG = 0; # set this true to see each test running
+ $Template::Test::EXTRA = 2; # 2 extra tests follow test_expect()...
+
+ # ok() can be called any number of times before test_expect
+ ok( $true_or_false )
+
+ # test_expect() splits $input into individual tests, processes each
+ # and compares generated output against expected output
+ test_expect($input, $template, \%replace );
+
+ # $input is text or filehandle (e.g. DATA section after __END__)
+ test_expect( $text );
+ test_expect( \*DATA );
+
+ # $template is a Template object or configuration hash
+ my $template_cfg = { ... };
+ test_expect( $input, $template_cfg );
+ my $template_obj = Template->new($template_cfg);
+ test_expect( $input, $template_obj );
+
+ # $replace is a hash reference of template variables
+ my $replace = {
+ a => 'alpha',
+ b => 'bravo'
+ };
+ test_expect( $input, $template, $replace );
+
+ # ok() called after test_expect should be declared in $EXTRA (2)
+ ok( $true_or_false )
+ ok( $true_or_false )
+
+=head1 DESCRIPTION
+
+The Template::Test module defines the test_expect() and other related
+subroutines which can be used to automate test scripts for the
+Template Toolkit. See the numerous tests in the 't' sub-directory of
+the distribution for examples of use.
+
+The test_expect() subroutine splits an input document into a number
+of separate tests, processes each one using the Template Toolkit and
+then compares the generated output against an expected output, also
+specified in the input document. It generates the familiar "ok/not
+ok" output compatible with Test::Harness.
+
+The test input should be specified as a text string or a reference to
+a filehandle (e.g. GLOB or IO::Handle) from which it can be read. In
+particular, this allows the test input to be placed after the __END__
+marker and read via the DATA filehandle.
+
+ use Template::Test;
+
+ test_expect(\*DATA);
+
+ __END__
+ # this is the first test (this is a comment)
+ -- test --
+ blah blah blah [% foo %]
+ -- expect --
+ blah blah blah value_of_foo
+
+ # here's the second test (no surprise, so is this)
+ -- test --
+ more blah blah [% bar %]
+ -- expect --
+ more blah blah value_of_bar
+
+Blank lines between test sections are generally ignored. Any line starting
+with '#' is treated as a comment and is ignored.
+
+The second and third parameters to test_expect() are optional. The second
+may be either a reference to a Template object which should be used to
+process the template fragments, or a reference to a hash array containing
+configuration values which should be used to instantiate a new Template
+object.
+
+ # pass reference to config hash
+ my $config = {
+ INCLUDE_PATH => '/here/there:/every/where',
+ POST_CHOMP => 1,
+ };
+ test_expect(\*DATA, $config);
+
+ # or create Template object explicitly
+ my $template = Template->new($config);
+ test_expect(\*DATA, $template);
+
+
+The third parameter may be used to reference a hash array of template
+variable which should be defined when processing the tests. This is
+passed to the Template process() method.
+
+ my $replace = {
+ a => 'alpha',
+ b => 'bravo',
+ };
+
+ test_expect(\*DATA, $config, $replace);
+
+The second parameter may be left undefined to specify a default Template
+configuration.
+
+ test_expect(\*DATA, undef, $replace);
+
+For testing the output of different Template configurations, a
+reference to a list of named Template objects also may be passed as
+the second parameter.
+
+ my $tt1 = Template->new({ ... });
+ my $tt2 = Template->new({ ... });
+ my @tts = [ one => $tt1, two => $tt1 ];
+
+The first object in the list is used by default. Other objects may be
+switched in with the '-- use $name --' marker. This should immediately
+follow a '-- test --' line. That object will then be used for the rest
+of the test, or until a different object is selected.
+
+ -- test --
+ -- use one --
+ [% blah %]
+ -- expect --
+ blah, blah
+
+ -- test --
+ still using one...
+ -- expect --
+ ...
+
+ -- test --
+ -- use two --
+ [% blah %]
+ -- expect --
+ blah, blah, more blah
+
+The test_expect() sub counts the number of tests, and then calls ntests()
+to generate the familiar "1..$ntests\n" test harness line. Each
+test defined generates two test numbers. The first indicates
+that the input was processed without error, and the second that the
+output matches that expected.
+
+Additional test may be run before test_expect() by calling ok().
+These test results are cached until ntests() is called and the final
+number of tests can be calculated. Then, the "1..$ntests" line is
+output, along with "ok $n" / "not ok $n" lines for each of the cached
+test result. Subsequent calls to ok() then generate an output line
+immediately.
+
+ my $something = SomeObject->new();
+ ok( $something );
+
+ my $other = AnotherThing->new();
+ ok( $other );
+
+ test_expect(\*DATA);
+
+If any tests are to follow after test_expect() is called then these
+should be pre-declared by setting the $EXTRA package variable. This
+value (default: 0) is added to the grand total calculated by ntests().
+The results of the additional tests are also registered by calling ok().
+
+ $Template::Test::EXTRA = 2;
+
+ # can call ok() any number of times before test_expect()
+ ok( $did_that_work );
+ ok( $make_sure );
+ ok( $dead_certain );
+
+ # <some> number of tests...
+ test_expect(\*DATA, $config, $replace);
+
+ # here's those $EXTRA tests
+ ok( defined $some_result && ref $some_result eq 'ARRAY' );
+ ok( $some_result->[0] eq 'some expected value' );
+
+If you don't want to call test_expect() at all then you can call
+ntests($n) to declare the number of tests and generate the test
+header line. After that, simply call ok() for each test passing
+a true or false values to indicate that the test passed or failed.
+
+ ntests(2);
+ ok(1);
+ ok(0);
+
+If you're really lazy, you can just call ok() and not bother declaring
+the number of tests at all. All tests results will be cached until the
+end of the script and then printed in one go before the program exits.
+
+ ok( $x );
+ ok( $y );
+
+You can identify only a specific part of the input file for testing
+using the '-- start --' and '-- stop --' markers. Anything before the
+first '-- start --' is ignored, along with anything after the next
+'-- stop --' marker.
+
+ -- test --
+ this is test 1 (not performed)
+ -- expect --
+ this is test 1 (not performed)
+
+ -- start --
+
+ -- test --
+ this is test 2
+ -- expect --
+ this is test 2
+
+ -- stop --
+
+ ...
+
+For historical reasons and general utility, the module also defines a
+'callsign' subroutine which returns a hash mapping a..z to their phonetic
+alphabet equivalent (e.g. radio callsigns). This is used by many
+of the test scripts as a "known source" of variable values.
+
+ test_expect(\*DATA, $config, callsign());
+
+A banner() subroutine is also provided which prints a simple banner
+including any text passed as parameters, if $DEBUG is set.
+
+ banner('Testing something-or-other');
+
+example output:
+
+ #------------------------------------------------------------
+ # Testing something-or-other (27 tests completed)
+ #------------------------------------------------------------
+
+The $DEBUG package variable can be set to enable debugging mode.
+
+The $PRESERVE package variable can be set to stop the test_expect()
+from converting newlines in the output and expected output into
+the literal strings '\n'.
+
+=head1 HISTORY
+
+This module started its butt-ugly life as the t/texpect.pl script. It
+was cleaned up to became the Template::Test module some time around
+version 0.29. It underwent further cosmetic surgery for version 2.00
+but still retains some rear-end resemblances.
+
+=head1 BUGS / KNOWN "FEATURES"
+
+Imports all methods by default. This is generally a Bad Thing, but
+this module is only used in test scripts (i.e. at build time) so a) we
+don't really care and b) it saves typing.
+
+The line splitter may be a bit dumb, especially if it sees lines like
+-- this -- that aren't supposed to be special markers. So don't do that.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Tools/tpage.pod b/lib/Template/Tools/tpage.pod
new file mode 100644
index 0000000..e2b20fa
--- /dev/null
+++ b/lib/Template/Tools/tpage.pod
@@ -0,0 +1,76 @@
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Tools::tpage - Process templates from command line
+
+=head1 USAGE
+
+ tpage [ --define var=value ] file(s)
+
+=head1 DESCRIPTION
+
+The B<tpage> script is a simple wrapper around the Template Toolkit processor.
+Files specified by name on the command line are processed in turn by the
+template processor and the resulting output is sent to STDOUT and can be
+redirected accordingly. e.g.
+
+ tpage myfile > myfile.out
+ tpage header myfile footer > myfile.html
+
+If no file names are specified on the command line then B<tpage> will read
+STDIN for input.
+
+The C<--define> option can be used to set the values of template variables.
+e.g.
+
+ tpage --define author="Andy Wardley" skeleton.pm > MyModule.pm
+
+See L<Template> for general information about the Perl Template
+Toolkit and the template language and features.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<ttree|Template::Tools::ttree>
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Tools/ttree.pod b/lib/Template/Tools/ttree.pod
new file mode 100644
index 0000000..8984c51
--- /dev/null
+++ b/lib/Template/Tools/ttree.pod
@@ -0,0 +1,332 @@
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Tools::ttree - Process entire directory trees of templates
+
+=head1 SYNOPSIS
+
+ ttree [options] [files]
+
+=head1 DESCRIPTION
+
+The F<ttree> script is used to process entire directory trees containing
+template files. The resulting output from processing each file is then
+written to a corresponding file in a destination directory. The script
+compares the modification times of source and destination files (where
+they already exist) and processes only those files that have been modified.
+In other words, it is the equivalent of 'make' for the Template Toolkit.
+
+It supports a number of options which can be used to configure
+behaviour, define locations and set Template Toolkit options. The
+script first reads the F<.ttreerc> configuration file in the HOME
+directory, or an alternative file specified in the TTREERC environment
+variable. Then, it processes any command line arguments, including
+any additional configuration files specified via the C<-f> (file)
+option.
+
+=head2 The F<.ttreerc> Configuration File
+
+When you run F<ttree> for the first time it will ask you if you want
+it to create a F<.ttreerc> file for you. This will be created in your
+home directory.
+
+ $ ttree
+ Do you want me to create a sample '.ttreerc' file for you?
+ (file: /home/abw/.ttreerc) [y/n]: y
+ /home/abw/.ttreerc created. Please edit accordingly and re-run ttree
+
+The purpose of this file is to set any I<global> configuration options
+that you want applied I<every> time F<ttree> is run. For example, you
+can use the C<ignore> and C<copy> option to provide regular expressions
+that specify which files should be ignored and which should be copied
+rather than being processed as templates. You may also want to set
+flags like C<verbose> and C<recurse> according to your preference.
+
+A minimal F<.ttreerc>:
+
+ # ignore these files
+ ignore = \b(CVS|RCS)\b
+ ignore = ^#
+ ignore = ~$
+
+ # copy these files
+ copy = \.(gif|png|jpg|pdf)$
+
+ # recurse into directories
+ recurse
+
+ # provide info about what's going on
+ verbose
+
+In most cases, you'll want to create a different F<ttree> configuration
+file for each project you're working on. The C<cfg> option allows you
+to specify a directory where F<ttree> can find further configuration
+files.
+
+ cfg = /home/abw/.ttree
+
+The C<-f> command line option can be used to specify which configuration
+file should be used. You can specify a filename using an absolute or
+relative path:
+
+ $ ttree -f /home/abw/web/example/etc/ttree.cfg
+ $ ttree -f ./etc/ttree.cfg
+ $ ttree -f ../etc/ttree.cfg
+
+If the configuration file does not begin with C</> or C<.> or something
+that looks like a MS-DOS absolute path (e.g. C<C:\\etc\\ttree.cfg>) then
+F<ttree> will look for it in the directory specified by the C<cfg> option.
+
+ $ ttree -f test1 # /home/abw/.ttree/test1
+
+The C<cfg> option can only be used in the F<.ttreerc> file. All the
+other options can be used in the F<.ttreerc> or any other F<ttree>
+configuration file. They can all also be specified as command line
+options.
+
+Remember that F<.ttreerc> is always processed I<before> any
+configuration file specified with the C<-f> option. Certain options
+like C<lib> can be used any number of times and accumulate their values.
+
+For example, consider the following configuration files:
+
+F</home/abw/.ttreerc>:
+
+ cfg = /home/abw/.ttree
+ lib = /usr/local/tt2/templates
+
+F</home/abw/.ttree/myconfig>:
+
+ lib = /home/abw/web/example/templates/lib
+
+When F<ttree> is invoked as follows:
+
+ $ ttree -f myconfig
+
+the C<lib> option will be set to the following directories:
+
+ /usr/local/tt2/templates
+ /home/abw/web/example/templates/lib
+
+Any templates located under F</usr/local/tt2/templates> will be used
+in preference to those located under
+F</home/abw/web/example/templates/lib>. This may be what you want,
+but then again, it might not. For this reason, it is good practice to
+keep the F<.ttreerc> as simple as possible and use different
+configuration files for each F<ttree> project.
+
+=head2 Directory Options
+
+The C<src> option is used to define the directory containing the
+source templates to be processed. It can be provided as a command
+line option or in a configuration file as shown here:
+
+ src = /home/abw/web/example/templates/src
+
+Each template in this directory typically corresponds to a single
+web page or other document.
+
+The C<dest> option is used to specify the destination directory for the
+generated output.
+
+ dest = /home/abw/web/example/html
+
+The C<lib> option is used to define one or more directories containing
+additional library templates. These templates are not documents in
+their own right and typically comprise of smaller, modular components
+like headers, footers and menus that are incorporated into pages templates.
+
+ lib = /home/abw/web/example/templates/lib
+ lib = /usr/local/tt2/templates
+
+The C<lib> option can be used repeatedly to add further directories to
+the search path.
+
+A list of templates can be passed to F<ttree> as command line arguments.
+
+ $ ttree foo.html bar.html
+
+It looks for these templates in the C<src> directory and processes them
+through the Template Toolkit, using any additional template components
+from the C<lib> directories. The generated output is then written to
+the corresponding file in the C<dest> directory.
+
+If F<ttree> is invoked without explicitly specifying any templates
+to be processed then it will process every file in the C<src> directory.
+If the C<-r> (recurse) option is set then it will additionally iterate
+down through sub-directories and process and other template files it finds
+therein.
+
+ $ ttree -r
+
+If a template has been processed previously, F<ttree> will compare the
+modification times of the source and destination files. If the source
+template (or one it is dependant on) has not been modified more
+recently than the generated output file then F<ttree> will not process
+it. The F<-a> (all) option can be used to force F<ttree> to process
+all files regardless of modification time.
+
+ $ tree -a
+
+Any templates explicitly named as command line argument are always
+processed and the modification time checking is bypassed.
+
+=head2 File Options
+
+The C<ignore>, C<copy> and C<accept> options are used to specify Perl
+regexen to filter file names. Files that match any of the C<ignore>
+options will not be processed. Remaining files that match any of the
+C<copy> regexen will be copied to the destination directory. Remaining
+files that then match any of the C<accept> criteria are then processed
+via the Template Toolkit. If no C<accept> parameter is specified then
+all files will be accepted for processing if not already copied or
+ignored.
+
+ # ignore these files
+ ignore = \b(CVS|RCS)\b
+ ignore = ^#
+ ignore = ~$
+
+ # copy these files
+ copy = \.(gif|png|jpg|pdf)$
+
+ # accept only .tt2 templates
+ accept = \.tt2$
+
+The C<suffix> option is used to define mappings between the file
+extensions for source templates and the generated output files. The
+following example specifies that source templates with a C<.tt2>
+suffix should be output as C<.html> files:
+
+ suffix tt2=html
+
+Or on the command line,
+
+ --suffix tt2=html
+
+You can provide any number of different suffix mappings by repeating
+this option.
+
+=head2 Template Dependencies
+
+The C<depend> and C<depend_file> options allow you to specify
+how any given template file depends on another file or group of files.
+The C<depend> option is used to express a single dependency.
+
+ $ ttree --depend foo=bar,baz
+
+This command line example shows the C<--depend> option being used to
+specify that the F<foo> file is dependant on the F<bar> and F<baz>
+templates. This option can be used many time on the command line:
+
+ $ ttree --depend foo=bar,baz --depend crash=bang,wallop
+
+or in a configuration file:
+
+ depend foo=bar,baz
+ depend crash=bang,wallop
+
+The file appearing on the left of the C<=> is specified relative to
+the C<src> or C<lib> directories. The file(s) appearing on the right
+can be specified relative to any of these directories or as absolute
+file paths.
+
+For example:
+
+ $ ttree --depend foo=bar,/tmp/baz
+
+To define a dependency that applies to all files, use C<*> on the
+left of the C<=>.
+
+ $ ttree --depend *=header,footer
+
+or in a configuration file:
+
+ depend *=header,footer
+
+Any templates that are defined in the C<pre_process>, C<post_process>,
+C<process> or C<wrapper> options will automatically be added to the
+list of global dependencies that apply to all templates.
+
+The C<depend_file> option can be used to specify a file that contains
+dependency information.
+
+ $ ttree --depend_file=/home/abw/web/example/etc/ttree.dep
+
+Here is an example of a dependency file:
+
+ # This is a comment. It is ignored.
+
+ index.html: header footer menubar
+
+ header: titlebar hotlinks
+
+ menubar: menuitem
+
+ # spanning multiple lines with the backslash
+ another.html: header footer menubar \
+ sidebar searchform
+
+Lines beginning with the C<#> character are comments and are ignored.
+Blank lines are also ignored. All other lines should provide a
+filename followed by a colon and then a list of dependant files
+separated by whitespace, commas or both. Whitespace around the colon
+is also optional. Lines ending in the C<\> character are continued
+onto the following line.
+
+Files that contain spaces can be quoted. That is only necessary
+for files after the colon (':'). The file before the colon may be
+quoted if it contains a colon.
+
+As with the command line options, the C<*> character can be used
+as a wildcard to specify a dependency for all templates.
+
+ * : config,header
+
+=head2 Template Toolkit Options
+
+F<ttree> also provides access to the usual range of Template Toolkit
+options. For example, the C<--pre_chomp> and C<--post_chomp> F<ttree>
+options correspond to the C<PRE_CHOMP> and C<POST_CHOMP> options.
+
+Run C<ttree -h> for a summary of the options available.
+
+=head1 AUTHORS
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+With contributions from Dylan William Hardison (support for
+dependencies), Bryce Harrington (C<absolute> and C<relative> options),
+Mark Anderson (C<suffix> and C<debug> options), Harald Joerg and Leon
+Brocard who gets everywhere, it seems.
+
+=head1 VERSION
+
+2.69, distributed as part of the
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<tpage|Template::Tools::tpage>
+
diff --git a/lib/Template/Tutorial.pod b/lib/Template/Tutorial.pod
new file mode 100644
index 0000000..b6c894f
--- /dev/null
+++ b/lib/Template/Tutorial.pod
@@ -0,0 +1,109 @@
+#============================================================= -*-perl-*-
+#
+# Template::Tutorial
+#
+# DESCRIPTION
+# This section includes tutorials on using the Template Toolkit.
+# Subjects currently include an general overview of the Template
+# Toolkit, showing users how to get quickly up to speed building web
+# content, and a tutorial on generating and using data files, with
+# particular reference to XML.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Tutorial - Template Toolkit Tutorials
+
+=head1 DESCRIPTION
+
+This section includes tutorials on using the Template Toolkit. Subjects
+currently include an general overview of the Template Toolkit, showing
+users how to get quickly up to speed building web content, and a
+tutorial on generating and using data files, with particular reference
+to XML.
+
+=over 4
+
+=item L<Template::Tutorial::Web|Template::Tutorial::Web>
+
+Generating Web Content Using the Template Toolkit
+
+This tutorial provides an introduction to the Template Toolkit and a
+"quick start" guide to getting up to speed. Its primarily focus is on
+using the Template Toolkit to build web content and it covers 4 basic
+areas: using tpage and ttree; using the Template.pm module in CGI
+scripts; writing Apache/mod_perl handlers; and extending the toolkit by
+writing plugins.
+
+=item L<Template::Tutorial::Datafile|Template::Tutorial::Datafile>
+
+Creating Data Output Files Using the Template Toolkit
+
+This tutorial gives an overview of the Template Toolkit, showing in
+particular how to use it to read and write data files in various
+different formats and styles. It was written by Dave Cross and first
+appeared as a lead article at http://www.perl.com/ earlier in the year
+(2001).
+
+
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Tutorial/Datafile.pod b/lib/Template/Tutorial/Datafile.pod
new file mode 100644
index 0000000..652f79d
--- /dev/null
+++ b/lib/Template/Tutorial/Datafile.pod
@@ -0,0 +1,461 @@
+#============================================================= -*-perl-*-
+#
+# Template::Tutorial::Datafile
+#
+# DESCRIPTION
+# This tutorial gives an overview of the Template Toolkit, showing in
+# particular how to use it to read and write data files in various
+# different formats and styles. It was written by Dave Cross and
+# first appeared as a lead article at http://www.perl.com/ earlier in
+# the year (2001).
+#
+# AUTHOR
+# Dave Cross <dave@dave.org.uk>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Tutorial::Datafile - Creating Data Output Files Using the Template Toolkit
+
+=head1 DESCRIPTION
+
+This tutorial gives an overview of the Template Toolkit, showing in
+particular how to use it to read and write data files in various
+different formats and styles. It was written by Dave Cross and first
+appeared as a lead article at http://www.perl.com/ earlier in the year
+(2001).
+
+=head1 Introducing the Template Toolkit
+
+There are a number of Perl modules that are universally
+recognised as The Right Thing To Use for certain tasks. If you
+accessed a database without using DBI, pulled data from the WWW
+without using one of the LWP modules or parsed XML without using
+XML::Parser or one of its subclasses then you'd run the risk of
+being shunned by polite Perl society.
+
+I believe that the year 2000 saw the emergence of another 'must
+have' Perl module - the Template Toolkit. I don't think I'm
+alone in this belief as the Template Toolkit won the 'Best New
+Module' award at the Perl Conference last summer. Version 2.0 of
+the Template Toolkit (known as TT2 to its friends) was recently
+released to the CPAN.
+
+TT2 was designed and written by Andy Wardley E<lt>abw@wardley.orgE<gt>.
+It was born out of Andy's previous templating module,
+Text::Metatext, in best Fred Brooks 'plan to throw one away'
+manner; and aims to be the most useful (or, at least, the most
+I<used>) Perl templating system.
+
+TT2 provides a way to take a file of fixed boilerplate text
+(the template) and embed variable data within it. One obvious
+use of this is in the creation of dynamic web pages and this is
+where a lot of the attention that TT2 has received has been
+focussed. In this article, I hope to demonstrate that TT2 is
+just as useful in non-web applications.
+
+=head1 Using the Template Toolkit
+
+Let's look at how we'd use TT2 to process a simple data file.
+TT2 is an object oriented Perl module. Having downloaded it from
+CPAN and installed it in the usual manner, using it in your
+program is as easy as putting the lines
+
+ use Template;
+ my $tt = Template->new;
+
+in your code. The constructor function, C<new>, takes
+a number of optional parameters which are documented in the
+copious manual pages that come with the module, but for the
+purposes of this article we'll keep things as simple as
+possible.
+
+To process the template, you would call the C<process> method
+like this
+
+ $tt->process('my_template', \%data)
+ || die $tt->error;
+
+We pass two parameters to C<process>, the first is the name of
+the file containing the template to process (in this case,
+my_template) and the second is a reference to a hash which
+contains the data items that you want to use in the template. If
+processing the template gives any kind of error, the program
+will die with a (hopefully) useful error message.
+
+So what kinds of things can go in C<%data>? The answer is just
+about anything. Here's an example showing data about English
+Premier League football teams.
+
+ my @teams = ({ name => 'Man Utd',
+ played => 16,
+ won => 12,
+ drawn => 3,
+ lost => 1 },
+ { name => 'Bradford',
+ played => 16,
+ won => 2,
+ drawn => 5,
+ lost => 9 });
+
+ my %data = ( name => 'English Premier League',
+ season => '2000/01',
+ teams => \@teams );
+
+This creates three data items which can be accessed within the
+template, called C<name>, C<season> and C<teams>. Notice that
+C<teams> is a complex data structure.
+
+Here is a template that we might use to process this data.
+
+ League Standings
+
+ League Name: [% name %]
+ Season : [% season %]
+
+ Teams:
+ [% FOREACH team = teams -%]
+ [% team.name %] [% team.played -%]
+ [% team.won %] [% team.drawn %] [% team.lost %]
+ [% END %]
+
+Running this template with this data gives us the following
+output
+
+ League Standings
+
+ League Name: English Premier League
+ Season : 2000/01
+
+ Teams:
+ Man Utd 16 12 3 1
+ Bradford 16 2 5 9
+
+Hopefully the syntax of the template is simple enough to
+follow. There are a few points to note.
+
+=over 4
+
+=item *
+
+Template processing directives are written using a simple
+language which is not Perl.
+
+=item *
+
+The keys of the C<%data> have become the names of the data
+variables within the template.
+
+=item *
+
+Template processing directives are surrounded by C<[%> and
+C<%]> sequences.
+
+=item *
+
+If these tags are replaced with C<[%-> C<-%]> then the preceding
+or following linefeed is suppressed.
+
+=item *
+
+In the C<FOREACH> loop, each element of the C<teams> list was
+assigned, in turn, to the temporary variable C<team>.
+
+=item *
+
+Each item assigned to the C<team> variable is a Perl hash.
+Individual values within the hash are accessed using a dot notation.
+
+=back
+
+It's probably the first and last of these points which are the
+most important. The first point emphasises the separation of the
+data acquisition logic from the presentation logic. The person
+creating the presentation template doesn't need to know Perl,
+they only need to know the data items which will be passed into
+the template.
+
+The last point demonstrates the way that TT2 protects the
+template designer from the implementation of the data structures.
+The data objects passed to the template processor can be scalars,
+arrays, hashes, objects or even subroutines. The template
+processor will just interpret your data correctly and Do The
+Right Thing to return the correct value to you. In this example
+each team was a hash, but in a larger system each team might be
+an object, in which case C<name>, C<played>, etc. would be accessor
+methods to the underlying object attributes. No changes would be
+required to the template as the template processor would realise
+that it needed to call methods rather than access hash values.
+
+=head2 A more complex example
+
+Stats about the English Football League are usually presented in
+a slightly more complex format than the one we used above. A
+full set of stats will show the number of games that a team has
+won, lost or drawn, the number of goals scored for and against
+the team and the number of points that the team therefore has.
+Teams gain three points for a win and one point for a draw. When
+teams have the same number of points they are separated by the
+goal difference, that is the number of goals the team has scored
+minus the number of team scored against them. To complicate
+things even further, the games won, drawn and lost and the goals
+for and against are often split between home and away games.
+
+Therefore if you have a data source which lists the team name
+togther with the games won, drawn and lost and the goals for and
+against split into home and away (a total of eleven data items)
+you can calculate all of the other items (goal difference,
+points awarded and even position in the league). Let's take such
+a file, but we'll only look at the top three teams. It will look
+something like this:
+
+ Man Utd,7,1,0,26,4,5,2,1,15,6
+ Arsenal,7,1,0,17,4,2,3,3,7,9
+ Leicester,4,3,1,10,8,4,2,2,7,4
+
+A simple script to read this data into an array of hashes will
+look something like this (I've simplified the names of the data
+columns - w, d, and l are games won, drawn and lost and f and a
+are goals scored for and against; h and a at the front of a data
+item name indicates whether it's a home or away statistic):
+
+ my @cols = qw(name hw hd hl hf ha aw ad al af aa);
+
+ my @teams;
+ while (<>) {
+ chomp;
+
+ my %team;
+
+ @team{@cols} = split /,/;
+
+ push @teams, \%team;
+ }
+
+We can then go thru the teams again and calculate all of the
+derived data items:
+
+ foreach (@teams) {
+ $_->{w} = $_->{hw} + $_->{aw};
+ $_->{d} = $_->{hd} + $_->{ad};
+ $_->{l} = $_->{hl} + $_->{al};
+
+ $_->{pl} = $_->{w} + $_->{d} + $_->{l};
+
+ $_->{f} = $_->{hf} + $_->{af};
+ $_->{a} = $_->{ha} + $_->{aa};
+
+ $_->{gd} = $_->{f} - $_->{a};
+ $_->{pt} = (3 * $_->{w}) + $_->{d};
+ }
+
+And then produce a list sorted in descending order:
+
+ @teams = sort {
+ $b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd}
+ } @teams;
+
+And finally add the league position data item:
+
+ $teams[$_]->{pos} = $_ + 1
+ foreach 0 .. $#teams;
+
+Having pulled all of our data into an internal data structure
+we can start to produce output using out templates. A template
+to create a CSV file containing the data split between home and
+away stats would look like this:
+
+ [% FOREACH team = teams -%]
+ [% team.pos %],[% team.name %],[% team.pl %],[% team.hw %],
+ [%- team.hd %],[% team.hl %],[% team.hf %],[% team.ha %],
+ [%- team.aw %],[% team.ad %],[% team.al %],[% team.af %],
+ [%- team.aa %],[% team.gd %],[% team.pt %]
+ [%- END %]
+
+And processing it like this:
+
+ $tt->process('split.tt', { teams => \@teams }, 'split.csv')
+ || die $tt->error;
+
+produces the following output:
+
+ 1,Man Utd,16,7,1,0,26,4,5,2,1,15,6,31,39
+ 2,Arsenal,16,7,1,0,17,4,2,3,3,7,9,11,31
+ 3,Leicester,16,4,3,1,10,8,4,2,2,7,4,5,29
+
+Notice that we've introduced the third parameter to C<process>.
+If this parameter is missing then the TT2 sends its output to
+C<STDOUT>. If this parameter is a scalar then it is taken as the
+name of a file to write the output to. This parameter can also be
+(amongst other things) a filehandle or a reference to an object w
+hich is assumed to implement a C<print> method.
+
+If we weren't interested in the split between home and away games,
+then we could use a simpler template like this:
+
+ [% FOREACH team = teams -%]
+ [% team.pos %],[% team.name %],[% team.pl %],[% team.w %],
+ [%- team.d %],[% team.l %],[% team.f %],[% team.a %],
+ [%- team.aa %],[% team.gd %],[% team.pt %]
+ [% END -%]
+
+Which would produce output like this:
+
+ 1,Man Utd,16,12,3,1,41,10,6,31,39
+ 2,Arsenal,16,9,4,3,24,13,9,11,31
+ 3,Leicester,16,8,5,3,17,12,4,5,29
+
+=head1 Producing XML
+
+This is starting to show some of the power and flexibility of
+TT2, but you may be thinking that you could just as easily produce
+this output with a C<foreach> loop and a couple of C<print>
+statements in your code. This is, of course, true; but that's
+because I've chosen a deliberately simple example to explain the
+concepts. What if we wanted to produce an XML file containing the
+data? And what if (as I mentioned earlier) the league data was held
+in an object? The code would then look even easier as most of the code
+we've written earlier would be hidden away in C<FootballLeague.pm>.
+
+ use FootballLeague;
+ use Template;
+
+ my $league = FootballLeague->new(name => 'English Premier');
+
+ my $tt = Template->new;
+
+ $tt->process('league_xml.tt', { league => $league })
+ || die $tt->error;
+
+And the template in C<league_xml.tt> would look something like this:
+
+ <?xml version="1.0"?>
+ <!DOCTYPE LEAGUE SYSTEM "league.dtd">
+
+ <league name="[% league.name %]" season="[% league.season %]">
+ [% FOREACH team = league.teams -%]
+ <team name="[% team.name %]"
+ pos="[% team.pos %]"
+ played="[% team.pl %]"
+ goal_diff="[% team.gd %]"
+ points="[% team.pt %]">
+ <stats type="home">
+ win="[% team.hw %]"
+ draw="[%- team.hd %]"
+ lose="[% team.hl %]"
+ for="[% team.hf %]"
+ against="[% team.ha %]" />
+ <stats type="away">
+ win="[% team.aw %]"
+ draw="[%- team.ad %]"
+ lose="[% team.al %]"
+ for="[% team.af %]"
+ against="[% team.aa %]" />
+ </team>
+ [% END -%]
+ &/league>
+
+Notice that as we've passed the whole object into C<process> then
+we need to put an extra level of indirection on our template
+variables - everything is now a component of the C<league> variable.
+Other than that, everything in the template is very similar to what
+we've used before. Presumably now C<team.name> calls an accessor
+function rather than carrying out a hash lookup, but all of this
+is transparent to our template designer.
+
+=head1 Multiple Formats
+
+As a final example, let's suppose that we need to create output
+football league tables in a number of formats. Perhaps we are
+passing this data on to other people and they can't all use the
+same format. Some of our users need CSV files and others need
+XML. Some require data split between home and away matches and
+other just want the totals. In total, then, we'll need four
+different templates, but the good news is that they can use the
+same data object. All the script needs to do is to establish
+which template is required and process it.
+
+ use FootballLeague;
+ use Template;
+
+ my ($name, $type, $stats) = @_;
+
+ my $league = FootballLeague->new(name => $name);
+
+ my $tt = Template->new;
+
+ $tt->process("league_${type}_$stats.tt",
+ { league => $league }
+ "league_$stats.$type")
+ || die $tt->error;
+
+For example, you can call this script as
+
+ league.pl 'English Premier' xml split
+
+This will process a template called C<league_xml_split.tt>
+and put the results in a file called C<league_split.xml>.
+
+This starts to show the true strength of the Template Toolkit.
+If we later wanted to add another file format - perhaps we
+wanted to create a league table HTML page or even a LaTeX
+document - then we would just need to create the appropriate
+template and name it according to our existing naming
+convention. We would need to make no changes to the code.
+
+I hope you can now see why the Template Toolkit is fast becoming
+an essential part of many people's Perl installation.
+
+=head1 AUTHOR
+
+Dave Cross E<lt>dave@dave.org.ukE<gt>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+
+Copyright (C) 2001 Dave Cross E<lt>dave@dave.org.ukE<gt>
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/Tutorial/Web.pod b/lib/Template/Tutorial/Web.pod
new file mode 100644
index 0000000..a5ed9bb
--- /dev/null
+++ b/lib/Template/Tutorial/Web.pod
@@ -0,0 +1,801 @@
+#============================================================= -*-perl-*-
+#
+# Template::Tutorial::Web
+#
+# DESCRIPTION
+# This tutorial provides an introduction to the Template Toolkit and
+# a "quick start" guide to getting up to speed. Its primarily focus
+# is on using the Template Toolkit to build web content and it covers
+# 4 basic areas: using tpage and ttree; using the Template.pm module
+# in CGI scripts; writing Apache/mod_perl handlers; and extending the
+# toolkit by writing plugins.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+#
+#
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Tutorial::Web - Generating Web Content Using the Template Toolkit
+
+=head1 DESCRIPTION
+
+This tutorial document provides a introduction to the Template Toolkit
+and demonstrates some of the typical ways it may be used for
+generating web content. It covers the generation of static pages from
+templates using the L<tpage|Template::Tools::tpage> and
+L<ttree|Template::Tools::ttree> scripts and then goes on to
+show dynamic content generation using CGI scripts and Apache/mod_perl
+handlers.
+
+Various features of the Template Toolkit are introduced and described
+briefly and explained by use of example. For further information,
+see L<Template>, L<Template::Manual> and the various sections within
+it. e.g.
+
+ perldoc Template # Template.pm module usage
+ perldoc Template::Manual # index to manual
+ perldoc Template::Manual::Config # e.g. configuration options
+
+The documentation is now also distributed in HTML format (or rather,
+in the form of HTML templates). See the 'docs' sub-directory of the
+distribution for further information on building the HTML documentation.
+
+If you're already reading this as part of the HTML documentation, then
+you don't need to worry about all that. You can have a seat, sit back.
+ back and enjoy the rest of the tutorial...
+
+=head1 INTRODUCTION
+
+The Template Toolkit is a set of Perl modules which collectively
+implement a template processing system. In this context, a template
+is a text document containing special markup tags called 'directives'.
+A directive is an instruction for the template processor to perform
+some action and substitute the result into the document in place of
+the original directive. Directives include those to define or insert
+a variable value, iterate through a list of values (FOREACH), declare
+a conditional block (IF/UNLESS/ELSE), include and process another template
+file (INCLUDE) and so on.
+
+In all other respects, the document is a plain text file and may
+contain any other content (e.g. HTML, XML, RTF, LaTeX, etc). Directives
+are inserted in the document within the special markup tags which are
+'[%' and '%]' by default, but can be changed via the module
+configuration options. Here's an example of an HTML document with
+additional Template Toolkit directives.
+
+ [% INCLUDE header
+ title = 'This is an HTML example'
+ %]
+
+ <h1>Some Interesting Links</h1>
+
+ [% webpages = [
+ { url => 'http://foo.org', title => 'The Foo Organisation' }
+ { url => 'http://bar.org', title => 'The Bar Organisation' }
+ ]
+ %]
+
+ Links:
+ <ul>
+ [% FOREACH link = webpages %]
+ <li><a href="[% link.url %]">[% link.title %]</a>
+ [% END %]
+ </ul>
+
+ [% INCLUDE footer %]
+
+This example shows how the INCLUDE directive is used to load and process
+separate 'header' and 'footer' template files, including the output in
+the current document. These files might look like this:
+
+header:
+
+ <html>
+ <head>
+ <title>[% title %]</title>
+ </head>
+
+ <body bgcolor="#ffffff">
+
+footer:
+
+ <hr>
+
+ <center>
+ &copy; Copyright 2000 Me, Myself, I
+ </center>
+
+ </body>
+ </html>
+
+The example also uses the FOREACH directive to iterate through the
+'webpages' list to build a table of links. In this example, we have
+defined this list within the template to contain a number of hash references,
+each containing a 'url' and 'title' member. The FOREACH directive
+iterates through the list, aliasing 'link' to each item (hash ref).
+The B<[% link.url %]> and B<[% link.title %]> directives then access
+the individual values in the hash and insert them into the document.
+
+The following sections show other ways in which data can be defined for
+use in a template.
+
+=head1 GENERATING STATIC PAGES
+
+Having created a template file we can now process it to generate some
+real output. The quickest and easiest way to do this is to use the
+F<tpage> script. This is provided as part of the Template Toolkit and
+should be installed in your usual Perl bin directory.
+
+Assuming you saved your template file as 'mypage.html', you would run
+the command:
+
+ tpage mypage.html
+
+This will process the template file, sending the output to STDOUT
+(i.e. whizzing past you on the screen). You may want to redirect the
+output to a file but be careful not to specify the same name as the
+template file, or you'll overwrite it. You may want to use one prefix
+for your templates such as '.atml' (for 'Another Template Markup
+Language', perhaps?) and the regular '.html' for the output files
+(assuming you're creating HTML, that is). Alternatively, you might
+redirect the output to another directory. e.g.
+
+ tpage mypage.atml > mypage.html
+ tpage templates/mypage.html > html/mypage.html
+
+The B<tpage> script is very basic and only really intended to give you
+an easy way to process a template without having to write any Perl code.
+A much more flexible tool is B<ttree>, described below, but for now let's
+look at the output generated by processing the above example (some
+whitespace removed for brevity):
+
+ <html>
+ <head>
+ <title>This is an HTML example</title>
+ </head>
+
+ <body bgcolor="#ffffff">
+
+ <h1>Some Interesting Links</h1>
+
+ Links:
+ <ul>
+ <li><a href="http://foo.org">The Foo Organsiation</a>
+ <li><a href="http://bar.org">The Bar Organsiation</a>
+ </ul>
+
+ <hr>
+
+ <center>
+ &copy; Copyright 2000 Me, Myself, I
+ </center>
+
+ </body>
+ </html>
+
+The F<header> and F<footer> template files have been included (assuming
+you created them and they're in the current directory) and the link data
+has been built into an HTML list.
+
+The F<ttree> script, also distributed as part of the Template Toolkit,
+provides a more flexible way to process template documents. The first
+time you run the script, it will ask you if it should create a
+configuration file, usually called '.ttreerc' in your home directory.
+Answer 'y' to have it create the file.
+
+The F<ttree> documentation describes how you can change the location
+of this file and also explains the syntax and meaning of the various
+options in the file. Comments are written to the sample configuration
+file which should also help.
+
+ perldoc ttree
+ ttree -h
+
+In brief, the configuration file describes the directories in which
+template files are to be found (src), where the corresponding output
+should be written to (dest), and any other directories (lib) that may
+contain template files that you plan to INCLUDE into your source
+documents. You can also specify processing options (such as 'verbose'
+and 'recurse') and provide regular expression to match files that you
+don't want to process (ignore, accept) or should be copied instead of
+processed (copy).
+
+An example F<.ttreerc> file is shown here:
+
+$HOME/.ttreerc:
+ verbose
+ recurse
+
+ # this is where I keep other ttree config files
+ cfg = ~/.ttree
+
+ src = ~/websrc/src
+ lib = ~/websrc/lib
+ dest = ~/public_html/test
+
+ ignore = \b(CVS|RCS)\b
+ ignore = ^#
+
+You can create many different configuration files and store them
+in the directory specified in the 'cfg' option, shown above. You then
+add the C<-f filename> option to F<ttree> to have it read that file.
+
+When you run the script, it compares all the files in the 'src' directory
+(including those in sub-directories if the 'recurse' option is set), with
+those in the 'dest' directory. If the destination file doesn't exist or
+has an earlier modification time than the corresponding source file, then
+the source will be processed with the output written to the destination
+file. The C<-a> option forces all files to be processed, regardless of
+modification times.
+
+The script I<doesn't> process any of the files in the 'lib' directory,
+but it does add it to the INCLUDE_PATH for the template processor so
+that it can locate these files via an INCLUDE or PROCESS directive.
+Thus, the 'lib' directory is an excellent place to keep template elements
+such as header, footers, etc., that aren't complete documents in their
+own right.
+
+You can also specify various Template Toolkit options from the configuration
+file. Consult the B<ttree> documentation and help summary (C<ttree -h>)
+for full details. e.g.
+
+$HOME/.ttreerc:
+ pre_process = config
+ interpolate
+ post_chomp
+
+The 'pre_process' option allows you to specify a template file which
+should be processed before each file. Unsurprisingly, there's also a
+'post_process' option to add a template after each file. In the
+fragment above, we have specified that the 'config' template should be
+used as a prefix template. We can create this file in the 'lib'
+directory and use it to define some common variables, including those
+web page links we defined earlier and might want to re-use in other
+templates. We could also include an HTML header, title, or menu bar
+in this file which would then be prepended to each and every template
+file, but for now we'll keep all that in a separate 'header' file.
+
+$lib/config:
+
+ [% root = '~/abw'
+ home = "$root/index.html"
+ images = "$root/images"
+ email = 'abw@wardley.org'
+ graphics = 1
+ webpages = [
+ { url => 'http://foo.org', title => 'The Foo Organsiation' }
+ { url => 'http://bar.org', title => 'The Bar Organsiation' }
+ ]
+ %]
+
+Assuming you've created or copied the 'header' and 'footer' files from the
+earlier example into your 'lib' directory, you can now start to create
+web pages like the following in your 'src' directory and process them
+with F<ttree>.
+
+$src/newpage.html:
+
+ [% INCLUDE header
+ title = 'Another Template Toolkit Test Page'
+ %]
+
+ <a href="[% home %]">Home</a>
+ <a href="mailto:[% email %]">Email</a>
+
+ [% IF graphics %]
+ <img src="[% images %]/logo.gif" align=right width=60 height=40>
+ [% END %]
+
+ [% INCLUDE footer %]
+
+Here we've shown how pre-defined variables can be used as flags to
+enable certain feature (e.g. 'graphics') and to specify common items
+such as an email address and URL's for the home page, images directory
+and so on. This approach allows you to define these values once so
+that they're consistent across all pages and can easily be changed to
+new values.
+
+When you run B<ttree>, you should see output similar to the following
+(assuming you have the verbose flag set).
+
+ ttree 1.14 (Template Toolkit version 1.02a)
+
+ Source: /home/abw/websrc/src
+ Destination: /home/abw/public_html/test
+ Include Path: [ /home/abw/websrc/lib ]
+ Ignore: [ \b(CVS|RCS)\b, ^# ]
+ Copy: [ ]
+ Accept: [ * ]
+
+ + newpage.html
+
+The '+' before 'newpage.html' shows that the file was processed, with
+the output being written to the destination directory. If you run the
+same command again, you'll see the following line displayed instead
+showing a '-' and giving a reason why the file wasn't processed.
+
+ - newpage.html (not modified)
+
+It has detected a 'newpage.html' in the destination directory which is
+more recent than that in the source directory and so hasn't bothered
+to waste time re-processing it. To force all files to be processed,
+use the C<-a> option. You can also specify one or more filenames as
+command line arguments to F<ttree>:
+
+ tpage newpage.html
+
+This is what the destination page looks like.
+
+$dest/newpage.html:
+
+ <html>
+ <head>
+ <title>Another Template Toolkit Test Page</title>
+ </head>
+
+ <body bgcolor="#ffffff">
+
+ <a href="~/abw/index.html">Home</a>
+ <a href="mailto:abw@wardley.org">Email me</a>
+
+ <img src="~/abw/images/logo.gif" align=right width=60 height=40>
+
+ <hr>
+
+ <center>
+ &copy; Copyright 2000 Me, Myself, I
+ </center>
+
+ </body>
+ </html>
+
+You can add as many documents as you like to the 'src' directory and
+F<ttree> will apply the same process to them all. In this way, it is
+possible to build an entire tree of static content for a web site with
+a single command. The added benefit is that you can be assured of
+consistency in links, header style, or whatever else you choose to
+implement in terms of common templates elements or variables.
+
+=head1 DYNAMIC CONTENT GENERATION VIA CGI SCRIPT
+
+The L<Template|Template> module provides a simple front-end to the Template
+Toolkit for use in CGI scripts and Apache/mod_perl handlers. Simply
+'use' the Template module, create an object instance with the new()
+method and then call the process() method on the object, passing the
+name of the template file as a parameter. The second parameter passed
+is a reference to a hash array of variables that we want made available
+to the template:
+
+ #!/usr/bin/perl -w
+
+ use strict;
+ use Template;
+
+ my $file = 'src/greeting.html';
+ my $vars = {
+ message => "Hello World\n"
+ };
+
+ my $template = Template->new();
+
+ $template->process($file, $vars)
+ || die "Template process failed: ", $template->error(), "\n";
+
+So that our scripts will work with the same template files as our earlier
+examples, we'll can add some configuration options to the constructor to
+tell it about our environment:
+
+ my $template->new({
+ # where to find template files
+ INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib',
+ # pre-process lib/config to define any extra values
+ PRE_PROCESS => 'config',
+ });
+
+Note that here we specify the 'config' file as a PRE_PROCESS option.
+This means that the templates we process can use the same global
+variables defined earlier for our static pages. We don't have to
+replicate their definitions in this script. However, we can supply
+additional data and functionality specific to this script via the hash
+of variables that we pass to the process() method.
+
+These entries in this hash may contain simple text or other values,
+references to lists, others hashes, sub-routines or objects. The Template
+Toolkit will automatically apply the correct procedure to access these
+different types when you use the variables in a template.
+
+Here's a more detailed example to look over. Amongst the different
+template variables we define in C<$vars>, we create a reference to a
+CGI object and a 'get_user_projects' sub-routine.
+
+ #!/usr/bin/perl -w
+
+ use strict;
+ use Template;
+ use CGI;
+
+ $| = 1;
+ print "Content-type: text/html\n\n";
+
+ my $file = 'userinfo.html';
+ my $vars = {
+ 'version' => 3.14,
+ 'days' => [ qw( mon tue wed thu fri sat sun ) ],
+ 'worklist' => \&get_user_projects,
+ 'cgi' => CGI->new(),
+ 'me' => {
+ 'id' => 'abw',
+ 'name' => 'Andy Wardley',
+ },
+ };
+
+ sub get_user_projects {
+ my $user = shift;
+ my @projects = ... # do something to retrieve data
+ return \@projects;
+ }
+
+ my $template = Template->new({
+ INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib',
+ PRE_PROCESS => 'config',
+ });
+
+ $template->process($file, $vars)
+ || die $template->error();
+
+Here's a sample template file that we might create to build the output
+for this script.
+
+$src/userinfo.html:
+
+ [% INCLUDE header
+ title = 'Template Toolkit CGI Test'
+ %]
+
+ <a href="mailto:[% email %]">Email [% me.name %]</a>
+
+ <p>This is version [% version %]</p>
+
+ <h3>Projects</h3>
+ <ul>
+ [% FOREACH project = worklist(me.id) %]
+ <li> <a href="[% project.url %]">[% project.name %]</a>
+ [% END %]
+ </ul>
+
+ [% INCLUDE footer %]
+
+This example shows how we've separated the Perl implementation (code) from
+the presentation (HTML) which not only makes them easier to maintain in
+isolation, but also allows the re-use of existing template elements
+such as headers and footers, etc. By using template to create the
+output of your CGI scripts, you can give them the same consistency
+as your static pages built via L<ttree|Template::Tools::ttree> or
+other means.
+
+Furthermore, we can modify our script so that it processes any one of a
+number of different templates based on some condition. A CGI script to
+maintain a user database, for example, might process one template to
+provide an empty form for new users, the same form with some default
+values set for updating an existing user record, a third template for
+listing all users in the system, and so on. You can use any Perl
+functionality you care to write to implement the logic of your
+application and then choose one or other template to generate the
+desired output for the application state.
+
+=head1 DYNAMIC CONTENT GENERATION VIA APACHE/MOD_PERL HANDLER
+
+B<NOTE:> the Apache::Template module is now available from CPAN
+and provides a simple and easy to use Apache/mod_perl interface to the
+Template Toolkit. It's only in it's first release (0.01) at the time
+of writing and it currently only offers a fairly basic facility, but
+it implements most, if not all of what is described below, and it
+avoids the need to write your own handler. However, in many cases,
+you'll want to write your own handler to customise processing for your
+own need, and this section will show you how to get started.
+
+The Template module can be used in a similar way from an Apache/mod_perl
+handler. Here's an example of a typical Apache F<httpd.conf> file:
+
+ PerlModule CGI;
+ PerlModule Template
+ PerlModule MyOrg::Apache::User
+
+ PerlSetVar websrc_root /home/abw/websrc
+
+ <Location /user/bin>
+ SetHandler perl-script
+ PerlHandler MyOrg::Apache::User
+ </Location>
+
+This defines a location called '/user/bin' to which all requests will
+be forwarded to the handler() method of the MyOrg::Apache::User
+module. That module might look something like this:
+
+ package MyOrg::Apache::User;
+
+ use strict;
+ use vars qw( $VERSION );
+ use Apache::Constants qw( :common );
+ use Template qw( :template );
+ use CGI;
+
+ $VERSION = 1.59;
+
+ sub handler {
+ my $r = shift;
+
+ my $websrc = $r->dir_config('websrc_root')
+ or return fail($r, SERVER_ERROR,
+ "'websrc_root' not specified");
+
+ my $template = Template->new({
+ INCLUDE_PATH => "$websrc/src/user:$websrc/lib",
+ PRE_PROCESS => 'config',
+ OUTPUT => $r, # direct output to Apache request
+ });
+
+ my $params = {
+ uri => $r->uri,
+ cgi => CGI->new,
+ };
+
+ # use the path_info to determine which template file to process
+ my $file = $r->path_info;
+ $file =~ s[^/][];
+
+ $r->content_type('text/html');
+ $r->send_http_header;
+
+ $template->process($file, $params)
+ || return fail($r, SERVER_ERROR, $template->error());
+
+ return OK;
+ }
+
+ sub fail {
+ my ($r, $status, $message) = @_;
+ $r->log_reason($message, $r->filename);
+ return $status;
+ }
+
+The handler accepts the request and uses it to determine the 'websrc_root'
+value from the config file. This is then used to define an INCLUDE_PATH
+for a new Template object. The URI is extracted from the request and a
+CGI object is created. These are both defined as template variables.
+
+The name of the template file itself is taken from the PATH_INFO element
+of the request. In this case, it would comprise the part of the URL
+coming after '/user/bin', e.g for '/user/bin/edit', the template file
+would be 'edit' located in "$websrc/src/user". The headers are sent
+and the template file is processed. All output is sent directly to the
+print() method of the Apache request object.
+
+=head1 USING PLUGINS TO EXTEND FUNCTIONALITY
+
+As we've already shown, it is possible to bind Perl data and functions
+to template variables when creating dynamic content via a CGI script
+or Apache/mod_perl process. The Template Toolkit also supports a
+plugin interface which allows you define such additional data and/or
+functionality in a separate module and then load and use it as
+required with the USE directive.
+
+The main benefit to this approach is that you can load the extension into
+any template document, even those that are processed "statically" by
+F<tpage> or F<ttree>. You I<don't> need to write a Perl wrapper to
+explicitly load the module and make it available via the stash.
+
+Let's demonstrate this principle using the DBI plugin written by Simon
+Matthews E<lt>sam@knowledgepool.comE<gt>. You can create this
+template in your 'src' directory and process it using F<ttree> to see
+the results. Of course, this example relies on the existence of the
+appropriate SQL database but you should be able to adapt it to your
+own resources, or at least use it as a demonstrative example of what's
+possible.
+
+ [% INCLUDE header
+ title = 'User Info'
+ %]
+
+ [% USE DBI('dbi:mSQL:mydbname') %]
+
+ <table border=0 width="100%">
+ <tr>
+ <th>User ID</th>
+ <th>Name</th>
+ <th>Email</th>
+ </tr>
+
+ [% FOREACH user = DBI.query('SELECT * FROM user ORDER BY id') %]
+ <tr>
+ <td>[% user.id %]</td>
+ <td>[% user.name %]</td>
+ <td>[% user.email %]</td>
+ </tr>
+ [% END %]
+
+ </table>
+
+ [% INCLUDE footer %]
+
+A plugin is simply a Perl module in a known location and conforming to
+a known standard such that the Template Toolkit can find and load it
+automatically. You can create your own plugin by inheriting from the
+F<Template::Plugin> module.
+
+Here's an example which defines some data items ('foo' and 'people')
+and also an object method ('bar'). We'll call the plugin 'FooBar' for
+want of a better name and create it in the 'MyOrg::Template::Plugin::FooBar'
+package. We've added a 'MyOrg' to the regular 'Template::Plugin::*' package
+to avoid any conflict with existing plugins.
+
+You can create a module stub using the Perl utlity F<h2xs>:
+
+ h2xs -A -X -n MyOrg::Template::Plugin::FooBar
+
+This will create a directory structure representing the package name
+along with a set of files comprising your new module. You can then
+edit FooBar.pm to look something like this:
+
+ package MyOrg::Template::Plugin::FooBar;
+
+ use Template::Plugin;
+ use vars qw( $VERSION );
+ use base qw( Template::Plugin );
+
+ $VERSION = 1.23;
+
+ sub new {
+ my ($class, $context, @params) = @_;
+
+ bless {
+ _CONTEXT => $context,
+ foo => 25,
+ people => [ 'tom', 'dick', 'harry' ],
+ }, $class;
+ }
+
+ sub bar {
+ my ($self, @params) = @_;
+ # ...do something...
+ return $some_value;
+ }
+
+The plugin constructor new() receives the class name as the first
+parameter, as is usual in Perl, followed by a reference to something
+called a Template::Context object. You don't need to worry too much
+about this at the moment, other than to know that it's the main
+processing object for the Template Toolkit. It provides access to the
+functionality of the processor and some plugins may need to
+communicate with it. We don't at this stage, but we'll save the
+reference anyway in the '_CONTEXT' member. The leading underscore is
+a convention which indicates that this item is private and the
+Template Toolkit won't attempt to access this member. The other
+members defined, 'foo' and 'people' are regular data items which will be
+made available to templates using this plugin. Following the context
+reference are passed any additional parameters specified with the
+USE directive, such as the data source parameter, 'dbi:mSQL:mydbname',
+that we used in the earlier DBI example.
+
+If you used F<h2xs> to create the module stub then you'll already
+have a Makefile.PL and you can incite the familiar incantation to
+build and install it. Don't forget to add some tests to test.pl!
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+If you don't or can't install it to the regular place for your Perl
+modules (perhaps because you don't have the required privileges) then
+you can set the PERL5LIB environment variable to specify another location.
+If you're using F<ttree> then you can add the following line to your
+configuration file instead. This has the effect of add '/path/to/modules'
+to the @INC array to a similar end.
+
+$HOME/.ttreerc:
+
+ perl5lib = /path/to/modules
+
+One further configuration item must be added to inform the toolkit of
+the new package name we have adopted for our plugins:
+
+$HOME/.ttreerc:
+
+ plugin_base = 'MyOrg::Template::Plugin'
+
+If you're writing Perl code to control the Template modules directly,
+then this value can be passed as a configuration parameter when you
+create the module.
+
+ use Template;
+
+ my $template = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin'
+ });
+
+Now we can create a template which uses this plugin:
+
+ [% INCLUDE header
+ title = 'FooBar Plugin Test'
+ %]
+
+ [% USE FooBar %]
+
+ Some values available from this plugin:
+ [% FooBar.foo %] [% FooBar.bar %]
+
+ The users defined in the 'people' list:
+ [% FOREACH uid = FooBar.people %]
+ * [% uid %]
+ [% END %]
+
+ [% INCLUDE footer %]
+
+The 'foo', 'bar' and 'people' items of the FooBar plugin are
+automatically resolved to the appropriate data items or method calls
+on the underlying object.
+
+Using this approach, it is possible to create application
+functionality in a single module which can then be loaded and used on
+demand in any template. The simple interface between template
+directives and plugin objects allows complex, dynamic content to be
+built from a few simple template documents without knowing anything
+about the underlying implementation.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+Template Toolkit version 2.13, released on 30 January 2004.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
+#
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Template/View.pm b/lib/Template/View.pm
new file mode 100644
index 0000000..6de8d42
--- /dev/null
+++ b/lib/Template/View.pm
@@ -0,0 +1,752 @@
+#============================================================= -*-Perl-*-
+#
+# Template::View
+#
+# DESCRIPTION
+# A custom view of a template processing context. Can be used to
+# implement custom "skins".
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# TODO
+# * allowing print to have a hash ref as final args will cause problems
+# if you do this: [% view.print(hash1, hash2, hash3) %]. Current
+# work-around is to do [% view.print(hash1); view.print(hash2);
+# view.print(hash3) %] or [% view.print(hash1, hash2, hash3, { }) %]
+#
+# REVISION
+# $Id: View.pm,v 2.9 2004/01/30 18:36:11 abw Exp $
+#
+#============================================================================
+
+package Template::View;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD @BASEARGS $MAP );
+use base qw( Template::Base );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.9 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+@BASEARGS = qw( context );
+$MAP = {
+ HASH => 'hash',
+ ARRAY => 'list',
+ TEXT => 'text',
+ default => '',
+};
+
+$DEBUG = 0;
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Initialisation method called by the Template::Base class new()
+# constructor. $self->{ context } has already been set, by virtue of
+# being named in @BASEARGS. Remaining config arguments are presented
+# as a hash reference.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+
+ # move 'context' somewhere more private
+ $self->{ _CONTEXT } = $self->{ context };
+ delete $self->{ context };
+
+ # generate table mapping object types to templates
+ my $map = $config->{ map } || { };
+ $map->{ default } = $config->{ default } unless defined $map->{ default };
+ $self->{ map } = {
+ %$MAP,
+ %$map,
+ };
+
+ # local BLOCKs definition table
+ $self->{ _BLOCKS } = $config->{ blocks } || { };
+
+ # name of presentation method which printed objects might provide
+ $self->{ method } = defined $config->{ method }
+ ? $config->{ method } : 'present';
+
+ # view is sealed by default preventing variable update after
+ # definition, however we don't actually seal a view until the
+ # END of the view definition
+ my $sealed = $config->{ sealed };
+ $sealed = 1 unless defined $sealed;
+ $self->{ sealed } = $sealed ? 1 : 0;
+
+ # copy remaining config items from $config or set defaults
+ foreach my $arg (qw( base prefix suffix notfound silent )) {
+ $self->{ $arg } = $config->{ $arg } || '';
+ }
+
+ # name of data item used by view()
+ $self->{ item } = $config->{ item } || 'item';
+
+ # map methods of form ${include_prefix}_foobar() to include('foobar')?
+ $self->{ include_prefix } = $config->{ include_prefix } || 'include_';
+ # what about mapping foobar() to include('foobar')?
+ $self->{ include_naked } = defined $config->{ include_naked }
+ ? $config->{ include_naked } : 1;
+
+ # map methods of form ${view_prefix}_foobar() to include('foobar')?
+ $self->{ view_prefix } = $config->{ view_prefix } || 'view_';
+ # what about mapping foobar() to view('foobar')?
+ $self->{ view_naked } = $config->{ view_naked } || 0;
+
+ # the view is initially unsealed, allowing directives in the initial
+ # view template to create data items via the AUTOLOAD; once sealed via
+ # call to seal(), the AUTOLOAD will not update any internal items.
+ delete @$config{ qw( base method map default prefix suffix notfound item
+ include_prefix include_naked silent sealed
+ view_prefix view_naked blocks ) };
+ $config = { %{ $self->{ base }->{ data } }, %$config }
+ if $self->{ base };
+ $self->{ data } = $config;
+ $self->{ SEALED } = 0;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# seal()
+# unseal()
+#
+# Seal or unseal the view to allow/prevent new datat items from being
+# automatically created by the AUTOLOAD method.
+#------------------------------------------------------------------------
+
+sub seal {
+ my $self = shift;
+ $self->{ SEALED } = $self->{ sealed };
+}
+
+sub unseal {
+ my $self = shift;
+ $self->{ SEALED } = 0;
+}
+
+
+#------------------------------------------------------------------------
+# clone(\%config)
+#
+# Cloning method which takes a copy of $self and then applies to it any
+# modifications specified in the $config hash passed as an argument.
+# Configuration items may also be specified as a list of "name => $value"
+# arguments. Returns a reference to the cloned Template::View object.
+#
+# NOTE: may need to copy BLOCKS???
+#------------------------------------------------------------------------
+
+sub clone {
+ my $self = shift;
+ my $clone = bless { %$self }, ref $self;
+ my $config = ref $_[0] eq 'HASH' ? shift : { @_ };
+
+ # merge maps
+ $clone->{ map } = {
+ %{ $self->{ map } },
+ %{ $config->{ map } || { } },
+ };
+
+ # "map => { default=>'xxx' }" can be specified as "default => 'xxx'"
+ $clone->{ map }->{ default } = $config->{ default }
+ if defined $config->{ default };
+
+ # update any remaining config items
+ my @args = qw( base prefix suffix notfound item method include_prefix
+ include_naked view_prefix view_naked );
+ foreach my $arg (@args) {
+ $clone->{ $arg } = $config->{ $arg } if defined $config->{ $arg };
+ }
+ push(@args, qw( default map ));
+ delete @$config{ @args };
+
+ # anything left is data
+ my $data = $clone->{ data } = { %{ $self->{ data } } };
+ @$data{ keys %$config } = values %$config;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# print(@items, ..., \%config)
+#
+# Prints @items in turn by mapping each to an approriate template using
+# the internal 'map' hash. If an entry isn't found and the item is an
+# object that implements the method named in the internal 'method' item,
+# (default: 'present'), then the method will be called passing a reference
+# to $self, against which the presenter method may make callbacks (e.g.
+# to view_item()). If the presenter method isn't implemented, then the
+# 'default' map entry is consulted and used if defined. The final argument
+# may be a reference to a hash array providing local overrides to the internal
+# defaults for various items (prefix, suffix, etc). In the presence
+# of this parameter, a clone of the current object is first made, applying
+# any configuration updates, and control is then delegated to it.
+#------------------------------------------------------------------------
+
+sub print {
+ my $self = shift;
+
+ # if final config hash is specified then create a clone and delegate to it
+ # NOTE: potential problem when called print(\%data_hash1, \%data_hash2);
+ if ((scalar @_ > 1) && (ref $_[-1] eq 'HASH')) {
+ my $cfg = pop @_;
+ my $clone = $self->clone($cfg)
+ || return;
+ return $clone->print(@_)
+ || $self->error($clone->error());
+ }
+ my ($item, $type, $template, $present);
+ my $method = $self->{ method };
+ my $map = $self->{ map };
+ my $output = '';
+
+ # print each argument
+ foreach $item (@_) {
+ my $newtype;
+
+ if (! ($type = ref $item)) {
+ # non-references are TEXT
+ $type = 'TEXT';
+ $template = $map->{ $type };
+ }
+ elsif (! defined ($template = $map->{ $type })) {
+ # no specific map entry for object, maybe it implements a
+ # 'present' (or other) method?
+ if ( $method && UNIVERSAL::can($item, $method) ) {
+ $present = $item->$method($self); ## call item method
+ # undef returned indicates error, note that we expect
+ # $item to have called error() on the view
+ return unless defined $present;
+ $output .= $present;
+ next; ## NEXT
+ }
+ elsif ( UNIVERSAL::isa($item, 'HASH' )
+ && defined($newtype = $item->{$method})
+ && defined($template = $map->{"$method=>$newtype"})) {
+ }
+ elsif ( defined($newtype)
+ && defined($template = $map->{"$method=>*"}) ) {
+ $template =~ s/\*/$newtype/;
+ }
+ elsif (! ($template = $map->{ default }) ) {
+ # default not defined, so construct template name from type
+ ($template = $type) =~ s/\W+/_/g;
+ }
+ }
+# else {
+# $self->DEBUG("defined map type for $type: $template\n");
+# }
+ $self->DEBUG("printing view '", $template || '', "', $item\n") if $DEBUG;
+ $output .= $self->view($template, $item)
+ if $template;
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# view($template, $item, \%vars)
+#
+# Wrapper around include() which expects a template name, $template,
+# followed by a data item, $item, and optionally, a further hash array
+# of template variables. The $item is added as an entry to the $vars
+# hash (which is created empty if not passed as an argument) under the
+# name specified by the internal 'item' member, which is appropriately
+# 'item' by default. Thus an external object present() method can
+# callback against this object method, simply passing a data item to
+# be displayed. The external object doesn't have to know what the
+# view expects the item to be called in the $vars hash.
+#------------------------------------------------------------------------
+
+sub view {
+ my ($self, $template, $item) = splice(@_, 0, 3);
+ my $vars = ref $_[0] eq 'HASH' ? shift : { @_ };
+ $vars->{ $self->{ item } } = $item if defined $item;
+ $self->include($template, $vars);
+}
+
+
+#------------------------------------------------------------------------
+# include($template, \%vars)
+#
+# INCLUDE a template, $template, mapped according to the current prefix,
+# suffix, default, etc., where $vars is an optional hash reference
+# containing template variable definitions. If the template isn't found
+# then the method will default to any 'notfound' template, if defined
+# as an internal item.
+#------------------------------------------------------------------------
+
+sub include {
+ my ($self, $template, $vars) = @_;
+ my $context = $self->{ _CONTEXT };
+
+ $template = $self->template($template);
+
+ $vars = { } unless ref $vars eq 'HASH';
+ $vars->{ view } ||= $self;
+
+ $context->include( $template, $vars );
+
+# DEBUGGING
+# my $out = $context->include( $template, $vars );
+# print STDERR "VIEW return [$out]\n";
+# return $out;
+}
+
+
+#------------------------------------------------------------------------
+# template($template)
+#
+# Returns a compiled template for the specified template name, according
+# to the current configuration parameters.
+#------------------------------------------------------------------------
+
+sub template {
+ my ($self, $name) = @_;
+ my $context = $self->{ _CONTEXT };
+ return $context->throw(Template::Constants::ERROR_VIEW,
+ "no view template specified")
+ unless $name;
+
+ my $notfound = $self->{ notfound };
+ my $base = $self->{ base };
+ my ($template, $block, $error);
+
+ return $block
+ if ($block = $self->{ _BLOCKS }->{ $name });
+
+ # try the named template
+ $template = $self->template_name($name);
+ $self->DEBUG("looking for $template\n") if $DEBUG;
+ eval { $template = $context->template($template) };
+
+ # try asking the base view if not found
+ if (($error = $@) && $base) {
+ $self->DEBUG("asking base for $name\n") if $DEBUG;
+ eval { $template = $base->template($name) };
+ }
+
+ # try the 'notfound' template (if defined) if that failed
+ if (($error = $@) && $notfound) {
+ unless ($template = $self->{ _BLOCKS }->{ $notfound }) {
+ $notfound = $self->template_name($notfound);
+ $self->DEBUG("not found, looking for $notfound\n") if $DEBUG;
+ eval { $template = $context->template($notfound) };
+
+ return $context->throw(Template::Constants::ERROR_VIEW, $error)
+ if $@; # return first error
+ }
+ }
+ elsif ($error) {
+ $self->DEBUG("no 'notfound'\n")
+ if $DEBUG;
+ return $context->throw(Template::Constants::ERROR_VIEW, $error);
+ }
+ return $template;
+}
+
+
+#------------------------------------------------------------------------
+# template_name($template)
+#
+# Returns the name of the specified template with any appropriate prefix
+# and/or suffix added.
+#------------------------------------------------------------------------
+
+sub template_name {
+ my ($self, $template) = @_;
+ $template = $self->{ prefix } . $template . $self->{ suffix }
+ if $template;
+
+ $self->DEBUG("template name: $template\n") if $DEBUG;
+ return $template;
+}
+
+
+#------------------------------------------------------------------------
+# default($val)
+#
+# Special case accessor to retrieve/update 'default' as an alias for
+# '$map->{ default }'.
+#------------------------------------------------------------------------
+
+sub default {
+ my $self = shift;
+ return @_ ? ($self->{ map }->{ default } = shift)
+ : $self->{ map }->{ default };
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+
+# Returns/updates public internal data items (i.e. not prefixed '_' or
+# '.') or presents a view if the method matches the view_prefix item,
+# e.g. view_foo(...) => view('foo', ...). Similarly, the
+# include_prefix is used, if defined, to map include_foo(...) to
+# include('foo', ...). If that fails then the entire method name will
+# be used as the name of a template to include iff the include_named
+# parameter is set (default: 1). Last attempt is to match the entire
+# method name to a view() call, iff view_naked is set. Otherwise, a
+# 'view' exception is raised reporting the error "no such view member:
+# $method".
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ if ($item =~ /^[\._]/) {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "attempt to view private member: $item");
+ }
+ elsif (exists $self->{ $item }) {
+ # update existing config item (e.g. 'prefix') if unsealed
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "cannot update config item in sealed view: $item")
+ if @_ && $self->{ SEALED };
+ $self->DEBUG("accessing item: $item\n") if $DEBUG;
+ return @_ ? ($self->{ $item } = shift) : $self->{ $item };
+ }
+ elsif (exists $self->{ data }->{ $item }) {
+ # get/update existing data item (must be unsealed to update)
+ if (@_ && $self->{ SEALED }) {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "cannot update item in sealed view: $item")
+ unless $self->{ silent };
+ # ignore args if silent
+ @_ = ();
+ }
+ $self->DEBUG(@_ ? "updating data item: $item <= $_[0]\n"
+ : "returning data item: $item\n") if $DEBUG;
+ return @_ ? ($self->{ data }->{ $item } = shift)
+ : $self->{ data }->{ $item };
+ }
+ elsif (@_ && ! $self->{ SEALED }) {
+ # set data item if unsealed
+ $self->DEBUG("setting unsealed data: $item => @_\n") if $DEBUG;
+ $self->{ data }->{ $item } = shift;
+ }
+ elsif ($item =~ s/^$self->{ view_prefix }//) {
+ $self->DEBUG("returning view($item)\n") if $DEBUG;
+ return $self->view($item, @_);
+ }
+ elsif ($item =~ s/^$self->{ include_prefix }//) {
+ $self->DEBUG("returning include($item)\n") if $DEBUG;
+ return $self->include($item, @_);
+ }
+ elsif ($self->{ include_naked }) {
+ $self->DEBUG("returning naked include($item)\n") if $DEBUG;
+ return $self->include($item, @_);
+ }
+ elsif ($self->{ view_naked }) {
+ $self->DEBUG("returning naked view($item)\n") if $DEBUG;
+ return $self->view($item, @_);
+ }
+ else {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "no such view member: $item");
+ }
+}
+
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Template::View - customised view of a template processing context
+
+=head1 SYNOPSIS
+
+ # define a view
+ [% VIEW view
+ # some standard args
+ prefix => 'my_',
+ suffix => '.tt2',
+ notfound => 'no_such_file'
+ ...
+
+ # any other data
+ title => 'My View title'
+ other_item => 'Joe Random Data'
+ ...
+ %]
+ # add new data definitions, via 'my' self reference
+ [% my.author = "$abw.name <$abw.email>" %]
+ [% my.copy = "&copy; Copyright 2000 $my.author" %]
+
+ # define a local block
+ [% BLOCK header %]
+ This is the header block, title: [% title or my.title %]
+ [% END %]
+
+ [% END %]
+
+ # access data items for view
+ [% view.title %]
+ [% view.other_item %]
+
+ # access blocks directly ('include_naked' option, set by default)
+ [% view.header %]
+ [% view.header(title => 'New Title') %]
+
+ # non-local templates have prefix/suffix attached
+ [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
+
+ # more verbose form of block access
+ [% view.include( 'header', title => 'The Header Title' ) %]
+ [% view.include_header( title => 'The Header Title' ) %]
+
+ # very short form of above ('include_naked' option, set by default)
+ [% view.header( title => 'The Header Title' ) %]
+
+ # non-local templates have prefix/suffix attached
+ [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
+
+ # fallback on the 'notfound' template ('my_no_such_file.tt2')
+ # if template not found
+ [% view.include('missing') %]
+ [% view.include_missing %]
+ [% view.missing %]
+
+ # print() includes a template relevant to argument type
+ [% view.print("some text") %] # type=TEXT, template='text'
+
+ [% BLOCK my_text.tt2 %] # 'text' with prefix/suffix
+ Text: [% item %]
+ [% END %]
+
+ # now print() a hash ref, mapped to 'hash' template
+ [% view.print(some_hash_ref) %] # type=HASH, template='hash'
+
+ [% BLOCK my_hash.tt2 %] # 'hash' with prefix/suffix
+ hash keys: [% item.keys.sort.join(', ')
+ [% END %]
+
+ # now print() a list ref, mapped to 'list' template
+ [% view.print(my_list_ref) %] # type=ARRAY, template='list'
+
+ [% BLOCK my_list.tt2 %] # 'list' with prefix/suffix
+ list: [% item.join(', ') %]
+ [% END %]
+
+ # print() maps 'My::Object' to 'My_Object'
+ [% view.print(myobj) %]
+
+ [% BLOCK my_My_Object.tt2 %]
+ [% item.this %], [% item.that %]
+ [% END %]
+
+ # update mapping table
+ [% view.map.ARRAY = 'my_list_template' %]
+ [% view.map.TEXT = 'my_text_block' %]
+
+
+ # change prefix, suffix, item name, etc.
+ [% view.prefix = 'your_' %]
+ [% view.default = 'anyobj' %]
+ ...
+
+=head1 DESCRIPTION
+
+TODO
+
+=head1 METHODS
+
+=head2 new($context, \%config)
+
+Creates a new Template::View presenting a custom view of the specified
+$context object.
+
+A reference to a hash array of configuration options may be passed as the
+second argument.
+
+=over 4
+
+=item prefix
+
+Prefix added to all template names.
+
+ [% USE view(prefix => 'my_') %]
+ [% view.view('foo', a => 20) %] # => my_foo
+
+=item suffix
+
+Suffix added to all template names.
+
+ [% USE view(suffix => '.tt2') %]
+ [% view.view('foo', a => 20) %] # => foo.tt2
+
+=item map
+
+Hash array mapping reference types to template names. The print()
+method uses this to determine which template to use to present any
+particular item. The TEXT, HASH and ARRAY items default to 'test',
+'hash' and 'list' appropriately.
+
+ [% USE view(map => { ARRAY => 'my_list',
+ HASH => 'your_hash',
+ My::Foo => 'my_foo', } ) %]
+
+ [% view.print(some_text) %] # => text
+ [% view.print(a_list) %] # => my_list
+ [% view.print(a_hash) %] # => your_hash
+ [% view.print(a_foo) %] # => my_foo
+
+ [% BLOCK text %]
+ Text: [% item %]
+ [% END %]
+
+ [% BLOCK my_list %]
+ list: [% item.join(', ') %]
+ [% END %]
+
+ [% BLOCK your_hash %]
+ hash keys: [% item.keys.sort.join(', ')
+ [% END %]
+
+ [% BLOCK my_foo %]
+ Foo: [% item.this %], [% item.that %]
+ [% END %]
+
+=item method
+
+Name of a method which objects passed to print() may provide for presenting
+themselves to the view. If a specific map entry can't be found for an
+object reference and it supports the method (default: 'present') then
+the method will be called, passing the view as an argument. The object
+can then make callbacks against the view to present itself.
+
+ package Foo;
+
+ sub present {
+ my ($self, $view) = @_;
+ return "a regular view of a Foo\n";
+ }
+
+ sub debug {
+ my ($self, $view) = @_;
+ return "a debug view of a Foo\n";
+ }
+
+In a template:
+
+ [% USE view %]
+ [% view.print(my_foo_object) %] # a regular view of a Foo
+
+ [% USE view(method => 'debug') %]
+ [% view.print(my_foo_object) %] # a debug view of a Foo
+
+=item default
+
+Default template to use if no specific map entry is found for an item.
+
+ [% USE view(default => 'my_object') %]
+
+ [% view.print(objref) %] # => my_object
+
+If no map entry or default is provided then the view will attempt to
+construct a template name from the object class, substituting any
+sequence of non-word characters to single underscores, e.g.
+
+ # 'fubar' is an object of class Foo::Bar
+ [% view.print(fubar) %] # => Foo_Bar
+
+Any current prefix and suffix will be added to both the default template
+name and any name constructed from the object class.
+
+=item notfound
+
+Fallback template to use if any other isn't found.
+
+=item item
+
+Name of the template variable to which the print() method assigns the current
+item. Defaults to 'item'.
+
+ [% USE view %]
+ [% BLOCK list %]
+ [% item.join(', ') %]
+ [% END %]
+ [% view.print(a_list) %]
+
+ [% USE view(item => 'thing') %]
+ [% BLOCK list %]
+ [% thing.join(', ') %]
+ [% END %]
+ [% view.print(a_list) %]
+
+=item view_prefix
+
+Prefix of methods which should be mapped to view() by AUTOLOAD. Defaults
+to 'view_'.
+
+ [% USE view %]
+ [% view.view_header() %] # => view('header')
+
+ [% USE view(view_prefix => 'show_me_the_' %]
+ [% view.show_me_the_header() %] # => view('header')
+
+=item view_naked
+
+Flag to indcate if any attempt should be made to map method names to
+template names where they don't match the view_prefix. Defaults to 0.
+
+ [% USE view(view_naked => 1) %]
+
+ [% view.header() %] # => view('header')
+
+=back
+
+=head2 print( $obj1, $obj2, ... \%config)
+
+TODO
+
+=head2 view( $template, \%vars, \%config );
+
+TODO
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@kfs.orgE<gt>
+
+=head1 REVISION
+
+$Revision: 2.9 $
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>,
+
+=cut
+
+
+
+
+