File Coverage

blib/lib/Template/Lace/DOM.pm
Criterion Covered Total %
statement 249 301 82.7
branch 120 156 76.9
condition 46 57 80.7
subroutine 52 68 76.4
pod 43 51 84.3
total 510 633 80.5


line stmt bran cond sub pod time code
1 2     2   215404 use strict;
  2         4  
  2         49  
2 2     2   10 use warnings;
  2         3  
  2         65  
3             package Template::Lace::DOM;
4              
5 2     2   8 use base 'Mojo::DOM58';
  2         8  
  2         671  
6 2     2   44724 use Storable ();
  2         4592  
  2         48  
7 2     2   12 use Scalar::Util;
  2         5  
  2         76  
8 2     2   631 use Template::Tiny;
  2         1973  
  2         5841  
9              
10             # General Helpers
11              
12             my $tt = Template::Tiny->new;
13              
14             sub tt {
15 2     2 1 1942 my ($dom, @proto) = @_;
16 2 50       13 my %vars = ref($proto[0]) eq 'HASH' ? %{$proto[0]} : @proto;
  0         0  
17 2         7 $tt->process( \$dom->content, \%vars, \$dom->tree->[-1][1] ); # only works in some cases
18 2         592 return $dom;
19             }
20              
21             sub ctx {
22 1     1 1 3 my $self = shift;
23 1         3 local $_ = $self;
24 1 50       5 if(ref($_[0]) eq 'ARRAY') {
25 1         2 push @{$_[0]}, $_[1]->($self, @_[2..$#_]);
  1         6  
26             } else {
27 0         0 $_[0]->($self, @_[1..$#_]);
28             }
29 1         386 return $self;
30             }
31              
32             sub clone {
33 84     84 1 37505 return Storable::dclone(shift);
34             }
35              
36             sub overlay {
37 2     2 1 1746 my ($self, $cb, @args) = @_;
38 2         6 local $_ = $self;
39 2         7 my $overlay_dom = $cb->($self, @args);
40 2         315 $self->replace($overlay_dom);
41             }
42              
43             sub wrap_with {
44 1     1 0 4456 my ($self, $new, $target) = @_;
45 1   50     10 $target ||= '#content';
46             $self->overlay(sub {
47 1     1   4 $new->at($target)
48             ->content($self);
49 1         792 return $new;
50 1         9 });
51             }
52              
53             sub repeat {
54 28     28 1 3571 my ($self, $cb, @items) = @_;
55 28         52 my $index = 0;
56             my @nodes = map {
57 28         77 my $cloned_dom = $self->clone;
  73         190  
58 73         203 $index++;
59 73         160 my $returned_dom = $cb->($cloned_dom, $_, $index);
60 73         506 $returned_dom;
61             } @items;
62              
63              
64             # Might be a faster way to do this...
65 28         132 $self->replace(join '', @nodes);
66 28         21763 return $self;
67             }
68              
69             sub smart_content {
70 102     102 1 196 my ($self, $data) = @_;
71 102 100 100     204 if($self->tag eq 'input') {
    100          
    100          
72 8 100 100     122 if((ref($data)||'') eq 'HASH') {
73 4 100       11 if(exists($data->{selected})) {
74 1         3 $data->{selected} = 'on';
75             }
76 4         10 $self->attr($data);
77             } else {
78 4 100 50     14 if(($self->attr('type')||'') eq 'checkbox') {
79 1         24 $self->boolean_attribute_helper('checked', $data);
80             } else {
81 3         83 $self->attr(value=>$data);
82             }
83             }
84             } elsif($self->tag eq 'option') {
85 19 100 100     533 if((ref($data)||'') eq 'HASH') {
86 16         55 $self->attr(value=>$data->{value});
87 16 100       369 $self->attr(selected=>'on') if $data->{selected};
88 16         71 $self->content(escape_html($data->{content}));
89             } else {
90 3         10 $self->attr(value=>$data);
91 3         92 $self->content(escape_html($data));
92             }
93             } elsif($self->tag eq 'optgroup' and (ref($data) eq 'HASH')) {
94 4         154 $self->attr(label=>escape_html($data->{label}));
95 4 50       92 if(my $option_dom = $self->at('option')) {
96 4         594 $option_dom->fill($data->{options});
97             } else {
98 0         0 warn "optgroup with no options."
99             }
100             } else {
101 71         2739 $self->content(escape_html($data));
102             }
103 102         11381 return $self;
104             }
105              
106             sub fill {
107 158     158 1 7991 my ($self, $data, $is_loop, $is_form) = @_;
108 158 100 100     339 $is_form = 1 if ($self->tag||'') eq 'form';
109 158 100       2756 if(ref \$data eq 'SCALAR') {
    100          
    100          
    50          
    0          
110 68         162 $self->smart_content($data);
111             } elsif(ref $data eq 'CODE') {
112 3         6 local $_ = $self;
113 3         11 $data->($self, $data);
114             } elsif(ref $data eq 'ARRAY') {
115             ## Probably should handle the other spcial
116             ## tags helpers like DL, FORM, etc.
117 41 100 100     92 if(
    100 100        
      100        
      100        
118             (($self->tag||'') eq 'ol')
119             || (($self->tag||'') eq 'ul')
120             )
121             {
122 9         233 $self->at('li')
123             ->fill($data, $is_loop, $is_form);
124             } elsif(($self->tag||'') eq 'select') {
125 5 100       232 if(my $optgroup = $self->at('optgroup')) {
    50          
126 2         303 $optgroup->fill($data, $is_loop, $is_form);
127             } elsif(my $option = $self->at('option')) {
128 3         951 $option->fill($data, $is_loop, $is_form);
129             } else {
130 0         0 warn "Found 'select' without option or optgroup";
131             }
132             } else {
133             $self->repeat(sub {
134 70     70   144 my ($dom, $datum, $index) = @_;
135 70         194 $dom->fill($datum, 1);
136 70         391 return $dom;
137 27         1224 }, @$data);
138             }
139             } elsif(ref $data eq 'HASH') {
140 46 100 100     96 if(
    100 66        
    100 33        
      100        
      66        
      33        
      100        
      66        
141             (($self->tag||'') eq 'option')
142             and exists($data->{content})
143             and exists($data->{value})
144             ) {
145 16         277 $self->smart_content($data);
146             } elsif(
147             (($self->tag||'') eq 'optgroup')
148             and exists($data->{options})
149             and exists($data->{label})
150             ) {
151 4         146 $self->smart_content($data);
152             } elsif(
153             (($self->tag||'') eq 'input')
154             and exists($data->{value})
155             ) {
156 4         179 $self->smart_content($data);
157             } else {
158 22         893 foreach my $match (keys %{$data}) {
  22         79  
159 48 100       3195 if(!$is_loop) {
160 21         64 my $dom;
161 21 100       66 if($dom = $self->at("#$match")) {
    50          
162 13 100       3584 $is_form = 1 if $dom->tag eq 'form';
163 13         224 $dom->fill($data->{$match}, $is_loop, $is_form);
164 13         601 next;
165             } elsif($dom = $self->at("*[data-lace-id='$match']")) {
166 0 0       0 $is_form = 1 if $dom->tag eq 'form';
167 0         0 $dom->fill($data->{$match}, $is_loop, $is_form);
168 0         0 next;
169             }
170             }
171             $self->find(".$match, *[data-lace-id='$match']")->each(sub {
172 28     28   10140 my ($dom, $count) = @_;
173 28 50       58 $is_form = 1 if $dom->tag eq 'form';
174 28         484 $dom->fill($data->{$match}, $is_loop, $is_form);
175 35         8556 });
176              
177 35 100       6616 if($is_form) {
178             # Sorry, I'll come up with less suck when I can.
179             $self->find("input[name='$match']")->each(sub {
180 5     5   3112 my ($dom, $count) = @_;
181 5         15 $dom->fill($data->{$match}, $is_loop, $is_form);
182 7         30 });
183             $self->find("select[name='$match']")->each(sub {
184 2     2   925 my ($dom, $count) = @_;
185 2         7 $dom->fill($data->{$match}, $is_loop, $is_form);
186 7         1062 });
187             $self->find("textarea[name='$match']")->each(sub {
188 0     0   0 my ($dom, $count) = @_;
189 0         0 $dom->fill($data->{$match}, $is_loop, $is_form);
190 7         2707 });
191             }
192             }
193             }
194             } elsif(Scalar::Util::blessed $data) {
195 0 0       0 if($data->isa('Template::Lace::DOM')) {
196 0         0 $self->content($data);
197             } else {
198 0         0 my @fields = $data->meta->get_attribute_list;
199 0         0 foreach my $match (@fields) {
200 0 0       0 if(!$is_loop) {
201 0         0 my $dom = $self->at("#$match");
202 0 0       0 $dom->fill($data->$match, $is_loop) if $dom;
203             }
204             $self->find(".$match")->each(sub {
205 0     0   0 my ($dom, $count) = @_;
206 0         0 $dom->fill($data->$match, $is_loop);
207 0         0 });
208             }
209             }
210             } else {
211 0         0 die "method 'fill' does not recognize these arguments.";
212             }
213             }
214              
215             sub for {
216 2     2 1 2285 my ($dom, $match, @args) = @_;
217             $dom->find($match)
218             ->each(sub {
219 2     2   948 my ($dom, $idx) = @_;
220 2         7 $dom->fill(@args);
221 2         21 });
222 2         19 return $dom;
223             }
224              
225             sub append_js_src_uniquely {
226 3     3 1 23 my ($self, $src, $attrs) = @_;
227 3 100       15 unless($self->at("script[src='$src']")) {
228 2 50       691 my $extra_attrs = join ' ', map { "$_='$attrs->{$_}'" } keys %{$attrs||+{}};
  0         0  
  2         15  
229 2         11 $self->at('head')
230             ->append_content("<script type='text/javascript' src='$src' $extra_attrs></script>");
231             }
232 3         1247 return $self;
233             }
234              
235             sub append_link_uniquely {
236 6     6 1 8832 my $self = shift;
237 6         8 my $tag = 'link';
238 6 100       19 my $dom = ref($_[0]) ? shift : ref($self)->new(shift);
239 6 100 100     443 $dom = (($dom->tag||'') eq $tag) ?
240             $dom : $dom->at($tag);
241 6 50       962 if(my $id = $dom->attr('href')) {
242 6 50       125 if(my $head = $self->at("head")) {
243 6 100       1128 if($self->at($tag."[href='$id']")) {
244 2         541 return $self;
245             } else {
246 4         1027 $head->append_content($dom);
247 4         863 return $self;
248             }
249             } else {
250 0         0 $self->append_content($dom);
251             }
252             } else {
253 0         0 return $self;
254             }
255 0         0 return $self;
256             }
257              
258             sub append_style_uniquely {
259 9     9 1 8114 my $self = shift;
260 9         16 my $tag = 'style';
261 9 100       34 my $dom = ref($_[0]) ? shift : ref($self)->new(shift);
262 9 100 100     485 $dom = (($dom->tag||'') eq $tag) ?
263             $dom : $dom->at($tag);
264 9 50       905 if(my $id = $dom->attr('id')) {
265 9 50       163 if(my $head = $self->at("head")) {
266 9 100       1552 if($self->at($tag."[id='$id']")) {
267 3         940 return $self;
268             } else {
269 6         2229 $head->append_content($dom);
270 6         1567 return $self;
271             }
272             } else {
273 0         0 $self->append_content($dom);
274             }
275             } else {
276 0         0 return $self;
277             }
278 0         0 return $self;
279             }
280              
281             sub append_script_uniquely {
282 8     8 1 8043 my $self = shift;
283 8         15 my $tag = 'script';
284 8 100       26 my $dom = ref($_[0]) ? shift : ref($self)->new(shift);
285 8 100 100     920 $dom = (($dom->tag||'') eq $tag) ?
286             $dom : $dom->at($tag);
287 8 100       1351 if(my $id = $dom->attr('id')) {
    50          
288 6 50       136 if(my $head = $self->at("head")) {
289 6 100       1093 if($self->at($tag."[id='$id']")) {
290 2         549 return $self;
291             } else {
292 4         985 $head->append_content($dom);
293 4         950 return $self;
294             }
295             } else {
296 0         0 $self->append_content($dom);
297             }
298             } elsif(my $src = $dom->attr('src')) {
299 2 50       63 if(my $head = $self->at("head")) {
300 2 100       328 if($self->at($tag."[src='$src']")) {
301 1         453 return $self;
302             } else {
303 1         324 $head->append_content($dom);
304 1         260 return $self;
305             }
306             } else {
307 0         0 $self->append_content($dom);
308             }
309             } else {
310 0         0 return $self;
311             }
312 0         0 return $self;
313             }
314              
315              
316              
317             my %_escape_table = (
318             '&' => '&amp;',
319             '>' => '&gt;',
320             '<' => '&lt;',
321             q{"} => '&quot;',
322             q{'} => '&#39;' );
323              
324             sub escape_html {
325 94     94 0 159 my ($value) = @_;
326 94 100       233 return unless defined $value;
327 93         228 $value =~ s/([&><"'])/$_escape_table{$1}/ge;
  4         14  
328 93         241 return $value;
329             }
330              
331             sub _do_attr {
332 9     9   20 my ($self, $attr, $val) = @_;
333 9 50 66     53 if($attr eq ':content') {
    100 100        
334 0         0 $self->fill($val);
335             } elsif(
336             ($attr eq 'checked')
337             || ($attr eq 'selected')
338             || ($attr eq 'hidden')
339             ) {
340 2 50       13 $self->attr($attr=>'on') if $val;
341             } else {
342 7         19 $self->attr($attr=>$val);
343             }
344             }
345              
346             sub _do {
347 16     16   32 my ($self, $maybe_attr, $action) = @_;
348 16 100       41 if($maybe_attr) {
    100          
349 7 50       19 die "Current selected element is not a tag" unless $self->tag;
350 7 100       103 if($maybe_attr eq '*') {
351 1 50       4 die 'action must be a hashref' unless ref($action) eq 'HASH';
352 1         5 map { $self->_do_attr($_, $action->{$_}) } keys %$action;
  3         73  
353             } else {
354 6         17 $self->_do_attr($maybe_attr => $action);
355             }
356             } elsif(!ref $action) {
357 3 100       9 if(defined $action) {
358 2         6 $self->smart_content($action);
359             } else {
360 1         10 $self->remove;
361             }
362             } else {
363 6         19 $self->fill($action);
364             }
365             }
366              
367             sub do {
368 4     4 1 15486 my $self = shift;
369 4         17 while(@_) {
370 14         683 my ($matchspec, $action) = (shift, shift);
371 14         48 my ($css, $maybe_attr) = split('@', $matchspec);
372 14 100       39 if($css ne '.') {
373             $self->find($css)
374 12     14   36 ->each(sub { $_->_do($maybe_attr, $action) });
  14         6518  
375             } else {
376 2         7 $self->_do($maybe_attr, $action);
377             }
378             }
379 4         185 return $self;
380             }
381              
382             # attribute helpers (tag specific or otherwise
383              
384             sub attribute_helper {
385 13     13 0 34 my ($self, $attr, @args) = @_;
386 13         47 $self->attr($attr, @args);
387 13         294 return $self;
388             }
389              
390 1     1 1 3 sub target { shift->attribute_helper('target', @_) }
391 0     0 1 0 sub src { shift->attribute_helper('src', @_) }
392 1     1 1 7691 sub href { shift->attribute_helper('href', @_) }
393 1     1 1 3 sub id { shift->attribute_helper('id', @_) }
394 2     2 1 1411 sub action { shift->attribute_helper('action', @_) }
395 2     2 1 194 sub method { shift->attribute_helper('method', @_) }
396 0     0 1 0 sub colspan { shift->attribute_helper('colspan', @_) }
397 0     0 1 0 sub alt { shift->attribute_helper('alt', @_) }
398 0     0 1 0 sub enctype { shift->attribute_helper('enctype', @_) }
399 0     0 1 0 sub formaction { shift->attribute_helper('formaction', @_) }
400 0     0 1 0 sub headers { shift->attribute_helper('headers', @_) }
401 0     0 1 0 sub size { shift->attribute_helper('size', @_) }
402 0     0 1 0 sub value { shift->attribute_helper('value', @_) }
403              
404             sub multiple {
405 0     0 1 0 my $self = shift;
406 0         0 $self->attr(multiple=>'multiple');
407 0         0 return $self;
408             }
409              
410             sub class {
411 2     2 1 2321 my ($self, @proto) = @_;
412 2 100       10 if(ref($proto[0]) eq 'HASH') {
    50          
413 1         3 my $classes = join ' ', grep { $proto[0]->{$_} } keys %{$proto[0]};
  2         5  
  1         4  
414 1         5 return $self->attribute_helper('class', $classes);
415             } elsif(ref($proto[0]) eq 'ARRAY') {
416 0         0 my $classes = join ' ', @{$proto[0]};
  0         0  
417 0         0 return $self->attribute_helper('class', $classes);
418             } else {
419 1         3 return $self->attribute_helper('class',@proto);
420             }
421             }
422              
423             sub add_class {
424 3     3 1 14603 my ($self, @proto) = @_;
425 3   50     9 my $current_class = $self->attr('class') ||'';
426 3 100       61 if(ref($proto[0]) eq 'HASH') {
    100          
427 1         3 my $classes = join ' ', grep { $proto[0]->{$_} } keys %{$proto[0]};
  2         6  
  1         4  
428 1         5 return $self->attribute_helper('class', "$current_class $classes");
429             } elsif(ref($proto[0]) eq 'ARRAY') {
430 1         2 my $classes = join ' ', @{$proto[0]};
  1         4  
431 1         5 return $self->attribute_helper('class', "$current_class $classes");
432             } else {
433 1         3 my $classes = shift @proto;
434 1         7 return $self->attribute_helper('class', "$current_class $classes");
435             }
436             }
437              
438             sub boolean_attribute_helper {
439 1     1 0 3 my ($self, $name, $value) = @_;
440 1 50       6 $self->attribute_helper($name, 'on') if $value;
441             }
442              
443 0     0 1 0 sub checked { shift->boolean_attribute_helper('checked', @_) }
444 0     0 1 0 sub selected { shift->boolean_attribute_helper('selected', @_) }
445 0     0 1 0 sub hidden { shift->boolean_attribute_helper('hidden', @_) }
446              
447              
448             # unique tag helpers
449              
450             sub unique_tag_helper {
451 22     22 0 104 my ($self, $tag, $proto) = @_;
452 22         93 my $dom = $self->at($tag);
453 22 100       9762 if(ref $proto eq 'CODE') {
    100          
454 7         21 local $_ = $dom;
455 7         27 $proto->($dom);
456             } elsif(ref $proto) {
457 7         33 $dom->fill($proto);
458             } else {
459 8         41 $dom->smart_content($proto);
460             }
461 22         4018 return $self;
462             }
463              
464 6     6 1 14530 sub title { shift->unique_tag_helper('title', @_) }
465 6     6 1 369 sub body { shift->unique_tag_helper('body', @_) }
466 1     1 1 5 sub head { shift->unique_tag_helper('head', @_) }
467 1     1 1 4 sub html { shift->unique_tag_helper('html', @_) }
468              
469             # element helpers
470              
471             sub tag_helper_by_id {
472 0     0 0 0 my ($self, $tag, $id, $proto) = @_;
473 0         0 return $self->unique_tag_helper("$tag$id", $proto);
474             }
475              
476             sub list_helper_by_id {
477 4     4 0 13 my ($self, $tag, $id, $proto) = @_;
478 4 50       20 my $target = ref($proto) eq 'ARRAY' ? "$tag$id li" : "$tag$id";
479 4         12 return $self->unique_tag_helper($target, $proto);
480             }
481              
482 0     0 1 0 sub form { shift->tag_helper_by_id('form', @_) }
483 3     3 1 4159 sub ul { shift->list_helper_by_id('ul', @_) }
484 1     1 1 11 sub ol { shift->list_helper_by_id('ol', @_) }
485              
486             sub optgroup {
487 1     1 1 1570 my ($self, $id, $proto) = @_;
488 1 50       7 my $target = ref($proto) eq 'ARRAY' ? "optgroup$id option" : "optgroup$id";
489 1         5 return $self->unique_tag_helper($target, $proto);
490             }
491              
492             sub dl {
493 3     3 1 5705 my ($self, $id, $proto) = @_;
494 3         12 my $target = "dl$id";
495 3 100       15 if(ref($proto) eq 'HASH') {
    50          
496 2         13 $self->at("dl$id")->fill($proto);
497             } elsif(ref($proto) eq 'ARRAY') {
498 1         5 my $dl = $self->at("dl$id");
499 1         470 my $collection = $dl->find("dt,dd");
500 1         265 my $new = ref($self)
501             ->new($collection->join)
502             ->fill($proto);
503 1         6 $dl->content($new);
504             }
505 3         1059 return $self;
506             }
507              
508             sub select {
509 2     2 1 34 my ($self, $name, $proto) = @_;
510 2 50       11 my $tag = $name=~/^\.#/ ? "$name" : "select[name=$name]";
511 2         8 return $self->unique_tag_helper($tag, $proto);
512             }
513              
514             sub radio {
515 1     1 1 13 my ($self, $name, $proto) = @_;
516 1 50       8 my $tag = $name=~/^\.#/ ? "$name" : "input[type='radio'][name=$name]";
517 1         26 return $self->unique_tag_helper($tag, $proto);
518              
519             }
520              
521             sub at_id {
522 1     1 0 3 my ($self, $id, $data) = @_;
523 1         5 $self->at($id)->fill($data);
524 1         9 return $self;
525             }
526              
527              
528             1;
529              
530             =head1 NAME
531              
532             Template::Lace::DOM - DOM searching and tranformation engine
533              
534             =head1 SYNOPSIS
535              
536             sub process_dom {
537             my ($self, $dom) = @_;
538             $dom->body($self->body);
539             }
540              
541             =head1 DESCRIPTION
542              
543             L<Template::Lace::DOM> is a subclass of L<Mojo::DOM58> that exists to abstract
544             the DOM engine used by L<Template::Lace> as well as to provide some helper methods
545             intended to make the most common types of transformations on your DOM easier.
546              
547             The helper API described here is one of the more 'under consideration / development'
548             parts of L<Template::Lace> since without a lot of usage in the wild its a bit hard
549             to be sure exactly what type of helpers and in what form are most useful. Take
550             the follower API with regard to the fact I will change things if necessary.
551              
552             =head1 GENERAL HELPER METHODS
553              
554             This class defines the following methods for general use
555              
556             =head2 clone
557              
558             Uses L<Storable> C<dclone> to clone the current DOM.
559              
560             =head2 ctx
561              
562             Execute a DOM under a given data context, return DOM. Example
563              
564             my $dom = Template::Lace::DOM->new(qq[
565             <section>
566             <p id='story'>...</p>
567             </section>
568             ]);
569              
570             $dom->ctx(sub {
571             my ($self, $data) = @_;
572             $_->at('#story')->content($data);
573             }, "Don't look down")
574             ->... # more commands on $dom;
575              
576             Returns:
577              
578             <section>
579             <p id='story'>Don&#39;t look down'</p>
580             </section>
581              
582             Isolate running transformaions on a DOM to explicit data. Makes it easier to create
583             reusable snips of transformations.
584              
585             =head2 overlay
586              
587             Overlay the current DOM with a new one. Examples a coderef that should return the
588             new DOM and any additional arguments you want to pass to the coderef. Example;
589              
590             my $dom = Template::Lace::DOM->new(qq[
591             <h1 id="title">HW</h1>
592             <section id="body">Hello World</section>
593             </html>
594             ]);
595              
596             $dom->overlay(sub {
597             my ($dom, $now) = @_; # $dom is also localized to $_
598             my $new_dom = Template::Lace::DOM->new(qq[
599             <html>
600             <head>
601             <title>PAGE_TITLE</title>
602             </head>
603             <body>
604             STUFF
605             </body>
606             </html>
607             ]);
608              
609             $new_dom->title($dom->at('#title')->content)
610             ->body($dom->at('#body'))
611             ->at('head')
612             ->append_content("<meta startup='$now'>");
613              
614             return $new_dom;
615             }, scalar(localtime));
616              
617             Returns example:
618              
619             <html>
620             <head>
621             <title>HW</title>
622             <meta startup="Fri Apr 21 15:45:49 2017"></head>
623             <body>Hello World</body>
624             </html>
625              
626             Useful to encapsulate a lot of the work when you want to apply a standard
627             layout to a web page or section there of.
628              
629             =wrap_with
630              
631             Makes it easier to wrap a current DOM with a 'layout' DOM. Layout DOM
632             replaces original. Example
633              
634             my $master = Template::Lace::DOM->new(qq[
635             <html>
636             <head>
637             <title></title>
638             </head>
639             <body id="content">
640             </body>
641             </html>
642             ]);
643              
644             my $inner = Template::Lace::DOM->new(qq[
645             <h1>Hi</h1>
646             <p>This is a test of the emergency broadcasting networl</p>
647             ]);
648              
649             $inner->wrap_with($master)
650             ->title('Wrapped');
651              
652             print $inner;
653              
654             Returns:
655              
656             <html>
657             <head>
658             <title>Wrapped</title>
659             </head>
660             <body id="content">
661             <h1>Hi</h1>
662             <p>This is a test of the emergency broadcasting networl</p>
663             </body>
664             </html>
665              
666             By default we match the wrapping DOM ($master in the given example) at the '#content' id
667             for the template. You can specify an alternative match point by passing it as a second
668             argument to C<wrap_with>.
669              
670              
671             =head2 repeat
672              
673             Repeat a match as in a loop. Example:
674              
675             my $dom = Template::Lace::DOM->new("<ul><li>ITEMS</li></ul>");
676             my @items = (qw/aaa bbb ccc/);
677              
678             $dom->at('li')
679             ->repeat(sub {
680             my ($li, $item, $index) = @_;
681             # $li here is DOM that represents the original '<li>ITEMS</li>'
682             # each repeat gets that (as a lone of the original) and you can
683             # modify it.
684             $li->content($item);
685             return $li;
686             }, @items);
687              
688             print $dom->to_string;
689              
690             Returns:
691              
692             <ul>
693             <li>aaa</li>
694             <li>bbb</li>
695             <li>ccc</li>
696             <ul>
697              
698             Basically you have a coderef that gets a cloned copy of the matched DOM and you
699             need to return a new DOM that replaces it. Generally you might just modify the
700             current comment (as in the given example) but you are permitted to replace the
701             DOM totally.
702              
703             You might want to see L</LIST HELPERS> and L</fill> as well.
704              
705             =head2 smart_content
706              
707             Like C<content> but when called on a tag that does not have content
708             (like C<input>) will attempt to 'do the right thing'. For example
709             it will put the value into the 'value' attribute of the C<input>
710             tag.
711              
712             Returns the original DOM.
713              
714             B<NOTE> We also html escape values here, since this is usually the
715             safest thing.
716              
717             B<NOTE> Possibly magical method that will need lots of fixes.
718              
719             =head2 fill
720              
721             Used to 'fill' a DOM node with data inteligently by matching hash keys
722             or methods to classes (or ids) and creating repeat loops when the data
723             contains an arrayref.
724              
725             C<fill> will recursively descend the data structure you give it and match
726             hash keys to tag ids or tag classes and then arrayrefs to tag classes only
727             (since ids can't be repeated).
728              
729             Useful to rapidly fill data into a DOM if you don't mind the structual
730             binding between classes/ids and your data. Examples:
731              
732             my $dom = Template::Lace::DOM->new(q[
733             <section>
734             <ul id='stuff'>
735             <li></li>
736             </ul>
737             <ul id='stuff2'>
738             <li>
739             <a class='link'>Links</a> and Info:
740             <span class='info'></span>
741             </li>
742             </ul>
743             <ol id='ordered'>
744             <li></li>
745             </ol>
746             <dl id='list'>
747             <dt>Name</dt>
748             <dd id='name'></dd>
749             <dt>Age</dt>
750             <dd id='age'></dd>
751             </dl>
752             </section>
753             ]);
754              
755             $dom->fill({
756             stuff => [qw/aaa bbb ccc/],
757             stuff2 => [
758             { link=>'1.html', info=>'one' },
759             { link=>'2.html', info=>'two' },
760             { link=>'3.html', info=>'three' },
761             ],
762             ordered => [qw/11 22 33/],
763             list => {
764             name=>'joe',
765             age=>'32',
766             },
767             });
768              
769             Produces:
770              
771             <section>
772             <ul id="stuff">
773             <li>aaa</li><li>bbb</li><li>ccc</li>
774             </ul>
775             <ul id="stuff2">
776             <li>
777             <a class="link">1.html</a> and Info:
778             <span class="info">one</span>
779             </li><li>
780             <a class="link">2.html</a> and Info:
781             <span class="info">two</span>
782             </li><li>
783             <a class="link">3.html</a> and Info:
784             <span class="info">three</span>
785             </li>
786             </ul>
787             <ol id="ordered">
788             <li>11</li><li>22</li><li>33</li>
789             </ol>
790             <dl id="list">
791             <dt>Name</dt>
792             <dd id="name">joe</dd>
793             <dt>Age</dt>
794             <dd id="age">32</dd>
795             </dl>
796             </section>
797              
798             In addition we also match and fill form elements based on the C<name>
799             attribute, even automatically unrolling arrayrefs of hashrefs correctly
800             for C<select> and C<input[type='radio']>. HOWEVER you must first match
801             a form element (by id or class), for example:
802              
803             my $dom = Template::Lace::DOM->new(q[
804             <section>
805             <form id='login'>
806             <input type='text' name='user' />
807             <input type='checkbox' name='toggle'/>
808             <input type='radio' name='choose' />
809             <select name='cars'>
810             <option value='value1'>Value</option>
811             </select>
812             </form>
813             </section>]);
814              
815             $dom->at('html')
816             ->fill(+{
817             login => +{
818             user => 'Hi User',
819             toggle => 'on',
820             choose => [
821             +{id=>'id1', value=>1},
822             +{id=>'id2', value=>2, selected=>1},
823             ],
824             cars => [
825             +{ value=>'honda', content=>'Honda' },
826             +{ value=>'ford', content=>'Ford', selected=>1 },
827             +{ value=>'gm', content=>'General Motors' },
828             ],
829             },
830             });
831              
832             print $dom;
833              
834             Would return:
835              
836             <section>
837             <form id="login">
838             <input name="user" type="text" value="Hi User">
839             <input name="toggle" type="checkbox" value="on">
840             <input id="id1" name="choose" type="radio" value="1">
841             <input id="id2" name="choose" selected="on" type="radio" value="2">
842             <select name="cars">
843             <option value="honda">Honda</option>
844             <option selected="on" value="ford">Ford</option>
845             <option value="gm">General Motors</option>
846             </select>
847             </form>
848             </section>
849              
850             This is done because lookup by C<name> globally would impact performance
851             and return too many false positives.
852              
853             In general C<fill> will try to do the right thing, even coping with
854             list tags such as C<ol>, C<ul> and input type tags (including C<select> and
855             Radio input tags) correctly. You maye find it more magical than you like.
856             Also using this introduces a required structural
857             binding between you Model class and the ids and classes of tags in your
858             templates. You might find this a great convention or fragile binding
859             depending on your outlook.
860              
861             You might want to see L</LIST HELPERS> as well.
862              
863             =head2 for
864              
865             Syntax sugar that combines 'find' and 'fill'. So:
866              
867             $dom->find($match)
868             ->each(sub $_->fill($spec));
869              
870             Can be written as:
871              
872             $dom->for($match, $spec);
873              
874             Might save a bit of space.
875              
876             =head2 append_style_uniquely
877              
878             =head2 append_script_uniquely
879              
880             =head2 append_link_uniquely
881              
882             Appends a style, script or link tag to the header 'uniquely' (that is we
883             don't append it if its already there). The means used to determine
884             uniqueness is first to check for an exising id attribute, and then
885             in the case of scripts we look at the src tag, or the href tag for
886             a link.
887              
888             You need to add the id attributes yourself and be consistent. In the
889             future we may add some type of md5 checksum on content when that exists.
890              
891             Useful when you have a lot of components that need supporting scripts
892             or styles and you want to make sure you only add the required supporting
893             code once.
894              
895             Examples:
896              
897             $dom->append_style_uniquely(qq[
898             <style id='four'>
899             body h4 { border: 1px }
900             </style>]);
901              
902             B<NOTE> This should be the entire tag element.
903              
904             =head2 append_css_href_uniquely
905              
906             =head2 append_js_src_uniquely
907              
908             Similar to the previous group of helpers L</append_link_uniquely>, etc. but
909             instead of taking the entire tag this just wants a URI which is either the
910             src attribute for a C<script> tag, or the href attribute of a C<link> tag.
911             Useful for quickly adding common assets to your pages. URIs are added uniquely
912             so you don't have to worry about checking for the presence it first.
913              
914             $dom->append_js_src_uniquely('/js/common1.js')
915             ->append_js_src_uniquely('/js/common2.js')
916             ->append_js_src_uniquely('/js/common2.js')
917              
918             Would render similar to:
919              
920             <html>
921             <head>
922             <title>Wrapped</title>
923             <script src="/js/common1.js" type="text/javascript"></script>
924             <script src="/js/common2.js" type="text/javascript"></script>
925             </head>
926             <body id="content">
927             <h1>Hi</h1>
928             <p>This is a test of the emergency broadcasting networl</p>
929             </body>
930             </html>
931              
932             We append these to the last node inside the C<head> element content.
933              
934             =head2 do
935              
936             B<NOTE>: Helper is evolving and may change.
937              
938             Allows you to run a list of CSS matches at once. For example:
939              
940             my $dom = Template::Lace::DOM->new(q[
941             <section>
942             <h2>title</h2>
943             <ul id='stuff'>
944             <li></li>
945             </ul>
946             <ul id='stuff2'>
947             <li>
948             <a class='link'>Links</a> and Info:
949             <span class='info'></span>
950             </li>
951             </ul>
952              
953             <ol id='ordered'>
954             <li></li>
955             </ol>
956             <dl id='list'>
957             <dt>Name</dt>
958             <dd id='name'></dd>
959             <dt>Age</dt>
960             <dd id='age'></dd>
961             </dl>
962             <a>Link</a>
963             </section>
964             ]);
965              
966             $dom->do(
967             'section h2' => 'Wrathful Hound',
968             '#stuff', [qw/aaa bbbb ccc/],
969             '#stuff2', [
970             { link=>'1.html', info=>'one' },
971             { link=>'2.html', info=>'two' },
972             { link=>'3.html', info=>'three' },
973             ],
974             '#ordered', sub { $_->fill([qw/11 22 33/]) },
975             '#list', +{
976             name=>'joe',
977             age=>'32',
978             },
979             'a@href' => 'localhost://aaa.html',
980             );
981              
982             Returns:
983              
984             <section>
985             <h2>Wrathful Hound</h2>
986             <ul id="stuff">
987            
988             <li>aaa</li><li>bbbb</li><li>ccc</li></ul>
989             <ul id="stuff2">
990            
991             <li>
992             <a class="link" href="localhost://aaa.html">1.html</a> and Info:
993             <span class="info">one</span>
994             </li><li>
995             <a class="link" href="localhost://aaa.html">2.html</a> and Info:
996             <span class="info">two</span>
997             </li><li>
998             <a class="link" href="localhost://aaa.html">3.html</a> and Info:
999             <span class="info">three</span>
1000             </li></ul>
1001              
1002             <ol id="ordered">
1003            
1004             <li>11</li><li>22</li><li>33</li></ol>
1005             <dl id="list">
1006             <dt>Name</dt>
1007             <dd id="name">joe</dd>
1008             <dt>Age</dt>
1009             <dd id="age">32</dd>
1010             </dl>
1011             <a href="localhost://aaa.html">Link</a>
1012             </section>
1013              
1014             Takes a list of pairs where the first item in the pair is a match specification
1015             and the second is an action to take on it. The match specification is basically
1016             just a CSS match with one added feature to make it easier to fill values into
1017             attributes, if the match specification ends in C<@attr> the action taken is to
1018             fill that attribute.
1019              
1020             Additionally if the action is a simple, scalar value we automatically HTML escape
1021             it for you
1022              
1023             B<NOTE> if you want to set content or attributes on the DOM that ->do is run on
1024             you can use '.' as the match specification.
1025              
1026             =head2 tt
1027              
1028             Lets you fill the matching node's content via <Template::Tiny>. This of course
1029             violates the 'pure no logic templates' but its here as an escapt hatch should
1030             you find the transformation are very awkward. Also its probably the best way
1031             to fill variable into an inline script.
1032              
1033             my $dom = Template::Lace::DOM->new(
1034             qq[<span>Hello [% name%]! It is a [% weather %] day!</span>]);
1035              
1036             $dom->at('span')
1037             ->tt(name=>'John',
1038             weather=>'great');
1039              
1040             print $dom; # prints '<span>Hello John! It is a great day!</span>'
1041              
1042             =head1 ATTRIBUTE HELPERS
1043              
1044             The following methods are intended to make setting standard attributes on
1045             HTML tags easier. All methods return the DOM node instance of the tag making
1046             it easier to chain several calls.
1047              
1048             =head2 target
1049              
1050             =head2 src
1051              
1052             =head2 href
1053              
1054             =head2 id
1055              
1056             =head2 action
1057              
1058             =head2 method
1059              
1060             =head2 size
1061              
1062             =head2 headers
1063              
1064             =head2 formaction
1065              
1066             =head2 enctype
1067              
1068             =head2 alt
1069              
1070             =head2 colspan
1071              
1072             =head2 value
1073              
1074             Example
1075              
1076             $dom->at('form')
1077             ->id('#login_form')
1078             ->action('/login')
1079             ->method('POST');
1080              
1081             =head2 checked
1082              
1083             =head2 selected
1084              
1085             =head2 hidden
1086              
1087             =head2 multiple
1088              
1089             These attribute helpers have a special feature, since its basically a boolean attribute
1090             will check the passed value for its truth state, setting the attribute value to 'on'
1091             when true, but NOT setting the attribute at all if its false.
1092              
1093             =head2 class
1094              
1095             This attribute helper has a special shortcup to make it easier to programmtically set
1096             several classes based on a property. If your argument is a hashref, all the keys whose
1097             values are true will be added. For example:
1098              
1099             my $dom = Template::Lace::DOM->new('<html><div>aaa</div></html>');
1100              
1101             $dom->at('div')->class({ completed=>1, selected=>0});
1102              
1103             print $dom;
1104              
1105             Returns:
1106              
1107             <html><div class="completed">aaa</div></html>
1108              
1109             If you instead use an arrayref, all the classes are just added.
1110              
1111             Useful to reduce some boilerplate.
1112              
1113             =head2 add_class
1114              
1115             Works just like L</class> except the classes are added to any ones that are already
1116             there.
1117              
1118             =head1 UNIQUE TAG HELPERS
1119              
1120             Helpers to access tags that are 'unique', typically only appearing
1121             on a page once. Can accept a coderef, reference or scalar value.
1122             All return the original DOM for ease of chaining.
1123              
1124             =head2 html
1125              
1126             =head2 head
1127              
1128             =head2 title
1129              
1130             =head2 body
1131              
1132             Examples:
1133              
1134             my $data = +{
1135             intro_title => "Things Todo...",
1136             status => {
1137             active_items => 2,
1138             competed_items => 10,
1139             late_items => 0,
1140             },
1141             items => [
1142             'walk dogs',
1143             'buy milk',
1144             ],
1145             };
1146              
1147             my $dom = Template::Lace::DOM->new(qq[
1148             <html>
1149             <head>
1150             <title>TITLE</title>
1151             </head>
1152             <body>
1153             <h1 id='intro_title'>TITLE</h1>
1154             <dl>
1155             </dl>
1156             <dt>Active</dt>
1157             <dd id='active_items'>0</dd>
1158             <dt>Completed</dt>
1159             <dd id='completed_items'>0</dd>
1160             <dt>Late</dt>
1161             <dd id='late_items'>0</dd>
1162             </dl>
1163             <ol>
1164             <li class='items'>ITEMS</li>
1165             </ol>
1166             </body>
1167             </html>
1168             ]);
1169              
1170             $dom->title($data->{intro_title})
1171             ->head(sub {
1172             $_->append_content('<meta description="a page" />');
1173             $_->append_content('<link href="/css/core.css" />');
1174             })->body($data);
1175              
1176             print $dom->to_string;
1177              
1178             Returns
1179              
1180             <html>
1181             <head>
1182             <title>Things Todo...</title>
1183             </head>
1184             <body>
1185             <h1 id='intro_title'>Things Todo...</h1>
1186             <dl>
1187             </dl>
1188             <dt>Active</dt>
1189             <dd id='active_items'>2</dd>
1190             <dt>Completed</dt>
1191             <dd id='completed_items'>10</dd>
1192             <dt>Late</dt>
1193             <dd id='late_items'>0</dd>
1194             </dl>
1195             <ol>
1196             <li class='items'>walk dog</li>
1197             <li class='items'>buy milk</li>
1198             </ol>
1199             </body>
1200             </html>
1201              
1202             Under the hood we use L</fill> and L<smart_content> as well as L<repeat>
1203             as necessary. More magic for less code but at some code in performance
1204             and possible support / code understanding.
1205              
1206             =head1 LIST TAG HELPERS
1207              
1208             Helpers to make populating data into list type tags easier. All return
1209             the original DOM to make chaining easier.
1210              
1211             my $dom = Template::Lace::DOM->new(q[
1212             <section>
1213             <ul id='stuff'>
1214             <li></li>
1215             </ul>
1216             <ul id='stuff2'>
1217             <li>
1218             <a class='link'>Links</a> and Info:
1219             <span class='info'></span>
1220             </li>
1221             </ul>
1222              
1223             <ol id='ordered'>
1224             <li></li>
1225             </ol>
1226             <dl id='list'>
1227             <dt>Name</dt>
1228             <dd id='name'></dd>
1229             <dt>Age</dt>
1230             <dd id='age'></dd>
1231             </dl>
1232             </section>
1233             ]);
1234              
1235             $dom->ul('#stuff', [qw/aaa bbbb ccc/]);
1236             $dom->ul('#stuff2', [
1237             { link=>'1.html', info=>'one' },
1238             { link=>'2.html', info=>'two' },
1239             { link=>'3.html', info=>'three' },
1240             ]);
1241              
1242             $dom->ol('#ordered', [qw/11 22 33/]);
1243              
1244             $dom->dl('#list', {
1245             name=>'joe',
1246             age=>'32',
1247             });
1248              
1249              
1250             =head2 ul
1251              
1252             =head2 ol
1253              
1254             Both helper make it easier to populate an array reference of data into
1255             list tags.
1256              
1257             Example:
1258              
1259             my $dom = Template::Lace::DOM->new(q[
1260             <section>
1261             <ul id='stuff'>
1262             <li></li>
1263             </ul>
1264             <ul id='stuff2'>
1265             <li>
1266             <a class='link'>Links</a> and Info:
1267             <span class='info'></span>
1268             </li>
1269             </ul>
1270              
1271             <ol id='ordered'>
1272             <li></li>
1273             </ol>
1274             <dl id='list'>
1275             <dt>Name</dt>
1276             <dd id='name'></dd>
1277             <dt>Age</dt>
1278             <dd id='age'></dd>
1279             </dl>
1280             </section>
1281             ]);
1282              
1283             $dom->ul('#stuff', [qw/aaa bbbb ccc/]);
1284             $dom->ul('#stuff2', [
1285             { link=>'1.html', info=>'one' },
1286             { link=>'2.html', info=>'two' },
1287             { link=>'3.html', info=>'three' },
1288             ]);
1289              
1290             $dom->ol('#ordered', [qw/11 22 33/]);
1291              
1292             Returns:
1293              
1294             <section>
1295             <ul id="stuff">
1296             <li>aaa</li>
1297             <li>bbbb</li>
1298             <li>ccc</li>
1299             </ul>
1300             <ul id="stuff2">
1301             <li>
1302             <a class="link">1.html</a> and Info:
1303             <span class="info">one</span>
1304             </li>
1305             <li>
1306             <a class="link">2.html</a> and Info:
1307             <span class="info">two</span>
1308             </li>
1309             <li>
1310             <a class="link">3.html</a> and Info:
1311             <span class="info">three</span>
1312             </li>
1313             </ul>
1314             <ol id="ordered">
1315             <li>11</li>
1316             <li>22</li>
1317             <li>33</li>
1318             </ol>
1319             </section>
1320              
1321             =head2 dl
1322              
1323             This helper will take either an arrayref or hashref and attempt to 'do the
1324             right thing'. Example:
1325              
1326             my $dom = Template::Lace::DOM->new(q[
1327             <dl id='hashref'>
1328             <dt>Name</dt>
1329             <dd id='name'></dd>
1330             <dt>Age</dt>
1331             <dd id='age'></dd>
1332             </dl>
1333             <dl id='arrayref'>
1334             <dt class='term'></dt>
1335             <dd class='value'></dd>
1336             </dl>
1337             ]);
1338              
1339             $dom->dl('#hashref', +{
1340             name=>'John',
1341             age=> '48'
1342             });
1343              
1344             $dom->dl('#arrayref', [
1345             +{ term=>'Name', value=> 'John'},
1346             +{ term=>'Age', value=> 42 },
1347             +{ term=>'email', value=> [
1348             'jjn1056@gmail.com',
1349             'jjn1056@yahoo.com']},
1350             ]);
1351            
1352             Returns:
1353              
1354             <dl id="hashref">
1355             <dt>Name</dt>
1356             <dd id="name">John</dd>
1357             <dt>Age</dt>
1358             <dd id="age">48</dd>
1359             </dl>
1360             <dl id="arrayref">
1361             <dt class="term">Name</dt>
1362             <dd class="value">John</dd>
1363             <dt class="term">Age</dt>
1364             <dd class="value">42</dd>
1365             <dt class="term">email</dt>
1366             <dd class="value">jjn1056@gmail.com</dd>
1367             <dd class="value">jjn1056@yahoo.com</dd>
1368             </dl>
1369              
1370             =head2 select
1371              
1372             The C<select> tag for the purposes of filling its C<options> is
1373             treated as a type of list tag.
1374              
1375             my $dom = Template::Lace::DOM->new(q[
1376             <form>
1377             <select name='cars'>
1378             <option>Example</options>
1379             </select>
1380             </form>]);
1381              
1382             $dom->select('cars', [
1383             +{ value=>'honda', content=>'Honda' },
1384             +{ value=>'ford', content=>'Ford', selected=>1 },
1385             +{ value=>'gm', content=>'General Motors' },
1386             ]);
1387              
1388             print $dom;
1389              
1390             Returns:
1391              
1392             <select name="cars">
1393             <option value="honda">Honda</option>
1394             <option selected="on" value="ford">Ford</option>
1395             <option value="gm">General Motors</option>
1396             </select>
1397              
1398             Please note that match 'id' is on the C<name> attribute of the C<select>
1399             tag, not of the C<id> attribute as it is on other list helper types.
1400              
1401             You can also populate option groups as in the following:
1402              
1403             my $dom = Template::Lace::DOM->new(q[
1404             <select name='jobs'>
1405             <optgroup label='Example'>
1406             <option>Example</option>
1407             </optgroup>
1408             </select>]);
1409              
1410             $dom->select('jobs', [
1411             +{
1412             label=>'Easy',
1413             options => [
1414             +{ value=>'slacker', content=>'Slacker' },
1415             +{ value=>'couch_potato', content=>'Couch Potato' },
1416             ],
1417             },
1418             +{
1419             label=>'Hard',
1420             options => [
1421             +{ value=>'digger', content=>'Digger' },
1422             +{ value=>'brain', content=>'Brain Surgeon' },
1423             ],
1424             },
1425             ]);
1426              
1427             print $dom;
1428              
1429             Would return:
1430              
1431             <select name="jobs">
1432             <optgroup label="Easy">
1433             <option value="slacker">Slacker</option>
1434             <option value="couch_potato">Couch Potato</option>
1435             </optgroup>
1436             <optgroup label="Hard">
1437             <option value="digger">Digger</option>
1438             <option value="brain">Brain Surgeon</option>
1439             </optgroup>
1440             </select>
1441              
1442             =head2 radio
1443              
1444             List helper for a radio input type. Example
1445              
1446             my $dom = Template::Lace::DOM->new("
1447             <form>
1448             <input type='radio' name='choose' />
1449             </form>");
1450              
1451             $dom->radio('choose',[
1452             +{id=>'id1', value=>1},
1453             +{id=>'id2', value=>2, selected=>1},
1454             ]);
1455              
1456             print $dom;
1457              
1458             Returns;
1459              
1460             <form>
1461             <input id="id1" name="choose" type="radio" value="1">
1462             <input id="id2" name="choose" type="radio" value="2" selected="on">
1463             </form>
1464              
1465             =head2 optgroup
1466              
1467             Populates an optgroup tag:
1468              
1469             my $dom = Template::Lace::DOM->new(q[
1470             <form>
1471             <select name='states'>
1472             <optgroup id='usa_states' label='USA States'>
1473             <option class="state">Example</option>
1474             </optgroup>
1475             </select>
1476             </form>
1477             ]);
1478              
1479             $dom->optgroup('#usa_states')->fill({
1480             state => [
1481             +{ value=>'ny', content=>'New York' },
1482             +{ value=>'tx', content=>'Texas' },
1483             ]
1484             });
1485              
1486             Returns:
1487              
1488             <form>
1489             <select name="states">
1490             <optgroup id="usa_states" label="USA States">
1491             <option class="state" value="ny">New York</option>
1492             <option class="state" value="tx">Texas</option>
1493             </optgroup>
1494             </select>
1495             </form>
1496              
1497             =head1 GENERAL TAG HELPERS
1498              
1499             Helpers to work with common tags. All return the original DOM to make
1500             chaining easier.
1501              
1502             =head2 form
1503              
1504             Form tag helper. Example:
1505              
1506             $dom->form('#login', sub {
1507             $_->action('login.html');
1508             });
1509              
1510             =head1 SEE ALSO
1511            
1512             L<Template::Lace>.
1513              
1514             =head1 AUTHOR
1515              
1516             Please See L<Template::Lace> for authorship and contributor information.
1517            
1518             =head1 COPYRIGHT & LICENSE
1519            
1520             Please see L<Template::Lace> for copyright and license information.
1521              
1522             =cut