package HTML::Accessors; # @(#)$Id: Accessors.pm 23 2008-05-23 20:12:24Z pjf $ use strict; use warnings; use base qw(Class::Accessor::Fast); use HTML::GenerateUtil qw(generate_tag :consts); use HTML::Tagset; use NEXT; use Readonly; use version; our $VERSION = qv( sprintf '0.1.%d', q$Rev: 23 $ =~ /\d+/gmx ); Readonly my $ATTRS => { content_type => q(application/xhtml+xml) }; Readonly my $INP => { button => q(button), checkbox => q(checkbox), hidden => q(hidden), image_button => q(image), password_field => q(password), radio_button => q(radio), submit => q(submit), textfield => q(text) }; Readonly my $NUL => q(); __PACKAGE__->mk_accessors( keys %{ $ATTRS } ); sub new { my ($me, @rest) = @_; my $args = $me->_arg_list( @rest ); my $self = $me->_hash_merge( $ATTRS, $args ); return bless $self, ref $me || $me; } sub escape_html { my ($me, @rest) = @_; return HTML::GenerateUtil::escape_html( @rest ); } sub is_xml { return shift->content_type eq q(application/xhtml+xml) ? 1 : 0; } sub popup_menu { my ($me, @rest) = @_; my ($args, $def, $labels, $opt_attr, $options, $values); $rest[0] ||= $NUL; $args = $me->_arg_list( @rest ); $def = $args->{default} || $NUL; delete $args->{default}; $labels = $args->{labels} || {}; delete $args->{labels}; $values = $args->{values} || []; delete $args->{values}; for my $val (@{ $values }) { $opt_attr = $val eq $def ? { selected => q(selected) } : {}; if (exists $labels->{ $val }) { $opt_attr->{value} = $val; $val = $labels->{ $val }; } $options .= generate_tag( q(option), $opt_attr, $val, GT_ADDNEWLINE ); } if ($options) { $options = "\n".$options } else { $options = generate_tag( q(option), undef, $NUL, GT_ADDNEWLINE ) } return generate_tag( q(select), $args, $options, GT_ADDNEWLINE ); } sub radio_group { my ($me, @rest) = @_; my ($args, $cols, $def, $html, $i, $inp, $inp_attr); my ($labels, $mode, $name, $values); $rest[0] ||= $NUL; $args = $me->_arg_list( @rest ); $cols = $args->{columns} || q(999999); $def = $args->{default} || 0; $labels = $args->{labels} || {}; $name = $args->{name} || q(radio); $values = $args->{values} || []; $inp_attr = { name => $name, type => q(radio) }; $mode = $me->is_xml ? GT_CLOSETAG : 0; $i = 1; $inp_attr->{onchange} = $args->{onchange} if ($args->{onchange}); for my $val (@{ $values }) { $inp_attr->{value } = $val; $inp_attr->{tabindex} = $i; $inp_attr->{checked } = q(checked) if ($def !~ m{ \d+ }mx && $val eq $def); $inp_attr->{checked } = q(checked) if ($def =~ m{ \d+ }mx && $val == $def); $inp = generate_tag( q(input), $inp_attr, undef, $mode ); $inp .= $labels->{ $val } || $val; $html .= generate_tag( q(label), undef, "\n".$inp, GT_ADDNEWLINE ); if ($cols && $i % $cols == 0) { $html .= generate_tag( q(br), undef, undef, $mode ); } delete $inp_attr->{checked}; $i++; } return $html || $NUL; } sub scrolling_list { my ($me, @rest) = @_; my $args = $me->_arg_list( @rest ); $args->{multiple} = q(multiple); return $me->popup_menu( $args ); } ## no critic sub AUTOLOAD { ## critic my ($me, @rest) = @_; my ($args, $elem, $mode, $val); ($elem = $HTML::Accessors::AUTOLOAD) =~ s{ .* :: }{}mx; $mode = GT_ADDNEWLINE; if ($rest[0] && ref $rest[0] eq q(HASH)) { $args = { %{ $rest[0] } }; $val = $rest[1]; } else { $args = {}; $val = $rest[0] } if (exists $INP->{ $elem }) { $args->{type} = $INP->{ $elem }; $args->{value} = delete $args->{default} if (defined $args->{default}); $args->{value} ||= $NUL; $elem = q(input); } ## no critic unless ($HTML::Tagset::isKnown{ $elem }) { ## critic _carp( 'Unknown element '.$elem ); return $me->NEXT::AUTOLOAD( @rest ); } $val ||= defined $args->{default} ? delete $args->{default} : $NUL; ## no critic if ($HTML::Tagset::emptyElement{ $elem }) { ## critic $val = undef; $mode = $me->is_xml ? GT_CLOSETAG : 0; } return generate_tag( $elem, $args, $val, $mode ); } sub DESTROY { my ($me, @rest) = @_; return $me->NEXT::DESTROY( @rest ); } # Private methods sub _arg_list { my ($me, @rest) = @_; return {} unless ($rest[0]); return ref $rest[0] eq q(HASH) ? { %{ $rest[0] } } : { @rest }; } sub _carp { require Carp; goto &Carp::carp } sub _croak { require Carp; goto &Carp::croak } sub _hash_merge { my ($me, $l, $r) = @_; return { %{ $l }, %{ $r || {} } }; } 1; __END__ =pod =head1 Name HTML::Accessors - Generate HTML elements =head1 Version 0.1.$Rev: 23 $ =head1 Synopsis use HTML::Accessors; my $htag = HTML::Accessors->new(); # Create an anchor element $anchor = $htag->a( { href => 'http://...' }, 'This is a link' ); =head1 Description Uses L to create an autoload method for each of the elements defined by L. The API was loosely taken from L. Using the L module is undesirable in a L application (run from the development server) due go greediness issues over STDIN. The returned tags are either XHTML 1.1 or HTML 4.01 compliant. =head1 Configuration and Environment The constructor defines accessors and mutators for one attribute: =over 3 =item content_type Defaults to I which causes the generated tags to conform to the XHTML standard. Setting it to I will generate HTML compatible tags instead =back =head1 Subroutines/Methods =head2 new Uses C<_arg_list> to process the passed options =head2 escape_html Expose C =head2 is_xml Returns true if the returned tags will be XHTML =head2 popup_menu Returns the CselectE> element. The first option passed to C is either a hash ref or a list of key/value pairs. The keys are: =over 3 =item B Determines which of the values will be selected by default =item B Display these labels in place of the values (but return the value of the selected label). This is a hash ref with a key for each element in the C array =item B The key references an array ref whose values are used as the list of options returned in the body of the CselectE> element =back The rest of the keys and values are passed as attributes to the CselectE> element. For example: $ref = { default => 1, name => q(my_field), values => [ 1, 2 ] }; $htag->popup_menu( $ref ); would return: =head2 radio_group Generates a list of radio input buttons with labels. Break elements can be inserted to create rows of a given number of columns when displayed. The first option passed to C is either a hash ref or a list of key/value pairs. The keys are: =over 3 =item B Integer number of columns to display the generated buttons in. If zero then a list of radio buttons without breaks is generated =item B Determines which of the radio box will be selected by default =item B Display these labels next to each button. This is a hash ref with a key for each element in the C array =item B The form name of the generated buttons =item B An optional Javascript reference. The JS will be executed each time a different radio button is selected =item B The key references an array ref whose values are returned by the radio buttons =back For example: $ref = { columns => 2, default => 1, labels => { 1 => q(Button One), 2 => q(Button Two), 3 => q(Button Three), 4 => q(Button Four), }, name => q(my_field), values => [ 1, 2, 3, 4 ] }; $htag->radio_group( $ref ); would return:

