File Coverage

lib/HTML/FormWidgets.pm
Criterion Covered Total %
statement 102 162 62.9
branch 26 66 39.3
condition 13 53 24.5
subroutine 19 29 65.5
pod 18 18 100.0
total 178 328 54.2


line stmt bran cond sub pod time code
1             package HTML::FormWidgets;
2              
3 1     1   1183 use 5.01;
  1         4  
  1         53  
4 1     1   5 use strict;
  1         2  
  1         48  
5 1     1   6 use warnings;
  1         1  
  1         42  
6 1     1   6 use version; our $VERSION = qv( sprintf '0.23.%d', q$Rev: 1 $ =~ /\d+/gmx );
  1         1  
  1         15  
7 1     1   805 use parent 'Class::Accessor::Fast';
  1         605  
  1         6  
8              
9 1     1   5035 use Class::Load qw( is_class_loaded load_class );
  1         31320  
  1         85  
10 1     1   669 use English qw( -no_match_vars );
  1         2178  
  1         7  
11 1     1   1199 use HTML::Accessors;
  1         7150  
  1         49  
12 1     1   9 use Scalar::Util qw( blessed );
  1         3  
  1         60  
13 1     1   7 use Try::Tiny;
  1         1  
  1         5756  
14              
15             my $COLON = ' : ';
16             my $LSB = '[';
17             my $NB = ' † ';
18             my $NUL = q();
19             my $SPACE = ' ' x 3;
20             my $SPC = q( );
21             my $TTS = ' ~ ';
22             my $OPTIONS = {
23                content_type => 'text/html',
24                hidden => sub { {} },
25                js_object => 'html_formwidgets',
26                language => 'en',
27                l10n => undef,
28                list_key => 'items',
29                literal_js => sub { [] },
30                max_pwidth => undef,
31                ns => 'default',
32                optional_js => sub { [] },
33                pwidth => 30,
34                skip => sub { return { qw( options 1 id 1 name 1 type 1 ) } },
35                uri_for => undef,
36                width => 1000, };
37             my $ATTRS = {
38                check_field => undef, class => $NUL,
39                clear => $NUL, container => 1,
40                container_class => 'container', container_id => undef,
41                default => undef, frame_class => $NUL,
42                hacc => undef, hint_title => undef,
43                id => undef, name => undef,
44                onblur => undef, onchange => undef,
45                onkeypress => undef, options => undef,
46                pclass => 'prompt', pwidth => undef,
47                prompt => $NUL, readonly => 0,
48                required => 0, sep => undef,
49                stepno => undef, text => $NUL,
50                tip => $NUL, tiptype => 'dagger',
51                type => undef, };
52              
53             __PACKAGE__->mk_accessors( keys %{ $ATTRS } );
54              
55             # Private functions
56             my $_arg_list = sub {
57                my (@args) = @_; $args[ 0 ] or return {};
58              
59                return ref $args[ 0 ] eq 'HASH' ? $args[ 0 ] : { @args };
60             };
61              
62             my $_collect_items = sub {
63                my ($nitems, $stack) = @_; my $html = $NUL; $nitems or return $NUL;
64              
65                for (1 .. $nitems) {
66                   my $args = pop @{ $stack }; $html = ($args->{content} || $NUL).$html;
67                }
68              
69                return $html;
70             };
71              
72             my $_form_wrapper = sub {
73                my ($options, $item, $stack) = @_; my $content = $item->{content};
74              
75                my $hacc = $options->{hacc};
76                my $html = $_collect_items->( $content->{nitems}, $stack );
77                my $attr = $content->{config} // $content->{attrs} // {};
78              
79                $item->{content} = "\n".$hacc->form( $attr, "\n".$html );
80              
81                return $item;
82             };
83              
84             my $_group_fields = sub {
85                my ($options, $item, $stack) = @_; my $content = $item->{content}; my $class;
86              
87                $class = delete $content->{frame_class} and $item->{class} = $class;
88              
89                my $hacc = $options->{hacc};
90                my $legend = $hacc->legend( $content->{text} );
91                my $html = $_collect_items->( $content->{nitems}, $stack );
92              
93                $item->{content} = "\n".$hacc->fieldset( "\n".$legend.$html );
94                return $item;
95             };
96              
97             my $_inject = sub {
98                return { %{ $_[ 0 ] }, options => $_[ 1 ] };
99             };
100              
101             my $_merge_attributes = sub {
102                my ($dest, $src) = @_;
103              
104                for my $k (grep { not exists $dest->{ $_ } or not defined $dest->{ $_ } }
105                           keys %{ $src }) {
106                   my $v = $src->{ $k };
107              
108                   defined $v and ref $v eq 'CODE' and $v = $v->();
109                   defined $v and $dest->{ $k } = $v;
110                }
111              
112                return $dest;
113             };
114              
115             my $_build_widget = sub {
116                my ($class, $opts, $item, $stack) = @_; $item or return;
117              
118                (ref $item and (ref $item->{content} eq 'HASH' or blessed $item->{content}))
119                   or return $item;
120              
121                my $content = $item->{content};
122              
123                $content->{form} and return $_form_wrapper->( $opts, $item, $stack );
124              
125                if ($content->{group}) {
126                   $opts->{skip_groups} and return;
127                   return $_group_fields->( $opts, $item, $stack );
128                }
129              
130                my $widget = blessed $content ? $content
131                           : $content->{widget} ? $class->new( $_inject->( $content, $opts ))
132                                                : undef;
133              
134                $widget or return $item;
135                $widget->frame_class and $item->{class} = $widget->frame_class;
136                $item->{content} = $widget->render;
137                return $item;
138             };
139              
140             # Private object methods
141             my $_bootstrap = sub { # Bare minimum is fields + id to get a useful widget
142                my ($self, $args) = @_;
143              
144                for my $attr (grep { exists $args->{ $_ } } qw( id name type )) {
145                   $self->$attr( $args->{ $attr } );
146                }
147              
148             # Defaults id from name
149                my $id = $self->id; my $name = $self->name; my $type = $self->type;
150              
151                if ($id and not $name) {
152                   $name = $self->name( $id =~ m{ \. }mx ? (split m{ \. }mx, $id)[ 1 ]
153                                                         : (split m{ \_ }mx, $id)[ -1 ] );
154                }
155              
156                not $id and $name and $id = $self->id( $name ); $args->{options} //= {};
157              
158             # We can get the widget type from the config file
159                if (not $type and $id and exists $args->{options}->{fields}) {
160                   my $fields = $args->{options}->{fields};
161              
162                   exists $fields->{ $id } and exists $fields->{ $id }->{type}
163                      and $type = $self->type( $fields->{ $id }->{type} );
164                }
165                else { $args->{options}->{fields} //= {} }
166              
167             # This is the default widget type if not overidden in the config
168                $type or $type = $self->type( 'textfield' );
169                $name or $self->name( $type );
170                return;
171             };
172              
173             my $_build_hacc = sub {
174             # Now we can create HTML elements like we could with CGI.pm
175                my $self = shift; my $hacc = $self->options->{hacc};
176              
177                $hacc or $hacc = HTML::Accessors->new
178                   ( { content_type => $self->options->{content_type} } );
179                return $hacc
180             };
181              
182             my $_build_pwidth = sub { # Calculate the prompt width
183                my $self = shift;
184                my $opts = $self->options;
185                my $width = $opts->{width} || 1024;
186                my $pwidth = defined $self->pwidth ? $self->pwidth : $opts->{pwidth};
187              
188                if ($pwidth and $pwidth =~ m{ \A \d+ \z }mx) {
189                   $pwidth = int $pwidth * $width / 100;
190                   $opts->{max_pwidth} and $pwidth > $opts->{max_pwidth}
191                      and $pwidth = $opts->{max_pwidth};
192                   $pwidth .= 'px';
193                }
194              
195                return $pwidth;
196             };
197              
198             my $_build_sep = sub {
199                my $self = shift; my $sep = $self->sep;
200              
201                not defined $sep and $self->prompt and $sep = $COLON;
202                    defined $sep and $sep eq 'space' and $sep = $SPACE;
203                    defined $sep and $sep eq 'none' and $sep = $NUL;
204                return $sep;
205             };
206              
207             my $_init_args = sub {
208                my ($self, $args) = @_; my $skip = $self->options->{skip}; my $v;
209              
210                for (grep { not $skip->{ $_ } } keys %{ $args }) {
211                   exists $self->{ $_ } and defined ($v = $args->{ $_ }) and $self->$_( $v );
212                }
213              
214                return;
215             };
216              
217             my $_init_fields = sub {
218                my ($self, $args) = @_; my $fields = $args->{options}->{fields}; my $id;
219              
220                $fields and $id = $self->id and exists $fields->{ $id }
221                   and $self->$_init_args( $fields->{ $id } );
222                return;
223             };
224              
225             my $_init_hint_title = sub {
226                my ($self, $args) = @_;
227              
228                $args->{hint_title} and return $self->hint_title( $args->{hint_title} );
229              
230                return $self->hint_title( $self->loc( 'Hint' ) );
231             };
232              
233             my $_init_options = sub {
234                $_[ 0 ]->options( $_merge_attributes->( $_[ 1 ]->{options}, $OPTIONS ) );
235                return;
236             };
237              
238             my $_next_step = sub {
239                return $_[ 0 ]->options->{iterator}->();
240             };
241              
242             my $_render_field = sub {
243                my $self = shift; my $id = $self->id; my $args = {};
244              
245                $id and $args->{id } = $id;
246                $self->name and $args->{name } = $self->name;
247                $self->check_field and $args->{class } = 'server';
248                $self->required and $args->{class } .= ' required';
249                $self->onblur and $args->{onblur } = $self->onblur;
250                $self->onkeypress and $args->{onkeypress} = $self->onkeypress;
251                $self->readonly and $args->{readonly } = 'readonly';
252              
253                defined $self->default and $args->{default} = $self->default;
254              
255                my $html = $self->render_field( $args );
256                my $name = $self->options->{name} // $NUL;
257                my $ns = $self->options->{ns } // $NUL;
258              
259                $self->check_field and $self->add_literal_js( 'server', $id, {
260                   args => "[ '${id}', '${name}', '${ns}' ]", event => "'blur'",
261                   method => "'checkField'" } );
262                return $html;
263             };
264              
265             my $_set_error = sub {
266                my ($self, $error) = @_; return $self->text( $error );
267             };
268              
269             my $_build_stepno = sub {
270                my $self = shift; my $stepno = $self->stepno;
271              
272                defined $stepno and ref $stepno eq 'HASH' and return $stepno;
273                defined $stepno and $stepno eq 'none' and return $NUL;
274                defined $stepno and $stepno == -1 and $stepno = $self->$_next_step;
275                defined $stepno and $stepno == 0 and $stepno = $SPACE;
276                        $stepno and $stepno ne $SPACE and $stepno = "${stepno}.";
277                return $stepno;
278             };
279              
280             my $_ensure_class_loaded = sub {
281                my ($self, $class) = @_; my $error;
282              
283                try { load_class( $class ) } catch { $error = $self->$_set_error( $_ ) };
284              
285                $error and return;
286              
287                is_class_loaded( $class )
288                   or ( $self->$_set_error
289                        ( "Class ${class} loaded but package undefined" ) and return );
290              
291                return bless $self, $class; # Rebless ourself as subclass
292             };
293              
294             my $_init = sub {
295                my ($self, $args) = @_;
296              
297                $self->$_init_options ( $args );
298                $self->init ( $args ); # Allow subclass to set it's own defaults
299                $self->$_init_fields ( $args );
300                $self->$_init_args ( $args );
301                $self->$_init_hint_title( $args );
302                $self->hacc ( $self->$_build_hacc );
303                $self->pwidth ( $self->$_build_pwidth );
304                $self->sep ( $self->$_build_sep );
305                $self->stepno ( $self->$_build_stepno );
306                return;
307             };
308              
309             # Class methods
310             sub build {
311 0   0 0 1 0    my ($class, $options) = @_; $options //= {}; my $step = 0;
  0         0  
  0         0  
312              
313 0   0     0    my $data = delete $options->{data } // [];
314 0   0     0    my $key = $options->{list_key } //= $OPTIONS->{list_key };
315 0   0     0    my $type = $options->{content_type} //= $OPTIONS->{content_type};
316              
317 0   0     0    $options->{hacc } //= HTML::Accessors->new( content_type => $type );
318 0   0 0   0    $options->{iterator} //= sub { return ++$step };
  0         0  
319              
320 0 0       0    for my $list (grep { $_ and ref $_ eq 'HASH' } @{ $data }) {
  0         0  
  0         0  
321 0 0       0       ref $list->{ $key } eq 'ARRAY' or next; my @stack = ();
  0         0  
322              
323 0         0       for my $item (@{ $list->{ $key } }) {
  0         0  
324 0         0          my $built = $_build_widget->( $class, $options, $item, \@stack );
325              
326 0 0       0          $built and push @stack, $built;
327                   }
328              
329 0         0       $list->{ $key } = \@stack;
330                }
331              
332 0         0    return $data;
333             }
334              
335             sub new {
336 27     27 1 42216    my ($self, @args) = @_; my $args = $_arg_list->( @args );
  27         99  
337              
338             # Start with some hard coded defaults
339 27   33     40    my $new = bless { %{ $ATTRS } }, blessed $self || $self;
  27         788  
340              
341             # Set minimum requirements from the supplied args and the defaults
342 27         115    $new->$_bootstrap( $args );
343              
344             # Your basic factory method trick
345 27         69    my $class = ucfirst $new->type;
346 27 50       240       $class = ('+' eq substr $class, 0, 1)
347                          ? (substr $class, 1) : __PACKAGE__."::${class}";
348              
349 27         69    $new->$_ensure_class_loaded( $class );
350 27         82    $new->$_init( $args ); # Complete the initialization
351              
352 27         167    return $new;
353             }
354              
355             # Public object methods
356             sub add_hidden {
357 1     1 1 14    my ($self, $name, $value) = @_;
358              
359 1   50     3    my $key = $self->options->{list_key} || 'items';
360 1   50     15    my $hidden = $self->options->{hidden } || {}; $hidden->{ $key } ||= [];
  1   50     18  
361              
362 1         2    push @{ $hidden->{ $key } }, {
  1         5  
363                   content => "\n".$self->hacc->input( {
364                      name => $name, type => 'hidden', value => $value } ) };
365 1         64    return;
366             }
367              
368             sub add_literal_js {
369 3     3 1 47    my ($self, $js_class, $id, $config) = @_; my $list = $NUL;
  3         8  
370              
371 3 50 33     34    ($js_class and $id and $config) or return;
      33        
372              
373 3 50       13    if (ref $config eq 'HASH') {
374 3         4       while (my ($k, $v) = each %{ $config }) {
  17         59  
375 14 100 100     27          if ($k) { $list and $list .= ', '; $list .= "${k}: ".($v || 'null') }
  14 50       45  
  14         51  
376                   }
377                }
378 0         0    else { $list = $config };
379              
380 3   50     23    my $obj = $self->options->{js_object}; $self->options->{literal_js} ||= [];
  3         22  
381              
382 3         20    push @{ $self->options->{literal_js} },
  3         9  
383                   "${obj}.config.${js_class}[ '${id}' ] = { ${list} };";
384 3         32    return;
385             }
386              
387             sub add_optional_js {
388 1   50 1 1 4    my ($self, @args) = @_; $self->options->{optional_js} ||= [];
  1         4  
389              
390 1         8    push @{ $self->options->{optional_js} }, @args;
  1         4  
391 1         8    return;
392             }
393              
394             sub inflate {
395 4     4 1 6    my ($self, $args) = @_;
396              
397 4 50 33     24    (defined $args and ref $args eq 'HASH') or return $args;
398              
399 4         12    return __PACKAGE__->new( $_inject->( $args, $self->options ) )->render;
400             }
401              
402 0     0 1 0 sub init { # Can be overridden in factory subclass
403             }
404              
405             sub is_xml {
406 0 0   0 1 0    return $_[ 0 ]->options->{content_type} =~ m{ / (.*) xml \z }mx ? 1 : 0;
407             }
408              
409             sub loc {
410 34     34 1 97    my ($self, $text, @rest) = @_; my $opts = $self->options; my $l10n;
  34         82  
  34         122  
411              
412 34 50       91    if (defined ($l10n = $opts->{l10n})) {
413 0         0       my $args = { language => $opts->{language}, ns => $opts->{ns} };
414              
415 0         0       return $l10n->( $args, $text, @rest );
416                }
417              
418 34 50       77    $text or return; $text = $NUL.$text; # Stringify
  34         69  
419              
420             # Expand positional parameters of the form [_<n>]
421 34 100       299    0 > index $text, $LSB and return $text;
422              
423 1 50 33     14    my @args = $rest[0] && ref $rest[0] eq 'ARRAY' ? @{ $rest[0] } : @rest;
  0         0  
424              
425 1         5    push @args, map { '[?]' } 0 .. 10;
  11         21  
426 1         80    $text =~ s{ \[ _ (\d+) \] }{$args[ $1 - 1 ]}gmx;
427 1         8    return $text;
428             }
429              
430             sub render {
431 28 50 0 28 1 1669    my $self = shift; $self->type or return $self->text || $NUL;
  28         120  
432              
433 28 50       187    my $field = $self->$_render_field or return $NUL; my $lead = $NUL;
  28         49  
434              
435 28 50       67    $self->stepno and $lead .= $self->render_stepno;
436 28 50       165    $self->prompt and $lead .= $self->render_prompt;
437 28 50       146    $self->sep and $lead .= $self->render_separator;
438 28 50       252    $self->tip and $field = $self->render_tip( $field );
439 28 50       150    $self->check_field and $field = $self->render_check_field( $field );
440              
441 28         149    $field = $lead.$field;
442              
443 28 100       135    $self->container and $field = $self->render_container( $field );
444 28 50       783    $self->clear eq 'left' and $field = $self->hacc->br.$field;
445 28         283    return "\n${field}";
446             }
447              
448             sub render_check_field {
449 0     0 1 0    my ($self, $field) = @_; my $hacc = $self->hacc; my $id = $self->id;
  0         0  
  0         0  
450              
451 0         0    $field .= $hacc->span( { class => 'hidden', id => "${id}_ajax" } );
452              
453 0         0    return $hacc->div( { class => 'field_group' }, $field );
454             }
455              
456             sub render_container {
457 17     17 1 167    my ($self, $field) = @_; my $args = { class => $self->container_class };
  17         80  
458              
459 17 50       158    $self->container_id and $args->{id} = $self->container_id;
460              
461 17         104    return $self->hacc->div( $args, $field );
462             }
463              
464             sub render_field {
465 0 0   0 1 0    my ($self, $args) = @_; $self->text and return $self->text;
  0         0  
466              
467 0   0     0    my $id = $args->{id} || '*unknown id*';
468              
469 0         0    return $self->$_set_error( "No render_field method for field ${id}" );
470             }
471              
472             sub render_prompt {
473 0     0 1 0    my $self = shift; my $args = { class => $self->pclass };
  0         0  
474              
475 0 0 0     0    $self->id and $args->{for} = $self->id and $args->{id} = $self->id.'_label';
476              
477 0 0       0    $self->pwidth and $args->{style} .= 'width: '.$self->pwidth.';';
478              
479 0         0    return $self->hacc->label( $args, $self->prompt );
480             }
481              
482             sub render_separator {
483 0     0 1 0    my $self = shift; my $class = 'separator';
  0         0  
484              
485 0 0       0    if ($self->sep eq 'break') {
486 0         0       $class = 'separator_break'; $self->sep( $NUL );
  0         0  
487                }
488              
489 0         0    return $self->hacc->span( { class => $class }, $self->sep );
490             }
491              
492             sub render_stepno {
493 0     0 1 0    my $self = shift; my $stepno = $self->stepno;
  0         0  
494              
495 0 0       0    ref $stepno eq 'HASH' and return $self->inflate( $stepno );
496              
497 0         0    return $self->hacc->span( { class => 'step_number' }, $stepno );
498             }
499              
500             sub render_tip {
501 0     0 1 0    my ($self, $field) = @_; my $hacc = $self->hacc; my $break = 'EOL';
  0         0  
  0         0  
502              
503 0         0    (my $tip = $self->tip) =~ s{ \n }{$break}gmx;
504              
505 0 0       0    $tip !~ m{ $TTS }mx and $tip = $self->hint_title.$TTS.$tip;
506 0         0    $tip =~ s{ \s+ }{ }gmx;
507              
508 0         0    my $args = { class => 'help tips', title => $tip };
509              
510 0 0       0    $self->tiptype eq 'dagger' or return $hacc->span( $args, "\n${field}" );
511              
512 0         0    $field .= $hacc->span( $args, $NB );
513              
514 0         0    return $hacc->div( { class => 'field_group' }, "\n".$field );
515             }
516              
517             sub uri_for {
518 3 100   3 1 15    my ($self, $url) = @_; defined $url or return;
  3         12  
519              
520 1 50 33     6    ($url !~ m{ \A http[s]?: }mx and defined $self->options->{uri_for})
521                   and return $self->options->{uri_for}->( $url );
522              
523 1         9    return $url;
524             }
525              
526             1;
527              
528             __END__
529            
530             =pod
531            
532             =encoding utf8
533            
534             =begin html
535            
536             <a href="https://travis-ci.org/pjfl/p5-html-formwidgets"><img src="https://travis-ci.org/pjfl/p5-html-formwidgets.svg?branch=master" alt="Travis CI Badge"></a>
537             <a href="http://badge.fury.io/pl/HTML-FormWidgets"><img src="https://badge.fury.io/pl/HTML-FormWidgets.svg" alt="CPAN Badge"></a>
538             <a href="http://cpants.cpanauthors.org/dist/HTML-FormWidgets"><img src="http://cpants.cpanauthors.org/dist/HTML-FormWidgets.png" alt="Kwalitee Badge"></a>
539            
540             =end html
541            
542             =head1 Name
543            
544             HTML::FormWidgets - Create HTML user interface components
545            
546             =head1 Version
547            
548             Describes version v0.23.$Rev: 1 $ of L<HTML::FormWidgets>
549            
550             =head1 Synopsis
551            
552             use HTML::FormWidgets;
553            
554             my $widget = HTML::FormWidgets->new( id => 'test' );
555            
556             print $widget->render;
557             # <div class="container">
558             # <input value="" name="test" type="text" id="test" class="ifield" size="40">
559             # </div>
560            
561             =head1 Description
562            
563             Transforms a Perl data structure which defines one or more "widgets"
564             into HTML or XHTML. Each widget is comprised of these optional
565             components: a line or question number, a prompt string, a separator,
566             an input field, additional field help, and Ajax field error string.
567            
568             Input fields are selected by the widget C<type> attribute. A factory
569             subclass implements the method that generates the HTML or XHTML for
570             that input field type. Adding more widget types is straightforward
571            
572             This module is using the L<MooTools|http://mootools.net/> Javascript
573             library to modify default browser behaviour
574            
575             This module is used by L<CatalystX::Usul::View> and as such its
576             main use is as a form generator within a L<Catalyst> application
577            
578             =head1 Configuration and Environment
579            
580             The following are passed to L</build> in the C<config> hash (they
581             reflect this modules primary use within a L<Catalyst> application):
582            
583             =over 3
584            
585             =item C<assets>
586            
587             Some of the widgets require image files. This attribute is used to
588             create the URI for those images
589            
590             =item C<content_type>
591            
592             Either C<application/xhtml+xml> which generates XHTML 1.1 or
593             C<text/html> which generates HTML 4.01 and is the default
594            
595             =item C<fields>
596            
597             This hash ref contains the fields definitions. Static parameters for
598             each widget can be stored in configuration files. This reduces the
599             number of attributes that have to be passed in the call to the
600             constructor
601            
602             =item C<hidden>
603            
604             So that the L</File> and L</Table> subclasses can store the number
605             of rows added as the hidden form attribute C<nRows>
606            
607             =item C<js_object>
608            
609             This is the name of the global Javascript variable that holds
610             C<config> object. Defaults to C<html_formwidgets>
611            
612             =item C<root>
613            
614             The path to the document root for this application
615            
616             =item C<width>
617            
618             Width in pixels of the browser window. This is used to calculate the
619             width of the field prompt. The field prompt needs to be a fixed length
620             so that the separator colons align vertically
621            
622             =item C<templatedir>
623            
624             The path to template files used by the L</Template> subclass
625            
626             =back
627            
628             Sensible defaults are provided by C<new> if any of the above are undefined
629            
630             =head1 Subroutines/Methods
631            
632             =head2 Public Methods
633            
634             =head3 build
635            
636             HTML::FormWidgets->build( $config_hash );
637            
638             The L</build> method iterates over a data structure that represents the
639             form. One or more lists of widget definitions are processed in
640             turn. New widgets are created and their rendered output replaces their
641             definitions in the data structure
642            
643             =head3 new
644            
645             $widget = HTML::FormWidgets->new( [{] key1 => value1, ... [}] );
646            
647             Construct a widget. Mostly this is called by the L</build> method. It
648             requires the factory subclass for the widget type.
649            
650             This method takes a large number of options with each widget using
651             only few of them. Each option is described in the factory subclasses
652             which use that option
653            
654             =head3 add_hidden
655            
656             $widget->add_hidden( $key, $value );
657            
658             The key / value pair are added to list of hidden input elements that will
659             be included in the page
660            
661             =head3 add_literal_js
662            
663             $widet->add_literal_js( $js_class_name, $id, $config );
664            
665             The config hash will be serialised and added to the literal Javascript on
666             the page
667            
668             =head3 add_optional_js
669            
670             $widget->add_optional_js( @filenames );
671            
672             The list of Javascript filenames (with extension, without path) are added
673             to the list of files which will be included on the page
674            
675             =head3 inflate
676            
677             $widget->inflate( $args );
678            
679             Creates L<new|HTML::FormWidgets/new> objects and returns their rendered output.
680             Called by the L</_render> methods in the factory subclasses to inflate
681             embeded widget definitions
682            
683             =head3 init
684            
685             $widget->init( $args );
686            
687             Initialises this object with data from the passed arguments. This is
688             usually overridden in the factory subclass which sets the default for
689             it's own attributes. In the base class this method does nothing
690            
691             =head3 is_xml
692            
693             $bool = $widget->is_xml;
694            
695             Returns true if the content type matches C<xml>
696            
697             =head3 loc
698            
699             $message_text = $widget->loc( $message_id, @args );
700            
701             Use the supplied key to return a value from the C<l10n> object. This
702             object was passed to the constructor and should localise the key to
703             the required language. The C<@args> list contains parameters to substituted
704             in place of the placeholders which have the form C<[_n]>
705            
706             =head3 render
707            
708             $html = $widget->render;
709            
710             Assemble the components of the generated widget. Each component is
711             concatenated onto a scalar which is the returned value. This method
712             calls L</render_field> which should be defined in the factory subclass for
713             this widget type.
714            
715             This method uses these attributes:
716            
717             =over 3
718            
719             =item C<clear>
720            
721             If set to C<left> the widget begins with an C<< <br> >> element
722            
723             =item C<stepno>
724            
725             If true it's value is wrapped in a C<< <span class="lineNumber"> >>
726             element and appended to the return value
727            
728             =item C<prompt>
729            
730             If true it's value is wrapped in a C<< <label class="prompt_class"> >>
731             element and appended to the return value. The prompt class is set by
732             the C<pclass> attribute. The C<id> attribute is used to set the C<for>
733             attribute of the C<< <label> >> element. The C<pwidth> attribute sets
734             the width style attribute in the C<< <label> >> element
735            
736             =item C<sep>
737            
738             If true it's value is wrapped in a C<< <span class="separator"> >>
739             element and appended to the return value
740            
741             =item C<container>
742            
743             If true the value return by the L</_render> method is wrapped in
744             C<< <span class="container"> >> element
745            
746             =item C<tip>
747            
748             The text of the field help. If C<tiptype> is set to C<dagger>
749             (which is the default) then a dagger symbol is
750             wrapped in a C<< <span class="help tips"> >> and this is appended to the
751             returned input field. The tip text is used as the C<title>
752             attribute. If the C<tiptype> is not set to C<dagger> then the help
753             text is wrapped around the input field itself
754            
755             =item C<check_field>
756            
757             Boolean which if true causes the field to generate server side check field
758             requests
759            
760             =back
761            
762             =head3 render_check_field
763            
764             Adds markup for the Ajax field validation
765            
766             =head3 render_container
767            
768             Wraps the rendered field in a containing div
769            
770             =head3 render_field
771            
772             Should be overridden in the factory subclass. It should return the markup
773             for the specified field type
774            
775             =head3 render_prompt
776            
777             Adds a label element to the generated markup
778            
779             =head3 render_separator
780            
781             Insert a spacing element between the prompt and the field
782            
783             =head3 render_stepno
784            
785             Markup containing the step number on the form if required
786            
787             =head3 render_tip
788            
789             Flyover tooltip field help text
790            
791             =head3 uri_for
792            
793             Makes absolute URI from relative paths by calling the supplied function
794            
795             =head2 Private Methods
796            
797             =head3 _bootstrap
798            
799             $widget->$_bootstrap( $args );
800            
801             Determine the C<id>, C<name> and C<type> attributes of the widget from
802             the supplied arguments
803            
804             =head3 _ensure_class_loaded
805            
806             $widget->$_ensure_class_loaded( $class );
807            
808             Once the factory subclass is known this method ensures that it is loaded
809             and then re-blesses the self referential object into the correct class
810            
811             =head3 _set_error
812            
813             $widget->$_set_error( $error_text );
814            
815             Stores the passed error message in the C<text> attribute so that it
816             gets rendered in place of the widget
817            
818             =head2 Private Subroutines
819            
820             =head3 _arg_list
821            
822             $args = $_arg_list->( @args );
823            
824             Accepts either a single argument of a hash ref or a list of key/value
825             pairs. Returns a hash ref in either case.
826            
827             =head3 _form_wrapper
828            
829             $item = $_form_wrapper->( $options, $item, $stack );
830            
831             Wraps the top C<nitems> number of widgets on the build stack in a C<<
832             <form> >> element
833            
834             =head3 _group_fields
835            
836             $item = $_group_fields->( $options, $item, $stack );
837            
838             Wraps the top C<nitems> number of widgets on the build stack in a C<<
839             <fieldset> >> element with a legend
840            
841             =head1 Factory Subclasses
842            
843             These are the possible values for the C<type> attribute which defaults
844             to C<textfield>. Each subclass implements the L</_render> method, it
845             receives a hash ref of options an returns a scalar containing some
846             XHTML.
847            
848             The distribution ships with the following factory subclasses:
849            
850             =head2 Anchor
851            
852             Returns an C<< <anchor> >> element with a class set from the C<class>
853             argument (which defaults to C<linkFade>). It's C<href> attribute
854             set to the C<href> argument. The anchor body is set to the C<text>
855             argument
856            
857             =head2 Async
858            
859             Returns a C<< <div> >> element with a class set from the C<class>
860             argument (which defaults to C<server>). The div body is set to the
861             C<text> argument. When the JavaScript C<onload> event handler fires it
862             will asynchronously load the content of the div if it is visible
863            
864             =head2 Button
865            
866             Generates an image button where C<name> identifies the image
867             file in C<assets> and is also used as the return value. The
868             button name is set to C<_verb>. If the image file does not
869             exist a regular input button is rendered instead
870            
871             =head2 Checkbox
872            
873             Return a C<< <checkbox> >> element of value C<value>. Use the
874             element's value as key to the C<labels> hash. The hash value
875             (which defaults null) is used as the displayed label. The
876             C<checked> argument determines the checkbox's initial
877             setting
878            
879             =head2 Chooser
880            
881             Creates a popup window which allows one item to be selected from a
882             long list of items
883            
884             =head2 Cloud
885            
886             Creates list of links from the data set supplied in the C<data> argument
887            
888             =head2 Date
889            
890             Return another C<< <textfield> >>, this time with a calendar icon
891             which when clicked pops up a Javascript date picker. Requires the
892             appropriate JavaScript library to have been loaded by the page. Attribute
893             C<width> controls the size of the C<< <textfield> >> (default 10
894             characters) and C<format> defaults to C<dd/mm/yyyy>. Setting the
895             C<readonly> attribute to true (which is the default) causes the input
896             C<< <textfield> >> to become read only
897            
898             =head2 File
899            
900             Display the contents of a file pointed to by C<path>. Supports the
901             following subtypes:
902            
903             =over 3
904            
905             =item C<csv>
906            
907             Return a table containing the CSV formatted file. This and the C<file>
908             subtype are selectable if C<select> >= 0 and represents the
909             column number of the key field
910            
911             =item C<file>
912            
913             Default subtype. Like the logfile subtype but without the C<< <pre> >> tags
914            
915             =item C<html>
916            
917             The L</_render> method returns an C<< <iframe> >> element whose C<src>
918             attribute is set to C<path>. Paths that do not
919             begin with C<http:> will passed to L</uri_for>
920            
921             =item C<logfile>
922            
923             The L</_render> method returns a table where each line of the logfile
924             appears as a separate row containing one cell. The logfile lines are
925             each wrapped in C<< <pre> >> tags
926            
927             =item C<source>
928            
929             The module L<Syntax::Highlight::Perl> is used to provide colour
930             highlights for the Perl source code. Tabs are expanded to
931             C<tabstop> spaces and the result is returned wrapped in
932             C<< <pre> >> tags
933            
934             =back
935            
936             =head2 Freelist
937            
938             New values entered into a text field can be added to the
939             list. Existing list values (passed in C<values>) can be
940             removed. The height of the list is set by C<height>.
941            
942             =head2 GroupMembership
943            
944             Displays two lists which allow for membership of a group. The first
945             scrolling list contains "all" values (C<all>), the second
946             contains those values currently selected (C<current>). The
947             height of the scrolling lists is set by C<height>
948            
949             =head2 Hidden
950            
951             Generates a hidden input field. Uses the C<default> attribute as the value
952            
953             =head2 Image
954            
955             Generates an image tag. The C<text> attribute contains the source URI. The
956             C<fhelp> attribute contains the alt text and the C<tiptype> attribute is
957             defaulted to C<normal> (wraps the image in a span with a JavaScript tooltip)
958            
959             =head2 Label
960            
961             Calls L</loc> with the C<text> attribute if set otherwise returns nothing.
962             If C<dropcap> is true the first character of the text is wrapped
963             in a C<< <span class="dropcap"> >>. Wraps the text in a span of class
964             C<class> which defaults to C<label_text>
965            
966             =head2 List
967            
968             Generates an ordered and unordered lists of items. Set the C<ordered>
969             attribute to true for an ordered list. Defaults to false
970            
971             =head2 Menu
972            
973             Generates an unordered list of links. Used with some applied CSS to
974             implement a navigation menu
975            
976             =head2 Note
977            
978             Calls L</localize> with the C<name> attribute as the message key. If
979             the message does not exist the value if the C<text> attribute is
980             used. The text is wrapped in a c<< <span class="note"> >> with
981             C<width> setting the style width
982            
983             =head2 POD
984            
985             Uses L<Pod::Html> to render the POD in the given module as HTML
986            
987             =head2 Paragraphs
988            
989             Newspaper like paragraphs rendered in a given number of columns, each
990             approximately the same length. Defines these attributes;
991            
992             =over 3
993            
994             =item C<column_class>
995            
996             CSS class name of the C<< <span> >> wrapped around each column. Defaults
997             to null
998            
999             =item C<columns>
1000            
1001             Number of columns to render the paragraphs in. Defaults to 1
1002            
1003             =item C<data>
1004            
1005             Paragraphs of text. A hash ref whose C<values> attribute is an array
1006             ref. The values of that array are the hash refs that define each
1007             paragraph. The keys of the paragraph hash ref are C<class>, C<heading>, and
1008             C<text>.
1009            
1010             =item C<hclass>
1011            
1012             Each paragraph can have a heading. This is the class of the C<<
1013             <div> >> that wraps the heading text. Defaults to null
1014            
1015             =item C<max_width>
1016            
1017             Maximum width of all paragraphs expressed as a percentage. Defaults
1018             to 90
1019            
1020             =item C<para_lead>
1021            
1022             Paragraph leading. This value is in characters. It is added to the size of
1023             each paragraph to account for the leading applied by the CSS to each
1024             paragraph. If a paragraph is split, then the first part must by greater
1025             than twice this value or the widows and orphans trap will reap it
1026            
1027             =back
1028            
1029             =head2 Password
1030            
1031             Returns a password field of width C<width> which defaults to
1032             twenty characters. If C<subtype> equals C<verify> then the
1033             message C<vPasswordPrompt> and another password field are
1034             appended. The fields C<id> and C<name> are expected
1035             to contain the digit 1 which will be substituted for the digit 2 in
1036             the attributes of the second field
1037            
1038             =head2 PopupMenu
1039            
1040             Returns a list of C<< <option> >> elements wrapped in a C<< <select> >>
1041             element. The list of options is passed in C<values> with the
1042             display labels in C<labels>. The C<onchange> event handler will
1043             be set to the C<onchange> attribute value
1044            
1045             =head2 RadioGroup
1046            
1047             The attribute C<columns> sets the number of columns for the
1048             returned table of radio buttons. The list of button values is passed in
1049             C<values> with the display labels in C<labels>. The
1050             C<onchange> event handler will be set to C<onchange>
1051            
1052             =head2 Rule
1053            
1054             Generates a horizontal rule with optional clickable action
1055            
1056             =head2 ScrollPin
1057            
1058             Implements clickable navigation markers that scroll the page to given
1059             location. Returns an unordered list of class C<class> which defaults
1060             to C<pintray>. This is the default selector class for the JavaScript
1061             C<ScrollPins> object
1062            
1063             =head2 ScrollingList
1064            
1065             The C<height> attribute controls the number of options the scrolling
1066             list displays. The list of options is passed in C<values> with the
1067             display labels in C<labels>. The C<onchange> event handler will
1068             be set to C<onchange>
1069            
1070             =head2 SidebarPanel
1071            
1072             Generates the markup for a sidebar accordion panel (a "header" C<div>
1073             and a "body" C<div>). The panel contents are requested asynchronously
1074             by the browser. The L</SidebarPanel> widget defines these attributes:
1075            
1076             =over 3
1077            
1078             =item C<config>
1079            
1080             A hash ref whose keys and values are written out as literal JavaScript by
1081             L</add_literal_js>
1082            
1083             =item C<header>
1084            
1085             A hash that provides the C<id>, C<class>, and C<text> for header C<div>
1086            
1087             =item C<panel>
1088            
1089             A hash that provides the C<id> and C<class> for body C<div>
1090            
1091             =back
1092            
1093             =head2 Slider
1094            
1095             Implements a dragable slider which returns an integer value. The L</Slider>
1096             widget defines these attributes:
1097            
1098             =over 3
1099            
1100             =item C<display>
1101            
1102             Boolean which if true causes the widget to display a read only text
1103             field containing the sliders current value. If false a C< <hidden> >>
1104             element is generated instead. Defaults to C<1>
1105            
1106             =item C<element>
1107            
1108             Name of the Javascript instance variable. This will need setting to a
1109             unique value for each slider on the same form. Defaults to
1110             C<behaviour.sliderElement>
1111            
1112             =item C<hide>
1113            
1114             If the C<display> attribute is false the current value is pushed onto
1115             this array. Defaults to C<[]>
1116            
1117             =item C<mode>
1118            
1119             Which orientation to render in. Defaults to C<horizontal>
1120            
1121             =item C<offset>
1122            
1123             Sets the minimum value for the slider. Defaults to C<0>
1124            
1125             =item C<range>
1126            
1127             The range is either the offset plus the number of steps or the two
1128             values of this array if it is set. Defaults to C<false>
1129            
1130             =item C<snap>
1131            
1132             Snap to the nearest step value? Defaults to C<1>
1133            
1134             =item C<steps>
1135            
1136             Sets the number of steps. Defaults to C<100>
1137            
1138             =item C<wheel>
1139            
1140             Use the mouse wheel? Defaults to C<1>
1141            
1142             =back
1143            
1144             =head2 TabSwapper
1145            
1146             A list of C<div>s is constructed that can be styled to display only one at
1147             a time. Clicking the tab header displays the corresponding C<div>
1148            
1149             =head2 Table
1150            
1151             The input data is in C<< $data->{values} >> which is an array
1152             ref for which each element is an array ref containing the list of
1153             field values.
1154            
1155             =head2 TableRow
1156            
1157             Returns markup for a table row. Used to generate responses for the C<LiveGrid>
1158             JavaScript class
1159            
1160             =head2 Template
1161            
1162             Look in C<templatedir> for a L<Template::Toolkit> template
1163             called C<id> with a F<.tt> extension. Slurp it in and return
1164             it as the content for this widget. This provides for a "user defined"
1165             widget type
1166            
1167             =head2 Textarea
1168            
1169             A text area. It defaults to five lines high (C<height>) and
1170             sixty characters wide (C<width>)
1171            
1172             =head2 Textfield
1173            
1174             This is the default widget type. Your basic text field which defaults
1175             to sixty characters wide (C<width>)
1176            
1177             =head2 Tree
1178            
1179             Implements an expanding tree of selectable objects
1180            
1181             =head1 Diagnostics
1182            
1183             None
1184            
1185             =head1 Dependencies
1186            
1187             =over 3
1188            
1189             =item L<Class::Accessor::Fast>
1190            
1191             =item L<Class::Load>
1192            
1193             =item L<HTML::Accessors>
1194            
1195             =item L<Syntax::Highlight::Perl>
1196            
1197             =item L<Text::ParseWords>
1198            
1199             =item L<Text::Tabs>
1200            
1201             =item L<Try::Tiny>
1202            
1203             =back
1204            
1205             Included in the distribution are the Javascript files whose methods
1206             are called by the event handlers associated with these widgets
1207            
1208             =head2 F<05htmlparser.js>
1209            
1210             HTML Parser By John Resig (ejohn.org)
1211             Original code by Erik Arvidsson, Mozilla Public License
1212             http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
1213            
1214             Used to reimplement C<innerHTML> assignments from XHTML
1215            
1216             =head2 F<10mootools.js>
1217            
1218             Mootools - My Object Oriented javascript.
1219             License: MIT-style license.
1220             WWW: http://mootools.net/
1221            
1222             This is the main JavaScript library used with this package
1223            
1224             =head2 F<15html-formwidgets.js>
1225            
1226             Replaces Mootools' C<setHTML> method with one that uses the HTML
1227             parser. The included copy has a few hacks that improve the Accordion
1228             widget
1229            
1230             =head2 F<50calendar.js>
1231            
1232             Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
1233             The DHTML Calendar, version 1.0 | www.dynarch.com/projects/calendar
1234             License: GNU Lesser General Public License
1235            
1236             Implements the calendar popup used by the C<::Date> subclass
1237            
1238             =head2 F<behaviour.js>
1239            
1240             Is included from the L<App::Munchies> default skin. It uses the
1241             MooTools library to implement the server side field validation
1242            
1243             Also included in the C<images> subdirectory of the distribution are
1244             example PNG files used by some of the widgets.
1245            
1246             =head1 Incompatibilities
1247            
1248             There are no known incompatibilities in this module.
1249            
1250             =head1 Bugs and Limitations
1251            
1252             The installation script does nothing with the Javascript or PNG files
1253             which are included in the distribution for completeness
1254            
1255             There are no known bugs in this module. Please report problems to
1256             http://rt.cpan.org/NoAuth/Bugs.html?Dist=HTML-FormWidgets. Patches are
1257             welcome
1258            
1259             =head1 Author
1260            
1261             Peter Flanigan, C<< <pjfl@cpan.org> >>
1262            
1263             =head1 License and Copyright
1264            
1265             Copyright (c) 2015 Peter Flanigan. All rights reserved
1266            
1267             This program is free software; you can redistribute it and/or modify it
1268             under the same terms as Perl itself. See L<perlartistic>
1269            
1270             This program is distributed in the hope that it will be useful,
1271             but WITHOUT WARRANTY; without even the implied warranty of
1272             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
1273            
1274             =cut
1275            
1276             # Local Variables:
1277             # mode: perl
1278             # tab-width: 3
1279             # End:
1280