| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 15 |  |  | 15 |  | 10568 | use strict; | 
|  | 15 |  |  |  |  | 34 |  | 
|  | 15 |  |  |  |  | 510 |  | 
| 2 | 15 |  |  | 15 |  | 74 | use warnings; | 
|  | 15 |  |  |  |  | 28 |  | 
|  | 15 |  |  |  |  | 719 |  | 
| 3 |  |  |  |  |  |  | package HTML::Widget::Plugin::Select; | 
| 4 |  |  |  |  |  |  | # ABSTRACT: a widget for selection from a list | 
| 5 |  |  |  |  |  |  | $HTML::Widget::Plugin::Select::VERSION = '0.202'; | 
| 6 | 15 |  |  | 15 |  | 84 | use parent 'HTML::Widget::Plugin'; | 
|  | 15 |  |  |  |  | 36 |  | 
|  | 15 |  |  |  |  | 118 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | #pod =head1 SYNOPSIS | 
| 9 |  |  |  |  |  |  | #pod | 
| 10 |  |  |  |  |  |  | #pod   $widget_factory->select({ | 
| 11 |  |  |  |  |  |  | #pod     id      => 'the-selector', # if no name attr given, defaults to id value | 
| 12 |  |  |  |  |  |  | #pod     value   => 10, | 
| 13 |  |  |  |  |  |  | #pod     options => [ | 
| 14 |  |  |  |  |  |  | #pod       [  0 => "Zero" ], | 
| 15 |  |  |  |  |  |  | #pod       [  5 => "Five" ], | 
| 16 |  |  |  |  |  |  | #pod       [ 10 => "Ten"  ], | 
| 17 |  |  |  |  |  |  | #pod     ], | 
| 18 |  |  |  |  |  |  | #pod   }); | 
| 19 |  |  |  |  |  |  | #pod | 
| 20 |  |  |  |  |  |  | #pod =head1 DESCRIPTION | 
| 21 |  |  |  |  |  |  | #pod | 
| 22 |  |  |  |  |  |  | #pod This plugin provides a select-from-list widget. | 
| 23 |  |  |  |  |  |  | #pod | 
| 24 |  |  |  |  |  |  | #pod The C attribute may be used to add a default class to every | 
| 25 |  |  |  |  |  |  | #pod produced input.  This class cannot be overridden. | 
| 26 |  |  |  |  |  |  | #pod | 
| 27 |  |  |  |  |  |  | #pod   my $plugin = HTML::Widget::Factory::Input->new({ | 
| 28 |  |  |  |  |  |  | #pod     default_classes => [ qw(foo bar) ], | 
| 29 |  |  |  |  |  |  | #pod   }); | 
| 30 |  |  |  |  |  |  | #pod | 
| 31 |  |  |  |  |  |  | #pod =head1 METHODS | 
| 32 |  |  |  |  |  |  | #pod | 
| 33 |  |  |  |  |  |  | #pod =head2 C< provided_widgets > | 
| 34 |  |  |  |  |  |  | #pod | 
| 35 |  |  |  |  |  |  | #pod This plugin provides the following widgets: select | 
| 36 |  |  |  |  |  |  | #pod | 
| 37 |  |  |  |  |  |  | #pod =cut | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 17 |  |  | 17 | 1 | 57 | sub provided_widgets { qw(select) } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | #pod =head2 C< select > | 
| 42 |  |  |  |  |  |  | #pod | 
| 43 |  |  |  |  |  |  | #pod This method returns a select-from-list widget. | 
| 44 |  |  |  |  |  |  | #pod | 
| 45 |  |  |  |  |  |  | #pod In addition to the generic L attributes, the following | 
| 46 |  |  |  |  |  |  | #pod are valid arguments: | 
| 47 |  |  |  |  |  |  | #pod | 
| 48 |  |  |  |  |  |  | #pod =over | 
| 49 |  |  |  |  |  |  | #pod | 
| 50 |  |  |  |  |  |  | #pod =item disabled | 
| 51 |  |  |  |  |  |  | #pod | 
| 52 |  |  |  |  |  |  | #pod If true, this option indicates that the select widget can't be changed by the | 
| 53 |  |  |  |  |  |  | #pod user. | 
| 54 |  |  |  |  |  |  | #pod | 
| 55 |  |  |  |  |  |  | #pod =item ignore_invalid | 
| 56 |  |  |  |  |  |  | #pod | 
| 57 |  |  |  |  |  |  | #pod If this is given and true, an invalid value is ignored instead of throwing an | 
| 58 |  |  |  |  |  |  | #pod exception. | 
| 59 |  |  |  |  |  |  | #pod | 
| 60 |  |  |  |  |  |  | #pod =item options | 
| 61 |  |  |  |  |  |  | #pod | 
| 62 |  |  |  |  |  |  | #pod This may be an arrayref of arrayrefs, each containing a value/name pair, or it | 
| 63 |  |  |  |  |  |  | #pod may be a hashref of values and names. | 
| 64 |  |  |  |  |  |  | #pod | 
| 65 |  |  |  |  |  |  | #pod Use the array form if you need multiple entries for a single value or if order | 
| 66 |  |  |  |  |  |  | #pod is important. | 
| 67 |  |  |  |  |  |  | #pod | 
| 68 |  |  |  |  |  |  | #pod =item value | 
| 69 |  |  |  |  |  |  | #pod | 
| 70 |  |  |  |  |  |  | #pod If this argument is given, the option with this value will be pre-selected in | 
| 71 |  |  |  |  |  |  | #pod the widget's initial state. | 
| 72 |  |  |  |  |  |  | #pod | 
| 73 |  |  |  |  |  |  | #pod An exception will be thrown if more or less than one of the provided options | 
| 74 |  |  |  |  |  |  | #pod has this value. | 
| 75 |  |  |  |  |  |  | #pod | 
| 76 |  |  |  |  |  |  | #pod =back | 
| 77 |  |  |  |  |  |  | #pod | 
| 78 |  |  |  |  |  |  | #pod =cut | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 15 |  |  | 15 |  | 1435 | use HTML::Element; | 
|  | 15 |  |  |  |  | 53 |  | 
|  | 15 |  |  |  |  | 95 |  | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 33 |  |  | 33 |  | 120 | sub _attribute_args { qw(disabled) } | 
| 83 | 82 |  |  | 82 |  | 2909 | sub _boolean_args   { qw(disabled) } | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | sub select { ## no critic Builtin | 
| 86 | 11 |  |  | 11 | 1 | 17 | my ($self, $factory, $arg) = @_; | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 11 |  |  |  |  | 24 | $self->build($factory, $arg); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | #pod =head2 C< build > | 
| 92 |  |  |  |  |  |  | #pod | 
| 93 |  |  |  |  |  |  | #pod  my $widget = $class->build($factory, \%arg) | 
| 94 |  |  |  |  |  |  | #pod | 
| 95 |  |  |  |  |  |  | #pod This method does the actual construction of the widget based on the args set up | 
| 96 |  |  |  |  |  |  | #pod in the exported widget-constructing call.  It's here for subclasses to exploit. | 
| 97 |  |  |  |  |  |  | #pod | 
| 98 |  |  |  |  |  |  | #pod =cut | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | sub build { | 
| 101 | 15 |  |  | 15 | 1 | 23 | my ($self, $factory, $arg) = @_; | 
| 102 | 15 | 100 |  |  |  | 56 | $arg->{attr}{name} = $arg->{attr}{id} unless $arg->{attr}{name}; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 15 |  |  |  |  | 54 | my $widget = HTML::Element->new('select'); | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 15 |  |  |  |  | 358 | my @options; | 
| 107 | 15 | 100 |  |  |  | 48 | if (ref $arg->{options} eq 'HASH') { | 
| 108 | 1 |  |  |  |  | 2 | @options = map { [ $_, $arg->{options}{$_} ] } keys %{ $arg->{options} }; | 
|  | 3 |  |  |  |  | 10 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 109 |  |  |  |  |  |  | } else { | 
| 110 | 14 |  |  |  |  | 17 | @options = @{ $arg->{options} }; | 
|  | 14 |  |  |  |  | 40 |  | 
| 111 | 56 | 100 | 100 |  |  | 501 | Carp::croak "undefined value passed to select widget" | 
| 112 | 14 | 100 |  |  |  | 23 | if grep { not(defined $_) or ref $_ and not defined $_->[0] } @options; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 13 | 100 |  |  |  | 72 | $self->validate_value($arg->{value}, \@options) unless $arg->{ignore_invalid}; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 10 |  |  |  |  | 23 | for my $entry (@options) { | 
| 118 | 44 | 100 |  |  |  | 504 | my ($value, $name) = (ref $entry) ? @$entry : ($entry) x 2; | 
| 119 | 44 |  |  |  |  | 107 | my $option = $self->make_option($factory, $value, $name, $arg); | 
| 120 | 44 |  |  |  |  | 106 | $widget->push_content($option); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 10 |  |  |  |  | 134 | $widget->attr($_ => $arg->{attr}{$_}) for keys %{ $arg->{attr} }; | 
|  | 10 |  |  |  |  | 55 |  | 
| 124 | 10 |  |  |  |  | 328 | return $widget->as_XML; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | #pod =head2 C< make_option > | 
| 128 |  |  |  |  |  |  | #pod | 
| 129 |  |  |  |  |  |  | #pod   my $option = $class->make_option($factory, $value, $name, $arg); | 
| 130 |  |  |  |  |  |  | #pod | 
| 131 |  |  |  |  |  |  | #pod This method constructs the HTML::Element option element that will represent one | 
| 132 |  |  |  |  |  |  | #pod of the options that may be put into the select box.  This is here for | 
| 133 |  |  |  |  |  |  | #pod subclasses to exploit. | 
| 134 |  |  |  |  |  |  | #pod | 
| 135 |  |  |  |  |  |  | #pod =cut | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub make_option { | 
| 138 | 32 |  |  | 32 | 1 | 44 | my ($self, $factory, $value, $name, $arg) = @_; | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 32 |  |  |  |  | 80 | my $option = HTML::Element->new('option', value => $value); | 
| 141 | 32 |  |  |  |  | 844 | $option->push_content($name); | 
| 142 | 32 | 100 | 100 |  |  | 448 | $option->attr(selected => 'selected') | 
| 143 |  |  |  |  |  |  | if defined $arg->{value} and $arg->{value} eq $value; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 32 |  |  |  |  | 94 | return $option; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | #pod =head2 C< validate_value > | 
| 149 |  |  |  |  |  |  | #pod | 
| 150 |  |  |  |  |  |  | #pod This method checks whether the given value option is valid.  See C> | 
| 151 |  |  |  |  |  |  | #pod for an explanation of its default rules. | 
| 152 |  |  |  |  |  |  | #pod | 
| 153 |  |  |  |  |  |  | #pod =cut | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | sub validate_value { | 
| 156 | 7 |  |  | 7 | 1 | 14 | my ($class, $value, $options) = @_; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 7 | 100 |  |  |  | 11 | my @options = map { ref $_ ? $_->[0] : $_ } @$options; | 
|  | 32 |  |  |  |  | 70 |  | 
| 159 |  |  |  |  |  |  | # maybe this should be configurable? | 
| 160 | 7 | 100 |  |  |  | 22 | if ($value) { | 
| 161 | 4 |  |  |  |  | 7 | my $matches = grep { $value eq $_ } @options; | 
|  | 11 |  |  |  |  | 23 |  | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 4 | 100 |  |  |  | 91 | if (not $matches) { | 
|  |  | 100 |  |  |  |  |  | 
| 164 | 1 |  |  |  |  | 202 | Carp::croak "provided value '$value' not in given options: " | 
| 165 | 1 |  |  |  |  | 4 | . join(q{ }, map { "'$_'" } @options); | 
| 166 |  |  |  |  |  |  | } elsif ($matches > 1) { | 
| 167 | 1 |  |  |  |  | 155 | Carp::croak "provided value '$matches' matches more than one option"; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | sub rewrite_arg { | 
| 173 | 15 |  |  | 15 | 1 | 35 | my ($self, $arg, @rest) = @_; | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 15 |  |  |  |  | 71 | $arg = $self->SUPER::rewrite_arg($arg, @rest); | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 15 | 100 |  |  |  | 54 | if ($self->{default_classes}) { | 
| 178 | 2 |  |  |  |  | 6 | my $class = join q{ }, @{ $self->{default_classes} }; | 
|  | 2 |  |  |  |  | 37 |  | 
| 179 | 2 | 100 |  |  |  | 12 | $arg->{attr}{class} = defined $arg->{attr}{class} | 
| 180 |  |  |  |  |  |  | ? "$class $arg->{attr}{class}" | 
| 181 |  |  |  |  |  |  | : $class; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 15 |  |  |  |  | 103 | return $arg; | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | 1; | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | __END__ |