File Coverage

blib/lib/HTML/TagHelper.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package HTML::TagHelper;
2              
3 2     2   44512 use warnings;
  2         6  
  2         68  
4 2     2   11 use strict;
  2         4  
  2         68  
5 2     2   1882 use Moo;
  2         40538  
  2         12  
6 2     2   5540 use HTML::Entities;
  2         13209  
  2         218  
7 2     2   3256 use HTML::Element;
  2         46017  
  2         17  
8 2     2   1227 use DateTime;
  0            
  0            
9              
10             =head1 NAME
11              
12             HTML::TagHelper - Generate HTML tags in an easy way
13              
14             =head1 VERSION
15              
16             Version 0.02
17              
18             =cut
19              
20             our $VERSION = '0.04';
21              
22             =head1 SYNOPSIS
23              
24             use HTML::TagHelper;
25              
26             my $th = HTML::TagHelper->new();
27             $th->t( 'bar', class => 'test', 0);
28             $th->link_to('http://example.com/', title => 'Foo', sub { 'Foo' });
29             $th->js('amcharts/ammap');
30             $th->css('amcharts/style');
31             $th->form_for('/links', sub {
32             $th->text_field(foo => 'bar')
33             . $th->input_tag(baz => 'yada', class => 'tset')
34             . $th->submit_button
35             });
36             $th->date_select_field('date', {
37             year_start => 2013,
38             year_end => 2013
39             });
40             $th->options_for_select(
41             [ {title => "Option 1", value => "option1"},
42             {title => "Option 2", value => "option2"}, ],
43             [ 'option1' ],
44             );
45             $th->textarea(e => (cols => 40, rows => 50) => sub {'text in textarea'});
46             $th->image('/uploads/001.jpg');
47              
48              
49             =head1 DESCRIPTION
50              
51             The module contains some code generators to easily create tags for links, images, select-field etc.
52              
53             This is mostly a port of the Ruby on Rails helper tags for use in L. And alias tags name
54             as L.
55              
56             =head1 FUNCTIONS
57              
58             =head2 color_field
59              
60             =head2 email_field
61              
62             =head2 number_field
63              
64             =head2 range_field
65              
66             =head2 search_field
67              
68             =head2 tel_field
69              
70             =head2 text_field
71              
72             =head2 url_field
73              
74             =cut
75              
76             no strict 'refs';
77             for my $name (qw(color email number range search tel text url)) {
78             *{ __PACKAGE__ . "::${name}_field" } =
79             sub { shift->_input( @_, type => $name ) };
80             }
81              
82             =head2 tag/t
83              
84             =cut
85              
86             for my $name (qw(t tag)) {
87             *{ __PACKAGE__ . "::${name}" } = sub { shift->_tag(@_) };
88             }
89              
90             =head2 check_box
91              
92             =cut
93              
94             sub check_box {
95             shift->_input( shift, value => shift, @_, type => 'checkbox' );
96             }
97              
98             =head2 file_field
99              
100             =cut
101              
102             sub file_field { shift->_tag( 'input', name => shift, @_, type => 'file' ) }
103              
104             =head2 image
105              
106             =cut
107              
108             sub image { shift->_tag( 'img', src => shift, @_ ) }
109              
110             =head2 input_tag
111              
112             =cut
113              
114             sub input_tag { shift->_input(@_) }
115              
116             =head2 password_field
117              
118             =cut
119              
120             sub password_field {
121             shift->_tag( 'input', name => shift, @_, type => 'password' );
122             }
123              
124             =head2 radio_button
125              
126             =cut
127              
128             sub radio_button {
129             shift->_input( shift, value => shift, @_, type => 'radio' );
130             }
131              
132             =head2 form_for
133              
134             =cut
135              
136             sub form_for {
137             shift->_tag( 'form', action => shift, @_ );
138             }
139              
140             =head2 hidden_field
141              
142             =cut
143              
144             sub hidden_field {
145             shift->_tag( 'input', name => shift, value => shift, type => 'hidden', @_ );
146             }
147              
148             =head2 js
149              
150             =cut
151              
152             sub js {
153             my $self = shift;
154             my $uri = shift;
155             $uri = "/javascripts/$uri.js" unless $uri =~ /\.js$/;
156             return $self->_tag(
157             'script',
158             languages => 'javascript',
159             src => $uri,
160             type => 'text/javascript',
161             @_
162             );
163             }
164              
165             =head2 css
166              
167             =cut
168              
169             sub css {
170             my $self = shift;
171             my $uri = shift;
172             $uri = "/css/$uri.css" unless $uri =~ /\.css$/;
173             return $self->_tag(
174             'link',
175             rel => 'stylesheet',
176             href => $uri,
177             type => 'text/css',
178             @_
179             );
180             }
181              
182             =head2 link_to
183              
184             =cut
185              
186             sub link_to {
187             my ($self, $content) = (shift, shift);
188             my $url = $content;
189             # Content
190             unless (defined $_[-1] && ref $_[-1] eq 'CODE') {
191             $url = shift;
192             push @_, $content;
193             }
194             return $self->_tag('a', href => $url, @_);
195             }
196              
197             =head2 submit_button
198              
199             =cut
200              
201             sub submit_button {
202             shift->_tag( 'input', value => shift // 'Ok', @_, type => 'submit' );
203             }
204              
205             =head2 textarea
206              
207             =cut
208              
209             sub textarea {
210             my $self = shift;
211             my $name = shift;
212              
213             my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
214             my $content = @_ % 2 ? shift : undef;
215              
216             if ( defined $content ) {
217             $cb = sub { encode_entities $content }
218             }
219             return $self->_tag('textarea', name => $name, @_, $cb);
220             }
221              
222             sub _tag {
223             my $self = shift;
224             my $name = shift;
225              
226             my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
227             my $content = @_ % 2 ? pop : undef;
228              
229             my $tag = "<$name";
230              
231             my %attrs = @_;
232             for my $key ( sort keys %attrs ) {
233             $tag .= qq{ $key="} . encode_entities( $attrs{$key} // '' ) . '"';
234             }
235              
236             if ($cb || defined $content ) {
237             $tag .= '>' . ($cb ? $cb->() : encode_entities($content)) . "";
238             }
239             else {
240             $tag .= ' />';
241             }
242             return $tag;
243             }
244              
245             sub _input {
246             my ( $self, $name ) = ( shift, shift );
247             my %attrs = @_ % 2 ? ( value => shift, @_ ) : @_;
248             $attrs{type} ||= '';
249             $attrs{value} //= '';
250             return $self->_tag( 'input', name => $name, %attrs );
251             }
252              
253             =head2 select_field
254              
255             =over 4
256              
257             =item $th->select_field($name, \@options, \%html_options)
258              
259             Create a select html element.
260              
261             Required options are:
262              
263             C: The content of the name attribute on the tag
264              
265             The options array must contain either the output of C or an array of hashes with title and value as keys.
266              
267             Addtional html_options are:
268              
269             C: The content of the id attribute on the tag (defaults to the value of C).
270              
271             Besides this html_option, you can enter any option you want as an attribute on the tag, e.g. class, id etc.
272              
273             =back
274              
275             =cut
276              
277             sub select_field {
278             my ( $self, $name, $options, $html_options ) = @_;
279             return("You need to specify a name for the selector") unless $name;
280              
281             if ( defined($options) && ref $options eq 'ARRAY' ) {
282             my $value = delete $html_options->{value};
283             $options = $self->options_for_select( $options, $value );
284             }
285              
286             $html_options ||= {};
287             my %html_options = (
288             name => $name,
289             id => $name,
290             %$html_options,
291             );
292              
293             my $tag = HTML::Element->new( 'select', %html_options );
294             $tag->push_content($options) if defined($options);
295             return $tag->as_HTML("");
296             }
297              
298             =head2 options_for_select
299              
300             =over 4
301              
302             =item $th->options_for_select(\@options)
303              
304             Create all options html elements to put inside C.
305              
306             Required options are:
307              
308             C: This is an array of hashes, where the title pair will be used for content of the tag, and the value pair will be used for value.
309              
310             Example:
311              
312             $th->options_for_select( [{title => "Option 1", value="option1"}, {title => "Option 2", value => "option2"}] );
313              
314             =back
315              
316             =cut
317              
318             sub options_for_select {
319             my ( $self, $optionlist, $selected ) = @_;
320             $selected = () unless defined $selected;
321             my $options = "";
322             my $content;
323             my $tag;
324              
325             foreach my $optionset (@$optionlist) {
326             $content = delete $optionset->{title};
327             $optionset->{selected} = "true"
328             if ( grep { $_ eq $optionset->{value} } @$selected );
329             $tag = HTML::Element->new( 'option', %$optionset );
330             $tag->push_content($content);
331             $options .= $tag->as_HTML("") . "\n";
332             }
333             return $options;
334             }
335              
336             =head2 date_select_field
337              
338             =over 4
339              
340             =item $th->date_select_field($name, \%options)
341              
342             Create 3 select html element - one for day, one for month and one for year.
343              
344             Required options are:
345              
346             C: The content of the name attribute on the tag. They are all post-fixed with "day", "month" or "year"
347              
348             The options array must contain either the output of C or an array of hashes with title and value as keys.
349              
350             Addtional options are:
351              
352             C: Which year should be the first option. Defaults to DateTime->now->year
353              
354             C: Which your should be the last option. Default to C + 5
355              
356             C: The content of the id attribute on the tag (defaults to the value of C).
357              
358             C: The content of the class attributes on the tags.
359              
360             Besides this html_option, you can enter any option you want as an attribute on the tag, e.g. class, id etc.
361              
362             =back
363              
364             =cut
365              
366             sub date_select_field {
367             my ( $self, $name, $options ) = @_;
368             return("You need to specify a name for the selector") unless $name;
369              
370             $options ||= {};
371             my %html_options = (
372             name => $name,
373             id => $name,
374             year_start => DateTime->now->year,
375             year_end => DateTime->now->year + 5,
376             selected_date => DateTime->now,
377             %$options,
378             );
379              
380             my $sel_year = $html_options{selected_date}->year;
381             my $sel_month = $html_options{selected_date}->month;
382             my $sel_day = $html_options{selected_date}->day;
383             delete $html_options{selected_date};
384             my $year_start = delete $html_options{year_start};
385             my $year_end = delete $html_options{year_end};
386             my $year_name = $html_options{name} . "_year";
387             my $year_id = $html_options{id} . "_year";
388             my $month_name = $html_options{name} . "_month";
389             my $month_id = $html_options{id} . "_month";
390             my $day_name = $html_options{name} . "_day";
391             my $day_id = $html_options{id} . "_day";
392             delete $html_options{name};
393             delete $html_options{id};
394             delete $html_options{year_start};
395             delete $html_options{year_end};
396             my $year_options = "";
397             my $tmp_option;
398              
399             foreach my $year ( $year_start .. $year_end ) {
400             $tmp_option = HTML::Element->new('option');
401             $tmp_option->attr( 'value', $year );
402             $tmp_option->attr( 'selected', 'true' ) if ( $year == $sel_year );
403             $tmp_option->push_content($year);
404             $year_options .= $tmp_option->as_HTML("");
405             }
406              
407             my $month_options = "";
408             foreach my $month ( 1 .. 12 ) {
409             $tmp_option = HTML::Element->new('option');
410             $tmp_option->attr( 'value', $month );
411             $tmp_option->attr( 'selected', 'true' ) if ( $month == $sel_month );
412             $tmp_option->push_content($month);
413             $month_options .= $tmp_option->as_HTML("");
414             }
415              
416             my $day_options = "";
417             foreach my $day ( 1 .. 31 ) {
418             $tmp_option = HTML::Element->new('option');
419             $tmp_option->attr( 'value', $day );
420             $tmp_option->attr( 'selected', 'true' ) if ( $day == $sel_day );
421             $tmp_option->push_content($day);
422             $day_options .= $tmp_option->as_HTML("");
423             }
424              
425             my $date_select = "";
426              
427             my $day_tag = HTML::Element->new( 'select', %html_options );
428             $day_tag->attr( 'id', $day_id );
429             $day_tag->attr( 'name', $day_name );
430             $day_tag->push_content($day_options);
431             $date_select .= $day_tag->as_HTML("");
432              
433             my $month_tag = HTML::Element->new( 'select', %html_options );
434             $month_tag->attr( 'id', $month_id );
435             $month_tag->attr( 'name', $month_name );
436             $month_tag->push_content($month_options);
437             $date_select .= $month_tag->as_HTML("");
438              
439             my $year_tag = HTML::Element->new( 'select', %html_options );
440             $year_tag->attr( 'id', $year_id );
441             $year_tag->attr( 'name', $year_name );
442             $year_tag->push_content($year_options);
443             $date_select .= $year_tag->as_HTML("");
444              
445             return $date_select;
446             }
447              
448             sub _convert_options_to_javascript {
449             my ( $self, $html_options, $url ) = @_;
450             my $confirm = delete $html_options->{confirm};
451             my $popup = delete $html_options->{popup};
452             my $method = delete $html_options->{method};
453             my $href = delete $html_options->{href};
454              
455             $html_options->{onclick} =
456             ( $popup && $method )
457             ? return("You can't use :popup and :method in the same link\n")
458             : ( $confirm && $popup ) ? "if ("
459             . $self->_confirm_javascript_function($confirm) . ") { "
460             . $self->_popup_javascript_function($popup)
461             . " };return false;"
462             : ( $confirm && $method ) ? "if ("
463             . $self->_confirm_javascript_function($confirm) . ") { "
464             . $self->_method_javascript_function($method)
465             . " };return false;"
466             : ($confirm)
467             ? "return " . $self->_confirm_javascript_function($confirm) . ";"
468             : ($method) ? $self->_method_javascript_function( $method, $url, $href )
469             . "return false;"
470             : ($popup) ? $self->_popup_javascript_function($popup) . ' return false;'
471             : $html_options->{onclick};
472             return $html_options;
473             }
474              
475             sub _confirm_javascript_function {
476             my ( $self, $confirm ) = @_;
477             return "confirm('" . $self->_escape_javascript($confirm) . "')";
478             }
479              
480             sub _popup_javascript_function {
481             my ( $self, $popup ) = @_;
482             return ( ref $popup eq 'ARRAY' )
483             ? "window.open(this.href, '"
484             . shift(@$popup) . "', '"
485             . pop(@$popup) . "');"
486             : "window.open(this.href);";
487             }
488              
489             sub _method_javascript_function {
490             my ( $self, $method, $url, $href ) = @_;
491             $url = "" unless defined $url;
492             $href = undef unless defined $href;
493             my $action = ( $href && length($url) > 0 ) ? "'" . $url . "'" : "this.href";
494             my $submit_function =
495             "var f = document.createElement('form'); f.style.display = 'none'; "
496             . "this.parentNode.appendChild(f); f.method = 'POST'; f.action = "
497             . $action . ";";
498             unless ( $method eq 'post' ) {
499             $submit_function .=
500             "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); ";
501             $submit_function .=
502             "m.setAttribute('name', '_method'); m.setAttribute('value', '"
503             . $method
504             . "'); f.appendChild(m);";
505             }
506              
507             $submit_function .= "f.submit();";
508             return $submit_function;
509             }
510              
511             sub _tag_options {
512             my ( $self, $options, $escape ) = @_;
513             $escape = 1 unless defined $escape;
514              
515             my @boolean_attributes = qw/disabled readonly multiple/;
516              
517             if ($options) {
518             if ($escape) {
519             while ( my ( $key, $value ) = each %$options ) {
520             next unless ($value);
521             $value =
522             ( grep { $_ eq $key } @boolean_attributes ) ? $key : $value;
523             $options->{$key} = $value;
524             }
525             }
526             }
527             return $options;
528             }
529              
530             sub _escape_javascript {
531             my ( $self, $javascript ) = @_;
532              
533             $javascript ||= '';
534             $javascript =~ s|\\|\0\0|g;
535             $javascript =~ s|
536             $javascript =~ s|\r\n|\\n|g;
537             $javascript =~ s|["']||g;
538             return $javascript;
539             }
540              
541             =head1 AUTHOR
542              
543             Gitte Wange Olrik, C<< >>
544              
545             Chenryn, C<< >>
546              
547             =head1 BUGS
548              
549             Please report any bugs or feature requests to C, or through
550             the web interface at L. I will be notified, and then you'll
551             automatically be notified of progress on your bug as I make changes.
552              
553              
554              
555              
556             =head1 SUPPORT
557              
558             You can find documentation for this module with the perldoc command.
559              
560             perldoc HTML::TagHelper
561              
562              
563             You can also look for information at:
564              
565             =over 4
566              
567             =item * RT: CPAN's request tracker
568              
569             L
570              
571             =item * AnnoCPAN: Annotated CPAN documentation
572              
573             L
574              
575             =item * CPAN Ratings
576              
577             L
578              
579             =item * Search CPAN
580              
581             L
582              
583             =back
584              
585              
586             =head1 ACKNOWLEDGEMENTS
587              
588              
589             =head1 COPYRIGHT & LICENSE
590              
591             Copyright 2008 Gitte Wange Olrik, all rights reserved.
592              
593             This program is free software; you can redistribute it and/or modify it
594             under the same terms as Perl itself.
595              
596              
597             =cut
598              
599             1; # End of HTML::TagHelper