
=head1 NAME

 HTML::Transmorgify - HTML transformation compiler

=head1 SYNOPSIS

 use HTML::Transmorgify;

 my $magic = HTML::Transmorgify->new(xml_quoting => 1);

 $magic->mixin('HTML::Transmorgify::Metatags');
 $magic->mixin('HTML::Transmorgify::FormDefaults');

 my $output = $magic->process($input_text, { %options }, %variables);
 my %output_by_type = $magic->process($input_text, { %options }, %variables);

=head1 DESCRIPTION

HTML::Transmorgify is an HTML compiler framework.  It transforms
HTML into a arrays of static text and CODE callbacks.  The arrays
can be turned into customized HTML very quickly.
The compilation process is quick and the runtime process is 
even quicker.  It is designed for sites that will dynamically
generate all their content.

By itself, HTML::Transmorgify doesn't do anything useful: it is 
just a framework.  Most users will want to use one or more of the
addon modules for HTML::Transmorgify:

=over 4

=item L<HTML::Transmorgify::Metatags>

Provides a template language processor with macros, control
flow, and basic functions.

=item L<HTML::Transmorgify::FormChecksum>

Adds cryptographic checksums to forms so that users cannot add
additional elements, change or remove hidden values, or set dropdowns
or radio buttons to values that were not provided.

=item L<HTML::Transmorgify::FormDefault>

Overrides the default values in the form definition so that the
end-user's prior input is remembered when re-displaying the form.

=item L<HTML::Transmorgify::Crumbs>

Adds cookie crumbs to URLs to and forms so to protect against 
cross-site scripting attacks that direct a logged-in user back
to a page that will do something naughty.

=item L<HTML::Transmorgify::Images>

Changes multiple E<lt>imgE<gt> tags into E<lt>divE<gt> tags with 
background attributes that point into shared composite image.  This
decreases page load time because many fewer items need to be fetched.

It also adds C<height> and C<width> attributes to images that don't
have them yet.

It notices roll-over images and adds javascript to display them.

As of January 2011, this addon is not yet complete.

=back

Additional modules can be added to the framework.

=head1 USING HTML::Transmorgify

By itself, HTML::Transmorgify doesn't do anything useful.  You need to
blend in one of the addon modules or write your own.  

You invoke HTML::Transmorgify with:

 my $output = $magic->process($input_text, { %options }, %variables);

or

 my %output_by_type = $magic->process($input_text, { %options }, %variables);

In array context, the results from processing templates are returned 
as a hash, mapping the type of result to the result string.  Two result
types are always present: C<text> and C<script>.  Other result types 
may be added by add-on modules. 

Currently, all of the options in C<{ %options }> are made available to 
addon modules via the C<$invocation_options> variable.  
In additon the
following items are singled out and have defined meanings:

=over 15

=item query_param

A reference to a L<CGI.pm>-style query parameters hash.

=item input_file

The filename from which the C<$input_text> came from.

=item input_line

A line-number offset from the beginning of the input_file
so that error messages can refer to the line number of the
error.

=back

The C<%variables> are used by L<HTML::Transmorgify::Metatags> as pre-defined
macro values.  They can be strings, hashes, arrays, or objects.  If they're
objects, they should inherit from L<HTML::Transmorgify::ObjectGlue>.

=head1 WRITING ADDON MODULES

HTML::Transmorgify compiles HTML.  It compiles HTML into an
array of callbacks and literals.  It then executes the compiled
array.  Compilation output from an input string is cached for 
reuse.  Files are compiled separately.

When you write an addon module, you
register to be called back for particular HTML tags.  
When you are called back, you need to generate strings to be appended to the 
the final document or callbacks to be called at runtime.

The runtime program is an array: C<@$HTML::Transmorgify::rbuf>.  There is a
little function for appending to that array: C<rbuf()>.

Addon modules generally start the same:

 package HTML::Transmorgify::MODULE_NAME;

 use HTML::Transmorgify qw(continue_compile capture_compile run queue_intercept rbuf postbuf);

 our @ISA = qw(HTML::Transmorgify Exporter);

 sub add_tags
 {
	my ($self, $tobj) = @_;
	$self->intercept_exclusive($tobj, __PACKAGE__, A_NUMBER, %EXCLUSIVE_TAGS)
	$self->intercept_shared($tobj, __PACKAGE__, A_NUMBER, %SHARED_TAGS);
 }