=head2 scrolling_list Calls C with the C argument set to C. This has the effect of allowing multiple selections to be returned from the popup menu =head2 AUTOLOAD Uses L to check if the requested method is a known HTML element. If it is C uses L to create the tag If the first option is a hash ref then the keys and values are copied and passed to C which uses them to set the attributes on the created element. The next option is treated as the element's body text and overrides the C attribute which is passed and deleted from the options hash If the requested element exists in the hard coded list of input elements, then the element is set to C and the mapped value used as the type attribute in the call to C. For example; $htag->textfield( { default => q(default value), name => q(my_field) } ); would return The list of input elements contains; button, checkbox, hidden, image_button, password_field, radio_button, submit, and textfield =head2 DESTROY Implement the C method so that the C method doesn't get called instead. Re-dispatches the call upstream =head2 _arg_list Returns a hash ref containing the passed parameter list. Enables methods to be called with either a list or a hash ref as it's input parameters. Makes copies as it goes so that you can change the contents without altering the parameters if they were passed by reference =head2 _carp Call C. Don't load L if we don't have to =head2 _croak Call C. Don't load L if we don't have to =head2 _hash_merge Simplistic merging of two hashes =head1 Diagnostics C is called to issue a warning about undefined elements =head1 Dependencies =over 4 =item L =item L =item L =item L =item L =back =head1 Incompatibilities There are no known incompatibilities in this module =head1 Bugs and Limitations There are no known bugs in this module. Please report problems to the address below. Patches are welcome =head1 Author Peter Flanigan, C<< >> =head1 License and Copyright Copyright (c) 2008 Peter Flanigan. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. This program is distributed in the hope that it will be useful, but WITHOUT WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =cut # Local Variables: # mode: perl # tab-width: 3 # End: