File Coverage

blib/lib/Rose/HTML/Form/Field/Group.pm
Criterion Covered Total %
statement 256 304 84.2
branch 84 122 68.8
condition 30 47 63.8
subroutine 40 55 72.7
pod 9 39 23.0
total 419 567 73.9


line stmt bran cond sub pod time code
1              
2             use strict;
3 13     13   86  
  13         24  
  13         296  
4             use Carp();
5 13     13   55  
  13         21  
  13         169  
6             # XXX: Can't use Scalar::Defer 0.11 (or possibly later) until some things
7             # XXX: are sorted out. See: http://rt.cpan.org/Ticket/Display.html?id=31039
8             # XXX: Scalar::Defer 0.18 seems to work again. Yay!
9             # XXX: ...but this whole approach is a bt too clever.
10             #use Scalar::Defer();
11              
12             use Scalar::Util();
13 13     13   53 use Rose::HTML::Util();
  13         20  
  13         135  
14 13     13   50  
  13         20  
  13         270  
15             use base 'Rose::HTML::Form::Field';
16 13     13   59  
  13         19  
  13         2321  
17             our $VERSION = '0.606';
18              
19             our $Debug = undef;
20              
21             use Rose::Object::MakeMethods::Generic
22             (
23             scalar => [ qw(rows columns) ],
24 13         143  
25             'scalar --get_set_init' =>
26             [
27             qw(html_linebreak xhtml_linebreak)
28             ],
29              
30             boolean => [ 'linebreak' => { default => 1 } ],
31             );
32 13     13   88  
  13         23  
33             {
34             my($self) = shift;
35              
36 112     112 1 247 $self->{'items'} = [];
37             $self->{'values'} = {};
38 112         441 $self->{'labels'} = {};
39 112         289 $self->{'defaults'} = {};
40 112         284  
41 112         340 $self->SUPER::init(@_);
42             }
43 112         400  
44             use constant HTML_LINEBREAK => "<br>\n";
45             use constant XHTML_LINEBREAK => "<br />\n";
46 13     13   1705  
  13         25  
  13         858  
47 13     13   74  
  13         23  
  13         41362  
