File Coverage

blib/lib/Rose/HTML/Object.pm
Criterion Covered Total %
statement 365 397 91.9
branch 132 168 78.5
condition 41 56 73.2
subroutine 85 91 93.4
pod 38 58 65.5
total 661 770 85.8


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