]> iEval git - html-element-library.git/blob - lib/HTML/Element/Library.pm
fixed test suite
[html-element-library.git] / lib / HTML / Element / Library.pm
1 package HTML::Element::Library;
2
3 use 5.006001;
4 use strict;
5 use warnings;
6
7
8 our $DEBUG = 0;
9 #our $DEBUG = 1;
10
11 use Array::Group qw(:all);
12 use Carp qw(confess);
13 use Data::Dumper;
14 use HTML::Element;
15 use List::Util qw(first);
16 use List::MoreUtils qw/:all/;
17 use Params::Validate qw(:all);
18 use Scalar::Listify;
19 #use Tie::Cycle;
20 use List::Rotation::Cycle;
21
22 our %EXPORT_TAGS = ( 'all' => [ qw() ] );
23 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24 our @EXPORT = qw();
25
26
27
28 our $VERSION = '3.53';
29
30
31 # Preloaded methods go here.
32
33 sub HTML::Element::siblings {
34 my $element = shift;
35 my $p = $element->parent;
36 return () unless $p;
37 $p->content_list;
38 }
39
40 sub HTML::Element::hash_map {
41 my $container = shift;
42
43 my %p = validate(@_, {
44 hash => { type => HASHREF },
45 to_attr => 1,
46 excluding => { type => ARRAYREF , default => [] },
47 debug => { default => 0 },
48 });
49
50 warn 'The container tag is ', $container->tag if $p{debug} ;
51 warn 'hash' . Dumper($p{hash}) if $p{debug} ;
52 warn 'at_under' . Dumper(\@_);
53
54 my @same_as = $container->look_down( $p{to_attr} => qr/.+/ ) ;
55
56 warn 'Found ' . scalar(@same_as) . ' nodes' if $p{debug} ;
57
58
59 for my $same_as (@same_as) {
60 my $attr_val = $same_as->attr($p{to_attr}) ;
61 if (first { $attr_val eq $_ } @{$p{excluding}}) {
62 warn "excluding $attr_val" if $p{debug} ;
63 next;
64 }
65 warn "processing $attr_val" if $p{debug} ;
66 $same_as->replace_content( $p{hash}->{$attr_val} ) ;
67 }
68
69 }
70
71
72 sub HTML::Element::passover {
73 my ($tree, $child_id) = @_;
74
75 warn "ARGS: my ($tree, $child_id)" if $DEBUG;
76 warn $tree->as_HTML(undef, ' ') if $DEBUG;
77
78 my $exodus = $tree->look_down(id => $child_id);
79
80 warn "E: $exodus" if $DEBUG;
81
82 my @s = HTML::Element::siblings($exodus);
83
84 for my $s (@s) {
85 next unless ref $s;
86 if ($s->attr('id') eq $child_id) {
87 ;
88 } else {
89 $s->delete;
90 }
91 }
92
93 return $exodus; # Goodbye Egypt! http://en.wikipedia.org/wiki/Passover
94
95 }
96
97 sub HTML::Element::sibdex {
98
99 my $element = shift;
100 firstidx { $_ eq $element } $element->siblings
101
102 }
103
104 sub HTML::Element::addr { goto &HTML::Element::sibdex }
105
106 sub HTML::Element::replace_content {
107 my $elem = shift;
108 $elem->delete_content;
109 $elem->push_content(@_);
110 }
111
112 sub HTML::Element::wrap_content {
113 my($self, $wrap) = @_;
114 my $content = $self->content;
115 if (ref $content) {
116 $wrap->push_content(@$content);
117 @$content = ($wrap);
118 }
119 else {
120 $self->push_content($wrap);
121 }
122 $wrap;
123 }
124
125 sub HTML::Element::Library::super_literal {
126 my($text) = @_;
127
128 HTML::Element->new('~literal', text => $text);
129 }
130
131
132 sub HTML::Element::position {
133 # Report coordinates by chasing addr's up the
134 # HTML::ElementSuper tree. We know we've reached
135 # the top when a) there is no parent, or b) the
136 # parent is some HTML::Element unable to report
137 # it's position.
138 my $p = shift;
139 my @pos;
140 while ($p) {
141 my $a = $p->addr;
142 unshift(@pos, $a) if defined $a;
143 $p = $p->parent;
144 }
145 @pos;
146 }
147
148
149 sub HTML::Element::content_handler {
150 my ($tree, %content_hash) = @_;
151
152 for my $k (keys %content_hash) {
153 $tree->set_child_content(id => $k, $content_hash{$k});
154 }
155
156
157 }
158
159
160 sub make_counter {
161 my $i = 1;
162 sub {
163 shift() . ':' . $i++
164 }
165 }
166
167
168 sub HTML::Element::iter {
169 my ($tree, $p, @data) = @_;
170
171 # warn 'P: ' , $p->attr('id') ;
172 # warn 'H: ' , $p->as_HTML;
173
174 # my $id_incr = make_counter;
175 my @item = map {
176 my $new_item = clone $p;
177 $new_item->replace_content($_);
178 # $new_item->attr('id', $id_incr->( $p->attr('id') ));
179 $new_item;
180 } @data;
181
182 $p->replace_with(@item);
183
184 }
185
186
187 sub HTML::Element::iter2 {
188
189 my $tree = shift;
190
191 #warn "INPUT TO TABLE2: ", Dumper \@_;
192
193 my %p = validate(
194 @_, {
195 wrapper_ld => { default => ['_tag' => 'dl'] },
196 wrapper_data => 1,
197 wrapper_proc => { default => undef },
198 item_ld => { default => sub {
199 my $tree = shift;
200 [
201 $tree->look_down('_tag' => 'dt'),
202 $tree->look_down('_tag' => 'dd')
203 ];
204 }
205 },
206 item_data => { default => sub { my ($wrapper_data) = @_;
207 shift(@{$wrapper_data}) ;
208 }},
209 item_proc => {
210 default => sub {
211 my ($item_elems, $item_data, $row_count) = @_;
212 $item_elems->[$_]->replace_content($item_data->[$_]) for (0,1) ;
213 $item_elems;
214 }},
215 splice => { default => sub {
216 my ($container, @item_elems) = @_;
217 $container->splice_content(0, 2, @item_elems);
218 }
219 },
220 debug => {default => 0}
221 }
222 );
223
224 warn "wrapper_data: " . Dumper $p{wrapper_data} if $p{debug} ;
225
226 my $container = ref_or_ld($tree, $p{wrapper_ld});
227 warn "container: " . $container if $p{debug} ;
228 warn "wrapper_(preproc): " . $container->as_HTML if $p{debug} ;
229 $p{wrapper_proc}->($container) if defined $p{wrapper_proc} ;
230 warn "wrapper_(postproc): " . $container->as_HTML if $p{debug} ;
231
232 my $_item_elems = $p{item_ld}->($container);
233
234
235
236 my $row_count;
237 my @item_elem;
238 {
239 my $item_data = $p{item_data}->($p{wrapper_data});
240 last unless defined $item_data;
241
242 warn Dumper("item_data", $item_data);
243
244
245 my $item_elems = [ map { $_->clone } @{$_item_elems} ] ;
246
247 if ($p{debug}) {
248 for (@{$item_elems}) {
249 warn "ITEM_ELEMS ", $_->as_HTML;
250 }
251 }
252
253 my $new_item_elems = $p{item_proc}->($item_elems, $item_data, ++$row_count);
254
255 if ($p{debug}) {
256 for (@{$new_item_elems}) {
257 warn "NEWITEM_ELEMS ", $_->as_HTML;
258 }
259 }
260
261
262 push @item_elem, @{$new_item_elems} ;
263
264 redo;
265 }
266
267 warn "pushing " . @item_elem . " elems " if $p{debug} ;
268
269 $p{splice}->($container, @item_elem);
270
271 }
272
273 sub HTML::Element::dual_iter {
274 my ($parent, $data) = @_;
275
276 my ($prototype_a, $prototype_b) = $parent->content_list;
277
278 # my $id_incr = make_counter;
279
280 my $i;
281
282 @$data %2 == 0 or
283 confess 'dataset does not contain an even number of members';
284
285 my @iterable_data = ngroup 2 => @$data;
286
287 my @item = map {
288 my ($new_a, $new_b) = map { clone $_ } ($prototype_a, $prototype_b) ;
289 $new_a->splice_content(0,1, $_->[0]);
290 $new_b->splice_content(0,1, $_->[1]);
291 #$_->attr('id', $id_incr->($_->attr('id'))) for ($new_a, $new_b) ;
292 ($new_a, $new_b)
293 } @iterable_data;
294
295 $parent->splice_content(0, 2, @item);
296
297 }
298
299
300 sub HTML::Element::set_child_content {
301 my $tree = shift;
302 my $content = pop;
303 my @look_down = @_;
304
305 my $content_tag = $tree->look_down(@look_down);
306
307 unless ($content_tag) {
308 warn "criteria [@look_down] not found";
309 return;
310 }
311
312 $content_tag->replace_content($content);
313
314 }
315
316 sub HTML::Element::highlander {
317 my ($tree, $local_root_id, $aref, @arg) = @_;
318
319 ref $aref eq 'ARRAY' or confess
320 "must supply array reference";
321
322 my @aref = @$aref;
323 @aref % 2 == 0 or confess
324 "supplied array ref must have an even number of entries";
325
326 warn __PACKAGE__ if $DEBUG;
327
328 my $survivor;
329 while (my ($id, $test) = splice @aref, 0, 2) {
330 warn $id if $DEBUG;
331 if ($test->(@arg)) {
332 $survivor = $id;
333 last;
334 }
335 }
336
337
338 my @id_survivor = (id => $survivor);
339 my $survivor_node = $tree->look_down(@id_survivor);
340 # warn $survivor;
341 # warn $local_root_id;
342 # warn $node;
343
344 warn "survivor: $survivor" if $DEBUG;
345 warn "tree: " . $tree->as_HTML if $DEBUG;
346
347 $survivor_node or die "search for @id_survivor failed in tree($tree): " . $tree->as_HTML;
348
349 my $survivor_node_parent = $survivor_node->parent;
350 $survivor_node = $survivor_node->clone;
351 $survivor_node_parent->replace_content($survivor_node);
352
353 warn "new tree: " . $tree->as_HTML if $DEBUG;
354
355 $survivor_node;
356 }
357
358
359 sub HTML::Element::highlander2 {
360 my $tree = shift;
361
362 my %p = validate(@_, {
363 cond => { type => ARRAYREF },
364 cond_arg => { type => ARRAYREF,
365 default => []
366 },
367 debug => { default => 0 }
368 }
369 );
370
371
372 my @cond = @{$p{cond}};
373 @cond % 2 == 0 or confess
374 "supplied array ref must have an even number of entries";
375
376 warn __PACKAGE__ if $p{debug};
377
378 my @cond_arg = @{$p{cond_arg}};
379
380 my $survivor; my $then;
381 while (my ($id, $if_then) = splice @cond, 0, 2) {
382
383 warn $id if $p{debug};
384 my ($if, $_then);
385
386 if (ref $if_then eq 'ARRAY') {
387 ($if, $_then) = @$if_then;
388 } else {
389 ($if, $_then) = ($if_then, sub {});
390 }
391
392 if ($if->(@cond_arg)) {
393 $survivor = $id;
394 $then = $_then;
395 last;
396 }
397
398 }
399
400 my @ld = (ref $survivor eq 'ARRAY')
401 ? @$survivor
402 : (id => $survivor)
403 ;
404
405 warn "survivor: ", $survivor if $p{debug};
406 warn "survivor_ld: ", Dumper \@ld if $p{debug};
407
408
409 my $survivor_node = $tree->look_down(@ld);
410
411 $survivor_node or confess
412 "search for @ld failed in tree($tree): " . $tree->as_HTML;
413
414 my $survivor_node_parent = $survivor_node->parent;
415 $survivor_node = $survivor_node->clone;
416 $survivor_node_parent->replace_content($survivor_node);
417
418
419 # **************** NEW FUNCTIONALITY *******************
420
421 # apply transforms on survivor node
422
423
424 warn "SURV::pre_trans " . $survivor_node->as_HTML if $p{debug};
425 $then->($survivor_node, @cond_arg);
426 warn "SURV::post_trans " . $survivor_node->as_HTML if $p{debug};
427
428 # **************** NEW FUNCTIONALITY *******************
429
430
431
432
433 $survivor_node;
434 }
435
436
437 sub overwrite_action {
438 my ($mute_node, %X) = @_;
439
440 $mute_node->attr($X{local_attr}{name} => $X{local_attr}{value}{new});
441 }
442
443
444 sub HTML::Element::overwrite_attr {
445 my $tree = shift;
446
447 $tree->mute_elem(@_, \&overwrite_action);
448 }
449
450
451
452 sub HTML::Element::mute_elem {
453 my ($tree, $mute_attr, $closures, $post_hook) = @_;
454
455 warn "my mute_node = $tree->look_down($mute_attr => qr/.*/) ;";
456 my @mute_node = $tree->look_down($mute_attr => qr/.*/) ;
457
458 for my $mute_node (@mute_node) {
459 my ($local_attr,$mute_key) = split /\s+/, $mute_node->attr($mute_attr);
460 my $local_attr_value_current = $mute_node->attr($local_attr);
461 my $local_attr_value_new = $closures->{$mute_key}->($tree, $mute_node, $local_attr_value_current);
462 $post_hook->(
463 $mute_node,
464 tree => $tree,
465 local_attr => {
466 name => $local_attr,
467 value => {
468 current => $local_attr_value_current,
469 new => $local_attr_value_new
470 }
471 }
472 ) if ($post_hook) ;
473 }
474 }
475
476
477
478 sub HTML::Element::table {
479
480 my ($s, %table) = @_;
481
482 my $table = {};
483
484 # use Data::Dumper; warn Dumper \%table;
485
486 # ++$DEBUG if $table{debug} ;
487
488
489 # Get the table element
490 $table->{table_node} = $s->look_down(id => $table{gi_table});
491 $table->{table_node} or confess
492 "table tag not found via (id => $table{gi_table}";
493
494 # Get the prototype tr element(s)
495 my @table_gi_tr = listify $table{gi_tr} ;
496 my @iter_node = map
497 {
498 my $tr = $table->{table_node}->look_down(id => $_);
499 $tr or confess "tr with id => $_ not found";
500 $tr;
501 } @table_gi_tr;
502
503 warn "found " . @iter_node . " iter nodes " if $DEBUG;
504 # tie my $iter_node, 'Tie::Cycle', \@iter_node;
505 my $iter_node = List::Rotation::Cycle->new(@iter_node);
506
507 # warn $iter_node;
508 warn Dumper ($iter_node, \@iter_node) if $DEBUG;
509
510 # $table->{content} = $table{content};
511 #$table->{parent} = $table->{table_node}->parent;
512
513
514 # $table->{table_node}->detach;
515 # $_->detach for @iter_node;
516
517 my @table_rows;
518
519 {
520 my $row = $table{tr_data}->($table, $table{table_data});
521 last unless defined $row;
522
523 # get a sample table row and clone it.
524 my $I = $iter_node->next;
525 warn "I: $I" if $DEBUG;
526 my $new_iter_node = $I->clone;
527
528
529 $table{td_data}->($new_iter_node, $row);
530 push @table_rows, $new_iter_node;
531
532 redo;
533 }
534
535 if (@table_rows) {
536
537 my $replace_with_elem = $s->look_down(id => shift @table_gi_tr) ;
538 for (@table_gi_tr) {
539 $s->look_down(id => $_)->detach;
540 }
541
542 $replace_with_elem->replace_with(@table_rows);
543
544 }
545
546 }
547
548 sub ref_or_ld {
549
550 my ($tree, $slot) = @_;
551
552 if (ref($slot) eq 'CODE') {
553 $slot->($tree);
554 } else {
555 $tree->look_down(@$slot);
556 }
557 }
558
559
560
561 sub HTML::Element::table2 {
562
563 my $tree = shift;
564
565
566
567 my %p = validate(
568 @_, {
569 table_ld => { default => ['_tag' => 'table'] },
570 table_data => 1,
571 table_proc => { default => undef },
572
573 tr_ld => { default => ['_tag' => 'tr'] },
574 tr_data => { default => sub { my ($self, $data) = @_;
575 shift(@{$data}) ;
576 }},
577 tr_base_id => { default => undef },
578 tr_proc => { default => sub {} },
579 td_proc => 1,
580 debug => {default => 0}
581 }
582 );
583
584 warn "INPUT TO TABLE2: ", Dumper \@_ if $p{debug};
585
586 warn "table_data: " . Dumper $p{table_data} if $p{debug} ;
587
588 my $table = {};
589
590 # use Data::Dumper; warn Dumper \%table;
591
592 # ++$DEBUG if $table{debug} ;
593
594 # Get the table element
595 #warn 1;
596 $table->{table_node} = ref_or_ld( $tree, $p{table_ld} ) ;
597 #warn 2;
598 $table->{table_node} or confess
599 "table tag not found via " . Dumper($p{table_ld}) ;
600
601 warn "table: " . $table->{table_node}->as_HTML if $p{debug};
602
603
604 # Get the prototype tr element(s)
605 my @proto_tr = ref_or_ld( $table->{table_node}, $p{tr_ld} ) ;
606
607 warn "found " . @proto_tr . " iter nodes " if $p{debug};
608
609 @proto_tr or return ;
610
611 if ($p{debug}) {
612 warn $_->as_HTML for @proto_tr;
613 }
614 my $proto_tr = List::Rotation::Cycle->new(@proto_tr);
615
616 my $tr_parent = $proto_tr[0]->parent;
617 warn "parent element of trs: " . $tr_parent->as_HTML if $p{debug};
618
619 my $row_count;
620
621 my @table_rows;
622
623 {
624 my $row = $p{tr_data}->($table, $p{table_data}, $row_count);
625 warn "data row: " . Dumper $row if $p{debug};
626 last unless defined $row;
627
628 # wont work: my $new_iter_node = $table->{iter_node}->clone;
629 my $new_tr_node = $proto_tr->next->clone;
630 warn "new_tr_node: $new_tr_node" if $p{debug};
631
632 $p{tr_proc}->($tree, $new_tr_node, $row, $p{tr_base_id}, ++$row_count)
633 if defined $p{tr_proc};
634
635 warn "data row redux: " . Dumper $row if $p{debug};
636 #warn 3.3;
637
638 $p{td_proc}->($new_tr_node, $row);
639 push @table_rows, $new_tr_node;
640
641 #warn 4.4;
642
643 redo;
644 }
645
646 $_->detach for @proto_tr;
647
648 $tr_parent->push_content(@table_rows) if (@table_rows) ;
649
650 }
651
652
653 sub HTML::Element::unroll_select {
654
655 my ($s, %select) = @_;
656
657 my $select = {};
658
659 my $select_node = $s->look_down(id => $select{select_label});
660
661 my $option = $select_node->look_down('_tag' => 'option');
662
663 # warn $option;
664
665
666 $option->detach;
667
668 while (my $row = $select{data_iter}->($select{data}))
669 {
670 # warn Dumper($row);
671 my $o = $option->clone;
672 $o->attr('value', $select{option_value}->($row));
673 $o->attr('SELECTED', 1) if ($select{option_selected}->($row)) ;
674
675 $o->replace_content($select{option_content}->($row));
676 $select_node->push_content($o);
677 }
678
679
680 }
681
682
683
684 sub HTML::Element::set_sibling_content {
685 my ($elt, $content) = @_;
686
687 $elt->parent->splice_content($elt->pindex + 1, 1, $content);
688
689 }
690
691 sub HTML::TreeBuilder::parse_string {
692 my ($package, $string) = @_;
693
694 my $h = HTML::TreeBuilder->new;
695 HTML::TreeBuilder->parse($string);
696
697 }
698
699
700
701 1;
702 __END__
703 # Below is stub documentation for your module. You'd better edit it!
704
705 =head1 NAME
706
707 HTML::Element::Library - HTML::Element convenience functions
708
709 =head1 SYNOPSIS
710
711 use HTML::Element::Library;
712 use HTML::TreeBuilder;
713
714 =head1 DESCRIPTION
715
716 This method provides API calls for common actions on trees when using
717 L<HTML::Tree>.
718
719 =head1 METHODS
720
721 The test suite contains examples of each of these methods in a
722 file C<t/$method.t>
723
724 =head2 Positional Querying Methods
725
726 =head3 $elem->siblings
727
728 Return a list of all nodes under the same parent.
729
730 =head3 $elem->sibdex
731
732 Return the index of C<$elem> into the array of siblings of which it is
733 a part. L<HTML::ElementSuper> calls this method C<addr> but I don't think
734 that is a descriptive name. And such naming is deceptively close to the
735 C<address> function of C<HTML::Element>. HOWEVER, in the interest of
736 backwards compatibility, both methods are available.
737
738 =head3 $elem->addr
739
740 Same as sibdex
741
742 =head3 $elem->position()
743
744 Returns the coordinates of this element in the tree it inhabits.
745 This is accomplished by succesively calling addr() on ancestor
746 elements until either a) an element that does not support these
747 methods is found, or b) there are no more parents. The resulting
748 list is the n-dimensional coordinates of the element in the tree.
749
750 =head2 Element Decoration Methods
751
752 =head3 HTML::Element::Library::super_literal($text)
753
754 In L<HTML::Element>, Sean Burke discusses super-literals. They are
755 text which does not get escaped. Great for includng Javascript in
756 HTML. Also great for including foreign language into a document.
757
758 So, you basically toss C<super_literal> your text and back comes
759 your text wrapped in a C<~literal> element.
760
761 One of these days, I'll around to writing a nice C<EXPORT> section.
762
763 =head2 Tree Rewriting Methods
764
765 =head3 $elem->hash_map(hash => \%h, to_attr => $attr, excluding => \@excluded)
766
767 This method is designed to take a hashref and populate a series of elements. For example:
768
769
770 <table>
771 <tr sclass="tr" class="alt" align="left" valign="top">
772 <td sid="people_id">1</td>
773 <td sid="phone">(877) 255-3239</td>
774 <td sid="password">*********</td>
775 </tr>
776 </table>
777
778 In the table above, there are several attributes named C<sid>. If we have a hashref whose keys are the same:
779
780 my %data = (people_id => 888, phone => '444-4444', password => 'dont-you-dare-render');
781
782 Then a single API call allows us to populate the HTML while excluding those ones we dont:
783
784 $tree->hash_map(hash => \%data, to_attr => 'sid', excluding => ['password']);
785
786 Of course, the other way to prevent rendering some of the hash mapping is to not give that element the attr
787 you plan to use for hash mapping.
788
789
790 =head3 $elem->replace_content(@new_elem)
791
792 Replaces all of C<$elem>'s content with C<@new_elem>.
793
794 =head3 $elem->wrap_content($wrapper_element)
795
796 Wraps the existing content in the provided element. If the provided element
797 happens to be a non-element, a push_content is performed instead.
798
799 =head3 $elem->set_child_content(@look_down, $content)
800
801 This method looks down $tree using the criteria specified in @look_down using the the HTML::Element look_down() method.
802
803 After finding the node, it detaches the node's content and pushes $content as the node's content.
804
805 =head3 $tree->content_handler(%id_content)
806
807 This is a convenience method. Because the look_down criteria will often simply be:
808
809 id => 'fixme'
810
811 to find things like:
812
813 <a id=fixme href=http://www.somesite.org>replace_content</a>
814
815 You can call this method to shorten your typing a bit. You can simply type
816
817 $elem->content_handler( fixme => 'new text' )
818
819 Instead of typing:
820
821 $elem->set_child_content(sid => 'fixme', 'new text')
822
823 PLEASE 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:
824
825 my %id_content = (name => "Terrence Brannon",
826 email => 'tbrannon@in.com',
827 balance => 666,
828 content => $main_content);
829
830 $tree->content_handler(%id_content);
831
832 =head3 $tree->highlander($subtree_span_id, $conditionals, @conditionals_args)
833
834 This allows for "if-then-else" style processing. Highlander was a movie in
835 which only one would survive. Well, in terms of a tree when looking at a
836 structure that you want to process in C<if-then-else> style, only one child
837 will survive. For example, given this HTML template:
838
839 <span klass="highlander" id="age_dialog">
840 <span id="under10">
841 Hello, does your mother know you're
842 using her AOL account?
843 </span>
844 <span id="under18">
845 Sorry, you're not old enough to enter
846 (and too dumb to lie about your age)
847 </span>
848 <span id="welcome">
849 Welcome
850 </span>
851 </span>
852
853 We only want one child of the C<span> tag with id C<age_dialog> to remain
854 based on the age of the person visiting the page.
855
856 So, let's setup a call that will prune the subtree as a function of age:
857
858 sub process_page {
859 my $age = shift;
860 my $tree = HTML::TreeBuilder->new_from_file('t/html/highlander.html');
861
862 $tree->highlander
863 (age_dialog =>
864 [
865 under10 => sub { $_[0] < 10} ,
866 under18 => sub { $_[0] < 18} ,
867 welcome => sub { 1 }
868 ],
869 $age
870 );
871
872 And there we have it. If the age is less than 10, then the node with
873 id C<under10> remains. For age less than 18, the node with id C<under18>
874 remains.
875 Otherwise our "else" condition fires and the child with id C<welcome> remains.
876
877 =head3 $tree->passover($id_of_element)
878
879 In some cases, you know exactly which element should survive. In this case,
880 you can simply call C<passover> to remove it's siblings. For the HTML
881 above, you could delete C<under10> and C<welcome> by simply calling:
882
883 $tree->passover('under18');
884
885 =head3 $tree->highlander2($tree, $conditionals, @conditionals_args)
886
887 Right around the same time that C<table2()> came into being, Seamstress
888 began to tackle tougher and tougher processing problems. It became clear that
889 a more powerful highlander was needed... one that not only snipped the tree
890 of the nodes that should not survive, but one that allows for
891 post-processing of the survivor node. And one that was more flexible with
892 how to find the nodes to snip.
893
894 Thus (drum roll) C<highlander2()>.
895
896 So let's look at our HTML which requires post-selection processing:
897
898 <span klass="highlander" id="age_dialog">
899 <span id="under10">
900 Hello, little <span id=age>AGE</span>-year old,
901 does your mother know you're using her AOL account?
902 </span>
903 <span id="under18">
904 Sorry, you're only <span id=age>AGE</span>
905 (and too dumb to lie about your age)
906 </span>
907 <span id="welcome">
908 Welcome, isn't it good to be <span id=age>AGE</span> years old?
909 </span>
910 </span>
911
912 In this case, a branch survives, but it has dummy data in it. We must take
913 the surviving segment of HTML and rewrite the age C<span> with the age.
914 Here is how we use C<highlander2()> to do so:
915
916 sub replace_age {
917 my $branch = shift;
918 my $age = shift;
919 $branch->look_down(id => 'age')->replace_content($age);
920 }
921
922 my $if_then = $tree->look_down(id => 'age_dialog');
923
924 $if_then->highlander2(
925 cond => [
926 under10 => [
927 sub { $_[0] < 10} ,
928 \&replace_age
929 ],
930 under18 => [
931 sub { $_[0] < 18} ,
932 \&replace_age
933 ],
934 welcome => [
935 sub { 1 },
936 \&replace_age
937 ]
938 ],
939 cond_arg => [ $age ]
940 );
941
942 We pass it the tree (C<$if_then>), an arrayref of conditions
943 (C<cond>) and an arrayref of arguments which are passed to the
944 C<cond>s and to the replacement subs.
945
946 The C<under10>, C<under18> and C<welcome> are id attributes in the
947 tree of the siblings of which only one will survive. However,
948 should you need to do
949 more complex look-downs to find the survivor,
950 then supply an array ref instead of a simple
951 scalar:
952
953
954 $if_then->highlander2(
955 cond => [
956 [class => 'r12'] => [
957 sub { $_[0] < 10} ,
958 \&replace_age
959 ],
960 [class => 'z22'] => [
961 sub { $_[0] < 18} ,
962 \&replace_age
963 ],
964 [class => 'w88'] => [
965 sub { 1 },
966 \&replace_age
967 ]
968 ],
969 cond_arg => [ $age ]
970 );
971
972
973 =head3 $tree->overwrite_attr($mutation_attr => $mutating_closures)
974
975 This method is designed for taking a tree and reworking a set of nodes in
976 a stereotyped fashion. For instance let's say you have 3 remote image
977 archives, but you don't want to put long URLs in your img src
978 tags for reasons of abstraction, re-use and brevity. So instead you do this:
979
980 <img src="/img/smiley-face.jpg" fixup="src lnc">
981 <img src="/img/hot-babe.jpg" fixup="src playboy">
982 <img src="/img/footer.jpg" fixup="src foobar">
983
984 and then when the tree of HTML is being processed, you make this call:
985
986 my %closures = (
987 lnc => sub { my ($tree, $mute_node, $attr_value)= @_; "http://lnc.usc.edu$attr_value" },
988 playboy => sub { my ($tree, $mute_node, $attr_value)= @_; "http://playboy.com$attr_value" }
989 foobar => sub { my ($tree, $mute_node, $attr_value)= @_; "http://foobar.info$attr_value" }
990 )
991
992 $tree->overwrite_attr(fixup => \%closures) ;
993
994 and the tags come out modified like so:
995
996 <img src="http://lnc.usc.edu/img/smiley-face.jpg" fixup="src lnc">
997 <img src="http://playboy.com/img/hot-babe.jpg" fixup="src playboy">
998 <img src="http://foobar.info/img/footer.jpg" fixup="src foobar">
999
1000 =head3 $tree->mute_elem($mutation_attr => $mutating_closures, [ $post_hook ] )
1001
1002 This is a generalization of C<overwrite_attr>. C<overwrite_attr>
1003 assumes the return value of the
1004 closure is supposed overwrite an attribute value and does it for you.
1005 C<mute_elem> is a more general function which does nothing but
1006 hand the closure the element and let it mutate it as it jolly well pleases :)
1007
1008 In fact, here is the implementation of C<overwrite_attr>
1009 to give you a taste of how C<mute_attr> is used:
1010
1011 sub overwrite_action {
1012 my ($mute_node, %X) = @_;
1013
1014 $mute_node->attr($X{local_attr}{name} => $X{local_attr}{value}{new});
1015 }
1016
1017
1018 sub HTML::Element::overwrite_attr {
1019 my $tree = shift;
1020
1021 $tree->mute_elem(@_, \&overwrite_action);
1022 }
1023
1024
1025
1026
1027 =head2 Tree-Building Methods
1028
1029
1030
1031 =head3 Unrolling an array via a single sample element (<ul> container)
1032
1033 This is best described by example. Given this HTML:
1034
1035 <strong>Here are the things I need from the store:</strong>
1036 <ul>
1037 <li class="store_items">Sample item</li>
1038 </ul>
1039
1040 We can unroll it like so:
1041
1042 my $li = $tree->look_down(class => 'store_items');
1043
1044 my @items = qw(bread butter vodka);
1045
1046 $tree->iter($li => @items);
1047
1048 To produce this:
1049
1050
1051 <html>
1052 <head></head>
1053 <body>Here are the things I need from the store:
1054 <ul>
1055 <li class="store_items">bread</li>
1056 <li class="store_items">butter</li>
1057 <li class="store_items">vodka</li>
1058 </ul>
1059 </body>
1060 </html>
1061
1062 =head3 Unrolling an array via n sample elements (<dl> container)
1063
1064 C<iter()> was fine for awhile, but some things
1065 (e.g. definition lists) need a more general function to make them easy to
1066 do. Hence C<iter2()>. This function will be explained by example of unrolling
1067 a simple definition list.
1068
1069 So here's our mock-up HTML from the designer:
1070
1071 <dl class="dual_iter" id="service_plan">
1072 <dt>
1073 Artist
1074 </dt>
1075 <dd>
1076 A person who draws blood.
1077 </dd>
1078
1079 <dt>
1080 Musician
1081 </dt>
1082 <dd>
1083 A clone of Iggy Pop.
1084 </dd>
1085
1086 <dt>
1087 Poet
1088 </dt>
1089 <dd>
1090 A relative of Edgar Allan Poe.
1091 </dd>
1092
1093 <dt class="adstyle">sample header</dt>
1094 <dd class="adstyle2">sample data</dd>
1095
1096 </dl>
1097
1098
1099 And we want to unroll our data set:
1100
1101 my @items = (
1102 ['the pros' => 'never have to worry about service again'],
1103 ['the cons' => 'upfront extra charge on purchase'],
1104 ['our choice' => 'go with the extended service plan']
1105 );
1106
1107
1108 Now, let's make this problem a bit harder to show off the power of C<iter2()>.
1109 Let's assume that we want only the last <dt> and it's accompanying <dd>
1110 (the one with "sample data") to be used as the sample data
1111 for unrolling with our data set. Let's further assume that we want them to
1112 remain in the final output.
1113
1114 So now, the API to C<iter2()> will be discussed and we will explain how our
1115 goal of getting our data into HTML fits into the API.
1116
1117 =over 4
1118
1119 =item * wrapper_ld
1120
1121 This is how to look down and find the container of all the elements we will
1122 be unrolling. The <dl> tag is the container for the dt and dd tags we will be
1123 unrolling.
1124
1125 If you pass an anonymous subroutine, then it is presumed that execution of
1126 this subroutine will return the HTML::Element representing the container tag.
1127 If you pass an array ref, then this will be dereferenced and passed to
1128 C<HTML::Element::look_down()>.
1129
1130 default value: C<< ['_tag' => 'dl'] >>
1131
1132 Based on the mock HTML above, this default is fine for finding our container
1133 tag. So let's move on.
1134
1135 =item * wrapper_data
1136
1137 This is an array reference of data that we will be putting into the container.
1138 You must supply this. C<@items> above is our C<wrapper_data>.
1139
1140 =item * wrapper_proc
1141
1142 After we find the container via C<wrapper_ld>, we may want to pre-process
1143 some aspect of this tree. In our case the first two sets of dt and dd need
1144 to be removed, leaving the last dt and dd. So, we supply a C<wrapper_proc>
1145 which will do this.
1146
1147 default: undef
1148
1149 =item * item_ld
1150
1151 This anonymous subroutine returns an array ref of C<HTML::Element>s that will
1152 be cloned and populated with item data
1153 (item data is a "row" of C<wrapper_data>).
1154
1155 default: returns an arrayref consisting of the dt and dd element inside the
1156 container.
1157
1158 =item * item_data
1159
1160 This is a subroutine that takes C<wrapper_data> and retrieves one "row"
1161 to be "pasted" into the array ref of C<HTML::Element>s found via C<item_ld>.
1162 I hope that makes sense.
1163
1164 default: shifts C<wrapper_data>.
1165
1166 =item * item_proc
1167
1168 This is a subroutine that takes the C<item_data> and the C<HTML::Element>s
1169 found via C<item_ld> and produces an arrayref of C<HTML::Element>s which will
1170 eventually be spliced into the container.
1171
1172 Note that this subroutine MUST return the new items. This is done
1173 So that more items than were passed in can be returned. This is
1174 useful when, for example, you must return 2 dts for an input data item.
1175 And when would you do this? When a single term has multiple spellings
1176 for instance.
1177
1178 default: expects C<item_data> to be an arrayref of two elements and
1179 C<item_elems> to be an arrayref of two C<HTML::Element>s. It replaces the
1180 content of the C<HTML::Element>s with the C<item_data>.
1181
1182 =item * splice
1183
1184 After building up an array of C<@item_elems>, the subroutine passed as
1185 C<splice> will be given the parent container HTML::Element and the
1186 C<@item_elems>. How the C<@item_elems> end up in the container is up to this
1187 routine: it could put half of them in. It could unshift them or whatever.
1188
1189 default: C<< $container->splice_content(0, 2, @item_elems) >>
1190 In other words, kill the 2 sample elements with the newly generated
1191 @item_elems
1192
1193 =back
1194
1195 So now that we have documented the API, let's see the call we need:
1196
1197 $tree->iter2(
1198 # default wrapper_ld ok.
1199 wrapper_data => \@items,
1200 wrapper_proc => sub {
1201 my ($container) = @_;
1202
1203 # only keep the last 2 dts and dds
1204 my @content_list = $container->content_list;
1205 $container->splice_content(0, @content_list - 2);
1206 },
1207
1208 # default item_ld is fine.
1209 # default item_data is fine.
1210 # default item_proc is fine.
1211 splice => sub {
1212 my ($container, @item_elems) = @_;
1213 $container->unshift_content(@item_elems);
1214 },
1215 debug => 1,
1216 );
1217
1218
1219
1220
1221 =head3 Select Unrolling
1222
1223 The C<unroll_select> method has this API:
1224
1225 $tree->unroll_select(
1226 select_label => $id_label,
1227 option_value => $closure, # how to get option value from data row
1228 option_content => $closure, # how to get option content from data row
1229 option_selected => $closure, # boolean to decide if SELECTED
1230 data => $data # the data to be put into the SELECT
1231 data_iter => $closure # the thing that will get a row of data
1232 );
1233
1234 Here's an example:
1235
1236 $tree->unroll_select(
1237 select_label => 'clan_list',
1238 option_value => sub { my $row = shift; $row->clan_id },
1239 option_content => sub { my $row = shift; $row->clan_name },
1240 option_selected => sub { my $row = shift; $row->selected },
1241 data => \@query_results,
1242 data_iter => sub { my $data = shift; $data->next }
1243 )
1244
1245
1246
1247 =head2 Tree-Building Methods: Table Generation
1248
1249 Matthew Sisk has a much more intuitive (imperative)
1250 way to generate tables via his module
1251 L<HTML::ElementTable|HTML::ElementTable>.
1252 However, for those with callback fever, the following
1253 method is available. First, we look at a nuts and bolts way to build a table
1254 using only standard L<HTML::Tree> API calls. Then the C<table> method
1255 available here is discussed.
1256
1257 =head3 Sample Model
1258
1259 package Simple::Class;
1260
1261 use Set::Array;
1262
1263 my @name = qw(bob bill brian babette bobo bix);
1264 my @age = qw(99 12 44 52 12 43);
1265 my @weight = qw(99 52 80 124 120 230);
1266
1267
1268 sub new {
1269 my $this = shift;
1270 bless {}, ref($this) || $this;
1271 }
1272
1273 sub load_data {
1274 my @data;
1275
1276 for (0 .. 5) {
1277 push @data, {
1278 age => $age[rand $#age] + int rand 20,
1279 name => shift @name,
1280 weight => $weight[rand $#weight] + int rand 40
1281 }
1282 }
1283
1284 Set::Array->new(@data);
1285 }
1286
1287
1288 1;
1289
1290
1291 =head4 Sample Usage:
1292
1293 my $data = Simple::Class->load_data;
1294 ++$_->{age} for @$data
1295
1296 =head3 Inline Code to Unroll a Table
1297
1298 =head4 HTML
1299
1300 <html>
1301
1302 <table id="load_data">
1303
1304 <tr> <th>name</th><th>age</th><th>weight</th> </tr>
1305
1306 <tr id="iterate">
1307
1308 <td id="name"> NATURE BOY RIC FLAIR </td>
1309 <td id="age"> 35 </td>
1310 <td id="weight"> 220 </td>
1311
1312 </tr>
1313
1314 </table>
1315
1316 </html>
1317
1318
1319 =head4 The manual way (*NOT* recommended)
1320
1321 require 'simple-class.pl';
1322 use HTML::Seamstress;
1323
1324 # load the view
1325 my $seamstress = HTML::Seamstress->new_from_file('simple.html');
1326
1327 # load the model
1328 my $o = Simple::Class->new;
1329 my $data = $o->load_data;
1330
1331 # find the <table> and <tr>
1332 my $table_node = $seamstress->look_down('id', 'load_data');
1333 my $iter_node = $table_node->look_down('id', 'iterate');
1334 my $table_parent = $table_node->parent;
1335
1336
1337 # drop the sample <table> and <tr> from the HTML
1338 # only add them in if there is data in the model
1339 # this is achieved via the $add_table flag
1340
1341 $table_node->detach;
1342 $iter_node->detach;
1343 my $add_table;
1344
1345 # Get a row of model data
1346 while (my $row = shift @$data) {
1347
1348 # We got row data. Set the flag indicating ok to hook the table into the HTML
1349 ++$add_table;
1350
1351 # clone the sample <tr>
1352 my $new_iter_node = $iter_node->clone;
1353
1354 # find the tags labeled name age and weight and
1355 # set their content to the row data
1356 $new_iter_node->content_handler($_ => $row->{$_})
1357 for qw(name age weight);
1358
1359 $table_node->push_content($new_iter_node);
1360
1361 }
1362
1363 # reattach the table to the HTML tree if we loaded data into some table rows
1364
1365 $table_parent->push_content($table_node) if $add_table;
1366
1367 print $seamstress->as_HTML;
1368
1369
1370
1371 =head3 $tree->table() : API call to Unroll a Table
1372
1373 require 'simple-class.pl';
1374 use HTML::Seamstress;
1375
1376 # load the view
1377 my $seamstress = HTML::Seamstress->new_from_file('simple.html');
1378 # load the model
1379 my $o = Simple::Class->new;
1380
1381 $seamstress->table
1382 (
1383 # tell seamstress where to find the table, via the method call
1384 # ->look_down('id', $gi_table). Seamstress detaches the table from the
1385 # HTML tree automatically if no table rows can be built
1386
1387 gi_table => 'load_data',
1388
1389 # tell seamstress where to find the tr. This is a bit useless as
1390 # the <tr> usually can be found as the first child of the parent
1391
1392 gi_tr => 'iterate',
1393
1394 # the model data to be pushed into the table
1395
1396 table_data => $o->load_data,
1397
1398 # the way to take the model data and obtain one row
1399 # if the table data were a hashref, we would do:
1400 # my $key = (keys %$data)[0]; my $val = $data->{$key}; delete $data->{$key}
1401
1402 tr_data => sub { my ($self, $data) = @_;
1403 shift(@{$data}) ;
1404 },
1405
1406 # the way to take a row of data and fill the <td> tags
1407
1408 td_data => sub { my ($tr_node, $tr_data) = @_;
1409 $tr_node->content_handler($_ => $tr_data->{$_})
1410 for qw(name age weight) }
1411
1412 );
1413
1414
1415 print $seamstress->as_HTML;
1416
1417
1418
1419 =head4 Looping over Multiple Sample Rows
1420
1421 * HTML
1422
1423 <html>
1424
1425 <table id="load_data" CELLPADDING=8 BORDER=2>
1426
1427 <tr> <th>name</th><th>age</th><th>weight</th> </tr>
1428
1429 <tr id="iterate1" BGCOLOR="white" >
1430
1431 <td id="name"> NATURE BOY RIC FLAIR </td>
1432 <td id="age"> 35 </td>
1433 <td id="weight"> 220 </td>
1434
1435 </tr>
1436 <tr id="iterate2" BGCOLOR="#CCCC99">
1437
1438 <td id="name"> NATURE BOY RIC FLAIR </td>
1439 <td id="age"> 35 </td>
1440 <td id="weight"> 220 </td>
1441
1442 </tr>
1443
1444 </table>
1445
1446 </html>
1447
1448
1449 * Only one change to last API call.
1450
1451 This:
1452
1453 gi_tr => 'iterate',
1454
1455 becomes this:
1456
1457 gi_tr => ['iterate1', 'iterate2']
1458
1459 =head3 $tree->table2() : New API Call to Unroll a Table
1460
1461 After 2 or 3 years with C<table()>, I began to develop
1462 production websites with it and decided it needed a cleaner
1463 interface, particularly in the area of handling the fact that
1464 C<id> tags will be the same after cloning a table row.
1465
1466 First, I will give a dry listing of the function's argument parameters.
1467 This will not be educational most likely. A better way to understand how
1468 to use the function is to read through the incremental unrolling of the
1469 function's interface given in conversational style after the dry listing.
1470 But take your pick. It's the same information given in two different
1471 ways.
1472
1473 =head4 Dry/technical parameter documentation
1474
1475 C<< $tree->table2(%param) >> takes the following arguments:
1476
1477 =over
1478
1479 =item * C<< table_ld => $look_down >> : optional
1480
1481 How to find the C<table> element in C<$tree>. If C<$look_down> is an
1482 arrayref, then use C<look_down>. If it is a CODE ref, then call it,
1483 passing it C<$tree>.
1484
1485 Defaults to C<< ['_tag' => 'table'] >> if not passed in.
1486
1487 =item * C<< table_data => $tabular_data >> : required
1488
1489 The data to fill the table with. I<Must> be passed in.
1490
1491 =item * C<< table_proc => $code_ref >> : not implemented
1492
1493 A subroutine to do something to the table once it is found.
1494 Not currently implemented. Not obviously necessary. Just
1495 created because there is a C<tr_proc> and C<td_proc>.
1496
1497 =item * C<< tr_ld => $look_down >> : optional
1498
1499 Same as C<table_ld> but for finding the table row elements. Please note
1500 that the C<tr_ld> is done on the table node that was found I<instead>
1501 of the whole HTML tree. This makes sense. The C<tr>s that you want exist
1502 below the table that was just found.
1503
1504 Defaults to C<< ['_tag' => 'tr'] >> if not passed in.
1505
1506 =item * C<< tr_data => $code_ref >> : optional
1507
1508 How to take the C<table_data> and return a row. Defaults to:
1509
1510 sub { my ($self, $data) = @_;
1511 shift(@{$data}) ;
1512 }
1513
1514 =item * C<< tr_proc => $code_ref >> : optional
1515
1516 Something to do to the table row we are about to add to the
1517 table we are making. Defaults to a routine which makes the C<id>
1518 attribute unique:
1519
1520 sub {
1521 my ($self, $tr, $tr_data, $tr_base_id, $row_count) = @_;
1522 $tr->attr(id => sprintf "%s_%d", $tr_base_id, $row_count);
1523 }
1524
1525 =item * C<< td_proc => $code_ref >> : required
1526
1527 This coderef will take the row of data and operate on the C<td> cells that
1528 are children of the C<tr>. See C<t/table2.t> for several usage examples.
1529
1530 Here's a sample one:
1531
1532 sub {
1533 my ($tr, $data) = @_;
1534 my @td = $tr->look_down('_tag' => 'td');
1535 for my $i (0..$#td) {
1536 $td[$i]->splice_content(0, 1, $data->[$i]);
1537 }
1538 }
1539
1540 =cut
1541
1542 =head4 Conversational parameter documentation
1543
1544 The first thing you need is a table. So we need a look down for that. If you
1545 don't give one, it defaults to
1546
1547 ['_tag' => 'table']
1548
1549 What good is a table to display in without data to display?!
1550 So you must supply a scalar representing your tabular
1551 data source. This scalar might be an array reference, a C<next>able iterator,
1552 a DBI statement handle. Whatever it is, it can be iterated through to build
1553 up rows of table data.
1554 These two required fields (the way to find the table and the data to
1555 display in the table) are C<table_ld> and C<table_data>
1556 respectively. A little more on C<table_ld>. If this happens to be a CODE ref,
1557 then execution
1558 of the code ref is presumed to return the C<HTML::Element>
1559 representing the table in the HTML tree.
1560
1561 Next, we get the row or rows which serve as sample C<tr> elements by doing
1562 a C<look_down> from the C<table_elem>. While normally one sample row
1563 is enough to unroll a table, consider when you have alternating
1564 table rows. This API call would need one of each row so that it can
1565 cycle through the
1566 sample rows as it loops through the data.
1567 Alternatively, you could always just use one row and
1568 make the necessary changes to the single C<tr> row by
1569 mutating the element in C<tr_proc>,
1570 discussed below. The default C<tr_ld> is
1571 C<< ['_tag' => 'tr'] >> but you can overwrite it. Note well, if you overwrite
1572 it with a subroutine, then it is expected that the subroutine will return
1573 the C<HTML::Element>(s)
1574 which are C<tr> element(s).
1575 The reason a subroutine might be preferred is in the case
1576 that the HTML designers gave you 8 sample C<tr> rows but only one
1577 prototype row is needed.
1578 So you can write a subroutine, to splice out the 7 rows you don't need
1579 and leave the one sample
1580 row remaining so that this API call can clone it and supply it to
1581 the C<tr_proc> and C<td_proc> calls.
1582
1583 Now, as we move through the table rows with table data,
1584 we need to do two different things on
1585 each table row:
1586
1587 =over 4
1588
1589 =item * get one row of data from the C<table_data> via C<tr_data>
1590
1591 The default procedure assumes the C<table_data> is an array reference and
1592 shifts a row off of it:
1593
1594 sub { my ($self, $data) = @_;
1595 shift(@{$data}) ;
1596 }
1597
1598 Your function MUST return undef when there is no more rows to lay out.
1599
1600 =item * take the C<tr> element and mutate it via C<tr_proc>
1601
1602 The default procedure simply makes the id of the table row unique:
1603
1604 sub { my ($self, $tr, $tr_data, $row_count, $root_id) = @_;
1605 $tr->attr(id => sprintf "%s_%d", $root_id, $row_count);
1606 }
1607
1608 =back
1609
1610 Now that we have our row of data, we call C<td_proc> so that it can
1611 take the data and the C<td> cells in this C<tr> and process them.
1612 This function I<must> be supplied.
1613
1614
1615 =head3 Whither a Table with No Rows
1616
1617 Often when a table has no rows, we want to display a message
1618 indicating this to the view. Use conditional processing to decide what
1619 to display:
1620
1621 <span id=no_data>
1622 <table><tr><td>No Data is Good Data</td></tr></table>
1623 </span>
1624 <span id=load_data>
1625 <html>
1626
1627 <table id="load_data">
1628
1629 <tr> <th>name</th><th>age</th><th>weight</th> </tr>
1630
1631 <tr id="iterate">
1632
1633 <td id="name"> NATURE BOY RIC FLAIR </td>
1634 <td id="age"> 35 </td>
1635 <td id="weight"> 220 </td>
1636
1637 </tr>
1638
1639 </table>
1640
1641 </html>
1642
1643 </span>
1644
1645
1646
1647
1648 =head1 SEE ALSO
1649
1650 =over
1651
1652 =item * L<HTML::Tree>
1653
1654 A perl package for creating and manipulating HTML trees
1655
1656 =item * L<HTML::ElementTable>
1657
1658 An L<HTML::Tree> - based module which allows for manipulation of HTML
1659 trees using cartesian coordinations.
1660
1661 =item * L<HTML::Seamstress>
1662
1663 An L<HTML::Tree> - based module inspired by
1664 XMLC (L<http://xmlc.enhydra.org>), allowing for dynamic
1665 HTML generation via tree rewriting.
1666
1667 =head1 TODO
1668
1669 =over
1670
1671 =item * highlander2
1672
1673 currently the API expects the subtrees to survive or be pruned to be
1674 identified by id:
1675
1676 $if_then->highlander2([
1677 under10 => sub { $_[0] < 10} ,
1678 under18 => sub { $_[0] < 18} ,
1679 welcome => [
1680 sub { 1 },
1681 sub {
1682 my $branch = shift;
1683 $branch->look_down(id => 'age')->replace_content($age);
1684 }
1685 ]
1686 ],
1687 $age
1688 );
1689
1690 but, it should be more flexible. the C<under10>, and C<under18> are
1691 expected to be ids in the tree... but it is not hard to have a check to
1692 see if this field is an array reference and if it, then to do a look
1693 down instead:
1694
1695 $if_then->highlander2([
1696 [class => 'under10'] => sub { $_[0] < 10} ,
1697 [class => 'under18'] => sub { $_[0] < 18} ,
1698 [class => 'welcome'] => [
1699 sub { 1 },
1700 sub {
1701 my $branch = shift;
1702 $branch->look_down(id => 'age')->replace_content($age);
1703 }
1704 ]
1705 ],
1706 $age
1707 );
1708
1709
1710
1711 =cut
1712
1713 =head1 SEE ALSO
1714
1715 L<HTML::Seamstress>
1716
1717 =head1 AUTHOR
1718
1719 Terrence Brannon, E<lt>tbone@cpan.orgE<gt>
1720
1721 Many thanks to BARBIE for his RT bug report.
1722
1723 =head1 COPYRIGHT AND LICENSE
1724
1725 Copyright (C) 2004 by Terrence Brannon
1726
1727 This library is free software; you can redistribute it and/or modify
1728 it under the same terms as Perl itself, either Perl version 5.8.4 or,
1729 at your option, any later version of Perl 5 you may have available.
1730
1731
1732 =cut
This page took 0.173954 seconds and 4 git commands to generate.