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