Where "A_NUMBER" is a priority number for choosing which callbacks get called first (lower
is earlier); %EXCLUSIVE_TAGS maps HTML tags to callbacks that compile that HTML tag; and
%SHARED_TAGS maps HTML tags to callbacks that participate in compiling that HTML tag.

Generally you want an exclusive tag when the tag isn't actually part of HTML like when
you are creating a template language like what is done in L<HTML::Transmorgify::Metatags>.

For situations where you are making an adjustment to regular HTML tags, you usually want
a shared tag.  For example, L<HTML::Transmorgify::FormChecksum> and 
L<HTML::Transmorgify::FormDefault> both modify forms and attach callbacks to the
E<lt>inputE<gt> tags.  

Shared tags must be handled carefully so that the multiple modules that are acting on
a tag do not get in each other's way.  Not everything can be handled by with a shared
tag
For example, the E<lt>foreachE<gt> tag defined in
L<HTML::Transmorgify::Metatags> is an exclusive tag.

All callbacks are invoked with the following arguments:

=over 8

=item $attr

A L<HTML::Transmorgify::Attributes> object representing the a tag and its attributes.

=item $closed

Closed if this is an end-tag or self-contained tag like E<lt>hr /E<gt>.

=back

=head2 Shared Tags

The return value from a shared tag callback is ignored unless
the return value is a CODE ref.  The callbacks are called at
compile time.  Any CODE refs returned by the callback will be
invoked at runtime BEFORE the tag is interpolated into the 
results.

Shared tags are always interpolated into the results.

The usual form of a shared tag is something like:

 $SHARED_TAGS{img} = \&img_tag;
 sub img_tag {
    my ($attr, $closed) = @_;
    return 1 unless $attr->raw('alt');
    $attr->eval_at_runtime(1);
    rbuf(sub {
    	my $text = $attr->get('src');
	$text =~ s{.*/}{};
	$text =~ s/([a-z])([A-Z])/$1 $2/g;
	$text =~ s/_/ /g;
	$attr->set(alt => $text);
    });
    return 1;
 }

This example transformation will try to turn the image file name into
an E<lt>altE<gt> tag.

It uses C<rbuf()> to add a callback that will run just before the
E<lt>imgE<gt>
tag is added to the output stream.

It calls

    $attr->eval_at_runtime(1);

to make sure that the 
E<lt>imgE<gt>
tag is evaluated at runtime rather than added as a literal string
at compile time.

It returns a value of 1 to indicate that the 
E<lt>imgE<gt>
tag should be included in the output stream.

=head2 Exclusive Tags

The return value from an exclusive tag indicates whether the
tag should be included in the resulting text or not.  A true
value will cause the tag to be inclucd; a false value will 
not.

=head2 APIs for Callback Writers

=over

=item rbuf()

The tiny rbuf function pushes its arguments on the 
C<HTML::Transmorgify::rbuf>
array.  This array 
represents the output from compiling the HTML.  
It can conntain the following elements:

=over 15

=item strings

Plain scalars will be interpolated into the final
output unchanged.

=item CODE refs

Code references will be called.  For them to 
add to the final output, they need to append to
the strings in C<@$HTML::Transmorgify::result>.  The first of these,
C<$$HTML::Transmorgify::result[0]> is used for regular results.
If additional types of output are needed, the array indexes
should be allocated by calling C<allocate_result_type>.

=back

=item postbuf()

Pushes its arguments onto 
C<@HTML::Transmorgify::post_intercept_push>.  This is used during
shared tag callback processing.  The contents of
C<@HTML::Transmorgify::post_intercept_push> is
pushed onto
C<HTML::Transmorgify::rbuf> after the rest of the
shared tag callback processing is done.  

=item allocate_result_type()

Alocates another "key" for the results array.  The predefined
keys are: C<text> and C<script>.

Keys can be looked up in C<%HTML::Transmorgify::result_index>.

=item boolean()

The boolean function returns false if it's argument is:
C<false>, C<no>, C<off>, C<0>, or undefined.  Otherwise
it returns true. 

=item run($buf, $results)

The run function "executes" the compiled HTML in 
C<@$buf>.   Literal strings will be added to C<$$results[0]>, 
code references will be invoked.

If C<$results> is not specified, it will default to
C<$HTML::Transmorgify::results>.

=item eat_cr()

I<Exclusive tag callbacks only.>
Advance the input parsing position 
C<pos($$HTML::Transmorgify::textref)>
past a newline.

=item $rbuf = compile($cacheline, $textref)

