File Coverage

blib/lib/HTML/TableContent/Table.pm
Criterion Covered Total %
statement 170 231 73.5
branch 35 52 67.3
condition n/a
subroutine 42 47 89.3
pod 31 33 93.9
total 278 363 76.5


%s', join( '', @row); %s', $headers; %s', $attr, $header_row; %s', $attr, $row;
line stmt bran cond sub pod time code
1             package HTML::TableContent::Table;
2              
3 20     20   66 use Moo;
  20         690  
  20         1482  
4              
5 20     20   12043 use HTML::TableContent::Table::Caption;
  20         49  
  20         554  
6 20     20   7421 use HTML::TableContent::Table::Header;
  20         338  
  20         472  
7 20     20   7090 use HTML::TableContent::Table::Row;
  20         39  
  20         43792  
8              
9             our $VERSION = '0.17';
10              
11             extends 'HTML::TableContent::Element';
12              
13             has caption => ( is => 'rw', lazy => 1 );
14              
15             has [qw(headers rows)] => (
16             is => 'rw',
17             lazy => 1,
18             default => sub { [] },
19             );
20              
21             has '+html_tag' => (
22             default => 'table',
23             );
24              
25             my @html5 = qw/html5 thead tbody vertical/;
26             for (@html5) {
27             has $_ => (
28             is => 'rw',
29             lazy => 1,
30             builder => 1,
31             );
32             }
33              
34             sub _build_html5 {
35             return defined $_[0]->attributes->{html5}
36             ? delete $_[0]->attributes->{html5}
37 45 50   45   387 : undef;
38             }
39              
40             sub _build_thead {
41             return defined $_[0]->attributes->{thead}
42             ? delete $_[0]->attributes->{thead}
43 0 0   0   0 : undef;
44             }
45              
46             sub _build_tbody {
47             return defined $_[0]->attributes->{tbody}
48             ? delete $_[0]->attributes->{tbody}
49 0 0   0   0 : undef;
50             }
51              
52             sub _build_vertical {
53             return defined $_[0]->attributes->{vertical}
54             ? delete $_[0]->attributes->{vertical}
55 45 50   45   535 : undef;
56             }
57              
58             around raw => sub {
59             my ( $orig, $self ) = ( shift, shift );
60              
61             my $table = $self->$orig(@_);
62              
63             if ( defined $self->caption ) { $table->{caption} = $self->caption->text }
64              
65             my @headers = map { $_->raw } $self->all_headers;
66             $table->{headers} = \@headers;
67              
68             my @rows = map { $_->raw} $self->all_rows;
69             $table->{rows} = \@rows;
70              
71             return $table;
72             };
73              
74             sub aoa {
75 11     11 1 1523 my $aoa = [ ];
76            
77 11         38 my @headers = map { $_->data->[0] } $_[0]->all_headers;
  26         140  
78 11 100       46 if ( scalar @headers > 0 ) {
79 10         13 push @{ $aoa }, \@headers;
  10         21  
80             }
81              
82 11         42 for ( $_[0]->all_rows ) {
83 41         144 my @row = $_->array;
84 41         40 push @{ $aoa }, \@row;
  41         74  
85             }
86            
87 11         52 return $aoa;
88             };
89              
90             sub aoh {
91 10     10 1 22 my $aoh = [ ];
92 10         30 for ($_[0]->all_rows) {
93 38         132 my $hash = $_->hash;
94 38         34 push @{ $aoh }, $hash;
  38         57  
95             }
96 10         35 return $aoh;
97             };
98              
99             sub add_caption {
100 18     18 1 243 my $caption = HTML::TableContent::Table::Caption->new($_[1]);
101 18         139 $_[0]->caption($caption);
102 18         27 return $caption;
103             }
104              
105 52 100   52 1 176 sub has_caption { return $_[0]->caption ? 1 : 0 };
106              
107 145     145 1 125 sub all_rows { return @{ $_[0]->rows }; }
  145         1962  
108              
109             sub add_row {
110 2236     2236 1 31912 my $row = HTML::TableContent::Table::Row->new($_[1]);
111 2236         11000 push @{ $_[0]->rows }, $row;
  2236         27257  
112 2236         9234 return $row;
113             }
114              
115 9208     9208 1 59774 sub row_count { return scalar @{ $_[0]->rows }; }
  9208         115900  
116              
117 8933     8933 1 130903 sub get_row { return $_[0]->rows->[ $_[1] ]; }
118              
119 139     139 1 40926 sub get_first_row { return $_[0]->get_row(0); }
120              
121 8748     8748 1 12646 sub get_last_row { return $_[0]->get_row( $_[0]->row_count - 1 ); }
122              
123 259     259 1 1211 sub clear_row { return splice @{ $_[0]->rows }, $_[1], 1; }
  259         2957  
124              
125 1     1 1 252 sub clear_first_row { return shift @{ $_[0]->rows }; }
  1         18  
126              
127 258     258 1 662 sub clear_last_row { return $_[0]->clear_row($_[0]->row_count - 1); }
128              
129 1503     1503 1 992 sub all_headers { return @{ $_[0]->headers }; }
  1503         19751  
130              
131             sub add_header {
132 563     563 1 7468 my $header = HTML::TableContent::Table::Header->new($_[1]);
133 563         3128 push @{ $_[0]->headers }, $header;
  563         8039  
134 563         1974 return $header;
135             }
136              
137 273     273 1 50502 sub header_count { return scalar @{ $_[0]->headers }; }
  273         4843  
138              
139 73     73 1 4199 sub get_header { return $_[0]->headers->[ $_[1] ]; }
140              
141 48     48 1 14015 sub get_first_header { return $_[0]->get_header(0); }
142              
143 12     12 1 2691 sub get_last_header { return $_[0]->get_header($_[0]->header_count - 1); }
144              
145 3     3 1 9 sub clear_header { return splice @{ $_[0]->headers }, $_[1], 1; }
  3         36  
146              
147 1     1 1 262 sub clear_first_header { return shift @{ $_[0]->headers } }
  1         19  