48              
49 8     8 0 151 {
50 0     0 0 0 Carp::croak "Cannot set children() for a pseudo-group ($_[0])" if(@_ > 1);
51             return wantarray ? () : [];
52 0     0   0 }
53 153     153   311  
54 0     0   0  
55 0     0   0 {
56             my($self) = shift;
57              
58             if(@_)
59 8 50   8 1 954 {
60 8 100       43 $self->{'items'} = $self->_args_to_items({ localized => 0 }, @_);
61             $self->label_items;
62             $self->init_items;
63 1     1 0 3 }
64              
65             return (wantarray) ? @{$self->{'items'} || []} : $self->{'items'};
66             }
67 2649     2649 0 4146  
68             {
69 2649 100       5027 my $items = shift->items or return;
70             return wantarray ? (grep { !$_->hidden } @$items) : [ grep { !$_->hidden } @$items ];
71 82         451 }
72 82         452  
73 82         323 {
74             my($self) = shift;
75              
76 2649 50       4883 foreach my $item ($self->items)
  2323 100       7706  
77             {
78             $item->hidden(0);
79             }
80             }
81 219 50   219 0 469  
82 219 100       576 {
  931         1819  
  35         69  
83             my($self) = shift;
84              
85             foreach my $item ($self->items)
86             {
87 4     4 0 13 $item->hidden(1);
88             }
89 4         192 }
90              
91 22         152 {
92             my($self, $value) = @_;
93              
94             my $delete_item = $self->item($value) or return;
95             my $group_class = $self->_item_group_class;
96              
97 4     4 0 13 my $items = $self->items || [];
98              
99 4         14 my $i = 0;
100              
101 20         48 foreach my $item (@$items)
102             {
103             if($item->isa($group_class))
104             {
105             if(my $deleted = $item->delete_item($value))
106             {
107 18     18 0 46 return $deleted;
108             }
109 18 100       63 }
110 14         38  
111             last if($item eq $delete_item);
112 14   50     34 $i++;
113             }
114 14         21  
115             return splice(@$items, $i, 1);
116 14         28 }
117              
118 47 100       118  
119             {
120 2 50       14 my($self) = shift;
121              
122 2         14 my $items = $self->items || [];
123              
124             if(@_)
125             {
126 45 100       93 my $offset = $#$items - $_[0];
127 33         53 return splice(@$items, $offset < 0 ? 0 : $offset)
128             }
129              
130 12         75 my @items = pop(@$items);
131             $self->init_items if(@items);
132             return @items;
133 0     0 0 0 }
134 0     0 0 0  
135              
136             {
137             my($self) = shift;
138 0     0 0 0  
139             my $items = $self->items || [];
140 0   0     0  
141             my @items = @_ ? splice(@$items, 0, $_[0]) : shift(@$items);
142 0 0       0 $self->init_items if(@items);
143             return @items;
144 0         0 }
145 0 0       0  
146              
147             {
148 0         0 my($self) = shift;
149 0 0       0  
150 0         0 unshift(@{$self->{'items'}}, $self->_args_to_items({ localized => 0 }, @_));
151              
152             $self->init_items;
153 0     0 0 0 }
154              
155              
156             {
157 0     0 0 0 my($self) = shift;
158              
159 0   0     0 foreach my $arg (@_)
160             {
161 0 0       0 $self->delete_item($arg);
162 0 0       0 }
163 0         0 }
164              
165             {
166 0     0 0 0 my($self, $value) = @_;
167              
168             my $group_class = $self->_item_group_class;
169             my $delete_item = UNIVERSAL::isa($value, $group_class) ? $value : ($self->item_group($value) or return);
170 0     0 0 0  
171             my $items = $self->items || [];
172 0         0  
  0         0  
173             my $i = 0;
174 0         0  
175             foreach my $item (@$items)
176             {
177 0     0 0 0 last if($item eq $delete_item);
178              
179             if($item->isa($group_class))
180             {
181 4     4 0 11 if(my $deleted = $item->delete_item($value))
182             {
183 4         12 return $deleted;
184             }
185 12         41 }
186              
187             $i++;
188             }
189              
190             return splice(@$items, $i, 1);
191 2     2 0 7 }
192              
193 2         8 {
194 2 50 50     24 my($self) = shift;
195              
196 2   50     6 foreach my $arg (@_)
197             {
198 2         4 $self->delete_item_group($arg);
199             }
200 2         5 }
201              
202 4 100       13 {
203             my($self) = shift;
204 2 50       10  
205             if(@_)
206 0 0       0 {
207             $self->{'items'} = $self->_args_to_items({ localized => 1 }, @_);
208 0         0 $self->init_items;
209             }
210              
211             return (wantarray) ? @{$self->{'items'}} : $self->{'items'};
212 2         5 }
213              
214             {
215 2         8 my($self, $name) = (shift, shift);
216              
217             if(@_)
218             {
219             foreach my $item ($self->items)
220 0     0 0 0 {
221             $item->html_attr($name, @_);
222 0         0 }
223              
224 0         0 return @_;
225             }
226              
227             foreach my $item (@{[ $self->items ]})
228             {
229             return $item->html_attr($name);
230 0     0 0 0 }
231              
232 0 0       0 return undef;
233             }
234 0         0  
235 0         0 {
236             my($self) = shift;
237              
238 0 0       0 foreach my $item ($self->items)
  0         0  
239             {
240             $item->delete_html_attr(@_);
241             }
242             }
243 16     16 0 2627  
244             *fields = \&items;
245 16 100       50 *fields_localized = \&items_localized;
246              
247 8         25  
248             {
249 28         64 my($self, $options) = (shift, shift);
250              
251             my(%labels, @choices, $items);
252 8         25  
253             my $class = $self->_item_class;
254             my $group_class = $self->_item_group_class;
255 8         17 my $label_method = $options->{'localized'} ? 'label_id' : 'label';
  8         23  
256              
257 8         26 if(@_ == 1 && ref $_[0] eq 'HASH')
258             {
259             %labels = %{$_[0]};
260 0         0 @choices = sort keys %labels;
261             }
262             else
263             {
264             my $args;
265 8     8 0 21  
266             # XXX: Hack to allow a reference to an array of plain scalars
267 8         23 # XXX: to be taken as a list of values.
268             if(@_ == 1 && ref $_[0] eq 'ARRAY')
269 28         85 {
270             $args = $_[0];
271              
272             unless(grep { ref $_ } @$args)
273             {
274             $args = [ map { $_ => $_ } @$args ];
275             }
276 42     42   97 }
277 14     14   36 else { $args = \@_ }
278              
279             while(@$args)
280             {
281 98     98   232 my $arg = shift(@$args);
282              
283 98         187 if(UNIVERSAL::isa($arg, $class) || UNIVERSAL::isa($arg, $group_class))
284             {
285 98         422 push(@$items, $arg);
286 98         378 }
287 98 50       306 elsif(!ref $arg)
288             {
289 98 100 100     461 my $item = $class->new(value => $arg);
290              
291 17         47 if(!ref $args->[0])
  17         75  
292 17         81 {
293             $item->$label_method(shift(@$args));
294             push(@$items, $item);
295             }
296 81         127 elsif(ref $args->[0] eq 'HASH')
297             {
298             my $pairs = shift(@$args);
299              
300 81 100 100     408 while(my($method, $value) = each(%$pairs))
301             {
302 52         108 $item->$method($value);
303             }
304 52 100       137  
  174         402  
305             push(@$items, $item);
306 44         105 }
  130         339  
307             elsif(ref $args->[0] eq 'ARRAY')
308             {
309 29         66 my $group = $group_class->new(label => $arg,
310             items => shift @$args);
311 81         240 push(@$items, $group);
312             }
313 214         350 else
314             {
315 214 100 100     1485 Carp::croak "Illegal or incorrectly positioned ", $self->_item_name_plural,
    50          
316             " argument: $args->[0]";
317 17         46 }
318              
319             }
320             else
321 197         778 {
322             Carp::croak "Illegal or incorrectly positioned ", $self->_item_name_plural,
323 197 100       467 " argument: $args->[0]";
    100          
    50          
324             }
325 181         681 }
326 181         1181 }
327              
328             if(keys %labels)
329             {
330 13         33 my @items;
331              
332 13         84 my $class = $self->_item_class;
333              
334 13         53 foreach my $value (@choices)
335             {
336             push(@$items, $class->new(value => $value,
337 13         119 $label_method => $labels{$value}));
338             }
339             }
340              
341 3         17 foreach my $item (@$items)
342             {
343 3         19 # Connect item to group
344             $item->parent_group($self) if($item->can('parent_group'));
345              
346             # XXX: This whole approach is a bit too clever and leak-prone.
347 0         0  
348             # # Speculatively hook up localizer and locale
349             # # XXX: Scalar::Defer 0.11 breaks this (http://rt.cpan.org/Ticket/Display.html?id=31039)
350             # # XXX: Scalar::Defer 0.18 seems to work again. Yay!
351             # Scalar::Util::weaken(my $welf = $self);
352             # $item->localizer(Scalar::Defer::defer { $welf->localizer });
353             # #$item->localizer(sub { $welf->localizer });
354 0         0 #
355             # # XXX: Use lame workaround instead.
356             # #Scalar::Util::weaken(my $welf = $self);
357             # #$item->localizer(sub { $welf->localizer });
358             #
359             # if(my $parent = $self->parent_form)
360 98 100       348 # {
361             # # XXX: Scalar::Defer 0.11 breaks this (http://rt.cpan.org/Ticket/Display.html?id=31039)
362 17         42 # # XXX: Scalar::Defer 0.18 seems to work again. Yay!
363             # Scalar::Util::weaken(my $warent = $parent);
364 17         57 # $item->locale(Scalar::Defer::defer { $warent->locale });
365             # # XXX: Use lame workaround instead.
366 17         49 # #$item->locale(sub { $parent->locale });
367             # }
368             }
369 35         150  
370             return (wantarray) ? @$items : $items;
371             }
372              
373 98         285 {
374             my($self) = shift;
375              
376 249 50       652 if(@_)
377             {
378             if(my $parent = $self->SUPER::parent_field(@_))
379             {
380             foreach my $item ($self->items)
381             {
382             $item->parent_field($parent) unless($item->parent_field);
383             }
384             }
385             }
386              
387             return $self->SUPER::parent_field;
388             }
389              
390             # XXX: This whole approach is a bit too clever and leak-prone.
391             # sub parent_form
392             # {
393             # my($self) = shift;
394             #
395             # if(@_)
396             # {
397             # if(my $parent = $self->SUPER::parent_form(@_))
398             # {
399             # foreach my $item ($self->items)
400             # {
401             # # XXX: Scalar::Defer 0.11 breaks this (http://rt.cpan.org/Ticket/Display.html?id=31039)
402 98 100       482 # # XXX: Scalar::Defer 0.18 seems to work again. Yay!
403             # Scalar::Util::weaken(my $warent = $parent);
404             # $item->locale(Scalar::Defer::defer { $warent->locale });
405             # # XXX: Use lame workaround instead.
406             # #$item->locale(sub { $parent->locale });
407 6102     6102 1 8330 # }
408             # }
409 6102 100       9675 # }
410             #
411 21 50       72 # return $self->SUPER::parent_form;
412             # }
413 21         64  
414             {
415 63 50       192 my($self) = shift;
416              
417             push(@{$self->{'items'}}, $self->_args_to_items({ localized => 0 }, @_));
418              
419             $self->init_items;
420 6102         11167 }
421              
422             *add_item = \&add_items;
423              
424             {
425             my($self) = shift;
426              
427             push(@{$self->{'items'}}, $self->_args_to_items({ localized => 1 }, @_));
428              
429             $self->init_items;
430             }
431              
432             *add_item_localized = \&add_items_localized;
433              
434             {
435             my($self) = shift;
436              
437             my $labels = $self->{'labels'} || {};
438             my $label_ids = $self->{'label_ids'} || {};
439              
440             return unless(%$labels || %$label_ids);
441              
442             foreach my $item ($self->items)
443             {
444             my $value = $item->html_attr('value');
445              
446             next unless(defined $value);
447              
448             if(exists $label_ids->{$value})
449 16     16 0 1253 {
450             $item->label_id($label_ids->{$value});
451 16         28 }
  16         80  
452             elsif(exists $labels->{$value})
453 16         58 {
454             $item->label($labels->{$value});
455             }
456             }
457             }
458              
459             {
460 0     0 0 0 my($self) = shift;
461              
462 0         0 $self->{'values'} = undef;
  0         0  
463              
464 0         0 foreach my $item ($self->items)
465             {
466             local $item->{'auto_invalidate_parent'} = $self->auto_invalidate_parent;
467             $item->clear;
468             }
469              
470             $self->error(undef);
471 91     91 0 192 $self->has_partial_value(0);
472             $self->is_cleared(1);
473 91   50     268  
474 91   100     399 $self->init_items;
475             }
476 91 100 100     445  
477             {
478 11         29 my($self) = shift;
479              
480 34         129 delete $self->{'labels'};
481             delete $self->{'label_ids'};
482 34 50       237  
483             foreach my $item ($self->items)
484 34 100       202 {
    100          
485             $item->label_id(undef);
486 10         37 $item->label('');
487             }
488              
489             return;
490 15         40 }
491              
492             {
493             my($self) = shift;
494              
495             delete $self->{'labels'};
496             delete $self->{'label_ids'};
497 393     393 1 592  
498             foreach my $item ($self->items)
499 393         729 {
500             $item->label_id(undef);
501 393         798 $item->label($item->value);
502             }
503 1240         4874  
504 1240         2668 return;
505             }
506              
507 393         2196 {
508 393         1076 my($self) = shift;
509 393         1775  
510             $self->input_value(undef);
511 393         1574  
512             foreach my $item ($self->items)
513             {
514             $item->reset;
515             }
516 1     1 0 4  
517             $self->error(undef);
518 1         4 $self->has_partial_value(0);
519 1         2 $self->is_cleared(0);
520              
521 1         3 $self->init_items;
522             }
523 7         41  
524 7         39  
525             {
526             my($self, $localized) = (shift, shift);
527 1         8  
528             my $key = $localized ? 'label_ids' : 'labels';
529              
530             if(@_)
531             {
532 1     1 0 2 my %labels;
533              
534 1         3 if(@_ == 1 && ref $_[0] eq 'HASH')
535 1         1 {
536             $self->{$key} = $_[0];
537 1         4 }
538             else
539 7         43 {
540 7         47 Carp::croak "Odd number of items found in $key() hash argument"
541             unless(@_ % 2 == 0);
542              
543 1         7 $self->{$key} = { @_ };
544             }
545              
546             $self->label_items;
547             }
548 20     20 1 49  
549             my $want = wantarray;
550 20         85  
551             return unless(defined $want);
552 20         57  
553             my $group_class = $self->_item_group_class;
554 88         202  
555             my %labels;
556              
557 20         64 # Dumb linear search for now
558 20         64 foreach my $item ($self->items)
559 20         97 {
560             if($item->isa($group_class))
561 20         87 {
562             foreach my $subitem ($item->items)
563             {
564 9     9 0 122 $labels{$subitem->html_attr('value')} = $subitem->label;
565 4     4 0 43 }
566             }
567             else
568             {
569 13     13   40 $labels{$item->html_attr('value')} = $item->label;
570             }
571 13 100       53 }
572              
573 13 100       42 return $want ? %labels : \%labels;
574             }
575 9         15  
576             # sub labels
577 9 100 66     52 # {
578             # my($self) = shift;
579 8         22 #
580             # if(@_)
581             # {
582             # my %labels;
583 1 50       4 #
584             # if(@_ == 1 && ref $_[0] eq 'HASH')
585             # {
586 1         3 # $self->{'labels'} = $_[0];
587             # }
588             # else
589 9         27 # {
590             # Carp::croak "Odd number of items found in labels() hash argument"
591             # unless(@_ % 2 == 0);
592 13         58 #
593             # $self->{'labels'} = { @_ };
594 13 100       44 # }
595             #
596 4         23 # $self->label_items;
597             # }
598 4         9 #
599             # my $want = wantarray;
600             #
601 4         13 # return unless(defined $want);
602             #
603 12 50       53 # my $group_class = $self->_item_group_class;
604             #
605 0         0 # my %labels;
606             #
607 0         0 # # Dumb linear search for now
608             # foreach my $item ($self->items)
609             # {
610             # if($item->isa($group_class))
611             # {
612 12         31 # foreach my $subitem ($item->items)
613             # {
614             # $labels{$subitem->html_attr('value')} = $subitem->label;
615             # }
616 4 50       40 # }
617             # else
618             # {
619             # $labels{$item->html_attr('value')} = $item->label;
620             # }
621             # }
622             #
623             # return $want ? %labels : \%labels;
624             # }
625              
626             {
627             my($self) = shift;
628             my $sep = ($self->linebreak) ? $self->html_linebreak : ' ';
629             return join($sep, map { $_->html_field } $self->visible_items);
630             }
631              
632             *html_fields = \&html_field;
633              
634             {
635             my($self) = shift;
636             my $sep = ($self->linebreak) ? $self->xhtml_linebreak : ' ';
637             return join($sep, map { $_->xhtml_field } $self->visible_items);
638             }
639              
640             *xhtml_fields = \&xhtml_field;
641              
642             {
643             my($self) = shift;
644              
645             if(@_)
646             {
647             my $val = $self->SUPER::escape_html(@_);
648              
649             foreach my $item ($self->items)
650             {
651             $item->escape_html($val);
652             }
653              
654             return $val;
655             }
656              
657             return $self->SUPER::escape_html(@_);
658             }
659              
660             {
661             my($self) = shift;
662              
663             my @hidden;
664              
665             foreach my $item ($self->items)
666             {
667             push(@hidden, $item->hidden_field) if(defined $item->internal_value);
668             }
669              
670             return (wantarray) ? @hidden : \@hidden;
671 41     41 1 87 }
672 41 100       146  
673 41         528 # XXX: Could someday use Rose::HTML::Table::*
  187         1249  