Compiles C<$$textref> and returns the results.
Results are are cached so compiling the same
string (C<$$textref>) in the same context will
result in a cached result.   Do not modify the
returned array (C<@$rbuf>) as since it can be 
handed out again from another call to compile.

The C<$cacheline> argument specifies the caching
context for the input string.
For compiling things in the normal context where
all currently active transformations can apply, use
the context C<$HTML::Transmorgify::modules>.

=item ($rbuf, $deferred) = capture_compile($tag, $starting_attr, $opts, %tags)

I<Exclusive tag callbacks only.>
Continues to compile the current C<$HTML::Transmorgify::textref> (as passed to
compile()) until a closing 
C<E<lt>/$tagE<gt>> 
is reached. 
For better error messages, the 
L<HTML::Transmorgify::Attributes> for the opening tag (C<$starting_attr>) is 
passed in.

The callbacks for multiple tags may be overridden with the C<%tags> argument.
Since these may be shared callbacks, only the callbacks for the module doing
the overridding should be modified.  That should be specified as:
C<$$opts{tag_package}>.

The call to capture_returns after the input pointer has moved past
C<E<lt>/$tagE<gt>>.  
No
C<E<lt>/$tagE<gt>>.  
is pushed into the compile output buffer
C<HTML::Transmorgify::rbuf>
and the callbacks requested for 
C<E<lt>/$tagE<gt>>
have not been called.  To invoke them, simply run:
 
 $deferred->doit();

This will also push a
C<E<lt>/$tagE<gt>>
into the output stream.

=item continue_compile($tag, $starting_attr, $opts, %tags)

I<Exclusive tag callbacks only.>
continue_compile() is very much like C<capture_compile()> except that
the output from the compilation process is appended to the 
existing 
C<HTML::Transmorgify::rbuf>
array and there is no return value from the function.

The purpose of continue_compile() is to allow the normal
compilation process to continue for a while.  The function
returns after 
C<E<lt>/$tagE<gt>>
has been rpocessed.  Tag callbacks may be temporarily 
overridden for the duration of the call.

If C<$tag> is undef, the compilation will continue until
the end of C<$$textref> is reached or until a 
C<HTML::Transmorgify::CloseTag> callback is invoked.

=item local($HTML::Transmorgify::dispatch{"/$tag"}) = HTML::Transmorgify::ClosedTag->new($HTML::Transmorgify::dispatch{"/$tag"});

This will override the callbacks for 
C<E<lt>/$tagE<gt>>
so that continue_compile or capture_compile will stop when
C<E<lt>/$tagE<gt>>
is reached.   This is normally done automatically but this invocation is needed if
stopping for more than one tag.

=item queue_capture($coderef)

I<Shared tag callbacks only.>
Shared tag callbacks cannot use capture_compile() and continue_compile():
they can use queue_capture() instead.  The function that handles calling
all the shared tag callbacks will do a capture_compile(), looking for
the close tag to match the current tag if any of the shared tag callbacks
uses queue_capture().

After the  C<$coderef>s are invoked (they receive the C<$rbuf> from
capture_compile() as their only argument) the temporary C<$rbuf> 
compile buffer will be appended to the main compile buffer.  After that,
the deferred callbacks from the close tag will be invoked.

=item queue_intercept($tag_pkg, %tags)

I<Shared tag callbacks only.>
Shared tag callbacks cannot use capture_compile() and continue_compile():
they can use queue_capture() instead.  The function that handles calling
all the shared tag callbacks will do a continue_compile(), looking for
the close tag to match the current tag if any of the shared tag callbacks
uses queue_intercept().  If queue_capture is also used by a tag callback,
then capture_compile() will be used instead.

For the duration of the continue_compile() (or capture_compile()) tag
callbacks will be overridden by C<%tags>.  C<$tag_pkg> should be set 
to the name of the invoking package (__PACKAGE__).

=back

=head1 NAMING

Calvin & Hobbs used to transmogrify not transmorgify things.  This module will be renamed
to fix my spelling mistake soon.  Other API changes may still be made.  
Please contact the author if you want to be included in dicussions of this
module's future.

=head1 SEE ALSO

Other modules that do similar things:
L<HTML::Seamstreess>

=head1 AUTHOR

David Muir Sharnoff E<lt>muir at idiom dot orgE<gt>

Development of this module is hosted on github: L<http://github.com/muir/HTML--Transmorgify>.

Some parts of this module are Copyright (C) Google, Inc.

=head1 LICENSE

Your choice of LGPL or Artistic License.