148              
149 1     1 1 279 sub clear_last_header { return $_[0]->clear_header( $_[0]->header_count - 1 ); }
150              
151             sub _render_element {
152 52 50   52   1122 return defined $_[0]->vertical ? $_[0]->_render_vertical_table : $_[0]->_render_horizontal_table;
153             }
154              
155             sub _render_vertical_table {
156 0     0   0 my $args = $_[0]->attributes;
157              
158 0         0 my @table_rows = ( );
159              
160 0 0       0 if ( $_[0]->has_caption ) {
161 0         0 push @table_rows, $_[0]->caption->render;
162             }
163            
164 0         0 for my $header ($_[0]->all_headers) {
165 0         0 my @row = ( );
166 0         0 push @row, $header->render;
167 0         0 for ($header->all_cells) {
168 0         0 push @row, $_->render;
169             }
170 0         0 push @table_rows, sprintf '
171             }
172              
173 0         0 my $table = join '', @table_rows;
174              
175 0         0 return $table;
176             }
177              
178             sub _render_horizontal_table {
179 52     52   119 my $args = $_[0]->attributes;
180            
181 52         70 my @table_rows = ( );
182            
183 52 100       106 if ( $_[0]->has_caption ) {
184 46         138 push @table_rows, $_[0]->caption->render;
185             }
186              
187 52 100       109 if ( $_[0]->header_count ) {
188 47         290 my @headers = map { $_->render } $_[0]->all_headers;
  139         390  
189 47         145 my $headers = sprintf '%s' x @headers, @headers;
190 47         84 my $header_row = sprintf '
191 47 50       784 if ( $_[0]->html5 ) {
192 0         0 my $attr = $_[0]->_generate_element_attr('thead');
193 0         0 $header_row = sprintf '
194             }
195 47         123 push @table_rows, $header_row;
196             }
197            
198 52 100       108 if ($_[0]->row_count) {
199 51         283 my @rows = map { $_->render } $_[0]->all_rows;
  143         423  
200 51         141 my $row = sprintf '%s' x @rows, @rows;
201 51 50       811 if ( $_[0]->html5 ) {
202 0         0 my $attr = $_[0]->_generate_element_attr('tbody');
203 0         0 $row = sprintf '
204             }
205 51         273 push @table_rows, $row;
206             }
207            
208 52         161 my $table = sprintf '%s' x @table_rows, @table_rows;
209 52         122 return $table;
210             }
211              
212             sub _generate_element_attr {
213 0     0   0 my ($self, $element) = @_;
214 0         0 my $attr = '';
215 0 0       0 if ( my $attributes = $self->$element ) {
216 0         0 for ( keys %{ $attributes } ) {
  0         0  
217 0         0 $attr .= sprintf '%s="%s" ', $_, $attributes->{$_};
218             }
219             }
220 0         0 return $attr;
221             }
222              
223             sub headers_spec {
224 65     65 1 726 my $headers = {};
225 65         114 map { $headers->{ $_->lc_text }++ } $_[0]->all_headers;
  172         511  
226 65         89 return $headers;
227             }
228              
229             sub header_exists {
230 63     63 1 104 my ( $self, @headers ) = @_;
231              
232 63         101 my $headers_spec = $self->headers_spec;
233 63 100       97 for (@headers) { return 1 if $headers_spec->{ lc $_ } }
  79         314  
234 12         41 return 0;
235             }
236              
237             sub get_col {
238 19     19 1 57 my %args = ( header => $_[1] );
239 19         64 return $_[0]->get_header_column(%args);
240             }
241              
242             sub get_col_text {
243 2     2 1 516 my %args = ( header => $_[1] );
244 2         9 return $_[0]->get_header_column_text(%args);
245             }
246              
247             sub get_header_column {
248 26     26 1 48 my ( $self, %args ) = @_;
249              
250 26         44 my @cells = ();
251 26         37 my $column = $args{header};
252 26         61 foreach my $header ( $self->all_headers ) {
253 52 100       223 if ( $header->lc_text =~ m{$column}ixms ) {
254 26         71 for ( $header->all_cells ) {
255 53         179 push @cells, $_;
256             }
257             }
258             }
259              
260 26 100       66 if ( defined $args{dedupe} ) {
261 3         10 @cells = $self->_dedupe_object_array_not_losing_order(@cells);
262             }
263              
264 26         94 return \@cells;
265             }
266              
267             sub parse_to_column {
268 5243     5243 1 5420 my ($self, $cell) = @_;
269            
270 5243         7049 my $row = $self->get_last_row;
271              
272 5243         16886 my $header;
273 5243 100       8754 if ( my $row_header = $row->header ) {
274 39         28 $header = $row_header;
275             }
276             else {
277 5204         9166 my $cell_index = $row->cell_count;
278 5204         73099 $header = $self->headers->[$cell_index - 1];
279             }
280              
281 5243 100       21378 return unless $header;
282              
283 5071         5856 $cell->header($header);
284 5071         3339 push @{ $header->cells }, $cell;
  5071         61250  
285              
286 5071         20070 return 1;
287             }
288              
289             sub get_header_column_text {
290 7     7 1 350 my ( $self, %args ) = @_;
291 7         21 my $cells = $self->get_header_column(%args);
292 7         12 my @cell_text = map { $_->text } @{$cells};
  13         23  
  7         10  
293 7         32 return \@cell_text;
294             }
295              
296             sub has_nested_table_column {
297 21     21 1 25 my $self = shift;
298 21         62 for my $header ( $self->all_headers ) {
299 41         196 for ( $header->all_cells ) {
300 63 100       264 return 1 if $_->has_nested;
301             }
302             }
303 2         7 return 0;
304             }
305              
306             sub nested_column_headers {
307 19     19 1 31 my $self = shift;
308              
309 19         26 my $columns = {};
310 19         36 for my $header ( $self->all_headers ) {
311 39         162 my $cell = $header->get_first_cell;
312 39 100       201 if ( $cell->has_nested ) {
313 19         70 $columns->{ $header->lc_text }++;
314             }
315             }
316 19         82 return $columns;
317             }
318              
319             sub _dedupe_object_array_not_losing_order {
320 3     3   6 my ( $self, @items ) = @_;
321              
322             # someone could probably do this in one line :)
323 3         4 my %args;
324 3         3 my @new_items = ();
325 3         5 foreach my $item (@items) {
326 8 100       19 if ( !defined $args{ $item->text } ) {
327 5         11 $args{ $item->text }++;
328 5         8 push @new_items, $item;
329             }
330             }
331              
332 3         8 return @new_items;
333             }
334              
335             sub _filter_headers {
336 49     49   62 my ( $self, @headers ) = @_;
337              
338 49         56 my $headers = [];
339 49         79 foreach my $header ( $self->all_headers ) {
340 130         272 for (@headers) {
341 266 100       395 if ( $header->lc_text =~ m/$_/ims ) {
342 89         80 push @{$headers}, $header;
  89         131  
343             }
344             }
345             }
346              
347 49         652 $self->headers($headers);
348              
349 49         216 foreach my $row ( $self->all_rows ) {
350 130         618 $row->_filter_headers($headers);
351             }
352              
353 49         212 return 1;
354             }
355              
356             sub clear_column {
357 2     2 0 464 my ($self, @headers) = @_;
358              
359 2         2 my @remove_cell;
360 2         5 for my $index (0 .. $self->header_count - 1) {
361 4         14 for (@headers) {
362 4 100       50 if ( $self->headers->[$index]->lc_text =~ m/$_/ims ){
363 2         3 $self->clear_header($index);
364 2         10 push @remove_cell, $index;
365             }
366             }
367             }
368              
369 2         4 foreach my $row ($self->all_rows ) {
370 62         287 for (@remove_cell) {
371 62         93 $row->clear_cell($_);
372             }
373             }
374              
375 2         14 return 1;
376             }
377              
378             sub sort {
379 0     0 0   my ($self, $options) = @_;
380              
381 0 0         if ( my $order = $options->{order}) {
382 0           my $headers = [ ];
383 0           foreach my $header (@{ $order }) {
  0            
384 0           push @{ $headers }, map { $_ } grep { $_->text =~ m/$header/ixms } $self->all_headers;
  0            
  0            
  0            
385             }
386 0           $self->headers($headers);
387            
388 0           foreach my $row ( $self->all_rows ) {
389 0           my $cells = [ ];
390 0           foreach my $header (@{ $order }) {
  0            
391 0           push @{ $cells }, grep { $_->header->text =~ m/$header/ixms } $row->all_cells;
  0            
  0            
392             }
393 0           $row->cells($cells);
394             }
395             }
396              
397 0 0         if ( my $order = $options->{order_template}) {
398 0           my $headers = [ ];
399 0           foreach my $header (@{ $order }) {
  0            
400 0           push @{ $headers }, map { $_ } grep { $_->template_attr =~ m/$header/ixms } $self->all_headers;
  0            
  0            
  0            
401             }
402 0           $self->headers($headers);
403            
404 0           foreach my $row ( $self->all_rows ) {
405 0           my $cells = [ ];
406 0           foreach my $header (@{ $order }) {
  0            
407 0           push @{ $cells }, grep { $_->header->text =~ m/$header/ixms } $row->all_cells;
  0            
  0            
408             }
409 0           $row->cells($cells);
410             }
411             }
412              
413              
414 0           return $self;
415             }
416              
417              
418             __PACKAGE__->meta->make_immutable;
419              
420             1;
421              
422             __END__