File Coverage

blib/lib/Data/Microformat.pm
Criterion Covered Total %
statement 161 163 98.7
branch 46 56 82.1
condition 11 12 91.6
subroutine 19 19 100.0
pod 5 5 100.0
total 242 255 94.9


line stmt bran cond sub pod time code
1             package Data::Microformat;
2              
3 18     18   2343 use strict;
  18         45  
  18         942  
4 18     18   83 use warnings;
  18         75  
  18         1034  
5              
6             our $VERSION = "0.04";
7              
8             our $AUTOLOAD;
9              
10 18     18   17991 use HTML::Entities;
  18         133556  
  18         1940  
11 18     18   27391 use HTML::TreeBuilder;
  18         606639  
  18         230  
12 18     18   23369 use HTML::Stream qw(html_escape);
  18         104269  
  18         1245  
13 18     18   142 use Carp;
  18         32  
  18         32543  
14              
15             sub new
16             {
17 90     90 1 155 my $class = shift;
18 90         171 my %opts = @_;
19 90         114 my $fields = ();
20 90         107 my $singulars = ();
21 90         308 foreach my $field ($class->singular_fields)
22             {
23 474         939 $fields->{$field} = undef;
24 474         13036 $singulars->{$field} = 1;
25             }
26 90         410 foreach my $field ($class->plural_fields)
27             {
28 457         844 $fields->{$field} = undef;
29             }
30            
31 90         336 my $class_name = $class->class_name;
32            
33 90         1089 my $self = bless { _class_name => $class_name, _singulars => $singulars, %$fields, config => {%opts} }, $class;
34 90         505 $self->_init();
35 90         381 return $self;
36             }
37              
38             sub _init
39             {
40 90     90   164 my $self = shift;
41             }
42              
43             sub AUTOLOAD
44             {
45 820     820   12728 my $self = shift;
46 820         968 my $parameter = shift;
47 820 100 100     4656 $parameter =~ s!(^\s*|\s*$)!!g if $parameter && !ref($parameter);
48              
49 820         1657 my $name = $AUTOLOAD;
50 820         2700 $name =~ s/.*://;
51              
52 820 50       2119 unless (exists $self->{$name}) {
53             #warn(ref($self)." does not have a parameter called $name.\n") unless $name =~ m/DESTROY/;
54             # Do nothing here, as there's no need to warn that some parts of hCards aren't valid
55 0         0 return;
56             }
57 820 100       1849 if ($self->{_singulars}{$name}) {
58 520 100 100     2231 $self->{$name} = $parameter if $parameter && (!$self->{_no_dupe_keys} || !defined $self->{$name});
      100        
59 520         2778 return $self->{$name};
60             } else {
61 300 100       588 push @{$self->{$name}}, $parameter if $parameter;
  149         325  
62 300 100       346 my @vals = @{$self->{$name} || []};
  300         1271  
63 300 100       1675 return wantarray? @vals : $vals[0];
64             }
65             }
66              
67             sub parse
68             {
69 30     30 1 430 my $class = shift;
70 30         61 my $content = shift;
71 30         57 my $representative_url = shift;
72            
73             # These few transforms allow us to decode "psychotic" encodings, see t/03type.t for details
74             # $content =~ tr/+/ /;
75             # $content =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg;
76             # $content =~ s///g;
77             # $content = decode_entities($content);
78             # $content =~ s/%([A-F0-9]{2})/pack("C",hex($1))/ieg;
79            
80 30         280 my $tree = HTML::TreeBuilder->new_from_content($content);
81 30         136873 $tree->elementify;
82            
83 30 100       3073 if (wantarray)
84             {
85 5         35 my @ret = $class->from_tree($tree, $representative_url);
86 5         19 $tree->delete;
87 5         358 return @ret;
88             }
89             else
90             {
91 25         923 my $ret = $class->from_tree($tree, $representative_url);
92 25         114 $tree->delete;
93 25         1937 return $ret;
94             }
95             }
96              
97             sub from_tree
98             {
99 19     19 1 33 my $class = shift;
100 19         74 my $tree = shift;
101            
102 19         25 my @objects;
103 19         74 my $class_name = $class->class_name;
104 19         479 my @object_trees = $tree->look_down("class", qr/(^|\s)$class_name($|\s)/);
105            
106 19 50       1729 return unless (@object_trees);
107            
108 19         42 foreach my $object_tree (@object_trees)
109             {
110 19         103 my $object = $class->new;
111 19         98 $object->{_no_dupe_keys} = 1;
112 19         79 my @bits = $object_tree->descendants;
113            
114 19         3101 foreach my $bit (@bits)
115             {
116 79 50       216 next unless $bit->attr('class');
117            
118 79         828 my @types = split(" ", $bit->attr('class'));
119 79         681 foreach my $type (@types)
120             {
121 79         169 $type =~ s/\-/\_/g;
122 79         234 $type = $class->_trim($type);
123 79         232 my @cons = $bit->content_list;
124            
125 79         505 my $data = $class->_trim($cons[0]);
126 79 100 66     228 if ($bit->tag eq "abbr" && $bit->attr('title'))
127             {
128 13         210 $data = $class->_trim($bit->attr('title'));
129             }
130 79         933 $object->$type($data);
131             }
132             }
133 19         37 $object->{_no_dupe_keys} = 0;
134 19         55 push(@objects, $object)
135             }
136              
137 19 50       93 return wantarray? @objects : $objects[0];
138             }
139              
140             sub to_html
141             {
142 11     11 1 21 my $self = shift;
143            
144 11         92 my $tree = $self->_to_hcard_elements;
145 11         76 my $ret = $tree->as_HTML('<>&', "\t", { });
146 11         10838 $tree->delete;
147            
148 11         732 return $ret;
149             }
150              
151              
152             *to_hcard = \&to_html;
153              
154             sub to_text
155             {
156 9     9 1 23 my $self = shift;
157            
158 9         30 my $tree = $self->_to_hcard_elements;
159 9         33 my $ret = _as_text($tree);
160 9         41 $tree->delete;
161            
162 9         686 return $ret;
163             }
164              
165             sub _as_text
166             {
167 41     41   61 my $tree = shift;
168            
169 41 100       2683 if (scalar $tree->descendants == 0)
170             {
171 29         1732 return $tree->attr('class').": ".$tree->as_text;
172             }
173            
174 12         2824 my $ret = $tree->attr('class').": \n";
175            
176 12         230 foreach my $child ($tree->content_list)
177             {
178 32 50       170 next unless (ref($child) =~ m/HTML::Element/);
179 32         126 my $temp = _as_text($child);
180 32 100       923 $temp .= "\n" unless ($temp =~ m/\n$/s);
181 32         3952 $temp =~ s/^/\t/gm;
182 32         256 $ret .= $temp;
183             }
184 12         41 return $ret;
185             }
186              
187             sub _to_hcard_elements
188             {
189 26     26   43 my $self = shift;
190            
191 26         256 my $class_name = $self->{_class_name};
192            
193 26 100       126 if (defined $self->{kind})
194             {
195 7         14 $class_name = $self->{kind};
196             }
197 26         104 my $root = HTML::Element->new('div', class => $class_name);
198 26         774 for my $field ($self->singular_fields)
199             {
200 109 100       1010 next unless defined $self->{$field};
201 67 100       144 next if ($field eq "kind");
202 60 100       135 if (ref($self->{$field}) =~ m/Data::Microformat/)
203             {
204             # Then take the return and root it to our root
205 4         72 my $child = $self->{$field}->_to_hcard_elements;
206 4 50       13 if ($child->attr('class') eq "vcard")
207             {
208 0         0 $child->attr('class', $field." vcard"); # Since we know it's a vcard
209             }
210 4         39 $root->push_content($child);
211             }
212             else
213             {
214 56         83 my $name = $field;
215 56         89 $name =~ tr/_/-/;
216 56         187 my $child = HTML::Element->new('div', class => $name);
217 56         1370 $child->push_content($self->{$field});
218 56         741 $root->push_content($child);
219             }
220             }
221 26         209 for my $field ($self->plural_fields)
222             {
223 82 100       285 next unless defined $self->{$field};
224 12         18 my $name = $field;
225 12         16 $name =~ tr/_/-/;
226 12         22 my $fields = $self->{$field};
227 12         27 foreach my $value (@$fields)
228             {
229 14 100       78 if (ref($value) =~ m/Data::Microformat/)
230             {
231             # Then take the return and root it to our root
232 2         6 my $child = $value->_to_hcard_elements;
233 2 50       6 if ($child->attr('class') eq "vcard")
234             {
235 2         23 $child->attr('class', $field." vcard"); # Since we know it's a vcard
236             }
237 2         24 $root->push_content($child);
238             }
239             else
240             {
241 12         40 my $child = HTML::Element->new('div', class => $name);
242 12         290 $child->push_content($value);
243 12         188 $root->push_content($child);
244             }
245             }
246             }
247 26         135 return $root;
248             }
249              
250             sub _url_decode
251             {
252 46     46   513 my $class = shift;
253 46         63 my $content = shift;
254 46 50       139 return unless defined $content;
255 46         83 $content =~ s/%([\da-f]{2})/chr(hex($1))/eg;
  5         18  
256 46         281 return $content;
257             }
258              
259             sub _trim
260             {
261 615     615   1130 my $class = shift;
262 615         788 my $content = shift;
263 615 100       1218 return unless defined $content;
264 612         4009 $content =~ s/(^\s*|\s*$)//g;
265 612         1652 return $content;
266             }
267              
268             sub _remove_newlines
269             {
270 22     22   40 my $class = shift;
271 22         34 my $content = shift;
272 22 50       55 return unless defined $content;
273 22         42 $content =~ s/[\n\r]/ /g;
274 22         130 return $content;
275             }
276              
277              
278             sub _get_child_html_from_element
279             {
280 8     8   16 my $class = shift;
281 8         13 my $element = shift;
282 8         34 my @list = $element->content_list;
283 8 50       68 return $element->as_text unless @list;
284 8         15 my $out = "";
285 8         15 for my $child (@list) {
286 8 100       76 if (ref($child)) {
287 1         6 $out .= $child->as_HTML(undef,"\t",{});
288             } else {
289 7         33 $out .= $child;
290             }
291             }
292 8         406 return $out;
293             }
294              
295             1;
296              
297             __END__