File Coverage

blib/lib/Rose/HTML/Object.pm
Criterion Covered Total %
statement 363 395 91.9
branch 128 164 78.0
condition 41 56 73.2
subroutine 85 91 93.4
pod 38 58 65.5
total 655 764 85.7


line stmt bran cond sub pod time code
1              
2             use strict;
3 44     44   260408  
  44         293  
  44         1243  
4             use base 'Rose::HTML::Object::Localized';
5 44     44   501  
  44         256  
  44         15740  
6             use Carp;
7 43     43   245 use Scalar::Util();
  43         78  
  43         2914  
8 43     43   208 use List::MoreUtils qw(uniq);
  43         81  
  43         688  
9 43     43   20170  
  43         485108  
  43         253  
10             use Rose::HTML::Util();
11 43     43   42345 use Rose::HTML::Object::Message::Localizer;
  43         96  
  43         734  
12 43     43   193  
  43         83  
  43         2187  
13             our $VERSION = '0.618';
14              
15             our $Debug = undef;
16              
17             #
18             # Class data
19             #
20              
21             use Rose::Class::MakeMethods::Generic
22             (
23             inherited_hash =>
24 43         385 [
25             'object_type_class' => { plural_name => 'object_type_classes' },
26             ],
27             );
28 43     43   217  
  43         1155  
29             __PACKAGE__->default_localizer(Rose::HTML::Object::Message::Localizer->new);
30             __PACKAGE__->default_locale('en');
31              
32             __PACKAGE__->autoload_html_attr_methods(1);
33              
34             __PACKAGE__->add_valid_html_attrs
35             (
36             'id',
37             'class',
38             'style',
39             'title',
40             'lang',
41             'xml:lang',
42             'dir',
43             'onclick',
44             'ondblclick',
45             'onmousedown',
46             'onmouseup',
47             'onmouseover',
48             'onmousemove',
49             'onmouseout',
50             'onkeypress',
51             'onkeydown',
52             'onkeyup'
53             );
54              
55             __PACKAGE__->object_type_classes
56             (
57             'anchor' => 'Rose::HTML::Anchor',
58             'image' => 'Rose::HTML::Image',
59             'label' => 'Rose::HTML::Label',
60             'link' => 'Rose::HTML::Link',
61             'script' => 'Rose::HTML::Script',
62             'literal text' => 'Rose::HTML::Text',
63              
64             'form' => 'Rose::HTML::Form',
65             'repeatable form' => 'Rose::HTML::Form::Repeatable',
66              
67             'text' => 'Rose::HTML::Form::Field::Text',
68             'scalar' => 'Rose::HTML::Form::Field::Text',
69             'char' => 'Rose::HTML::Form::Field::Text',
70             'character' => 'Rose::HTML::Form::Field::Text',
71             'varchar' => 'Rose::HTML::Form::Field::Text',
72             'string' => 'Rose::HTML::Form::Field::Text',
73              
74             'text area' => 'Rose::HTML::Form::Field::TextArea',
75             'textarea' => 'Rose::HTML::Form::Field::TextArea',
76             'blob' => 'Rose::HTML::Form::Field::TextArea',
77              
78             'option' => 'Rose::HTML::Form::Field::Option',
79             'option group' => 'Rose::HTML::Form::Field::OptionGroup',
80              
81             'checkbox' => 'Rose::HTML::Form::Field::Checkbox',
82             'check' => 'Rose::HTML::Form::Field::Checkbox',
83              
84             'radio button' => 'Rose::HTML::Form::Field::RadioButton',
85             'radio' => 'Rose::HTML::Form::Field::RadioButton',
86              
87             'checkboxes' => 'Rose::HTML::Form::Field::CheckboxGroup',
88             'checks' => 'Rose::HTML::Form::Field::CheckboxGroup',
89             'checkbox group' => 'Rose::HTML::Form::Field::CheckboxGroup',
90             'check group' => 'Rose::HTML::Form::Field::CheckboxGroup',
91              
92             'radio buttons' => 'Rose::HTML::Form::Field::RadioButtonGroup',
93             'radios' => 'Rose::HTML::Form::Field::RadioButtonGroup',
94             'radio button group' => 'Rose::HTML::Form::Field::RadioButtonGroup',
95             'radio group' => 'Rose::HTML::Form::Field::RadioButtonGroup',
96              
97             'pop-up menu' => 'Rose::HTML::Form::Field::PopUpMenu',
98             'popup menu' => 'Rose::HTML::Form::Field::PopUpMenu',
99             'menu' => 'Rose::HTML::Form::Field::PopUpMenu',
100              
101             'select box' => 'Rose::HTML::Form::Field::SelectBox',
102             'selectbox' => 'Rose::HTML::Form::Field::SelectBox',
103             'select' => 'Rose::HTML::Form::Field::SelectBox',
104              
105             'submit' => 'Rose::HTML::Form::Field::Submit',
106             'submit button' => 'Rose::HTML::Form::Field::Submit',
107              
108             'reset' => 'Rose::HTML::Form::Field::Reset',
109             'reset button' => 'Rose::HTML::Form::Field::Reset',
110              
111             'file' => 'Rose::HTML::Form::Field::File',
112             'upload' => 'Rose::HTML::Form::Field::File',
113              
114             'password' => 'Rose::HTML::Form::Field::Password',
115              
116             'hidden' => 'Rose::HTML::Form::Field::Hidden',
117              
118             'num' => 'Rose::HTML::Form::Field::Numeric',
119             'number' => 'Rose::HTML::Form::Field::Numeric',
120             'numeric' => 'Rose::HTML::Form::Field::Numeric',
121              
122             'int' => 'Rose::HTML::Form::Field::Integer',
123             'integer' => 'Rose::HTML::Form::Field::Integer',
124              
125             'email' => 'Rose::HTML::Form::Field::Email',
126              
127             'phone' => 'Rose::HTML::Form::Field::PhoneNumber::US',
128             'phone us' => 'Rose::HTML::Form::Field::PhoneNumber::US',
129              
130             'phone us split' => 'Rose::HTML::Form::Field::PhoneNumber::US::Split',
131              
132             'set' => 'Rose::HTML::Form::Field::Set',
133              
134             'time' => 'Rose::HTML::Form::Field::Time',
135             'time split hms' => 'Rose::HTML::Form::Field::Time::Split::HourMinuteSecond',
136              
137             'time hours' => 'Rose::HTML::Form::Field::Time::Hours',
138             'time minutes' => 'Rose::HTML::Form::Field::Time::Minutes',
139             'time seconds' => 'Rose::HTML::Form::Field::Time::Seconds',
140              
141             'date' => 'Rose::HTML::Form::Field::Date',
142             'datetime' => 'Rose::HTML::Form::Field::DateTime',
143              
144             'datetime range' => 'Rose::HTML::Form::Field::DateTime::Range',
145              
146             'datetime start' => 'Rose::HTML::Form::Field::DateTime::StartDate',
147             'datetime end' => 'Rose::HTML::Form::Field::DateTime::EndDate',
148              
149             'datetime split mdy' => 'Rose::HTML::Form::Field::DateTime::Split::MonthDayYear',
150             'datetime split mdyhms' => 'Rose::HTML::Form::Field::DateTime::Split::MDYHMS',
151             );
152              
153             #
154             # Object data
155             #
156              
157             use Rose::Object::MakeMethods::Generic
158             (
159             scalar =>
160 43         432 [
161             'html_element', # may be read-only in subclasses
162             'xhtml_element', # may be read-only in subclasses
163             ],
164              
165             boolean =>
166             [
167             'escape_html' => { default => 1 },
168             'validate_html_attrs' => { default => 1 },
169             'is_self_closing' => { default => 0 },
170             ],
171              
172             'scalar --get_set_init' =>
173             [
174             'html_error_formatter',
175             'xhtml_error_formatter',
176             ],
177             );
178 43     43   28801  
  43         83  
179             use Rose::Class::MakeMethods::Generic
180             (
181             inheritable_scalar =>
182 43         176 [
183             'autoload_html_attr_methods',
184             'force_utf8',
185             ],
186             );
187 43     43   50970  
  43         82  
188             use Rose::Class::MakeMethods::Set
189             (
190             inheritable_set =>
191 43         453 [
192             required_html_attr =>
193             {
194             add_implies => 'add_valid_html_attr',
195             test_method => 'html_attr_is_required',
196             },
197             ],
198              
199             inherited_set =>
200             [
201             valid_html_attr =>
202             {
203             test_method => '_html_attr_is_valid',
204             delete_implies => [ 'delete_boolean_html_attr', 'delete_required_html_attr' ],
205             inherit_implies => 'inherit_boolean_html_attr',
206             },
207              
208             boolean_html_attr =>
209             {
210             add_implies => 'add_valid_html_attr',
211             test_method => 'html_attr_is_boolean',
212             },
213             ]
214             );
215 43     43   29711  
  43         140241  
216             use Rose::HTML::Object::MakeMethods::Generic
217             (
218             array =>
219 43         669 [
220             'children' => { interface => 'get_set_inited' },
221             'child' => { interface => 'get_item', hash_key => 'children' },
222             'push_children' => { interface => 'push', hash_key => 'children' },
223             'pop_children' => { interface => 'pop', hash_key => 'children' },
224             'shift_children' => { interface => 'shift', hash_key => 'children' },
225             'unshift_children' => { interface => 'unshift', hash_key => 'children' },
226             'delete_children' => { interface => 'clear', hash_key => 'children' },
227             'delete_child_at_index' => { interface => 'delete_item', hash_key => 'children' },
228             ],
229             );
230 43     43   53312  
  43         108  
231             #
232             # Class methods
233             #
234              
235              
236 1628     1628 0 6179 *object_type_names = \&object_type_class_keys;
237              
238             #
239             # Constructor
240             #
241              
242             {
243             my($class) = shift;
244              
245             my $self =
246 1630     1630 1 81417 {
247             html_attrs => {},
248 1630 100       4788 escape_html => 1,
249             error => undef,
250             validate_html_attrs => $class eq $class->generic_object_class ? 0 : 1,
251             };
252              
253             bless $self, $class;
254              
255             $self->init(@_);
256 1630         2980  
257             return $self;
258 1630         5026 }
259              
260 1630         8519 #
261             # Object methods
262             #
263              
264             {
265             my($self) = shift;
266              
267             @_ = (element => @_) if(@_ % 2);
268              
269 1720     1720 1 2755 my $class = ref $self;
270              
271 1720 100       4195 no strict 'refs';
272             while(my($k, $v) = each %{$class->required_html_attrs_hash})
273 1720         2622 {
274             $self->{'html_attrs'}{$k} = $v;
275 43     43   6933 }
  43         90  
  43         27293  
276 1720         2440  
  4449         9084  
277             $self->SUPER::init(@_);
278 2729         29768 }
279              
280              
281 1720         18046 {
282             my $children = shift->children;
283             return $children && @$children ? 1 : 0;
284 2     2 1 303 }
285 2     2 1 301  
286 6     6 1 1055  
287 4     4 1 21 {
288 2     2 1 10 my($self) = shift;
289 3     3 1 17  
290             if(@_)
291             {
292             my $old_parent = $self->parent;
293 1859     1859 1 4372  
294 1859 100 66     8172 Scalar::Util::weaken($self->{'parent'} = shift);
295              
296             my $new_parent = $self->{'parent'};
297 0 0   0 1 0  
298             if($old_parent && Scalar::Util::refaddr($old_parent) != Scalar::Util::refaddr($new_parent))
299             {
300             $old_parent->delete_child($self);
301 977     977 1 1305 $new_parent->push_child($self) unless($new_parent->has_child($self));
302             }
303 977 100       1660 }
304              
305 464         762 return $self->{'parent'};
306             }
307 464         1624  
308              
309 464         629 {
310             my($self) = shift;
311 464 100 100     942  
312             if($_[0] =~ /^[+-]?\d+$/)
313 1         4 {
314 1 50       4 return $self->delete_child_at_index(@_);
315             }
316              
317             my $refaddr = Scalar::Util::refaddr($_[0]);
318 977         2067  
319             my $i = 0;
320              
321 5     5 1 12 foreach my $child ($self->children)
  4         12  
322             {
323             if(Scalar::Util::refaddr($child) == $refaddr)
324             {
325 3     3 1 7 return $self->delete_child_at_index($i);
326             }
327 3 100       14  
328             $i++;
329 2         6 }
330              
331             return undef;
332 1         4 }
333              
334 1         2 {
335             my($self) = shift;
336 1         3  
337             my $refaddr = Scalar::Util::refaddr($_[0]);
338 1 50       4  
339             foreach my $child ($self->children)
340 1         4 {
341             if(Scalar::Util::refaddr($child) == $refaddr)
342             {
343 0         0 return 1;
344             }
345             }
346 0         0  
347             return 0;
348             }
349              
350             my %Loaded; # Lame, but trying to be fast here
351 1     1 1 3  
352             {
353 1         3 my($class) = shift;
354              
355 1         4 my $type_class = $class->object_type_class(@_);
356              
357 0 0       0 unless($Loaded{$type_class})
358             {
359 0         0 no strict 'refs';
360             unless(@{$type_class . '::ISA'})
361             {
362             my $error;
363 1         3  
364             TRY:
365             {
366             local $@;
367             eval "use $type_class";
368             $error = $@;
369             }
370 1363     1363 0 1878  
371             Carp::croak "Could not load class '$type_class' - $error" if($error);
372 1363         3504 }
373              
374 1363 100       31922 $Loaded{$type_class}++;
375             }
376 43     43   331  
  43         1042  
  43         83102  
377 61 100       105 return $type_class;
  61         366  
378             }
379 10         31  
380             {
381             my($self, %args) = @_;
382              
383 10         16 my $error_id = $args{'error_id'};
  10         21  
384 10     8   1171 my $msg_class = $args{'msg_class'} || $self->localizer->message_class;
  8         5424  
  8         231  
  8         64  
385 10         50 my $args = $args{'args'} || [];
386              
387             return $msg_class->new(id => $error_id, args => $args);
388 10 50       38 }
389              
390              
391 61         157  
392             {
393             my($self) = shift;
394 1363         2859  
395             return $self->html_element unless(@_);
396              
397             $self->xhtml_element(@_);
398             return $self->html_element(@_);
399 6     6 1 20 }
400              
401 6         12 {
402 6   33     16 my($self, $attr) = @_;
403 6   100     17  
404             if(@_ == 2)
405 6         26 {
406             return (exists $self->{'html_attrs'}{$attr}) ? 1 : 0;
407             }
408              
409       132 0   croak 'Missing attribute name';
410       0 0   }
411              
412             {
413             my($self) = shift;
414 13     13 1 104  
415             if(@_)
416 13 50       33 {
417             local $_;
418 13         63 delete $self->{'html_attrs'}{$_} for(@_);
419 13         47 return scalar @_;
420             }
421              
422             croak 'Missing attribute name';
423             }
424 427     427 0 666  
425              
426 427 50       824  
427             {
428 427 100       1274 my($self) = shift;
429              
430             if(@_)
431 0         0 {
432             local $_;
433             $self->{'html_attrs'}{$_} = undef for(@_);
434             return scalar @_;
435             }
436 44     44 1 70  
437             croak 'Missing attribute name';
438 44 50       92 }
439              
440 44         57  
441 44         157  
442 44         99 {
443             my($self, $attr, $value) = @_;
444              
445 0         0 croak "Invalid attribute: '$attr'"
446             unless(!$self->validate_html_attrs ||
447             $self->html_attr_is_valid($attr));
448 41     41 1 900  
449             my $hook = $self->html_attr_hook($attr);
450 1     1 1 3  
451             if(@_ == 3)
452             {
453             if($hook)
454 2     2 1 4 {
455             local $_ = $value;
456 2 50       7 $value = $self->$hook($value);
457             }
458 2         3  
459 2         6 return $self->{'html_attrs'}{$attr} = $value;
460 2         5 }
461             elsif(@_ == 2)
462             {
463 0         0 if(exists $self->{'html_attrs'}{$attr})
464             {
465             $value = $self->{'html_attrs'}{$attr};
466 1     1 1 3  
467             if($hook)
468 1     1 1 7 {
469             local $_ = $value;
470             $value = $self->$hook();
471             }
472 26699     26699 1 45817  
473             return $value;
474 26699 100 100     45794 }
475              
476             return undef;
477             }
478 26694         251098  
479             croak 'Missing attribute name';
480 26694 100       46101 }
    50          
