-# http://module-build.sourceforge.net/META-spec.html
-#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
-name: HTML-Element-Library
-version: 3.51
-version_from: lib/HTML/Element/Library.pm
-installdirs: site
+--- #YAML:1.0
+name: HTML-Element-Library
+version: 4.2.a
+abstract: ~
+author:
+ - Terrence Brannon <tbone@cpan.org>
+license: unknown
+distribution_type: module
+configure_requires:
+ ExtUtils::MakeMaker: 0
+build_requires:
+ ExtUtils::MakeMaker: 0
requires:
- Array::Group: 1
- File::Slurp: 9999.06
- HTML::PrettyPrinter: 0.03
- HTML::Tree: 3.19
- List::MoreUtils: 0.09
- List::Rotation::Cycle: 1.003
- Params::Validate: 0.8
- Scalar::Listify: 0.02
-
-distribution_type: module
-generated_by: ExtUtils::MakeMaker version 6.17
+ Array::Group: 1
+ File::Slurp: 9999.06
+ HTML::FillInForm: 0
+ HTML::PrettyPrinter: 0.03
+ HTML::Tree: 3.19
+ List::MoreUtils: 0.09
+ List::Rotation::Cycle: 1.003
+ Moose: 0
+ Params::Validate: 0.8
+ Scalar::Listify: 0.02
+no_index:
+ directory:
+ - t
+ - inc
+generated_by: ExtUtils::MakeMaker version 6.54
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
-WriteMakefile(
- NAME => 'HTML::Element::Library',
- VERSION_FROM => 'lib/HTML/Element/Library.pm', # finds $VERSION
- PREREQ_PM => {
- Array::Group => 1.00,
+WriteMakefile
+ (
+ NAME => 'HTML::Element::Library',
+ VERSION_FROM => 'lib/HTML/Element/Library.pm', # finds $VERSION
+ PREREQ_PM =>
+ {
+ Array::Group => 1.00,
- File::Slurp => 9999.06,
+ Moose => 0,
+ File::Slurp => 9999.06,
- HTML::PrettyPrinter => 0.03,
- HTML::Tree => 3.19,
+ HTML::FillInForm => 0,
+ HTML::PrettyPrinter => 0.03,
+ HTML::Tree => 3.19,
- List::Rotation::Cycle => 1.003,
- List::MoreUtils => 0.09,
+ List::Rotation::Cycle => 1.003,
+ List::MoreUtils => 0.09,
- Params::Validate => 0.80,
+ Params::Validate => 0.80,
- Scalar::Listify => 0.02,
- },
- ($] >= 5.005 ? ## Add these new keywords supported since 5.005
- (ABSTRACT_FROM => 'lib/HTML/Element/Library.pm', # retrieve abstract from module
- AUTHOR => 'Terrence Brannon <terry@hcoop.net>') : ()),
-);
+ Scalar::Listify => 0.02,
+ },
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (
+
+ AUTHOR => 'Terrence Brannon <tbone@cpan.org>') : ()),
+ );
sub MY::postamble { q{
package HTML::Element::Library;
-use 5.006001;
+
use strict;
use warnings;
-our $VERSION = '3.53';
+our $VERSION = '4.2.b';
# https://rt.cpan.org/Ticket/Display.html?id=44105
sub HTML::Element::fillinform {
- my ($tree, $hashref, $return_tree)=@_;
+ my ($tree, $hashref, $return_tree, $guts)=@_;
(ref $hashref) eq 'HASH' or die 'hashref not supplied as argument' ;
my $new_html = HTML::FillInForm->fill(\$html, $hashref);
if ($return_tree) {
- HTML::TreeBuilder->new_from_content($new_html);
+ my $tree = HTML::TreeBuilder->new_from_content($new_html);
+ $tree = $guts ? $tree->guts : $tree ;
} else {
$new_html;
}
warn 'The container tag is ', $container->tag if $p{debug} ;
warn 'hash' . Dumper($p{hash}) if $p{debug} ;
- warn 'at_under' . Dumper(\@_) if $p{debug} ;
+ #warn 'at_under' . Dumper(\@_) if $p{debug} ;
my @same_as = $container->look_down( $p{to_attr} => qr/.+/ ) ;
}
+sub HTML::Element::assign {
+ goto &HTML::Element::content_handler;
+}
+
sub make_counter {
my $i = 1;
1;
__END__
-# Below is stub documentation for your module. You'd better edit it!
-
-=head1 NAME
-
-HTML::Element::Library - HTML::Element convenience functions
-
-=head1 SYNOPSIS
-
- use HTML::Element::Library;
- use HTML::TreeBuilder;
-
-=head1 DESCRIPTION
-
-This method provides API calls for common actions on trees when using
-L<HTML::Tree>.
-
-=head1 METHODS
-
-The test suite contains examples of each of these methods in a
-file C<t/$method.t>
-
-=head2 Positional Querying Methods
-
-=head3 $elem->siblings
-
-Return a list of all nodes under the same parent.
-
-=head3 $elem->sibdex
-
-Return the index of C<$elem> into the array of siblings of which it is
-a part. L<HTML::ElementSuper> calls this method C<addr> but I don't think
-that is a descriptive name. And such naming is deceptively close to the
-C<address> function of C<HTML::Element>. HOWEVER, in the interest of
-backwards compatibility, both methods are available.
-
-=head3 $elem->addr
-
-Same as sibdex
-
-=head3 $elem->position()
-
-Returns the coordinates of this element in the tree it inhabits.
-This is accomplished by succesively calling addr() on ancestor
-elements until either a) an element that does not support these
-methods is found, or b) there are no more parents. The resulting
-list is the n-dimensional coordinates of the element in the tree.
-
-=head2 Element Decoration Methods
-
-=head3 HTML::Element::Library::super_literal($text)
-
-In L<HTML::Element>, Sean Burke discusses super-literals. They are
-text which does not get escaped. Great for includng Javascript in
-HTML. Also great for including foreign language into a document.
-
-So, you basically toss C<super_literal> your text and back comes
-your text wrapped in a C<~literal> element.
-
-One of these days, I'll around to writing a nice C<EXPORT> section.
-
-=head2 Tree Rewriting Methods
-
-=head3 Simplifying calls to HTML::FillInForm
-
-Since HTML::FillInForm gets and returns strings, using HTML::Element instances
-becomes tedious:
-
- 1. Seamstress has an HTML tree that it wants the form filled in on
- 2. Seamstress converts this tree to a string
- 3. FillInForm parses the string into an HTML tree and then fills in the form
- 4. FillInForm converts the HTML tree to a string
- 5. Seamstress re-parses the HTML for additional processing
-
-I've filed a bug about this:
-L<https://rt.cpan.org/Ticket/Display.html?id=44105>
-
-This function, fillinform,
-allows you to pass a tree to fillinform (along with your data structure) and
-get back a tree:
-
- my $new_tree = $html_tree->fillinform($data_structure);
-
-
-=head3 Mapping a hashref to HTML elements
-
-It is very common to get a hashref of data from some external source - flat file, database, XML, etc.
-Therefore, it is important to have a convenient way of mapping this data to HTML.
-
-As it turns out, there are 3 ways to do this in HTML::Element::Library.
-The most strict and structured way to do this is with
-C<content_handler>. Two other methods, C<hashmap> and C<datamap> require less manual mapping and may prove
-even more easy to use in certain cases.
-
-As is usual with Perl, a practical example is always best. So let's take some sample HTML:
-
- <h1>user data</h1>
- <span id="name">?</span>
- <span id="email">?</span>
- <span id="gender">?</span>
-
-Now, let's say our data structure is this:
-
- $ref = { email => 'jim@beam.com', gender => 'lots' } ;
-
-And let's start with the most strict way to get what you want:
-
- $tree->content_handler(email => $ref->{email} , gender => $ref->{gender}) ;
-
-
-In this case, you manually state the mapping between id tags and hashref keys and
-then C<content_handler> retrieves the hashref data and pops it in the specified place.
-
-Now let's look at the two (actually 2 and a half) other hash-mapping methods.
-
- $tree->hashmap(id => $ref);
-
-Now, what this function does is super-destructive. It finds every element in the tree
-with an attribute named id (since 'id' is a parameter, it could find every element with
-some other attribute also) and replaces the content of those elements with the hashref
-value.
-
-So, in the case above, the
-
- <span id="name">?</span>
-
-would come out as
-
- <span id="name"></span>
-
-(it would be blank) - because there is nothing in the hash with that value, so it substituted
-
- $ref->{name}
-
-which was blank and emptied the contents.
-
-Now, let's assume we want to protect name from being auto-assigned. Here is what you do:
-
- $tree->hashmap(id => $ref, ['name']);
-
-That last array ref is an exclusion list.
-
-But wouldnt it be nice if you could do a hashmap, but only assigned things which are defined
-in the hashref? C<< defmap() >> to the rescue:
-
- $tree->defmap(id => $ref);
-
-does just that, so
-
- <span id="name">?</span>
-
-would be left alone.
-
-
-=head4 $elem->hashmap($attr_name, \%hashref, \@excluded, $debug)
-
-This method is designed to take a hashref and populate a series of elements. For example:
-
-
- <table>
- <tr sclass="tr" class="alt" align="left" valign="top">
- <td smap="people_id">1</td>
- <td smap="phone">(877) 255-3239</td>
- <td smap="password">*********</td>
- </tr>
- </table>
-
-In the table above, there are several attributes named C<< smap >>. If we have a hashref whose keys are the same:
-
- my %data = (people_id => 888, phone => '444-4444', password => 'dont-you-dare-render');
-
-Then a single API call allows us to populate the HTML while excluding those ones we dont:
-
- $tree->hashmap(smap => \%data, ['password']);
-
-
-Note: the other way to prevent rendering some of the hash mapping is to not give that element the attr
-you plan to use for hash mapping.
-
-Also note: the function C<< hashmap >> has a simple easy-to-type API. Interally, it calls C<< hash_map >>
-(which has a more verbose keyword calling API). Thus, the above call to C<hashmap()> results in this call:
-
- $tree->hash_map(hash => \%data, to_attr => 'sid', excluding => ['password']);
-
-=head4 $elem->defmap($attr_name, \%hashref, $debug)
-
-C<defmap> was described above.
-
-
-=head4 $elem->content_handler(%hashref)
-
-C<content_handler> is described below.
-
-
-=head3 $elem->replace_content(@new_elem)
-
-Replaces all of C<$elem>'s content with C<@new_elem>.
-
-=head3 $elem->wrap_content($wrapper_element)
-
-Wraps the existing content in the provided element. If the provided element
-happens to be a non-element, a push_content is performed instead.
-
-=head3 $elem->set_child_content(@look_down, $content)
-
- This method looks down $tree using the criteria specified in @look_down using the the HTML::Element look_down() method.
-
-After finding the node, it detaches the node's content and pushes $content as the node's content.
-
-=head3 $tree->content_handler(%id_content)
-
-This is a convenience method. Because the look_down criteria will often simply be:
-
- id => 'fixme'
-
-to find things like:
-
- <a id=fixme href=http://www.somesite.org>replace_content</a>
-
-You can call this method to shorten your typing a bit. You can simply type
-
- $elem->content_handler( fixme => 'new text' )
-
-Instead of typing:
-
- $elem->set_child_content(sid => 'fixme', 'new text')
-
-ALSO NOTE: you can pass a hash whose keys are C<id>s and whose values are the content you want there and it will perform the replacement on each hash member:
-
- my %id_content = (name => "Terrence Brannon",
- email => 'tbrannon@in.com',
- balance => 666,
- content => $main_content);
-
- $tree->content_handler(%id_content);
-
-=head3 $tree->highlander($subtree_span_id, $conditionals, @conditionals_args)
-
-This allows for "if-then-else" style processing. Highlander was a movie in
-which only one would survive. Well, in terms of a tree when looking at a
-structure that you want to process in C<if-then-else> style, only one child
-will survive. For example, given this HTML template:
-
- <span klass="highlander" id="age_dialog">
- <span id="under10">
- Hello, does your mother know you're
- using her AOL account?
- </span>
- <span id="under18">
- Sorry, you're not old enough to enter
- (and too dumb to lie about your age)
- </span>
- <span id="welcome">
- Welcome
- </span>
- </span>
-
-We only want one child of the C<span> tag with id C<age_dialog> to remain
-based on the age of the person visiting the page.
-
-So, let's setup a call that will prune the subtree as a function of age:
-
- sub process_page {
- my $age = shift;
- my $tree = HTML::TreeBuilder->new_from_file('t/html/highlander.html');
-
- $tree->highlander
- (age_dialog =>
- [
- under10 => sub { $_[0] < 10} ,
- under18 => sub { $_[0] < 18} ,
- welcome => sub { 1 }
- ],
- $age
- );
-
-And there we have it. If the age is less than 10, then the node with
-id C<under10> remains. For age less than 18, the node with id C<under18>
-remains.
-Otherwise our "else" condition fires and the child with id C<welcome> remains.
-
-=head3 $tree->passover(@id_of_element)
-
-In some cases, you know exactly which element(s) should survive. In this case,
-you can simply call C<passover> to remove it's (their) siblings. For the HTML
-above, you could delete C<under10> and C<welcome> by simply calling:
-
- $tree->passover('under18');
-
-Because passover takes an array, you can specify several children to preserve.
-
-=head3 $tree->highlander2($tree, $conditionals, @conditionals_args)
-
-Right around the same time that C<table2()> came into being, Seamstress
-began to tackle tougher and tougher processing problems. It became clear that
-a more powerful highlander was needed... one that not only snipped the tree
-of the nodes that should not survive, but one that allows for
-post-processing of the survivor node. And one that was more flexible with
-how to find the nodes to snip.
-
-Thus (drum roll) C<highlander2()>.
-
-So let's look at our HTML which requires post-selection processing:
-
- <span klass="highlander" id="age_dialog">
- <span id="under10">
- Hello, little <span id=age>AGE</span>-year old,
- does your mother know you're using her AOL account?
- </span>
- <span id="under18">
- Sorry, you're only <span id=age>AGE</span>
- (and too dumb to lie about your age)
- </span>
- <span id="welcome">
- Welcome, isn't it good to be <span id=age>AGE</span> years old?
- </span>
-</span>
-
-In this case, a branch survives, but it has dummy data in it. We must take
-the surviving segment of HTML and rewrite the age C<span> with the age.
-Here is how we use C<highlander2()> to do so:
-
- sub replace_age {
- my $branch = shift;
- my $age = shift;
- $branch->look_down(id => 'age')->replace_content($age);
- }
-
- my $if_then = $tree->look_down(id => 'age_dialog');
-
- $if_then->highlander2(
- cond => [
- under10 => [
- sub { $_[0] < 10} ,
- \&replace_age
- ],
- under18 => [
- sub { $_[0] < 18} ,
- \&replace_age
- ],
- welcome => [
- sub { 1 },
- \&replace_age
- ]
- ],
- cond_arg => [ $age ]
- );
-
-We pass it the tree (C<$if_then>), an arrayref of conditions
-(C<cond>) and an arrayref of arguments which are passed to the
-C<cond>s and to the replacement subs.
-
-The C<under10>, C<under18> and C<welcome> are id attributes in the
-tree of the siblings of which only one will survive. However,
-should you need to do
-more complex look-downs to find the survivor,
-then supply an array ref instead of a simple
-scalar:
-
-
- $if_then->highlander2(
- cond => [
- [class => 'r12'] => [
- sub { $_[0] < 10} ,
- \&replace_age
- ],
- [class => 'z22'] => [
- sub { $_[0] < 18} ,
- \&replace_age
- ],
- [class => 'w88'] => [
- sub { 1 },
- \&replace_age
- ]
- ],
- cond_arg => [ $age ]
- );
-
-
-=head3 $tree->overwrite_attr($mutation_attr => $mutating_closures)
-
-This method is designed for taking a tree and reworking a set of nodes in
-a stereotyped fashion. For instance let's say you have 3 remote image
-archives, but you don't want to put long URLs in your img src
-tags for reasons of abstraction, re-use and brevity. So instead you do this:
-
- <img src="/img/smiley-face.jpg" fixup="src lnc">
- <img src="/img/hot-babe.jpg" fixup="src playboy">
- <img src="/img/footer.jpg" fixup="src foobar">
-
-and then when the tree of HTML is being processed, you make this call:
-
- my %closures = (
- lnc => sub { my ($tree, $mute_node, $attr_value)= @_; "http://lnc.usc.edu$attr_value" },
- playboy => sub { my ($tree, $mute_node, $attr_value)= @_; "http://playboy.com$attr_value" }
- foobar => sub { my ($tree, $mute_node, $attr_value)= @_; "http://foobar.info$attr_value" }
- )
-
- $tree->overwrite_attr(fixup => \%closures) ;
-
-and the tags come out modified like so:
-
- <img src="http://lnc.usc.edu/img/smiley-face.jpg" fixup="src lnc">
- <img src="http://playboy.com/img/hot-babe.jpg" fixup="src playboy">
- <img src="http://foobar.info/img/footer.jpg" fixup="src foobar">
-
-=head3 $tree->mute_elem($mutation_attr => $mutating_closures, [ $post_hook ] )
-
-This is a generalization of C<overwrite_attr>. C<overwrite_attr>
-assumes the return value of the
-closure is supposed overwrite an attribute value and does it for you.
-C<mute_elem> is a more general function which does nothing but
-hand the closure the element and let it mutate it as it jolly well pleases :)
-
-In fact, here is the implementation of C<overwrite_attr>
-to give you a taste of how C<mute_attr> is used:
-
- sub overwrite_action {
- my ($mute_node, %X) = @_;
-
- $mute_node->attr($X{local_attr}{name} => $X{local_attr}{value}{new});
- }
-
-
- sub HTML::Element::overwrite_attr {
- my $tree = shift;
-
- $tree->mute_elem(@_, \&overwrite_action);
- }
-
-
-
-
-=head2 Tree-Building Methods
-
-
-
-=head3 Unrolling an array via a single sample element (<ul> container)
-
-This is best described by example. Given this HTML:
-
- <strong>Here are the things I need from the store:</strong>
- <ul>
- <li class="store_items">Sample item</li>
- </ul>
-
-We can unroll it like so:
-
- my $li = $tree->look_down(class => 'store_items');
-
- my @items = qw(bread butter vodka);
-
- $tree->iter($li => @items);
-
-To produce this:
-
-
- <html>
- <head></head>
- <body>Here are the things I need from the store:
- <ul>
- <li class="store_items">bread</li>
- <li class="store_items">butter</li>
- <li class="store_items">vodka</li>
- </ul>
- </body>
- </html>
-
-Now, you might be wondering why the API call is:
-
- $tree->iter($li => @items)
-
-instead of:
-
- $li->iter(@items)
-
-and there is no good answer. The latter would be more concise and it is what I
-should have done.
-
-=head3 Unrolling an array via n sample elements (<dl> container)
-
-C<iter()> was fine for awhile, but some things
-(e.g. definition lists) need a more general function to make them easy to
-do. Hence C<iter2()>. This function will be explained by example of unrolling
-a simple definition list.
-
-So here's our mock-up HTML from the designer:
-
- <dl class="dual_iter" id="service_plan">
- <dt>
- Artist
- </dt>
- <dd>
- A person who draws blood.
- </dd>
-
- <dt>
- Musician
- </dt>
- <dd>
- A clone of Iggy Pop.
- </dd>
-
- <dt>
- Poet
- </dt>
- <dd>
- A relative of Edgar Allan Poe.
- </dd>
-
- <dt class="adstyle">sample header</dt>
- <dd class="adstyle2">sample data</dd>
-
- </dl>
-
-
-And we want to unroll our data set:
-
- my @items = (
- ['the pros' => 'never have to worry about service again'],
- ['the cons' => 'upfront extra charge on purchase'],
- ['our choice' => 'go with the extended service plan']
- );
-
-
-Now, let's make this problem a bit harder to show off the power of C<iter2()>.
-Let's assume that we want only the last <dt> and it's accompanying <dd>
-(the one with "sample data") to be used as the sample data
-for unrolling with our data set. Let's further assume that we want them to
-remain in the final output.
-
-So now, the API to C<iter2()> will be discussed and we will explain how our
-goal of getting our data into HTML fits into the API.
-
-=over 4
-
-=item * wrapper_ld
-
-This is how to look down and find the container of all the elements we will
-be unrolling. The <dl> tag is the container for the dt and dd tags we will be
-unrolling.
-
-If you pass an anonymous subroutine, then it is presumed that execution of
-this subroutine will return the HTML::Element representing the container tag.
-If you pass an array ref, then this will be dereferenced and passed to
-C<HTML::Element::look_down()>.
-
-default value: C<< ['_tag' => 'dl'] >>
-
-Based on the mock HTML above, this default is fine for finding our container
-tag. So let's move on.
-
-=item * wrapper_data
-
-This is an array reference of data that we will be putting into the container.
-You must supply this. C<@items> above is our C<wrapper_data>.
-
-=item * wrapper_proc
-
-After we find the container via C<wrapper_ld>, we may want to pre-process
-some aspect of this tree. In our case the first two sets of dt and dd need
-to be removed, leaving the last dt and dd. So, we supply a C<wrapper_proc>
-which will do this.
-
-default: undef
-
-=item * item_ld
-
-This anonymous subroutine returns an array ref of C<HTML::Element>s that will
-be cloned and populated with item data
-(item data is a "row" of C<wrapper_data>).
-
-default: returns an arrayref consisting of the dt and dd element inside the
-container.
-
-=item * item_data
-
-This is a subroutine that takes C<wrapper_data> and retrieves one "row"
-to be "pasted" into the array ref of C<HTML::Element>s found via C<item_ld>.
-I hope that makes sense.
-
-default: shifts C<wrapper_data>.
-
-=item * item_proc
-
-This is a subroutine that takes the C<item_data> and the C<HTML::Element>s
-found via C<item_ld> and produces an arrayref of C<HTML::Element>s which will
-eventually be spliced into the container.
-
-Note that this subroutine MUST return the new items. This is done
-So that more items than were passed in can be returned. This is
-useful when, for example, you must return 2 dts for an input data item.
-And when would you do this? When a single term has multiple spellings
-for instance.
-
-default: expects C<item_data> to be an arrayref of two elements and
-C<item_elems> to be an arrayref of two C<HTML::Element>s. It replaces the
-content of the C<HTML::Element>s with the C<item_data>.
-
-=item * splice
-
-After building up an array of C<@item_elems>, the subroutine passed as
-C<splice> will be given the parent container HTML::Element and the
-C<@item_elems>. How the C<@item_elems> end up in the container is up to this
-routine: it could put half of them in. It could unshift them or whatever.
-
-default: C<< $container->splice_content(0, 2, @item_elems) >>
-In other words, kill the 2 sample elements with the newly generated
-@item_elems
-
-=back
-
-So now that we have documented the API, let's see the call we need:
-
- $tree->iter2(
- # default wrapper_ld ok.
- wrapper_data => \@items,
- wrapper_proc => sub {
- my ($container) = @_;
-
- # only keep the last 2 dts and dds
- my @content_list = $container->content_list;
- $container->splice_content(0, @content_list - 2);
- },
-
- # default item_ld is fine.
- # default item_data is fine.
- # default item_proc is fine.
- splice => sub {
- my ($container, @item_elems) = @_;
- $container->unshift_content(@item_elems);
- },
- debug => 1,
- );
-
-
-
-
-=head3 Select Unrolling
-
-The C<unroll_select> method has this API:
-
- $tree->unroll_select(
- select_label => $id_label,
- option_value => $closure, # how to get option value from data row
- option_content => $closure, # how to get option content from data row
- option_selected => $closure, # boolean to decide if SELECTED
- data => $data # the data to be put into the SELECT
- data_iter => $closure # the thing that will get a row of data
- debug => $boolean,
- append => $boolean, # remove the sample <OPTION> data or append?
- );
-
-Here's an example:
-
- $tree->unroll_select(
- select_label => 'clan_list',
- option_value => sub { my $row = shift; $row->clan_id },
- option_content => sub { my $row = shift; $row->clan_name },
- option_selected => sub { my $row = shift; $row->selected },
- data => \@query_results,
- data_iter => sub { my $data = shift; $data->next },
- append => 0,
- debug => 0
- );
-
-
-
-=head2 Tree-Building Methods: Table Generation
-
-Matthew Sisk has a much more intuitive (imperative)
-way to generate tables via his module
-L<HTML::ElementTable|HTML::ElementTable>.
-However, for those with callback fever, the following
-method is available. First, we look at a nuts and bolts way to build a table
-using only standard L<HTML::Tree> API calls. Then the C<table> method
-available here is discussed.
-
-=head3 Sample Model
-
- package Simple::Class;
-
- use Set::Array;
-
- my @name = qw(bob bill brian babette bobo bix);
- my @age = qw(99 12 44 52 12 43);
- my @weight = qw(99 52 80 124 120 230);
-
-
- sub new {
- my $this = shift;
- bless {}, ref($this) || $this;
- }
-
- sub load_data {
- my @data;
-
- for (0 .. 5) {
- push @data, {
- age => $age[rand $#age] + int rand 20,
- name => shift @name,
- weight => $weight[rand $#weight] + int rand 40
- }
- }
-
- Set::Array->new(@data);
- }
-
-
- 1;
-
-
-=head4 Sample Usage:
-
- my $data = Simple::Class->load_data;
- ++$_->{age} for @$data
-
-=head3 Inline Code to Unroll a Table
-
-=head4 HTML
-
- <html>
-
- <table id="load_data">
-
- <tr> <th>name</th><th>age</th><th>weight</th> </tr>
-
- <tr id="iterate">
-
- <td id="name"> NATURE BOY RIC FLAIR </td>
- <td id="age"> 35 </td>
- <td id="weight"> 220 </td>
-
- </tr>
-
- </table>
-
- </html>
-
-
-=head4 The manual way (*NOT* recommended)
-
- require 'simple-class.pl';
- use HTML::Seamstress;
-
- # load the view
- my $seamstress = HTML::Seamstress->new_from_file('simple.html');
-
- # load the model
- my $o = Simple::Class->new;
- my $data = $o->load_data;
-
- # find the <table> and <tr>
- my $table_node = $seamstress->look_down('id', 'load_data');
- my $iter_node = $table_node->look_down('id', 'iterate');
- my $table_parent = $table_node->parent;
-
-
- # drop the sample <table> and <tr> from the HTML
- # only add them in if there is data in the model
- # this is achieved via the $add_table flag
-
- $table_node->detach;
- $iter_node->detach;
- my $add_table;
-
- # Get a row of model data
- while (my $row = shift @$data) {
-
- # We got row data. Set the flag indicating ok to hook the table into the HTML
- ++$add_table;
-
- # clone the sample <tr>
- my $new_iter_node = $iter_node->clone;
-
- # find the tags labeled name age and weight and
- # set their content to the row data
- $new_iter_node->content_handler($_ => $row->{$_})
- for qw(name age weight);
-
- $table_node->push_content($new_iter_node);
-
- }
-
- # reattach the table to the HTML tree if we loaded data into some table rows
-
- $table_parent->push_content($table_node) if $add_table;
-
- print $seamstress->as_HTML;
-
-
-
-=head3 $tree->table() : API call to Unroll a Table
-
- require 'simple-class.pl';
- use HTML::Seamstress;
-
- # load the view
- my $seamstress = HTML::Seamstress->new_from_file('simple.html');
- # load the model
- my $o = Simple::Class->new;
-
- $seamstress->table
- (
- # tell seamstress where to find the table, via the method call
- # ->look_down('id', $gi_table). Seamstress detaches the table from the
- # HTML tree automatically if no table rows can be built
-
- gi_table => 'load_data',
-
- # tell seamstress where to find the tr. This is a bit useless as
- # the <tr> usually can be found as the first child of the parent
-
- gi_tr => 'iterate',
-
- # the model data to be pushed into the table
-
- table_data => $o->load_data,
-
- # the way to take the model data and obtain one row
- # if the table data were a hashref, we would do:
- # my $key = (keys %$data)[0]; my $val = $data->{$key}; delete $data->{$key}
-
- tr_data => sub { my ($self, $data) = @_;
- shift(@{$data}) ;
- },
-
- # the way to take a row of data and fill the <td> tags
-
- td_data => sub { my ($tr_node, $tr_data) = @_;
- $tr_node->content_handler($_ => $tr_data->{$_})
- for qw(name age weight) }
-
- );
-
-
- print $seamstress->as_HTML;
-
-
-
-=head4 Looping over Multiple Sample Rows
-
-* HTML
-
- <html>
-
- <table id="load_data" CELLPADDING=8 BORDER=2>
-
- <tr> <th>name</th><th>age</th><th>weight</th> </tr>
-
- <tr id="iterate1" BGCOLOR="white" >
-
- <td id="name"> NATURE BOY RIC FLAIR </td>
- <td id="age"> 35 </td>
- <td id="weight"> 220 </td>
-
- </tr>
- <tr id="iterate2" BGCOLOR="#CCCC99">
-
- <td id="name"> NATURE BOY RIC FLAIR </td>
- <td id="age"> 35 </td>
- <td id="weight"> 220 </td>
-
- </tr>
-
- </table>
-
- </html>
-
-
-* Only one change to last API call.
-
-This:
-
- gi_tr => 'iterate',
-
-becomes this:
-
- gi_tr => ['iterate1', 'iterate2']
-
-=head3 $tree->table2() : New API Call to Unroll a Table
-
-After 2 or 3 years with C<table()>, I began to develop
-production websites with it and decided it needed a cleaner
-interface, particularly in the area of handling the fact that
-C<id> tags will be the same after cloning a table row.
-
-First, I will give a dry listing of the function's argument parameters.
-This will not be educational most likely. A better way to understand how
-to use the function is to read through the incremental unrolling of the
-function's interface given in conversational style after the dry listing.
-But take your pick. It's the same information given in two different
-ways.
-
-=head4 Dry/technical parameter documentation
-
-C<< $tree->table2(%param) >> takes the following arguments:
-
-=over
-
-=item * C<< table_ld => $look_down >> : optional
-
-How to find the C<table> element in C<$tree>. If C<$look_down> is an
-arrayref, then use C<look_down>. If it is a CODE ref, then call it,
-passing it C<$tree>.
-
-Defaults to C<< ['_tag' => 'table'] >> if not passed in.
-
-=item * C<< table_data => $tabular_data >> : required
-
-The data to fill the table with. I<Must> be passed in.
-
-=item * C<< table_proc => $code_ref >> : not implemented
-
-A subroutine to do something to the table once it is found.
-Not currently implemented. Not obviously necessary. Just
-created because there is a C<tr_proc> and C<td_proc>.
-
-=item * C<< tr_ld => $look_down >> : optional
-
-Same as C<table_ld> but for finding the table row elements. Please note
-that the C<tr_ld> is done on the table node that was found I<instead>
-of the whole HTML tree. This makes sense. The C<tr>s that you want exist
-below the table that was just found.
-
-Defaults to C<< ['_tag' => 'tr'] >> if not passed in.
-
-=item * C<< tr_data => $code_ref >> : optional
-
-How to take the C<table_data> and return a row. Defaults to:
-
- sub { my ($self, $data) = @_;
- shift(@{$data}) ;
- }
-
-=item * C<< tr_proc => $code_ref >> : optional
-
-Something to do to the table row we are about to add to the
-table we are making. Defaults to a routine which makes the C<id>
-attribute unique:
-
- sub {
- my ($self, $tr, $tr_data, $tr_base_id, $row_count) = @_;
- $tr->attr(id => sprintf "%s_%d", $tr_base_id, $row_count);
- }
-
-=item * C<< td_proc => $code_ref >> : required
-
-This coderef will take the row of data and operate on the C<td> cells that
-are children of the C<tr>. See C<t/table2.t> for several usage examples.
-
-Here's a sample one:
-
- sub {
- my ($tr, $data) = @_;
- my @td = $tr->look_down('_tag' => 'td');
- for my $i (0..$#td) {
- $td[$i]->splice_content(0, 1, $data->[$i]);
- }
- }
-
-=cut
-
-=head4 Conversational parameter documentation
-
-The first thing you need is a table. So we need a look down for that. If you
-don't give one, it defaults to
-
- ['_tag' => 'table']
-
-What good is a table to display in without data to display?!
-So you must supply a scalar representing your tabular
-data source. This scalar might be an array reference, a C<next>able iterator,
-a DBI statement handle. Whatever it is, it can be iterated through to build
-up rows of table data.
-These two required fields (the way to find the table and the data to
-display in the table) are C<table_ld> and C<table_data>
-respectively. A little more on C<table_ld>. If this happens to be a CODE ref,
-then execution
-of the code ref is presumed to return the C<HTML::Element>
-representing the table in the HTML tree.
-
-Next, we get the row or rows which serve as sample C<tr> elements by doing
-a C<look_down> from the C<table_elem>. While normally one sample row
-is enough to unroll a table, consider when you have alternating
-table rows. This API call would need one of each row so that it can
-cycle through the
-sample rows as it loops through the data.
-Alternatively, you could always just use one row and
-make the necessary changes to the single C<tr> row by
-mutating the element in C<tr_proc>,
-discussed below. The default C<tr_ld> is
-C<< ['_tag' => 'tr'] >> but you can overwrite it. Note well, if you overwrite
-it with a subroutine, then it is expected that the subroutine will return
-the C<HTML::Element>(s)
-which are C<tr> element(s).
-The reason a subroutine might be preferred is in the case
-that the HTML designers gave you 8 sample C<tr> rows but only one
-prototype row is needed.
-So you can write a subroutine, to splice out the 7 rows you don't need
-and leave the one sample
-row remaining so that this API call can clone it and supply it to
-the C<tr_proc> and C<td_proc> calls.
-
-Now, as we move through the table rows with table data,
-we need to do two different things on
-each table row:
-
-=over 4
-
-=item * get one row of data from the C<table_data> via C<tr_data>
-
-The default procedure assumes the C<table_data> is an array reference and
-shifts a row off of it:
-
- sub { my ($self, $data) = @_;
- shift(@{$data}) ;
- }
-
-Your function MUST return undef when there is no more rows to lay out.
-
-=item * take the C<tr> element and mutate it via C<tr_proc>
-
-The default procedure simply makes the id of the table row unique:
-
- sub { my ($self, $tr, $tr_data, $row_count, $root_id) = @_;
- $tr->attr(id => sprintf "%s_%d", $root_id, $row_count);
- }
-
-=back
-
-Now that we have our row of data, we call C<td_proc> so that it can
-take the data and the C<td> cells in this C<tr> and process them.
-This function I<must> be supplied.
-
-
-=head3 Whither a Table with No Rows
-
-Often when a table has no rows, we want to display a message
-indicating this to the view. Use conditional processing to decide what
-to display:
-
- <span id=no_data>
- <table><tr><td>No Data is Good Data</td></tr></table>
- </span>
- <span id=load_data>
- <html>
-
- <table id="load_data">
-
- <tr> <th>name</th><th>age</th><th>weight</th> </tr>
-
- <tr id="iterate">
-
- <td id="name"> NATURE BOY RIC FLAIR </td>
- <td id="age"> 35 </td>
- <td id="weight"> 220 </td>
-
- </tr>
-
- </table>
-
- </html>
-
- </span>
-
-
-
-
-=head1 SEE ALSO
-
-=over
-
-=item * L<HTML::Tree>
-
-A perl package for creating and manipulating HTML trees
-
-=item * L<HTML::ElementTable>
-
-An L<HTML::Tree> - based module which allows for manipulation of HTML
-trees using cartesian coordinations.
-
-=item * L<HTML::Seamstress>
-
-An L<HTML::Tree> - based module inspired by
-XMLC (L<http://xmlc.enhydra.org>), allowing for dynamic
-HTML generation via tree rewriting.
-
-=head1 TODO
-
-=over
-
-=item * highlander2
-
-currently the API expects the subtrees to survive or be pruned to be
-identified by id:
-
- $if_then->highlander2([
- under10 => sub { $_[0] < 10} ,
- under18 => sub { $_[0] < 18} ,
- welcome => [
- sub { 1 },
- sub {
- my $branch = shift;
- $branch->look_down(id => 'age')->replace_content($age);
- }
- ]
- ],
- $age
- );
-
-but, it should be more flexible. the C<under10>, and C<under18> are
-expected to be ids in the tree... but it is not hard to have a check to
-see if this field is an array reference and if it, then to do a look
-down instead:
-
- $if_then->highlander2([
- [class => 'under10'] => sub { $_[0] < 10} ,
- [class => 'under18'] => sub { $_[0] < 18} ,
- [class => 'welcome'] => [
- sub { 1 },
- sub {
- my $branch = shift;
- $branch->look_down(id => 'age')->replace_content($age);
- }
- ]
- ],
- $age
- );
-
-
-
-=cut
-
-=head1 SEE ALSO
-
-L<HTML::Seamstress>
-
-=head1 AUTHOR / SOURCE
-
-Terrence Brannon, E<lt>tbone@cpan.orgE<gt>
-
-Many thanks to BARBIE for his RT bug report.
-
-The source is at L<http://github.com/metaperl/html-element-library/tree/master>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2004 by Terrence Brannon
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.8.4 or,
-at your option, any later version of Perl 5 you may have available.
-
-
-=cut
--- /dev/null
+=head1 NAME
+
+HTML::Element::Library - HTML::Element convenience functions
+
+=head1 SYNOPSIS
+
+ use HTML::Element::Library;
+ use HTML::TreeBuilder;
+
+=head1 DESCRIPTION
+
+This method provides API calls for common actions on trees when using
+L<HTML::Tree>.
+
+=head1 METHODS
+
+The test suite contains examples of each of these methods in a
+file C<t/$method.t>
+
+=head2 Positional Querying Methods
+
+=head3 $elem->siblings
+
+Return a list of all nodes under the same parent.
+
+=head3 $elem->sibdex
+
+Return the index of C<$elem> into the array of siblings of which it is
+a part. L<HTML::ElementSuper> calls this method C<addr> but I don't think
+that is a descriptive name. And such naming is deceptively close to the
+C<address> function of C<HTML::Element>. HOWEVER, in the interest of
+backwards compatibility, both methods are available.
+
+=head3 $elem->addr
+
+Same as sibdex
+
+=head3 $elem->position()
+
+Returns the coordinates of this element in the tree it inhabits.
+This is accomplished by succesively calling addr() on ancestor
+elements until either a) an element that does not support these
+methods is found, or b) there are no more parents. The resulting
+list is the n-dimensional coordinates of the element in the tree.
+
+=head2 Element Decoration Methods
+
+=head3 HTML::Element::Library::super_literal($text)
+
+In L<HTML::Element>, Sean Burke discusses super-literals. They are
+text which does not get escaped. Great for includng Javascript in
+HTML. Also great for including foreign language into a document.
+
+So, you basically toss C<super_literal> your text and back comes
+your text wrapped in a C<~literal> element.
+
+One of these days, I'll around to writing a nice C<EXPORT> section.
+
+=head2 Tree Rewriting Methods
+
+=head3 Simplifying calls to HTML::FillInForm
+
+Since HTML::FillInForm gets and returns strings, using HTML::Element instances
+becomes tedious:
+
+ 1. Seamstress has an HTML tree that it wants the form filled in on
+ 2. Seamstress converts this tree to a string
+ 3. FillInForm parses the string into an HTML tree and then fills in the form
+ 4. FillInForm converts the HTML tree to a string
+ 5. Seamstress re-parses the HTML for additional processing
+
+I've filed a bug about this:
+L<https://rt.cpan.org/Ticket/Display.html?id=44105>
+
+This function, fillinform,
+allows you to pass a tree to fillinform (along with your data structure) and
+get back a tree:
+
+ my $new_tree = $html_tree->fillinform($data_structure);
+
+
+=head3 Mapping a hashref to HTML elements
+
+It is very common to get a hashref of data from some external source - flat file, database, XML, etc.
+Therefore, it is important to have a convenient way of mapping this data to HTML.
+
+As it turns out, there are 3 ways to do this in HTML::Element::Library.
+The most strict and structured way to do this is with
+C<content_handler>. Two other methods, C<hashmap> and C<datamap> require less manual mapping and may prove
+even more easy to use in certain cases.
+
+As is usual with Perl, a practical example is always best. So let's take some sample HTML:
+
+ <h1>user data</h1>
+ <span id="name">?</span>
+ <span id="email">?</span>
+ <span id="gender">?</span>
+
+Now, let's say our data structure is this:
+
+ $ref = { email => 'jim@beam.com', gender => 'lots' } ;
+
+And let's start with the most strict way to get what you want:
+
+ $tree->content_handler(email => $ref->{email} , gender => $ref->{gender}) ;
+
+
+In this case, you manually state the mapping between id tags and hashref keys and
+then C<content_handler> retrieves the hashref data and pops it in the specified place.
+
+Now let's look at the two (actually 2 and a half) other hash-mapping methods.
+
+ $tree->hashmap(id => $ref);
+
+Now, what this function does is super-destructive. It finds every element in the tree
+with an attribute named id (since 'id' is a parameter, it could find every element with
+some other attribute also) and replaces the content of those elements with the hashref
+value.
+
+So, in the case above, the
+
+ <span id="name">?</span>
+
+would come out as
+
+ <span id="name"></span>
+
+(it would be blank) - because there is nothing in the hash with that value, so it substituted
+
+ $ref->{name}
+
+which was blank and emptied the contents.
+
+Now, let's assume we want to protect name from being auto-assigned. Here is what you do:
+
+ $tree->hashmap(id => $ref, ['name']);
+
+That last array ref is an exclusion list.
+
+But wouldnt it be nice if you could do a hashmap, but only assigned things which are defined
+in the hashref? C<< defmap() >> to the rescue:
+
+ $tree->defmap(id => $ref);
+
+does just that, so
+
+ <span id="name">?</span>
+
+would be left alone.
+
+
+=head4 $elem->hashmap($attr_name, \%hashref, \@excluded, $debug)
+
+This method is designed to take a hashref and populate a series of elements. For example:
+
+
+ <table>
+ <tr sclass="tr" class="alt" align="left" valign="top">
+ <td smap="people_id">1</td>
+ <td smap="phone">(877) 255-3239</td>
+ <td smap="password">*********</td>
+ </tr>
+ </table>
+
+In the table above, there are several attributes named C<< smap >>. If we have a hashref whose keys are the same:
+
+ my %data = (people_id => 888, phone => '444-4444', password => 'dont-you-dare-render');
+
+Then a single API call allows us to populate the HTML while excluding those ones we dont:
+
+ $tree->hashmap(smap => \%data, ['password']);
+
+
+Note: the other way to prevent rendering some of the hash mapping is to not give that element the attr
+you plan to use for hash mapping.
+
+Also note: the function C<< hashmap >> has a simple easy-to-type API. Interally, it calls C<< hash_map >>
+(which has a more verbose keyword calling API). Thus, the above call to C<hashmap()> results in this call:
+
+ $tree->hash_map(hash => \%data, to_attr => 'sid', excluding => ['password']);
+
+=head4 $elem->defmap($attr_name, \%hashref, $debug)
+
+C<defmap> was described above.
+
+
+=head4 $elem->content_handler(%hashref)
+
+C<content_handler> is described below.
+
+
+=head3 $elem->replace_content(@new_elem)
+
+Replaces all of C<$elem>'s content with C<@new_elem>.
+
+=head3 $elem->wrap_content($wrapper_element)
+
+Wraps the existing content in the provided element. If the provided element
+happens to be a non-element, a push_content is performed instead.
+
+=head3 $elem->set_child_content(@look_down, $content)
+
+ This method looks down $tree using the criteria specified in @look_down using the the HTML::Element look_down() method.
+
+After finding the node, it detaches the node's content and pushes $content as the node's content.
+
+=head3 $tree->content_handler(%id_content)
+
+This is a convenience method. Because the look_down criteria will often simply be:
+
+ id => 'fixme'
+
+to find things like:
+
+ <a id=fixme href=http://www.somesite.org>replace_content</a>
+
+You can call this method to shorten your typing a bit. You can simply type
+
+ $elem->content_handler( fixme => 'new text' )
+
+Instead of typing:
+
+ $elem->set_child_content(sid => 'fixme', 'new text')
+
+ALSO NOTE: you can pass a hash whose keys are C<id>s and whose values are the content you want there and it will perform the replacement on each hash member:
+
+ my %id_content = (name => "Terrence Brannon",
+ email => 'tbrannon@in.com',
+ balance => 666,
+ content => $main_content);
+
+ $tree->content_handler(%id_content);
+
+=head3 $tree->highlander($subtree_span_id, $conditionals, @conditionals_args)
+
+This allows for "if-then-else" style processing. Highlander was a movie in
+which only one would survive. Well, in terms of a tree when looking at a
+structure that you want to process in C<if-then-else> style, only one child
+will survive. For example, given this HTML template:
+
+ <span klass="highlander" id="age_dialog">
+ <span id="under10">
+ Hello, does your mother know you're
+ using her AOL account?
+ </span>
+ <span id="under18">
+ Sorry, you're not old enough to enter
+ (and too dumb to lie about your age)
+ </span>
+ <span id="welcome">
+ Welcome
+ </span>
+ </span>
+
+We only want one child of the C<span> tag with id C<age_dialog> to remain
+based on the age of the person visiting the page.
+
+So, let's setup a call that will prune the subtree as a function of age:
+
+ sub process_page {
+ my $age = shift;
+ my $tree = HTML::TreeBuilder->new_from_file('t/html/highlander.html');
+
+ $tree->highlander
+ (age_dialog =>
+ [
+ under10 => sub { $_[0] < 10} ,
+ under18 => sub { $_[0] < 18} ,
+ welcome => sub { 1 }
+ ],
+ $age
+ );
+
+And there we have it. If the age is less than 10, then the node with
+id C<under10> remains. For age less than 18, the node with id C<under18>
+remains.
+Otherwise our "else" condition fires and the child with id C<welcome> remains.
+
+=head3 $tree->passover(@id_of_element)
+
+In some cases, you know exactly which element(s) should survive. In this case,
+you can simply call C<passover> to remove it's (their) siblings. For the HTML
+above, you could delete C<under10> and C<welcome> by simply calling:
+
+ $tree->passover('under18');
+
+Because passover takes an array, you can specify several children to preserve.
+
+=head3 $tree->highlander2($tree, $conditionals, @conditionals_args)
+
+Right around the same time that C<table2()> came into being, Seamstress
+began to tackle tougher and tougher processing problems. It became clear that
+a more powerful highlander was needed... one that not only snipped the tree
+of the nodes that should not survive, but one that allows for
+post-processing of the survivor node. And one that was more flexible with
+how to find the nodes to snip.
+
+Thus (drum roll) C<highlander2()>.
+
+So let's look at our HTML which requires post-selection processing:
+
+ <span klass="highlander" id="age_dialog">
+ <span id="under10">
+ Hello, little <span id=age>AGE</span>-year old,
+ does your mother know you're using her AOL account?
+ </span>
+ <span id="under18">
+ Sorry, you're only <span id=age>AGE</span>
+ (and too dumb to lie about your age)
+ </span>
+ <span id="welcome">
+ Welcome, isn't it good to be <span id=age>AGE</span> years old?
+ </span>
+</span>
+
+In this case, a branch survives, but it has dummy data in it. We must take
+the surviving segment of HTML and rewrite the age C<span> with the age.
+Here is how we use C<highlander2()> to do so:
+
+ sub replace_age {
+ my $branch = shift;
+ my $age = shift;
+ $branch->look_down(id => 'age')->replace_content($age);
+ }
+
+ my $if_then = $tree->look_down(id => 'age_dialog');
+
+ $if_then->highlander2(
+ cond => [
+ under10 => [
+ sub { $_[0] < 10} ,
+ \&replace_age
+ ],
+ under18 => [
+ sub { $_[0] < 18} ,
+ \&replace_age
+ ],
+ welcome => [
+ sub { 1 },
+ \&replace_age
+ ]
+ ],
+ cond_arg => [ $age ]
+ );
+
+We pass it the tree (C<$if_then>), an arrayref of conditions
+(C<cond>) and an arrayref of arguments which are passed to the
+C<cond>s and to the replacement subs.
+
+The C<under10>, C<under18> and C<welcome> are id attributes in the
+tree of the siblings of which only one will survive. However,
+should you need to do
+more complex look-downs to find the survivor,
+then supply an array ref instead of a simple
+scalar:
+
+
+ $if_then->highlander2(
+ cond => [
+ [class => 'r12'] => [
+ sub { $_[0] < 10} ,
+ \&replace_age
+ ],
+ [class => 'z22'] => [
+ sub { $_[0] < 18} ,
+ \&replace_age
+ ],
+ [class => 'w88'] => [
+ sub { 1 },
+ \&replace_age
+ ]
+ ],
+ cond_arg => [ $age ]
+ );
+
+
+=head3 $tree->overwrite_attr($mutation_attr => $mutating_closures)
+
+This method is designed for taking a tree and reworking a set of nodes in
+a stereotyped fashion. For instance let's say you have 3 remote image
+archives, but you don't want to put long URLs in your img src
+tags for reasons of abstraction, re-use and brevity. So instead you do this:
+
+ <img src="/img/smiley-face.jpg" fixup="src lnc">
+ <img src="/img/hot-babe.jpg" fixup="src playboy">
+ <img src="/img/footer.jpg" fixup="src foobar">
+
+and then when the tree of HTML is being processed, you make this call:
+
+ my %closures = (
+ lnc => sub { my ($tree, $mute_node, $attr_value)= @_; "http://lnc.usc.edu$attr_value" },
+ playboy => sub { my ($tree, $mute_node, $attr_value)= @_; "http://playboy.com$attr_value" }
+ foobar => sub { my ($tree, $mute_node, $attr_value)= @_; "http://foobar.info$attr_value" }
+ )
+
+ $tree->overwrite_attr(fixup => \%closures) ;
+
+and the tags come out modified like so:
+
+ <img src="http://lnc.usc.edu/img/smiley-face.jpg" fixup="src lnc">
+ <img src="http://playboy.com/img/hot-babe.jpg" fixup="src playboy">
+ <img src="http://foobar.info/img/footer.jpg" fixup="src foobar">
+
+=head3 $tree->mute_elem($mutation_attr => $mutating_closures, [ $post_hook ] )
+
+This is a generalization of C<overwrite_attr>. C<overwrite_attr>
+assumes the return value of the
+closure is supposed overwrite an attribute value and does it for you.
+C<mute_elem> is a more general function which does nothing but
+hand the closure the element and let it mutate it as it jolly well pleases :)
+
+In fact, here is the implementation of C<overwrite_attr>
+to give you a taste of how C<mute_attr> is used:
+
+ sub overwrite_action {
+ my ($mute_node, %X) = @_;
+
+ $mute_node->attr($X{local_attr}{name} => $X{local_attr}{value}{new});
+ }
+
+
+ sub HTML::Element::overwrite_attr {
+ my $tree = shift;
+
+ $tree->mute_elem(@_, \&overwrite_action);
+ }
+
+
+
+
+=head2 Tree-Building Methods
+
+
+
+=head3 Unrolling an array via a single sample element (<ul> container)
+
+This is best described by example. Given this HTML:
+
+ <strong>Here are the things I need from the store:</strong>
+ <ul>
+ <li class="store_items">Sample item</li>
+ </ul>
+
+We can unroll it like so:
+
+ my $li = $tree->look_down(class => 'store_items');
+
+ my @items = qw(bread butter vodka);
+
+ $tree->iter($li => @items);
+
+To produce this:
+
+
+ <html>
+ <head></head>
+ <body>Here are the things I need from the store:
+ <ul>
+ <li class="store_items">bread</li>
+ <li class="store_items">butter</li>
+ <li class="store_items">vodka</li>
+ </ul>
+ </body>
+ </html>
+
+Now, you might be wondering why the API call is:
+
+ $tree->iter($li => @items)
+
+instead of:
+
+ $li->iter(@items)
+
+and there is no good answer. The latter would be more concise and it is what I
+should have done.
+
+=head3 Unrolling an array via n sample elements (<dl> container)
+
+C<iter()> was fine for awhile, but some things
+(e.g. definition lists) need a more general function to make them easy to
+do. Hence C<iter2()>. This function will be explained by example of unrolling
+a simple definition list.
+
+So here's our mock-up HTML from the designer:
+
+ <dl class="dual_iter" id="service_plan">
+ <dt>
+ Artist
+ </dt>
+ <dd>
+ A person who draws blood.
+ </dd>
+
+ <dt>
+ Musician
+ </dt>
+ <dd>
+ A clone of Iggy Pop.
+ </dd>
+
+ <dt>
+ Poet
+ </dt>
+ <dd>
+ A relative of Edgar Allan Poe.
+ </dd>
+
+ <dt class="adstyle">sample header</dt>
+ <dd class="adstyle2">sample data</dd>
+
+ </dl>
+
+
+And we want to unroll our data set:
+
+ my @items = (
+ ['the pros' => 'never have to worry about service again'],
+ ['the cons' => 'upfront extra charge on purchase'],
+ ['our choice' => 'go with the extended service plan']
+ );
+
+
+Now, let's make this problem a bit harder to show off the power of C<iter2()>.
+Let's assume that we want only the last <dt> and it's accompanying <dd>
+(the one with "sample data") to be used as the sample data
+for unrolling with our data set. Let's further assume that we want them to
+remain in the final output.
+
+So now, the API to C<iter2()> will be discussed and we will explain how our
+goal of getting our data into HTML fits into the API.
+
+=over 4
+
+=item * wrapper_ld
+
+This is how to look down and find the container of all the elements we will
+be unrolling. The <dl> tag is the container for the dt and dd tags we will be
+unrolling.
+
+If you pass an anonymous subroutine, then it is presumed that execution of
+this subroutine will return the HTML::Element representing the container tag.
+If you pass an array ref, then this will be dereferenced and passed to
+C<HTML::Element::look_down()>.
+
+default value: C<< ['_tag' => 'dl'] >>
+
+Based on the mock HTML above, this default is fine for finding our container
+tag. So let's move on.
+
+=item * wrapper_data
+
+This is an array reference of data that we will be putting into the container.
+You must supply this. C<@items> above is our C<wrapper_data>.
+
+=item * wrapper_proc
+
+After we find the container via C<wrapper_ld>, we may want to pre-process
+some aspect of this tree. In our case the first two sets of dt and dd need
+to be removed, leaving the last dt and dd. So, we supply a C<wrapper_proc>
+which will do this.
+
+default: undef
+
+=item * item_ld
+
+This anonymous subroutine returns an array ref of C<HTML::Element>s that will
+be cloned and populated with item data
+(item data is a "row" of C<wrapper_data>).
+
+default: returns an arrayref consisting of the dt and dd element inside the
+container.
+
+=item * item_data
+
+This is a subroutine that takes C<wrapper_data> and retrieves one "row"
+to be "pasted" into the array ref of C<HTML::Element>s found via C<item_ld>.
+I hope that makes sense.
+
+default: shifts C<wrapper_data>.
+
+=item * item_proc
+
+This is a subroutine that takes the C<item_data> and the C<HTML::Element>s
+found via C<item_ld> and produces an arrayref of C<HTML::Element>s which will
+eventually be spliced into the container.
+
+Note that this subroutine MUST return the new items. This is done
+So that more items than were passed in can be returned. This is
+useful when, for example, you must return 2 dts for an input data item.
+And when would you do this? When a single term has multiple spellings
+for instance.
+
+default: expects C<item_data> to be an arrayref of two elements and
+C<item_elems> to be an arrayref of two C<HTML::Element>s. It replaces the
+content of the C<HTML::Element>s with the C<item_data>.
+
+=item * splice
+
+After building up an array of C<@item_elems>, the subroutine passed as
+C<splice> will be given the parent container HTML::Element and the
+C<@item_elems>. How the C<@item_elems> end up in the container is up to this
+routine: it could put half of them in. It could unshift them or whatever.
+
+default: C<< $container->splice_content(0, 2, @item_elems) >>
+In other words, kill the 2 sample elements with the newly generated
+@item_elems
+
+=back
+
+So now that we have documented the API, let's see the call we need:
+
+ $tree->iter2(
+ # default wrapper_ld ok.
+ wrapper_data => \@items,
+ wrapper_proc => sub {
+ my ($container) = @_;
+
+ # only keep the last 2 dts and dds
+ my @content_list = $container->content_list;
+ $container->splice_content(0, @content_list - 2);
+ },
+
+ # default item_ld is fine.
+ # default item_data is fine.
+ # default item_proc is fine.
+ splice => sub {
+ my ($container, @item_elems) = @_;
+ $container->unshift_content(@item_elems);
+ },
+ debug => 1,
+ );
+
+
+
+
+=head3 Select Unrolling
+
+The C<unroll_select> method has this API:
+
+ $tree->unroll_select(
+ select_label => $id_label,
+ option_value => $closure, # how to get option value from data row
+ option_content => $closure, # how to get option content from data row
+ option_selected => $closure, # boolean to decide if SELECTED
+ data => $data # the data to be put into the SELECT
+ data_iter => $closure # the thing that will get a row of data
+ debug => $boolean,
+ append => $boolean, # remove the sample <OPTION> data or append?
+ );
+
+Here's an example:
+
+ $tree->unroll_select(
+ select_label => 'clan_list',
+ option_value => sub { my $row = shift; $row->clan_id },
+ option_content => sub { my $row = shift; $row->clan_name },
+ option_selected => sub { my $row = shift; $row->selected },
+ data => \@query_results,
+ data_iter => sub { my $data = shift; $data->next },
+ append => 0,
+ debug => 0
+ );
+
+
+
+=head2 Tree-Building Methods: Table Generation
+
+Matthew Sisk has a much more intuitive (imperative)
+way to generate tables via his module
+L<HTML::ElementTable|HTML::ElementTable>.
+However, for those with callback fever, the following
+method is available. First, we look at a nuts and bolts way to build a table
+using only standard L<HTML::Tree> API calls. Then the C<table> method
+available here is discussed.
+
+=head3 Sample Model
+
+ package Simple::Class;
+
+ use Set::Array;
+
+ my @name = qw(bob bill brian babette bobo bix);
+ my @age = qw(99 12 44 52 12 43);
+ my @weight = qw(99 52 80 124 120 230);
+
+
+ sub new {
+ my $this = shift;
+ bless {}, ref($this) || $this;
+ }
+
+ sub load_data {
+ my @data;
+
+ for (0 .. 5) {
+ push @data, {
+ age => $age[rand $#age] + int rand 20,
+ name => shift @name,
+ weight => $weight[rand $#weight] + int rand 40
+ }
+ }
+
+ Set::Array->new(@data);
+ }
+
+
+ 1;
+
+
+=head4 Sample Usage:
+
+ my $data = Simple::Class->load_data;
+ ++$_->{age} for @$data
+
+=head3 Inline Code to Unroll a Table
+
+=head4 HTML
+
+ <html>
+
+ <table id="load_data">
+
+ <tr> <th>name</th><th>age</th><th>weight</th> </tr>
+
+ <tr id="iterate">
+
+ <td id="name"> NATURE BOY RIC FLAIR </td>
+ <td id="age"> 35 </td>
+ <td id="weight"> 220 </td>
+
+ </tr>
+
+ </table>
+
+ </html>
+
+
+=head4 The manual way (*NOT* recommended)
+
+ require 'simple-class.pl';
+ use HTML::Seamstress;
+
+ # load the view
+ my $seamstress = HTML::Seamstress->new_from_file('simple.html');
+
+ # load the model
+ my $o = Simple::Class->new;
+ my $data = $o->load_data;
+
+ # find the <table> and <tr>
+ my $table_node = $seamstress->look_down('id', 'load_data');
+ my $iter_node = $table_node->look_down('id', 'iterate');
+ my $table_parent = $table_node->parent;
+
+
+ # drop the sample <table> and <tr> from the HTML
+ # only add them in if there is data in the model
+ # this is achieved via the $add_table flag
+
+ $table_node->detach;
+ $iter_node->detach;
+ my $add_table;
+
+ # Get a row of model data
+ while (my $row = shift @$data) {
+
+ # We got row data. Set the flag indicating ok to hook the table into the HTML
+ ++$add_table;
+
+ # clone the sample <tr>
+ my $new_iter_node = $iter_node->clone;
+
+ # find the tags labeled name age and weight and
+ # set their content to the row data
+ $new_iter_node->content_handler($_ => $row->{$_})
+ for qw(name age weight);
+
+ $table_node->push_content($new_iter_node);
+
+ }
+
+ # reattach the table to the HTML tree if we loaded data into some table rows
+
+ $table_parent->push_content($table_node) if $add_table;
+
+ print $seamstress->as_HTML;
+
+
+
+=head3 $tree->table() : API call to Unroll a Table
+
+ require 'simple-class.pl';
+ use HTML::Seamstress;
+
+ # load the view
+ my $seamstress = HTML::Seamstress->new_from_file('simple.html');
+ # load the model
+ my $o = Simple::Class->new;
+
+ $seamstress->table
+ (
+ # tell seamstress where to find the table, via the method call
+ # ->look_down('id', $gi_table). Seamstress detaches the table from the
+ # HTML tree automatically if no table rows can be built
+
+ gi_table => 'load_data',
+
+ # tell seamstress where to find the tr. This is a bit useless as
+ # the <tr> usually can be found as the first child of the parent
+
+ gi_tr => 'iterate',
+
+ # the model data to be pushed into the table
+
+ table_data => $o->load_data,
+
+ # the way to take the model data and obtain one row
+ # if the table data were a hashref, we would do:
+ # my $key = (keys %$data)[0]; my $val = $data->{$key}; delete $data->{$key}
+
+ tr_data => sub { my ($self, $data) = @_;
+ shift(@{$data}) ;
+ },
+
+ # the way to take a row of data and fill the <td> tags
+
+ td_data => sub { my ($tr_node, $tr_data) = @_;
+ $tr_node->content_handler($_ => $tr_data->{$_})
+ for qw(name age weight) }
+
+ );
+
+
+ print $seamstress->as_HTML;
+
+
+
+=head4 Looping over Multiple Sample Rows
+
+* HTML
+
+ <html>
+
+ <table id="load_data" CELLPADDING=8 BORDER=2>
+
+ <tr> <th>name</th><th>age</th><th>weight</th> </tr>
+
+ <tr id="iterate1" BGCOLOR="white" >
+
+ <td id="name"> NATURE BOY RIC FLAIR </td>
+ <td id="age"> 35 </td>
+ <td id="weight"> 220 </td>
+
+ </tr>
+ <tr id="iterate2" BGCOLOR="#CCCC99">
+
+ <td id="name"> NATURE BOY RIC FLAIR </td>
+ <td id="age"> 35 </td>
+ <td id="weight"> 220 </td>
+
+ </tr>
+
+ </table>
+
+ </html>
+
+
+* Only one change to last API call.
+
+This:
+
+ gi_tr => 'iterate',
+
+becomes this:
+
+ gi_tr => ['iterate1', 'iterate2']
+
+=head3 $tree->table2() : New API Call to Unroll a Table
+
+After 2 or 3 years with C<table()>, I began to develop
+production websites with it and decided it needed a cleaner
+interface, particularly in the area of handling the fact that
+C<id> tags will be the same after cloning a table row.
+
+First, I will give a dry listing of the function's argument parameters.
+This will not be educational most likely. A better way to understand how
+to use the function is to read through the incremental unrolling of the
+function's interface given in conversational style after the dry listing.
+But take your pick. It's the same information given in two different
+ways.
+
+=head4 Dry/technical parameter documentation
+
+C<< $tree->table2(%param) >> takes the following arguments:
+
+=over
+
+=item * C<< table_ld => $look_down >> : optional
+
+How to find the C<table> element in C<$tree>. If C<$look_down> is an
+arrayref, then use C<look_down>. If it is a CODE ref, then call it,
+passing it C<$tree>.
+
+Defaults to C<< ['_tag' => 'table'] >> if not passed in.
+
+=item * C<< table_data => $tabular_data >> : required
+
+The data to fill the table with. I<Must> be passed in.
+
+=item * C<< table_proc => $code_ref >> : not implemented
+
+A subroutine to do something to the table once it is found.
+Not currently implemented. Not obviously necessary. Just
+created because there is a C<tr_proc> and C<td_proc>.
+
+=item * C<< tr_ld => $look_down >> : optional
+
+Same as C<table_ld> but for finding the table row elements. Please note
+that the C<tr_ld> is done on the table node that was found I<instead>
+of the whole HTML tree. This makes sense. The C<tr>s that you want exist
+below the table that was just found.
+
+Defaults to C<< ['_tag' => 'tr'] >> if not passed in.
+
+=item * C<< tr_data => $code_ref >> : optional
+
+How to take the C<table_data> and return a row. Defaults to:
+
+ sub { my ($self, $data) = @_;
+ shift(@{$data}) ;
+ }
+
+=item * C<< tr_proc => $code_ref >> : optional
+
+Something to do to the table row we are about to add to the
+table we are making. Defaults to a routine which makes the C<id>
+attribute unique:
+
+ sub {
+ my ($self, $tr, $tr_data, $tr_base_id, $row_count) = @_;
+ $tr->attr(id => sprintf "%s_%d", $tr_base_id, $row_count);
+ }
+
+=item * C<< td_proc => $code_ref >> : required
+
+This coderef will take the row of data and operate on the C<td> cells that
+are children of the C<tr>. See C<t/table2.t> for several usage examples.
+
+Here's a sample one:
+
+ sub {
+ my ($tr, $data) = @_;
+ my @td = $tr->look_down('_tag' => 'td');
+ for my $i (0..$#td) {
+ $td[$i]->splice_content(0, 1, $data->[$i]);
+ }
+ }
+
+=cut
+
+=head4 Conversational parameter documentation
+
+The first thing you need is a table. So we need a look down for that. If you
+don't give one, it defaults to
+
+ ['_tag' => 'table']
+
+What good is a table to display in without data to display?!
+So you must supply a scalar representing your tabular
+data source. This scalar might be an array reference, a C<next>able iterator,
+a DBI statement handle. Whatever it is, it can be iterated through to build
+up rows of table data.
+These two required fields (the way to find the table and the data to
+display in the table) are C<table_ld> and C<table_data>
+respectively. A little more on C<table_ld>. If this happens to be a CODE ref,
+then execution
+of the code ref is presumed to return the C<HTML::Element>
+representing the table in the HTML tree.
+
+Next, we get the row or rows which serve as sample C<tr> elements by doing
+a C<look_down> from the C<table_elem>. While normally one sample row
+is enough to unroll a table, consider when you have alternating
+table rows. This API call would need one of each row so that it can
+cycle through the
+sample rows as it loops through the data.
+Alternatively, you could always just use one row and
+make the necessary changes to the single C<tr> row by
+mutating the element in C<tr_proc>,
+discussed below. The default C<tr_ld> is
+C<< ['_tag' => 'tr'] >> but you can overwrite it. Note well, if you overwrite
+it with a subroutine, then it is expected that the subroutine will return
+the C<HTML::Element>(s)
+which are C<tr> element(s).
+The reason a subroutine might be preferred is in the case
+that the HTML designers gave you 8 sample C<tr> rows but only one
+prototype row is needed.
+So you can write a subroutine, to splice out the 7 rows you don't need
+and leave the one sample
+row remaining so that this API call can clone it and supply it to
+the C<tr_proc> and C<td_proc> calls.
+
+Now, as we move through the table rows with table data,
+we need to do two different things on
+each table row:
+
+=over 4
+
+=item * get one row of data from the C<table_data> via C<tr_data>
+
+The default procedure assumes the C<table_data> is an array reference and
+shifts a row off of it:
+
+ sub { my ($self, $data) = @_;
+ shift(@{$data}) ;
+ }
+
+Your function MUST return undef when there is no more rows to lay out.
+
+=item * take the C<tr> element and mutate it via C<tr_proc>
+
+The default procedure simply makes the id of the table row unique:
+
+ sub { my ($self, $tr, $tr_data, $row_count, $root_id) = @_;
+ $tr->attr(id => sprintf "%s_%d", $root_id, $row_count);
+ }
+
+=back
+
+Now that we have our row of data, we call C<td_proc> so that it can
+take the data and the C<td> cells in this C<tr> and process them.
+This function I<must> be supplied.
+
+
+=head3 Whither a Table with No Rows
+
+Often when a table has no rows, we want to display a message
+indicating this to the view. Use conditional processing to decide what
+to display:
+
+ <span id=no_data>
+ <table><tr><td>No Data is Good Data</td></tr></table>
+ </span>
+ <span id=load_data>
+ <html>
+
+ <table id="load_data">
+
+ <tr> <th>name</th><th>age</th><th>weight</th> </tr>
+
+ <tr id="iterate">
+
+ <td id="name"> NATURE BOY RIC FLAIR </td>
+ <td id="age"> 35 </td>
+ <td id="weight"> 220 </td>
+
+ </tr>
+
+ </table>
+
+ </html>
+
+ </span>
+
+
+
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<HTML::Tree>
+
+A perl package for creating and manipulating HTML trees
+
+=item * L<HTML::ElementTable>
+
+An L<HTML::Tree> - based module which allows for manipulation of HTML
+trees using cartesian coordinations.
+
+=item * L<HTML::Seamstress>
+
+An L<HTML::Tree> - based module inspired by
+XMLC (L<http://xmlc.enhydra.org>), allowing for dynamic
+HTML generation via tree rewriting.
+
+=head1 TODO
+
+=over
+
+=item * highlander2
+
+currently the API expects the subtrees to survive or be pruned to be
+identified by id:
+
+ $if_then->highlander2([
+ under10 => sub { $_[0] < 10} ,
+ under18 => sub { $_[0] < 18} ,
+ welcome => [
+ sub { 1 },
+ sub {
+ my $branch = shift;
+ $branch->look_down(id => 'age')->replace_content($age);
+ }
+ ]
+ ],
+ $age
+ );
+
+but, it should be more flexible. the C<under10>, and C<under18> are
+expected to be ids in the tree... but it is not hard to have a check to
+see if this field is an array reference and if it, then to do a look
+down instead:
+
+ $if_then->highlander2([
+ [class => 'under10'] => sub { $_[0] < 10} ,
+ [class => 'under18'] => sub { $_[0] < 18} ,
+ [class => 'welcome'] => [
+ sub { 1 },
+ sub {
+ my $branch = shift;
+ $branch->look_down(id => 'age')->replace_content($age);
+ }
+ ]
+ ],
+ $age
+ );
+
+
+
+=cut
+
+=head1 SEE ALSO
+
+L<HTML::Seamstress>
+
+=head1 AUTHOR / SOURCE
+
+Terrence Brannon, E<lt>tbone@cpan.orgE<gt>
+
+Many thanks to BARBIE for his RT bug report.
+
+The source is at L<http://github.com/metaperl/html-element-library/tree/master>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2004 by Terrence Brannon
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.4 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut