summaryrefslogtreecommitdiff
path: root/lib/Template/Plugin
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/Plugin
downloadxxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.gz
xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.bz2
* Move files to trunk
Diffstat (limited to 'lib/Template/Plugin')
-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
41 files changed, 10663 insertions, 0 deletions
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>
+