From 0b6e6b6947c21eb8b3d3df71100bdbe2ba576866 Mon Sep 17 00:00:00 2001 From: Marius Gavrilescu Date: Sat, 27 Dec 2014 11:59:20 +0200 Subject: [PATCH] Put POD after __END__ and rename AUTHOR AND ACKS to AUTHOR --- lib/HTML/Element/Library.pm | 1194 ++++++++++++++++++++++++++++++++++ lib/HTML/Element/Library.pod | 1193 --------------------------------- 2 files changed, 1194 insertions(+), 1193 deletions(-) delete mode 100644 lib/HTML/Element/Library.pod diff --git a/lib/HTML/Element/Library.pm b/lib/HTML/Element/Library.pm index 553fb0c..d4d82b8 100644 --- a/lib/HTML/Element/Library.pm +++ b/lib/HTML/Element/Library.pm @@ -683,3 +683,1197 @@ sub HTML::TreeBuilder::parse_string { 1; __END__ + +=encoding utf-8 + +=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. + +=head1 METHODS + +The test suite contains examples of each of these methods in a file +C + +=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 calls this method C but I don't +think that is a descriptive name. And such naming is deceptively close +to the C
function of C. 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, 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 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 section. + +=head2 Tree Rewriting Methods + +=head3 "de-prepping" HTML + +Oftentimes, the HTML to be worked with will have multiple sample rows: + +
    +
  1. bread +
  2. butter +
  3. beer +
  4. bacon +
+ +But, before you begin to rewrite the HTML with your model data, you +typically only want 1 or 2 sample rows. + +Thus, you want to "crunch" the multiple sample rows to a specified +amount. Hence the C method: + + $tree->crunch(look_down => [ '_tag' => 'li' ], leave => 2) ; + +The C argument defaults to 1 if not given. The call above would +"crunch" the above 4 sample rows to: + +
    +
  1. bread +
  2. butter +
+ +=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 + +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. Two other methods, C and +C 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: + +

user data

+ ? + ? + ? + +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 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 + + ? + +would come out as + + + +(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 + + ? + +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: + + + + + + + +
1(877) 255-3239*********
+ +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 results in this +call: + + $tree->hash_map(hash => \%data, to_attr => 'sid', excluding => ['password']); + +=head4 $elem->defmap($attr_name, \%hashref, $debug) + +C was described above. + +=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: + + replace_content + +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 Cs 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 +style, only one child will survive. For example, given this HTML +template: + + + + Hello, does your mother know you're + using her AOL account? + + + Sorry, you're not old enough to enter + (and too dumb to lie about your age) + + + Welcome + + + +We only want one child of the C tag with id C 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 remains. For age less than 18, the node with id +C remains. Otherwise our "else" condition fires and the child +with id C 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 to remove it's (their) +siblings. For the HTML above, you could delete C and +C 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 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. + +So let's look at our HTML which requires post-selection processing: + + + + Hello, little AGE-year old, + does your mother know you're using her AOL account? + + + Sorry, you're only AGE + (and too dumb to lie about your age) + + + Welcome, isn't it good to be AGE years old? + + + +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 with +the age. Here is how we use C 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) +and an arrayref of arguments which are passed to the Cs and to +the replacement subs. + +The C, C and C 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: + + + + + +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: + + + + + +=head3 $tree->mute_elem($mutation_attr => $mutating_closures, [ $post_hook ] ) + +This is a generalization of C. C +assumes the return value of the closure is supposed overwrite an +attribute value and does it for you. C 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 to give you a +taste of how C 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 (
    container) + +This is best described by example. Given this HTML: + + Here are the things I need from the store: +
      +
    • Sample item
    • +
    + +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: + + + + Here are the things I need from the store: +
      +
    • bread
    • +
    • butter
    • +
    • vodka
    • +
    + + + +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 (
    container) + +C was fine for awhile, but some things (e.g. definition lists) +need a more general function to make them easy to do. Hence +C. This function will be explained by example of unrolling a +simple definition list. + +So here's our mock-up HTML from the designer: + +
    +
    Artist
    +
    A person who draws blood.
    + +
    Musician
    +
    A clone of Iggy Pop.
    + +
    Poet
    +
    A relative of Edgar Allan Poe.
    + +
    sample header
    +
    sample data
    +
    + + +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. Let's assume that we want only the last
    and it's +accompanying
    (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 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
    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. + +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. + +=item * wrapper_proc + +After we find the container via C, 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 which will do this. + +default: undef + +=item * item_ld + +This anonymous subroutine returns an array ref of Cs +that will be cloned and populated with item data (item data is a "row" +of C). + +default: returns an arrayref consisting of the dt and dd element +inside the container. + +=item * item_data + +This is a subroutine that takes C and retrieves one +"row" to be "pasted" into the array ref of Cs found via +C. I hope that makes sense. + +default: shifts C. + +=item * item_proc + +This is a subroutine that takes the C and the +Cs found via C and produces an arrayref of +Cs 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 to be an arrayref of two elements and +C to be an arrayref of two Cs. It replaces +the content of the Cs with the C. + +=item * splice + +After building up an array of C<@item_elems>, the subroutine passed as +C 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 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