481              
482 12096 100       17639 {
483             my ($self, $attr) = @_;
484 1         2 return 1 if($attr =~ /^data-\w/);
485 1         4 return $self->_html_attr_is_valid($attr);
486             }
487              
488 12096         28604 {
489             wantarray ? sort keys %{$_[0]->{'html_attrs'}} :
490             [ sort keys %{$_[0]->{'html_attrs'}} ];
491             }
492 14598 100       23927  
493             {
494 13418         17367 my($self) = shift;
495              
496 13418 100       18464 if(@_)
497             {
498 1         3 my $attrs;
499 1         4  
500             if(@_ == 1 && ref $_[0] eq 'HASH')
501             {
502 13418         25293 $attrs = shift;
503             }
504             else
505 1180         2354 {
506             croak 'Odd number of arguments' if(@_ % 2);
507             $attrs = { @_ };
508 0         0 }
509              
510             while(my($attr, $value) = each(%$attrs))
511             {
512             $self->html_attr($attr => $value);
513 55249     55249 1 359445 }
514 55249 100       81906 }
515 55248         88770  
516             return (wantarray) ? %{$self->{'html_attrs'}} : $self->{'html_attrs'};
517             }
518              
519             {
520 4         31 my($self, $attr) = (shift, shift);
521 5 100   5 0 713  
  1         7  
522             croak "Invalid attribute: '$attr'"
523             unless(!$self->validate_html_attrs ||
524             $self->html_attr_is_valid($attr));
525              
526 2     2 1 1333 if(@_)
527             {
528 2 50       7 # XXX: possibly check that it's a code reference
529             return $self->{'html_attr_hook'}{$attr} = shift;
530 2         3 }
531              
532 2 100 66     57 if(exists $self->{'html_attr_hook'}{$attr})
533             {
534 1         2 return $self->{'html_attr_hook'}{$attr};
535             }
536              
537             return undef;
538 1 50       7 }
539 1         5  
540              
541              
542 2         11 {
543             my($self) = shift;
544 5         11  
545             if(my $code = $self->html_error_formatter)
546             {
547             return $code->($self);
548 2 50       7 }
  0         0  
549              
550             my $error = $self->error;
551              
552             if($error && length "$error")
553 26696     26696 1 37827 {
554             return qq(<span class="error">) .
555 26696 50 66     40622 ($self->escape_html ? Rose::HTML::Util::escape_html($error) : $error) .
556             '</span>';
557             }
558              
559 26696 100       229398 return '';
560             }
561              
562 2         10 {
563             my($self) = shift;
564              
565 26694 100       41977 if(my $code = $self->html_error_formatter)
566             {
567 4         9 return $code->($self);
568             }
569              
570 26690         34364 return $self->html_error;
571             }
572              
573 1     1 0 4 {
574             my($self) = shift;
575 2     2 1 13  
576 1     1 1 5 if(my $code = $self->html_error_formatter)
577             {
578             return $code->($self);
579             }
580 90     90 1 1372  
581             my $error = join(', ', grep { /\S/ } $self->errors);
582 90 50       284  
583             if($error)
584 0         0 {
585             return qq(<span class="error">) .
586             ($self->escape_html ? Rose::HTML::Util::escape_html($error) : $error) .
587 90         227 '</span>';
588             }
589 90 100 100     257  
590             return '';
591 17 100       63 }
592              
593             {
594             my($self) = shift;
595              
596 73         161 if(my $code = $self->html_error_formatter)
597             {
598             return $code->($self);
599             }
600              
601 36     36 1 1346 return $self->html_errors;
602             }
603 36 50       112  
604             {
605 0         0 my($self) = shift;
606              
607             my @html;
608 36         101  
609             local $_;
610              
611             my $required_attrs = $self->required_html_attrs_hash;
612             my %boolean_attrs = map { $_ => 1 } $self->boolean_html_attrs;
613 4     4 0 6  
614             foreach my $attr (sort(uniq(keys(%{$self->{'html_attrs'}}), keys(%$required_attrs))))
615 4 50       9 {
616             my $value;
617 0         0  
618             if(exists $self->{'html_attrs'}{$attr})
619             {
620 4         14 $value = $self->{'html_attrs'}{$attr};
  4         21  
621             }
622 4 50       23  
623             if(defined($value) || exists $boolean_attrs{$attr})
624 4 50       18 {
625             if($boolean_attrs{$attr})
626             {
627             push(@html, $attr) if($value);
628             next;
629 0         0 }
630              
631             $value = '' unless(defined $value);
632             push(@html, $attr . q(=") .
633             ($value =~ /\W/ ? Rose::HTML::Util::escape_html($value) : $value) . q("));
634 2     2 0 5 }
635             elsif(exists $required_attrs->{$attr})
636 2 50       5 {
637             $value = $required_attrs->{$attr};
638 0         0 $value = '' unless(defined $value);
639             push(@html, $attr . q(=") .
640             ($value =~ /\W/ ? Rose::HTML::Util::escape_html($value) : $value) . q("));
641 2         5 }
642             }
643              
644             return '' unless(@html);
645              
646 1493     1493 1 5464 return ' ' . join(' ', @html);
647             }
648 1493         1831  
649             our (%Boolean_Attrs, %Required_Attrs);
650 1493         2093  
651             {
652 1493         3119 my($self) = shift;
653 1493         12163  
  3678         37818  
654             my $class = ref($self);
655 1493         15655  
  1493         8349  
656             my @html;
657 3619         12564  
658             local $_;
659 3619 100       5850 my $required_attrs = $self->required_html_attrs_hash;
660             my %boolean_attrs = map { $_ => 1 } $self->boolean_html_attrs;
661 3601         4877  
662             foreach my $attr (sort(uniq(keys(%{$self->{'html_attrs'}}), keys(%$required_attrs))))
663             {
664 3619 100 100     6866 my $value;
    100          
665              
666 3377 100       5114 if(exists $self->{'html_attrs'}{$attr})
667             {
668 726 100       1211 $value = $self->{'html_attrs'}{$attr};
669 726         1070 }
670              
671             if(defined($value) || exists $boolean_attrs{$attr})
672 2651 50       3813 {
673 2651 100       8525 if($boolean_attrs{$attr})
674             {
675             push(@html, $attr . q(=") . ($attr =~ /\W/ ? Rose::HTML::Util::escape_html($attr) : $attr) . q(")) if($value);
676             next;
677             }
678 58         91  
679 58 100       119 $value = '' unless(defined $value);
680 58 50       368 push(@html, $attr . q(=") .
681             ($value =~ /\W/ ? Rose::HTML::Util::escape_html($value) : $value) . q("));
682             }
683             elsif(exists $required_attrs->{$attr})
684             {
685 1493 100       7609 $value = $required_attrs->{$attr};
686             $value = '' unless(defined $value);
687 1108         7184 push(@html, $attr . q(=") .
688             ($value =~ /\W/ ? Rose::HTML::Util::escape_html($value) : $value) . q("));
689             }
690             }
691              
692             return '' unless(@html);
693              
694 546     546 1 815 return ' ' . join(' ', @html);
695             }
696 546         785  
697              
698 546         662 {
699             my($self) = shift;
700 546         684  
701 546         1102 no warnings 'uninitialized';
702 546         4397  
  1625         17249  
703             if($self->has_children || !$self->is_self_closing)
704 546         3403 {
  546         3509  
705             return '<' . $self->html_element . $self->html_attrs_string . '>' .
706 1908         5872 join('', map { $_->html_tag } $self->children) .
707             '</' . $self->html_element . '>';
708 1908 100       3182 }
709              
710 1898         2521 return '<' . $self->html_element . $self->html_attrs_string . '>';
711             }
712              
713 1908 100 100     3664 {
    100          
714             my($self) = shift;
715 1800 100       2762  
716             no warnings 'uninitialized';
717 308 50       634  
    100          
718 308         473 if($self->has_children || !$self->is_self_closing)
719             {
720             return '<' . $self->xhtml_element . $self->xhtml_attrs_string . '>' .
721 1492 50       2079 join('', map { $_->xhtml_tag } $self->children) .
722 1492 100       4690 '</' . $self->xhtml_element . '>';
723             }
724              
725             return '<' . $self->xhtml_element . $self->xhtml_attrs_string . ' />';
726             }
727 41         68  
728 41 100       85 {
729 41 50       135 my ($self, $new_class) = @_;
730              
731             my $class = $self->html_attr('class');
732              
733             no warnings 'uninitialized';
734 546 100       3149 unless($class =~ /(?:^| )$new_class(?: |$)/)
735             {
736 525         3293 $self->html_attr(class => $class ? "$class $new_class" : $new_class);
737             }
738             }
739 21     21 1 541  
740 29     29 1 93 {
741             my ($self) = shift;
742              
743             no warnings 'uninitialized';
744 1360     1360 1 2707 foreach my $class ((ref $_[0] eq ref []) ? @{$_[0]} : @_)
745             {
746 43     43   366 $self->add_class($class);
  43         94  
  43         5916  
747             }
748 1360 100 100     2538 }
749              
750             {
751 869         1889 my ($self, $delete_class) = @_;
  868         1839  
752              
753             my $class = $self->html_attr('class');
754              
755 491         990 no warnings 'uninitialized';
756             if($class =~ s/(^| |\G)\Q$delete_class\E( |$)/$1$2/g)
757             {
758             for($class)
759             {
760 497     497 1 731 s/^ +//;
761             s/ +$//;
762 43     43   312 s/ +/ /g;
  43         82  
  43         5876  
763             }
764 497 100 100     889  
765             $self->html_attr(class => $class);
766             }
767 196         479 }
  196         462  
768              
769             {
770             my ($self) = shift;
771 301         737  
772             no warnings 'uninitialized';
773             foreach my $class ((ref $_[0] eq ref []) ? @{$_[0]} : @_)
774             {
775             $self->delete_class($class);
776 30     30 0 107 }
777             }
778 30         143  
779             #
780 43     43   292 # Lame start/end HTML fall-backs for lazy derived classes
  43         85  
  43         3899  
781 30 100       492 #
782              
783 13 100       54 {
784             my($self) = shift;
785              
786             return '<' . $self->html_element . $self->html_attrs_string . '>';
787              
788             #my $html = $self->html;
789 2     2 0 5 #$html =~ s{</\w+>\z}{};
790             #return $html;
791 43     43   317 }
  43         87  
  43         4334  
792 2 100       9  
  1         3  
793             {
794 6         14 my($self) = shift;
795              
796             return '</' . $self->html_element . '>';
797              
798             #my $html = $self->html;
799             #$html =~ m{</\w+>\z};
800 811     811 0 3048 #return $1 || '';
801             }
802 811         1270  
803             {
804 43     43   275 my($self) = shift;
  43         82  
  43         7048  
805 811 100       14486  
806             return '<' . $self->xhtml_element . $self->xhtml_attrs_string . '>';
807 15         40  
808             #my $xhtml = $self->xhtml;
809 15         40 #$xhtml =~ s{</\w+>\z}{};
810 15         40 #return $xhtml;
811 15         38 }
812              
813             {
814 15         33 my($self) = shift;
815              
816             return '</' . $self->xhtml_element . '>';
817              
818             #my $xhtml = $self->xhtml;
819             #$xhtml =~ m{</\w+>\z};
820 3     3 0 6 #return $1 || '';
821             }
822 43     43   332  
  43         94  
  43         19630  
823 3 100       13 {
  2         4  
824             my($class) = shift;
825 8         17 my($attr) = shift;
826              
827             if(@_)
828             {
829             $class->add_required_html_attr({ $attr => $_[0] });
830             return $_[0];
831             }
832              
833             return $class->required_html_attr_value($attr);
834             }
835 106     106 0 167  
836             {
837 106         267 my($self_or_class) = shift;
838              
839             my $class = ref($self_or_class) || $self_or_class;
840              
841             $class->localizer->load_all_messages(from_class => $class);
842             }
843              
844             our $AUTOLOAD;
845              
846 106     106 0 191 # We "can" do what will eventually be AUTOLOADed HTML attribute methods
847             {
848 106         243 my($self, $name) = @_;
849              
850             my $class = ref($self) || $self;
851              
852             my $code = $self->SUPER::can($name);
853             return $code if($code);
854              
855             return unless($self->html_attr_is_valid($name) && $class->autoload_html_attr_methods);
856              
857 44     44 0 240 # can() expects a code ref that will actually work...
858             return sub
859 44         118 {
860             my $self = $_[0];
861              
862             my $code = $self->SUPER::can($name); # exists already?
863             goto &$code if($code);
864              
865             $AUTOLOAD = $class;
866             goto &AUTOLOAD
867             };
868 44     44 0 76 }
869              
870 44         98 {
871             my($class) = ref($_[0]) || $_[0];
872             no strict 'refs';
873             exists ${$class . '::__AUTOLOADED'}{$_[1]};
874             }
875              
876             {
877             my($class) = shift;
878              
879 9     9 1 947 my $count = 0;
880 9         20  
881             foreach my $attr (@_ ? @_ : $class->valid_html_attrs)
882 9 50       34 {
883             no strict 'refs';
884 9         89 my $method = $class . '::' . $attr;
885 9         1920 next if(defined &$method);
886             *$method = sub { shift->html_attr($attr, @_) };
887             $count++;
888 0         0 }
889              
890             return $count;
891             }
892              
893 1     1 1 2 {
894             my($class) = shift;
895 1   33     6  
896             foreach my $arg (@_)
897 1         3 {
898             if($arg eq ':customize')
899             {
900             $class->import_methods(
901             { target_class => (caller)[0] },
902             qw(object_type_class_exists object_type_class_keys
903             delete_object_type_class object_type_classes
904             clear_object_type_classes object_type_class
905 9824     9824 0 617432 inherit_object_type_classes object_type_classes_cache
906             inherit_object_type_class add_object_type_classes
907 9824   66     20497 delete_object_type_classes add_object_type_class
908             localizer locale default_localizer default_locale));
909 9824         28796 }
910 9824 100       26969 else
911             {
912 1752 50 33     3249 carp "$class: Unknown import argument '$arg'";
913             }
914             }
915             }
916              
917 0     0   0 # XXX: This is undocumented for now...
918             #
919 0         0 # =item B<import_methods NAME1 [, NAME2, ...]>
920 0 0       0 #
921             # Import methods from the named class (the invocant) into the current class.
922 0         0 # This works by searching the class hierarchy, starting from the invocant class,
923 0         0 # and using a breadth-first search. When an existing method with the requested
924 0         0 # NAME is found, it is aliased into the current (calling) package. If a method
925             # of the desired name is not found, a fatal error is thrown.
926             #
927             # This is a somewhat evil hack that i used internally to get around some
928             # inconvenient consequences of multiple inheritence and its interaction with
929 0   0 0   0 # Perl's default left-most depth-first method dispatch.
930 43     43   293 #
  43         96  
  43         3977  
931 0         0 # This method is an implementation detail and is not part of the public "user"
  0         0  
932             # API. It is described here for the benefit of those who are subclassing
933             # L<Rose::HTML::Object> and who also may find themselves in a bit of a multiple
934             # inheritence bind.
935             #
936 1     1 0 1311 # Example:
937             #
938 1         2 # package MyTag;
939             #
940 1 50       5 # use base 'SomeTag';
941             #
942 43     43   498 # use MyOtherTag;
  43         101  
  43         11362  
943 2         7 #
944 2 50       9 # # Do a bredth-first search, starting in the class MyOtherTag,
945 2     0   13 # # for methods named 'foo' and 'bar', and alias them into
  0         0  
946 2         4 # # this package (MyTag)
947             # MyOtherTag->import_methods('foo', 'bar');
948              
949 1         4 # If method dispatch was breadth-first, I probably wouldn't need this...
950             {
951             my($this_class) = shift;
952              
953             my $options = ref $_[0] && ref $_[0] eq 'HASH' ? shift : {};
954 275     275   4042  
955             my $target_class = $options->{'target_class'} || (caller)[0];
956 275         21219  
957             my(@search_classes, @parents);
958 3 50       16  
959             @parents = ($this_class);
960 3         33  
961             while(my $class = shift(@parents))
962             {
963             push(@search_classes, $class);
964              
965             no strict 'refs';
966             foreach my $subclass (@{$class . '::ISA'})
967             {
968             push(@parents, $subclass);
969             }
970             }
971              
972 0         0 my %methods;
973              
974             foreach my $arg (@_)
975             {
976             if(ref $arg eq 'HASH')
977             {
978             $methods{$_} = $arg->{$_} for(keys %$arg);
979             }
980             else
981             {
982             $methods{$arg} = $arg;
983             }
984             }
985              
986             METHOD: while(my($method, $import_as) = each(%methods))
987             {
988             no strict 'refs';
989             foreach my $class (@search_classes)
990             {
991             if(defined &{$class . '::' . $method})
992             {
993             #print STDERR "${target_class}::$import_as = ${class}::$method\n";
994             *{$target_class . '::' . $import_as} = \&{$class . '::' . $method};
995             next METHOD;
996             }
997             }
998              
999             Carp::croak "Could not find method '$method' in any subclass of $this_class";
1000             }
1001             }
1002              
1003              
1004             {
1005             my($self) = $_[0];
1006              
1007             if(my $class = ref($self))
1008             {
1009             my $name = $AUTOLOAD;
1010             $name =~ s/.*://;
1011              
1012 46     46 0 114 if($class->html_attr_is_valid($name) && $class->autoload_html_attr_methods)
1013             {
1014 46 100 66     226 no strict 'refs';
1015             *$AUTOLOAD = sub { shift->html_attr($name, @_) };
1016 46   66     274 ${$class . '::__AUTOLOADED'}{$name} = 1;
1017             goto &$AUTOLOAD;
1018 46         103 }
1019              
1020 46         105 confess
1021             qq(Can't locate object method "$name" via package "$class" - ) .
1022 46         135 ($class->html_attr_is_valid($name) ?
1023             "did not auto-create method because $class->autoload_html_attr_methods is not set" :
1024 285         417 "no such method, and none auto-created because it is not a valid HTML attribute for this class");
1025             }
1026 43     43   309 else
  43         92  
  43         4951  
1027 285         307 {
  285         797  
1028             my $name = $AUTOLOAD;
1029 239         519 $name =~ s/.*://;
1030              
1031             confess qq(Can't locate class method "$name" via package "$self");
1032             }
1033 46         75 }
1034              
1035 46         90 1;
1036              
1037 210 50       311  
1038             =head1 NAME
1039 0         0  
1040             Rose::HTML::Object - HTML object base class.
1041              
1042             =head1 SYNOPSIS
1043 210         390  
1044             #
1045             # Generic HTML construction
1046             #
1047 46         197  
1048             $o = Rose::HTML::Object->new('p');
1049 43     43   319 $o->push_child('Hi');
  43         94  
  43         8828  
1050 210         289  
1051             print $o->html; # <p>hi</p>
1052 222 100       248  
  222         678  
1053             $br = Rose::HTML::Object->new(element => 'br', is_self_closing => 1);
1054              
1055 210         244 print $br->html; # <br>
  210         748  
  210         416  
1056 210         726 print $br->xhtml; # <br />
1057              
1058             $o->unshift_children($br, ' '); # add two children
1059              
1060 0         0 print $o->html; # <p><br> Hi</p>
1061              
1062             $b = Rose::HTML::Object->new('body', children => $o);
1063              
1064       0     print $b->html; # <body><p><br> Hi</p></body>
1065              
1066             foreach my $object ($b->descendants)
1067             {
1068 135     135   6702 ...
1069             }
1070 135 50       418  
1071             $d = Rose::HTML::Object->new('div', class => 'x');
1072 135         230  
1073 135         865 $b->child(0)->parent($d); # re-parent: $o now belongs to $d
1074              
1075 135 100 100     424 print $b->html; # <body></body>
1076             print $d->html; # <div class="x"><p><br> Hi</p></div>
1077 43     43   320  
  43         139  
  43         8496  
1078 132     1246   7581 #
  1246         6683  
1079 132         258 # Subclass to add strictures
  132         511  
1080 132         470 #
1081              
1082             package MyTag;
1083              
1084 3 100       54 use base 'Rose::HTML::Object';
1085              
1086             __PACKAGE__->add_valid_html_attrs
1087             (
1088             'foo',
1089             'bar',
1090             'baz',
1091 0         0 ...
1092 0         0 );
1093              
1094 0         0 __PACKAGE__->add_required_html_attrs(
1095             {
1096             foo => 5, # with default value
1097             goo => '', # required implies valid
1098             });
1099              
1100             __PACKAGE__->add_boolean_html_attrs
1101             (
1102             'selected', # boolean implies valid
1103             );
1104              
1105             sub html_element { 'mytag' }
1106             sub xhtml_element { 'mytag' }
1107              
1108             ...
1109              
1110             my $o = MyTag->new(bar => 'hello', selected => 1);
1111              
1112             # prints: bar="hello" foo="5" goo="" selected
1113             print $o->html_attrs_string;
1114              
1115             # prints: bar="hello" foo="5" goo="" selected="selected"
1116             print $o->xhtml_attrs_string;
1117              
1118             $o->html_attr(selected => 0);
1119              
1120             print "Has bar\n" if($o->html_attr_exists('bar'));
1121             $o->delete_html_attr('bar');
1122              
1123             $o->is_self_closing(1);
1124              
1125             print $o->html_tag; # <mytag foo="5" goo="">
1126             print $o->xhtml_tag; # <mytag foo="5" goo="" />
1127             ...
1128              
1129             =head1 DESCRIPTION
1130              
1131             L<Rose::HTML::Object> is the base class for HTML objects. It defines the HTML element name, provides methods for specifying, manipulating, and validating HTML attributes, and can serialize itself as either HTML or XHTML.
1132              
1133             This class inherits from, and follows the conventions of, L<Rose::Object>. See the L<Rose::Object> documentation for more information.
1134              
1135             =head1 HIERARCHY
1136              
1137             Each L<Rose::HTML::Object> may have zero or more L<children|/children>, each of which is another L<Rose::HTML::Object> (or L<Rose::HTML::Object>-derived) object. The L<html|/html> produced for an object will include the HTML for all of its L<descendants|/descendants>.
1138              
1139             =head1 VALIDATION
1140              
1141             Although several methods, data structures, and policies exist to aid the creation of valid HTML, they are in no way a replacement for real markup validation.
1142              
1143             This class and those that inherit from it try to support a superset of the elements and attributes specified in the HTML 4.01 and XHTML 1.x specifications. As a result, these classes will tend to be more permissive than actual validation. The support of these standards is not exhaustive, and will inevitably expand. Also remember that there are several variant DTDs that make up XHTML 1.x. By trying to support a superset of these standards, this class can't correctly enforce the rules of any individual standard.
1144              
1145             So I say again: these classes are not a replacement for real markup validation. Use an external validator.
1146              
1147             Going forward, the compatibility policy of these classes is that attribute specifications may be added in the future, but existing attribute specifications will never be removed (unless they originally existed in error, i.e., were never part of any HTML 4.01 or XHTML 1.x standard).
1148              
1149             This support policy is pragmatic rather than ideological. There is enough default validation to catch most typos or other unintentional errors, but not so much that the entire class hierarchy is weighed down by language lawyering and bookkeeping.
1150              
1151             If the runtime overhead of validating every HTML attribute is deemed too onerous, it can be turned off on a per-object basis with the L<validate_html_attrs|/validate_html_attrs> method. Subclasses can set this attribute during object construction to make the effect class-wide. (You will also want to look at the L<autoload_html_attr_methods|/autoload_html_attr_methods> class attribute.)
1152              
1153             There are also methods for adding and removing valid, required, and boolean HTML attributes for a class.
1154              
1155             Finally, all element and attribute names are case-sensitive and lowercase in order to comply with XHTML (and to be easy to type).
1156              
1157             =head1 CLASS METHODS
1158              
1159             These class methods can be called with a class name or an object as the invocant. Either way, remember that the data structures and attributes affected are part of the class as a whole, not any individual object. For example, adding a valid HTML attribute makes it valid for all objects of the class, including any objects that already exist.
1160              
1161             Many of the class methods manipulate "inheritable sets," "inherited sets," or "inherited hashes." See the L<Rose::Class::MakeMethods::Set> and L<Rose::Class::MakeMethods::Generic|Rose::Class::MakeMethods::Generic/inherited_hash> documentation for an explanation of these method types.
1162              
1163             The sets of valid and boolean HTML attributes are "inherited sets." The set of required HTML attributes is an "inheritable set." The L<object_type_classes|/object_type_classes> map is an "inherited hash."
1164              
1165             The inheritance behavior of these sets is noted here in order to facilitate subclassing. But it is an implementation detail, not a part of the public API. The requirements of the APIs themselves do not include any particular inheritance behavior.
1166              
1167             =over 4
1168              
1169             =item B<add_boolean_html_attr NAME>
1170              
1171             Adds a value to the list of boolean HTML attributes for this class. Boolean HTML attributes appear without values in HTML tags, (e.g., <dl compact>) or with fixed values in XHTML tags (e.g., <dl compact="compact">)
1172              
1173             =item B<add_boolean_html_attrs NAME1, NAME2, ...>
1174              
1175             Adds one or more values to the list of boolean HTML attributes for this class. Boolean HTML attributes appear without values in HTML tags, (e.g., <dl compact>) or with fixed values in XHTML tags (e.g., <dl compact="compact">)
1176              
1177             =item B<add_object_type_classes [MAP]>
1178              
1179             Add entries to the L<object_type_classes|/object_type_classes> hash that maps object type strings to the names of the L<Rose::HTML::Object>-derived classes. Example:
1180              
1181             My::HTML::Form->add_object_type_classes
1182             (
1183             blockquote => 'My::HTML::Blockquote',
1184             abbr => 'My::HTML::Abbr',
1185             ...
1186             );
1187              
1188             =item B<add_required_html_attr NAME [, DEFAULT]>
1189              
1190             Adds a value to the list of required HTML attributes for this class. Required HTML attributes will always appear in the HTML tag, with or without a non-empty value. You can set the default value for a required HTML attribute using the L<required_html_attr_value|/required_html_attr_value> method or by passing the DEFAULT parameter to this method.
1191              
1192             =item B<add_required_html_attrs NAME1, NAME2, ... | HASHREF>
1193              
1194             Adds one or more values to the list of required HTML attributes for this class. Required HTML attributes will always appear in the HTML tag, with or without a non-empty value. You can set the default value for a required HTML attribute using the L<required_html_attr_value|/required_html_attr_value> method or by passing a reference to a hash containing name/default pairs.
1195              
1196             =item B<add_valid_html_attr NAME>
1197              
1198             Adds a value to the list of valid HTML attributes for this class. If the object property C<validate_html_attrs> is true, then only valid attributes can be added to an object of this class.
1199              
1200             =item B<add_valid_html_attrs NAME1, NAME2, ...>
1201              
1202             Adds one or more values to the list of valid HTML attributes for this class. If the object property C<validate_html_attrs> is true, then only valid attributes can be added to an object of this class.
1203              
1204             =item B<autoload_html_attr_methods [BOOL]>
1205              
1206             Get or set the boolean flag that determines whether or not any valid HTML attribute can be used as a method call of the same name. The default is true, and the value is inherited by subclasses unless overridden.
1207              
1208             In the case of a name conflict, the existing method is called and a new method is not auto-created for the HTML attribute of the same name.
1209              
1210             Examples:
1211              
1212             MyTag->add_valid_html_attrs('foo', 'bar', 'error');
1213              
1214             $o = MyTag->new;
1215              
1216             # Auto-created method, equivalent to $o->html_attr(foo => 5)
1217             $o->foo(5);
1218              
1219             # Fatal error: invalid HTML attribute and not an existing method
1220             print $o->blah;
1221              
1222             MyTag->autoload_html_attr_methods(0); # stop autoloading
1223              
1224             # Fatal error: method does not exist and was never auto-created
1225             print $o->bar;
1226              
1227             # This still works: once the method is auto-created, it stays
1228             print $o->foo; # prints "5"
1229              
1230             # Calls the existing error() object method; does not affect
1231             # the HTML attribute named "error"
1232             $o->error(99);
1233              
1234             Yes, the existence of this capability means that adding a method to a future version of a L<Rose::HTML::Object>-derived class that has the same name as a valid HTML attribute may cause older code that calls the auto-created method of the same name to break.
1235              
1236             To avoid this, you can choose not to use any auto-created methods, opting instead to use the L<html_attr|/html_attr> method everywhere (and you can set C<autoload_html_attr_methods> to false to make sure that you don't accidentally use such a method).
1237              
1238             =item B<boolean_html_attrs>
1239              
1240             Returns a reference to a sorted list of boolean HTML attributes in scalar context, or a sorted list of boolean HTML attributes in list context. The default set of boolean HTML attributes is empty.
1241              
1242             See the introduction to the L<"CLASS METHODS"> section for more information about the "inherited set" implementation used by the set of boolean HTML attributes.
1243              
1244             =item B<default_html_attr_value NAME [, VALUE]>
1245              
1246             Returns the default value for the HTML attribute NAME.
1247              
1248             If passed both an attribute NAME and a VALUE, it adds NAME to the set of required HTML attributes and sets its default value to VALUE.
1249              
1250             =item B<default_locale [LOCALE]>
1251              
1252             Get or set the default L<locale|Rose::HTML::Object::Message::Localizer/LOCALES> for this class. The default value C<en>.
1253              
1254             =item B<default_localizer [LOCALIZER]>
1255              
1256             Get or set the default L<Rose::HTML::Object::Message::Localizer>-derived localizer object. Defaults to a new L<Rose::HTML::Object::Message::Localizer>-derived object.
1257              
1258             =item B<delete_boolean_html_attr NAME>
1259              
1260             Removes the HTML attribute NAME from the set of boolean HTML attributes.
1261              
1262             =item B<delete_object_type_class TYPE>
1263              
1264             Delete the type/class L<mapping|/object_type_classes> entry for the object type TYPE.
1265              
1266             =item B<delete_required_html_attr NAME>
1267              
1268             Removes the HTML attribute NAME from the set of required HTML attributes.
1269              
1270             =item B<delete_valid_html_attr NAME>
1271              
1272             Removes the HTML attribute NAME from the set of valid HTML attributes. The attribute is also removed from the set of required and boolean HTML attributes, if it existed in either set.
1273              
1274             =item B<html_attr_is_boolean NAME>
1275              
1276             Returns a boolean value indicating whether or not the attribute NAME is a boolean HTML attribute. A boolean attribute must also be a valid attribute.
1277              
1278             =item B<html_attr_is_required NAME>
1279              
1280             Returns a boolean value indicating whether or not the attribute NAME is a required HTML attribute. A required attribute must also be a valid attribute.
1281              
1282             =item B<html_attr_is_valid NAME>
1283              
1284             Returns a boolean value indicating whether or not the attribute NAME is a valid HTML attribute.
1285              
1286             =item B<load_all_messages>
1287              
1288             Ask the L<localizer|/localizer> to L<load_all_messages|Rose::HTML::Object::Message::Localizer/load_all_messages> from this class.
1289              
1290             =item B<locale [LOCALE]>
1291              
1292             This method may be called as a class method or an object method.
1293              
1294             When called as a class method and a L<LOCALE|Rose::HTML::Object::Message::Localizer/LOCALES> is passed, then the L<default_locale|/default_locale> is set. When called as an object method and a L<LOCALE|Rose::HTML::Object::Message::Localizer/LOCALES> is passed, then the L<locale|Rose::HTML::Object::Message::Localizer/LOCALES> of this object is set.
1295              
1296             If no locale is set for this class (when called as a class method) then the L<localizer|/localizer>'s L<locale|Rose::HTML::Object::Message::Localizer/locale> is returned, if it is set. Otherwise, the L<default_locale|/default_locale> is returned.
1297              
1298             If no locale is set for this object (when called as an object method), then the the first defined locale from the object's L<parent_group|Rose::HTML::Form::Field/parent_group>, L<parent_field|Rose::HTML::Form::Field/parent_field>, L<parent_form|Rose::HTML::Form::Field/parent_form>, or generic L<parent|/parent> is returned. If none of those locales are defined, then the L<localizer|/localizer>'s L<locale|Rose::HTML::Object::Message::Localizer/locale> is returned, if it is set. Otherwise, the L<default_locale|/default_locale> is returned.
1299              
1300             =item B<object_type_class TYPE [, CLASS]>
1301              
1302             Given the object type string TYPE, return the name of the L<Rose::HTML::Object>-derived class mapped to that name. If a CLASS is passed, the object type TYPE is mapped to CLASS.
1303              
1304             This map of type names to classes is an L<inherited hash|Rose::Class::MakeMethods::Generic/inherited_hash> representing the union of the hashes of all superclasses, minus any keys that are explicitly L<deleted|/delete_object_type_class> in the current class.
1305              
1306             =item B<object_type_classes [MAP]>
1307              
1308             Get or set the hash that maps object type strings to the names of the L<Rose::HTML::Object>-derived classes.
1309              
1310             If passed MAP (a list of type/class pairs or a reference to a hash of the same) then MAP replaces the current object type mapping. Returns a list of type/class pairs (in list context) or a reference to a hash of type/class mappings (in scalar context).
1311              
1312             This map of type names to classes is an L<inherited hash|Rose::Class::MakeMethods::Generic/inherited_hash> representing the union of the hashes of all superclasses, minus any keys that are explicitly L<deleted|/delete_object_type_class> in the current class.
1313              
1314             The default mapping of type names to class names is:
1315              
1316             'image' => Rose::HTML::Image
1317             'label' => Rose::HTML::Label
1318             'link' => Rose::HTML::Link
1319             'script' => Rose::HTML::Script
1320             'literal text' => Rose::HTML::Text
1321              
1322             'form' => Rose::HTML::Form
1323             'repeatable form' => Rose::HTML::Form::Repeatable
1324              
1325             'text' => Rose::HTML::Form::Field::Text
1326             'scalar' => Rose::HTML::Form::Field::Text
1327             'char' => Rose::HTML::Form::Field::Text
1328             'character' => Rose::HTML::Form::Field::Text
1329             'varchar' => Rose::HTML::Form::Field::Text
1330             'string' => Rose::HTML::Form::Field::Text
1331              
1332             'text area' => Rose::HTML::Form::Field::TextArea
1333             'textarea' => Rose::HTML::Form::Field::TextArea
1334             'blob' => Rose::HTML::Form::Field::TextArea
1335              
1336             'option' => Rose::HTML::Form::Field::Option
1337             'option group' => Rose::HTML::Form::Field::OptionGroup
1338              
1339             'checkbox' => Rose::HTML::Form::Field::Checkbox
1340             'check' => Rose::HTML::Form::Field::Checkbox
1341              
1342             'radio button' => Rose::HTML::Form::Field::RadioButton
1343             'radio' => Rose::HTML::Form::Field::RadioButton
1344              
1345             'checkboxes' => Rose::HTML::Form::Field::CheckboxGroup
1346             'checks' => Rose::HTML::Form::Field::CheckboxGroup
1347             'checkbox group' => Rose::HTML::Form::Field::CheckboxGroup
1348             'check group' => Rose::HTML::Form::Field::CheckboxGroup
1349              
1350             'radio buttons' => Rose::HTML::Form::Field::RadioButtonGroup
1351             'radios' => Rose::HTML::Form::Field::RadioButtonGroup
1352             'radio button group' => Rose::HTML::Form::Field::RadioButtonGroup
1353             'radio group' => Rose::HTML::Form::Field::RadioButtonGroup
1354              
1355             'pop-up menu' => Rose::HTML::Form::Field::PopUpMenu
1356             'popup menu' => Rose::HTML::Form::Field::PopUpMenu
1357             'menu' => Rose::HTML::Form::Field::PopUpMenu
1358              
1359             'select box' => Rose::HTML::Form::Field::SelectBox
1360             'selectbox' => Rose::HTML::Form::Field::SelectBox
1361             'select' => Rose::HTML::Form::Field::SelectBox
1362              
1363             'submit' => Rose::HTML::Form::Field::Submit
1364             'submit button' => Rose::HTML::Form::Field::Submit
1365              
1366             'reset' => Rose::HTML::Form::Field::Reset
1367             'reset button' => Rose::HTML::Form::Field::Reset
1368              
1369             'file' => Rose::HTML::Form::Field::File
1370             'upload' => Rose::HTML::Form::Field::File
1371              
1372             'password' => Rose::HTML::Form::Field::Password
1373              
1374             'hidden' => Rose::HTML::Form::Field::Hidden
1375              
1376             'num' => Rose::HTML::Form::Field::Numeric
1377             'number' => Rose::HTML::Form::Field::Numeric
1378             'numeric' => Rose::HTML::Form::Field::Numeric
1379              
1380             'int' => Rose::HTML::Form::Field::Integer
1381             'integer' => Rose::HTML::Form::Field::Integer
1382              
1383             'email' => Rose::HTML::Form::Field::Email
1384              
1385             'phone' => Rose::HTML::Form::Field::PhoneNumber::US
1386             'phone us' => Rose::HTML::Form::Field::PhoneNumber::US
1387              
1388             'phone us split' =>
1389             Rose::HTML::Form::Field::PhoneNumber::US::Split
1390              
1391             'set' => Rose::HTML::Form::Field::Set
1392              
1393             'time' => Rose::HTML::Form::Field::Time
1394              
1395             'time split hms' =>
1396             Rose::HTML::Form::Field::Time::Split::HourMinuteSecond
1397              
1398             'time hours' => Rose::HTML::Form::Field::Time::Hours
1399             'time minutes' => Rose::HTML::Form::Field::Time::Minutes
1400             'time seconds' => Rose::HTML::Form::Field::Time::Seconds
1401              
1402             'date' => Rose::HTML::Form::Field::Date
1403             'datetime' => Rose::HTML::Form::Field::DateTime
1404              
1405             'datetime range' => Rose::HTML::Form::Field::DateTime::Range
1406              
1407             'datetime start' => Rose::HTML::Form::Field::DateTime::StartDate
1408             'datetime end' => Rose::HTML::Form::Field::DateTime::EndDate
1409              
1410             'datetime split mdy' =>
1411             Rose::HTML::Form::Field::DateTime::Split::MonthDayYear
1412              
1413             'datetime split mdyhms' =>
1414             Rose::HTML::Form::Field::DateTime::Split::MDYHMS
1415              
1416             =item B<required_html_attrs>
1417              
1418             Returns a reference to a sorted list of required HTML attributes in scalar context, or a sorted list of required HTML attributes in list context. The default set of required HTML attributes is empty.
1419              
1420             Required HTML attributes are included in the strings generated by the L<html_attrs_string|/html_attrs_string> and L<xhtml_attrs_string|/xhtml_attrs_string> methods, even if they have been deleted using the L<delete_html_attr|/delete_html_attr> method or one of its variants. If a required HTML attribute does not have a default value, its value defaults to an empty string or, if the attribute is also boolean, the name of the attribute.
1421              
1422             See the introduction to the L<"CLASS METHODS"> section for more information about the "inheritable set" implementation used by the set of boolean HTML attributes.
1423              
1424             =item B<required_html_attr_value ATTR [, VALUE]>
1425              
1426             Get or set the default value of the required HTML attrbute ATTR. If both ATTR and VALUE are passed, the value is set. The current value is returned.
1427              
1428             =item B<valid_html_attrs>
1429              
1430             Returns a reference to a sorted list of valid HTML attributes in scalar context, or a sorted list of valid HTML attributes in list context. The default set is:
1431              
1432             id
1433             class
1434             style
1435             title
1436             lang
1437             xml:lang
1438             dir
1439             onclick
1440             ondblclick
1441             onmousedown
1442             onmouseup
1443             onmouseover
1444             onmousemove
1445             onmouseout
1446             onkeypress
1447             onkeydown
1448             onkeyup
1449              
1450             See the L<"VALIDATION"> section for more on the philosophy and policy of validation. See the introduction to the L<"CLASS METHODS"> section for more information about the "inherited set" implementation used by the set of valid HTML attributes.
1451              
1452             =item B<xhtml_element [NAME]>
1453              
1454             Get or set the name of the XHTML element. The XHTML element is the name of the tag, e.g. "img", "p", "a", "select", "textarea", etc.
1455              
1456             This attribute may be read-only in subclasses, but is read/write here for increased flexibility. The value is inherited by subclasses.
1457              
1458             =back
1459              
1460             =head1 CONSTRUCTOR
1461              
1462             =over 4
1463              
1464             =item B<new [ PARAMS | ELEMENT, PARAMS ]>
1465              
1466             Constructs a new L<Rose::HTML::Object> object. If an odd number of arguments is passed, the first argument is taken as the value for the L<element|/element> parameter. Otherwise an even number of PARAMS name/value pairs are expected. Any object method is a valid parameter name.
1467              
1468             =back
1469              
1470             =head1 OBJECT METHODS
1471              
1472             =over 4
1473              
1474             =item B<add_child OBJECT>
1475              
1476             This is an alias for the L<push_child|/push_child> method.
1477              
1478             =item B<add_children OBJECTS>
1479              
1480             This is an alias for the L<push_children|/push_children> method.
1481              
1482             =item B<child INT>
1483              
1484             Returns the L<child|/children> at the index specified by INT. The first child is at index zero (0).
1485              
1486             =item B<children [LIST]>
1487              
1488             Get or set the list of L<Rose::HTML::Object>-derived objects that are contained within, or otherwise "children of" this object. Any plain scalar in LIST is converted to a L<Rose::HTML::Text> object, with the scalar used as the value of the L<text|Rose::HTML::Text/text> attribute.
1489              
1490             Returns a list (in list context) or a reference to an array (in scalar context) of L<Rose::HTML::Object>-derived objects. The array reference return value should be treated as read-only. The individual items may be treated as read/write provided that you understand that you're modifying the actual children, not copies.
1491              
1492             =item B<clear_all_html_attrs>
1493              
1494             Clears all the HTML attributes by settings their values to undef.
1495              
1496             =item B<clear_html_attr NAME>
1497              
1498             Clears the HTML attribute NAME by settings its value to undef.
1499              
1500             =item B<clear_html_attrs NAME1, NAME2, ...>
1501              
1502             Clears the HTML attributes specified by NAME1, NAME2, etc. by settings their values to undef.
1503              
1504             =item B<delete_all_html_attrs>
1505              
1506             Deletes all the HTML attributes.
1507              
1508             =item B<delete_child [ INDEX | OBJECT ]>
1509              
1510             Delete the L<child|/child> at INDEX (starting from zero) or the exact child OBJECT.
1511              
1512             =item B<delete_children>
1513              
1514             Deletes all L<children|/children>.
1515              
1516             =item B<delete_html_attr NAME>
1517              
1518             Deletes the HTML attribute NAME.
1519              
1520             =item B<delete_html_attrs NAME1, NAME2, ...>
1521              
1522             Deletes the HTML attributes specified by NAME1, NAME2, etc.
1523              
1524             =item B<descendants>
1525              
1526             Returns a list of the L<children|/children> of this object, plus all their children, and so on.
1527              
1528             =item B<element [NAME]>
1529              
1530             If passed a NAME, sets both L<html_element|/html_element> and L<xhtml_element|/xhtml_element> to NAME. Returns L<html_element|/html_element>.
1531              
1532             =item B<error [TEXT]>
1533              
1534             Get or set an error string.
1535              
1536             =item B<error_id [ID [, ARGS]]>
1537              
1538             Get or set an integer L<error|Rose::HTML::Object::Errors> id. When setting the error id, an optional ARGS hash reference should be passed if the L<localized text|Rose::HTML::Object::Message::Localizer/"LOCALIZED TEXT"> for the L<corresponding|/message_for_error_id> message contains any L<placeholders|Rose::HTML::Object::Message::Localizer/"LOCALIZED TEXT">. Example:
1539              
1540             # Set error id, passing args for the label and value placeholders
1541             $obj->error_id(NUM_ABOVE_MAX, { label => $l, => value => $v });
1542              
1543             =item B<escape_html [BOOL]>
1544              
1545             This flag may be used by other methods to decide whether or not to escape HTML. It is set to true by default. The only method in L<Rose::HTML::Object> that references it is L<html_error|/html_error>. All other HTML is escaped as appropriate regardless of the L<escape_html|/escape_html> setting (e.g. the text returned by C<html_attrs_string> always has its attribute values escaped). Subclasses may consult this flag for similar purposes (which they must document, of course).
1546              
1547             =item B<has_child OBJECT>
1548              
1549             Returns true if OBJECT is a L<child|/child> of this object, false otherwise.
1550              
1551             =item B<has_children>
1552              
1553             Returns true if there are any L<children|/child>, false otherwise.
1554              
1555             =item B<has_parent>
1556              
1557             Returns true if this object is the L<child|/child> of another object, false otherwise.
1558              
1559             =item B<has_error>
1560              
1561             Returns true if an L<error|/error> is set, false otherwise.
1562              
1563             =item B<html>
1564              
1565             A synonym for the L<html_tag|/html_tag> method.
1566              
1567             =item B<html_attr NAME [, VALUE]>
1568              
1569             Get or set the HTML attribute NAME. If just NAME is passed, it returns the value of the HTML attribute specified by NAME, or undef if there is no such attribute.
1570              
1571             If both NAME and VALUE are passed, it sets the HTML attribute NAME to VALUE.
1572              
1573             If NAME is not a valid attribute, a fatal error is thrown.
1574              
1575             Examples:
1576              
1577             $o->html_attr(color => 'red'); # set color to red
1578             $color = $o->html_attr('color'); # get color
1579              
1580             =item B<html_attrs [ATTRS]>
1581              
1582             If called with an argument, this method sets and/or adds the HTML attributes specified by ATTRS, where ATTRS is a series of name/value pairs or a reference to a hash of name/value pairs.
1583              
1584             Returns all of the existing HTML attributes as a hash (in list context) or a reference to a hash (in scalar context).
1585              
1586             Note that the reference returned in scalar context is a reference to the object's actual hash of attributes; modifying it will change the state of the object! I recommend that you treat the contents of the referenced hash as read-only, and I cannot promise that I will not find a way to force it to be read-only in the future.
1587              
1588             The order of the attributes in the return value is indeterminate.
1589              
1590             Examples:
1591              
1592             # Set/add attributes
1593             $o->html_attrs(color => 'red', age => 5); # name/value pairs
1594             $o->html_attrs({ style => fancy }); # hashref
1595              
1596             %h = $o->html_attrs; # get all three attributes as a hash
1597             $h = $o->html_attrs; # get all three attributes as a hash ref
1598              
1599             =item B<html_attrs_string>
1600              
1601             If there are any HTML attributes, it returns a sorted list of HTML attributes and their values in a string suitable for use in an HTML tag. The string includes a leading space.
1602              
1603             If there are no HTML attributes, an empty string is returned.
1604              
1605             Examples:
1606              
1607             MyTag->add_valid_html_attrs('color', 'age');
1608             MyTag->add_boolean_html_attr('happy');
1609              
1610             $o = MyTag->new;
1611              
1612             $o->html_attrs(color => 'red<', age => 5, happy => 12345);
1613              
1614             $s = $o->html_attrs_string; # ' age="5" color="red&lt;" happy'
1615              
1616             =item B<html_attr_hook NAME [, CODE]>
1617              
1618             If called with two arguments, it sets the hook method for the attribute NAME to the code reference CODE.
1619              
1620             If called with one or two arguments, it returns the hook method for the HTML attribute NAME as a code reference, or undef if there is no hook method.
1621              
1622             Hook methods are called whenever their corresponding HTML attribute is set or retrieved. When the attribute is set, the hook method gets the proposed value of the attribute as an argument. The return value of the hook method is then used as the actual value of the attribute.
1623              
1624             When an attribute is retrieved, the hook method is called with no arguments, and its return value is what is actually returned to the caller.
1625              
1626             In both cases, the default variable C<$_> is localized and then set to the new or existing value of the attribute before the hook method is called.
1627              
1628             Examples:
1629              
1630             # Set hook for 'color' attribute
1631             $o->html_attr_hook(color => sub
1632             {
1633             my($self) = shift;
1634              
1635             if(@_) # attribute is being set
1636             {
1637             return uc shift; # make it uppercase
1638             }
1639              
1640             # attribute being retrieved:
1641             return $_; # return the existing attribute value as-is
1642             });
1643              
1644             $o->html_attr(color => 'red'); # color set to 'RED'
1645             $color = $o->html_attr('color'); # $color = 'RED'
1646              
1647             =item B<html_element [NAME]>
1648              
1649             Get or set the name of the HTML element. The HTML element is the name of the tag, e.g. "img", "p", "a", "select", "textarea", etc.
1650              
1651             This attribute may be read-only in subclasses.
1652              
1653             =item B<html_error>
1654              
1655             Returns the error text, if any, as a snippet of HTML that looks like this:
1656              
1657             <span class="error">Error text goes here</span>
1658              
1659             If the L<escape_html|/escape_html> flag is set to true (the default), then the error text has any HTML in it escaped.
1660              
1661             =item B<html_tag>
1662              
1663             Serializes the object as an HTML tag. In other words, it is the concatenation of the strings returned by the L<html_element|/html_element> and L<html_attrs_string|/html_attrs_string> methods, wrapped with the appropriate angled brackets.
1664              
1665             =item B<is_self_closing [BOOL]>
1666              
1667             Get or set a boolean attribute that determines whether or not the HTML for this object requires a separate closing tag. If set to true, then an empty "foo" tag would look like this:
1668              
1669             HTML: <foo>
1670             XHTML: <foo />
1671              
1672             If false, then the tags above would look like this instead:
1673              
1674             HTML: <foo></foo>
1675             XHTML: <foo></foo>
1676              
1677             The default value is false. This attribute may be read-only in subclasses.
1678              
1679             =item B<locale [LOCALE]>
1680              
1681             This method may be called as a class method or an object method.
1682              
1683             When called as an object method and a L<LOCALE|Rose::HTML::Object::Message::Localizer/LOCALES> is passed, then the L<locale|Rose::HTML::Object::Message::Localizer/LOCALES> of this object is set. When called as a class method and a L<LOCALE|Rose::HTML::Object::Message::Localizer/LOCALES> is passed, then the L<default_locale|/default_locale> is set.
1684              
1685             If no locale is set for this object (when called as an object method), then the the first defined locale from the object's L<parent_group|Rose::HTML::Form::Field/parent_group>, L<parent_field|Rose::HTML::Form::Field/parent_field>, L<parent_form|Rose::HTML::Form::Field/parent_form>, or generic L<parent|/parent> is returned. If none of those locales are defined, then the L<localizer|/localizer>'s L<locale|Rose::HTML::Object::Message::Localizer/locale> is returned, if it is set. Otherwise, the L<default_locale|/default_locale> is returned.
1686              
1687             If no locale is set for this class (when called as a class method) then the L<localizer|/localizer>'s L<locale|Rose::HTML::Object::Message::Localizer/locale> is returned, if it is set. Otherwise, the L<default_locale|/default_locale> is returned.
1688              
1689             =item B<localizer [LOCALIZER]>
1690              
1691             Get or set the L<Rose::HTML::Object::Message::Localizer>-derived object used to localize message text on behalf of this object. If no localizer is set then the L<default_localizer|/default_localizer> is returned.
1692              
1693             =item B<message_for_error_id [PARAMS]>
1694              
1695             Given an L<error|Rose::HTML::Object::Errors> id, return the corresponding L<message|Rose::HTML::Object::Message::Localizer/message_class> object. The default implementation simply looks for a message with the same integer id as the error. Valid PARAMS name/value pairs are:
1696              
1697             =over 4
1698              
1699             =item B<error_id ID>
1700              
1701             The integer error id. This parameter is required.
1702              
1703             =item B<args HASHREF>
1704              
1705             A reference to a hash of name/value pairs to be used as the L<message arguments|Rose::HTML::Object::Message/args>.
1706              
1707             =back
1708              
1709             =item B<parent [OBJECT]>
1710              
1711             Get or set the parent object.
1712              
1713             =item B<pop_child [INT]>
1714              
1715             Remove an object from the end of the list of L<children|/children> and return it.
1716              
1717             =item B<pop_children [INT]>
1718              
1719             Remove INT objects from the end of the list of L<children|/children> and return them. If INT is ommitted, it defaults to 1.
1720              
1721             =item B<push_child OBJECT>
1722              
1723             Add OBJECT to the end of the list of L<children|/children>. The object must be of or derived from the L<Rose::HTML::Object> class, or a plain scalar. If it's a plain scalar, it will be converted to a L<Rose::HTML::Text> object, with the scalar used as the value of the L<text|Rose::HTML::Text/text> attribute.
1724              
1725             =item B<push_children OBJECT1 [, OBJECT2, ...]>
1726              
1727             Add objects on to the end of the list of L<children|/children>. Each object must be of or derived from the L<Rose::HTML::Object> class, or a plain scalar. All plain scalars will be converted to L<Rose::HTML::Text> objects, with the scalar used as the value of the L<text|Rose::HTML::Text/text> attribute.
1728              
1729             =item B<set_error>
1730              
1731             Set the L<error|/error> to a defined but "invisible" (zero-length) value. This value will not be displayed by the L<html_error|/html_error> or L<xhtml_error|/xhtml_error> methods. Use this method when you want to flag a field as having an error, but don't want a visible error message.
1732              
1733             =item B<shift_child [INT]>
1734              
1735             Remove an object from the start of the list of L<children|/children> and return it.
1736              
1737             =item B<shift_children [INT]>
1738              
1739             Remove INT objects from the start of the list of L<children|/children> and return them. If INT is ommitted, it defaults to 1.
1740              
1741             =item B<unshift_child OBJECT>
1742              
1743             Add OBJECT to the start of the list of L<children|/children>. The object must be of or derived from the L<Rose::HTML::Object> class, or a plain scalar. If it's a plain scalar, it will be converted to a L<Rose::HTML::Text> object, with the scalar used as the value of the L<text|Rose::HTML::Text/text> attribute.
1744              
1745             =item B<unshift_children OBJECT1 [, OBJECT2, ...]>
1746              
1747             Add objects to the start of the list of L<children|/children>. Each object must be of or derived from the L<Rose::HTML::Object> class, or a plain scalar. All plain scalars will be converted to L<Rose::HTML::Text> objects, with the scalar used as the value of the L<text|Rose::HTML::Text/text> attribute.
1748              
1749             =item B<unset_error>
1750              
1751             Set the L<error|/error> to a undef.
1752              
1753             =item B<validate_html_attrs BOOL>
1754              
1755             If set to true, HTML attribute arguments to C<html_attr> and C<html_attr_hook> will be validated by calling C<html_attr_is_valid(ATTR)>, where ATTR is the name of the attribute being set or read. The default value is true for any class derived from L<Rose::HTML::Object>, but false for objects whose class is L<Rose::HTML::Object>.
1756              
1757             =item B<xhtml>
1758              
1759             A synonym for the L<xhtml_tag|/xhtml_tag> method.
1760              
1761             =item B<xhtml_element [NAME]>
1762              
1763             Get or set the name of the XHTML element. The XHTML element is the name of the tag, e.g. "img", "p", "a", "select", "textarea", etc.
1764              
1765             This attribute may be read-only in subclasses.
1766              
1767             =item B<xhtml_error>
1768              
1769             Returns the error text, if any, as a snippet of XHTML that looks like this:
1770              
1771             <span class="error">Error text goes here</span>
1772              
1773             If the L<escape_html|/escape_html> flag is set to true (the default), then the error text has any HTML in it escaped.
1774              
1775             =item B<xhtml_tag>
1776              
1777             Serializes the object as an XHTML tag. In other words, it is the concatenation of the strings returned by the L<xhtml_element|/xhtml_element> and L<xhtml_attrs_string|/xhtml_attrs_string> methods, wrapped with the appropriate angled brackets and forward slash character.
1778              
1779             =item B<xhtml_attrs_string>
1780              
1781             If there are any HTML attributes, it returns a sorted list of HTML attributes and their values in a string suitable for use in an XHTML tag. The string includes a leading space.
1782              
1783             If there are no HTML attributes, an empty string is returned.
1784              
1785             Examples:
1786              
1787             MyTag->add_valid_html_attrs('color', 'age');
1788             MyTag->add_boolean_html_attr('happy');
1789              
1790             $o = MyTag->new;
1791              
1792             $o->html_attrs(color => 'red<', age => 5, happy => 12345);
1793              
1794             # ' age="5" color="red&lt;" happy="happy"'
1795             $s = $o->xhtml_attrs_string;
1796              
1797             =back
1798              
1799             =head1 SUPPORT
1800              
1801             Any L<Rose::HTML::Objects> questions or problems can be posted to the L<Rose::HTML::Objects> mailing list. To subscribe to the list or search the archives, go here:
1802              
1803             L<http://groups.google.com/group/rose-html-objects>
1804              
1805             Although the mailing list is the preferred support mechanism, you can also email the author (see below) or file bugs using the CPAN bug tracking system:
1806              
1807             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Rose-HTML-Objects>
1808              
1809             There's also a wiki and other resources linked from the Rose project home page:
1810              
1811             L<http://rosecode.org>
1812              
1813             =head1 AUTHOR
1814              
1815             John C. Siracusa (siracusa@gmail.com)
1816              
1817             =head1 LICENSE
1818              
1819             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.