674              
675             {
676             my($self, %args) = @_;
677              
678             my $items = $args{'items'};
679              
680 25     25 1 53 return unless(ref $items && @$items);
681 25 50       79  
682 25         352 my $xhtml = delete $args{'_xhtml'} || 0;
  115         880  
683             my $format_item = $args{'format_item'} || ($xhtml ? \&_xhtml_item : \&_html_item);
684              
685             my $total = @$items;
686             my $rows = $args{'rows'} || $self->rows || 1;
687             my $cols = $args{'columns'} || $self->columns || 1;
688              
689 16     16 1 36 my $per_cell = $total / ($rows * $cols);
690              
691 16 100       43 if($total % ($rows * $cols))
692             {
693 10         71 $per_cell = int($per_cell + 1);
694             }
695 10         62  
696             my @table;
697 30         104  
698             my $i = 0;
699              
700 10         40 for(my $x = 0; $x < $cols; $x++)
701             {
702             for(my $y = 0; $y < $rows; $y++)
703 6         20 {
704             my $end = $i + $per_cell - 1;
705             $end = $#$items if($end > $#$items);
706             $table[$y][$x] = [ @$items[$i .. $end] ];
707             $i += $per_cell;
708 12     12 1 22 }
709             }
710 12         17  
711             my $sep = ($self->linebreak) ? $xhtml ? $self->xhtml_linebreak : $self->html_linebreak : ' ';
712 12         37  
713             my $html = '<table' . Rose::HTML::Util::html_attrs_string($args{'table'}) . ">\n";
714 66 100       150  
715             my @tr_attrs = (ref $args{'tr'} eq 'ARRAY') ? @{$args{'tr'}} : ($args{'tr'});
716             my @td_attrs = (ref $args{'td'} eq 'ARRAY') ? @{$args{'td'}} : ($args{'td'});
717 12 50       53  
718             my $tr = 0;
719              
720             foreach my $col (@table)
721             {
722             my $tr_attrs = $tr_attrs[$tr] || $tr_attrs[-1];
723              
724 8     8 0 36 $html .= '<tr' . Rose::HTML::Util::html_attrs_string($tr_attrs) . ">\n";
725              
726 8         19 my $td = 0;
727              
728 8 50 33     38 foreach my $row (@$col)
729             {
730 8   100     30 my $td_attrs = $td_attrs[$td] || $td_attrs[-1];
731 8   33     24  
732             $html .= '<td' . Rose::HTML::Util::html_attrs_string($td_attrs) . '>' .
733 8         17 join($sep, map { $self->$format_item($_) } @$row) .
734 8   50     61 "</td>\n";
735 8   100     55  
736             $td++;
737 8         24 }
738              
739 8 100       23 $html .= "</tr>\n";
740             $tr++;
741 2         9 }
742              
743             $html .= "</table>\n";
744 8         13  
745             return $html;
746 8         12 }
747              
748 8         32 *xhtml_table = \&html_table;
749              
750 10         29 1;