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   118 use Moo;
  20         66  
  20         92  
4              
5 20     20   11673 use HTML::TableContent::Table::Caption;
  20         71  
  20         616  
6 20     20   7446 use HTML::TableContent::Table::Header;
  20         480  
  20         519  
7 20     20   7274 use HTML::TableContent::Table::Row;
  20         63  
  20         43489  
8              
9             our $VERSION = '0.18';
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   638 : 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   695 : 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 2775 my $aoa = [ ];
76            
77 11         47 my @headers = map { $_->data->[0] } $_[0]->all_headers;
  26         177  
78 11 100       56 if ( scalar @headers > 0 ) {
79 10         19 push @{ $aoa }, \@headers;
  10         29  
80             }
81              
82 11         40 for ( $_[0]->all_rows ) {
83 41         196 my @row = $_->array;
84 41         71 push @{ $aoa }, \@row;
  41         95  
85             }
86            
87 11         68 return $aoa;
88             };
89              
90             sub aoh {
91 10     10 1 29 my $aoh = [ ];
92 10         34 for ($_[0]->all_rows) {
93 38         181 my $hash = $_->hash;
94 38         61 push @{ $aoh }, $hash;
  38         87  
95             }
96 10         52 return $aoh;
97             };
98              
99             sub add_caption {
100 18     18 1 275 my $caption = HTML::TableContent::Table::Caption->new($_[1]);
101 18         160 $_[0]->caption($caption);
102 18         46 return $caption;
103             }
104              
105 52 100   52 1 258 sub has_caption { return $_[0]->caption ? 1 : 0 };
106              
107 145     145 1 271 sub all_rows { return @{ $_[0]->rows }; }
  145         2334  
108              
109             sub add_row {
110 2236     2236 1 35270 my $row = HTML::TableContent::Table::Row->new($_[1]);
111 2236         14673 push @{ $_[0]->rows }, $row;
  2236         30068  
112 2236         14576 return $row;
113             }
114              
115 9208     9208 1 88854 sub row_count { return scalar @{ $_[0]->rows }; }
  9208         128440  
116              
117 8933     8933 1 158109 sub get_row { return $_[0]->rows->[ $_[1] ]; }
118              
119 139     139 1 59695 sub get_first_row { return $_[0]->get_row(0); }
120              
121 8748     8748 1 21522 sub get_last_row { return $_[0]->get_row( $_[0]->row_count - 1 ); }
122              
123 259     259 1 2188 sub clear_row { return splice @{ $_[0]->rows }, $_[1], 1; }
  259         3306  
124              
125 1     1 1 552 sub clear_first_row { return shift @{ $_[0]->rows }; }
  1         21  
126              
127 258     258 1 1118 sub clear_last_row { return $_[0]->clear_row($_[0]->row_count - 1); }
128              
129 1503     1503 1 2229 sub all_headers { return @{ $_[0]->headers }; }
  1503         22014  
130              
131             sub add_header {
132 563     563 1 8450 my $header = HTML::TableContent::Table::Header->new($_[1]);
133 563         4052 push @{ $_[0]->headers }, $header;
  563         8269  
134 563         3145 return $header;
135             }
136              
137 273     273 1 74120 sub header_count { return scalar @{ $_[0]->headers }; }
  273         4984  
138              
139 73     73 1 5303 sub get_header { return $_[0]->headers->[ $_[1] ]; }
140              
141 48     48 1 20882 sub get_first_header { return $_[0]->get_header(0); }
142              
143 12     12 1 3896 sub get_last_header { return $_[0]->get_header($_[0]->header_count - 1); }
144              
145 3     3 1 12 sub clear_header { return splice @{ $_[0]->headers }, $_[1], 1; }
  3         41  
146              
147 1     1 1 538 sub clear_first_header { return shift @{ $_[0]->headers } }
  1         21  
148              
149 1     1 1 467 sub clear_last_header { return $_[0]->clear_header( $_[0]->header_count - 1 ); }
150              
151             sub _render_element {
152 52 50   52   1136 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   220 my $args = $_[0]->attributes;
180            
181 52         126 my @table_rows = ( );
182            
183 52 100       194 if ( $_[0]->has_caption ) {
184 46         228 push @table_rows, $_[0]->caption->render;
185             }
186              
187 52 100       174 if ( $_[0]->header_count ) {
188 47         458 my @headers = map { $_->render } $_[0]->all_headers;
  139         647  
189 47         242 my $headers = sprintf '%s' x @headers, @headers;
190 47         159 my $header_row = sprintf '
191 47 50       951 if ( $_[0]->html5 ) {
192 0         0 my $attr = $_[0]->_generate_element_attr('thead');
193 0         0 $header_row = sprintf '
194             }
195 47         194 push @table_rows, $header_row;
196             }
197            
198 52 100       171 if ($_[0]->row_count) {
199 51         485 my @rows = map { $_->render } $_[0]->all_rows;
  143         718  
200 51         205 my $row = sprintf '%s' x @rows, @rows;
201 51 50       899 if ( $_[0]->html5 ) {
202 0         0 my $attr = $_[0]->_generate_element_attr('tbody');
203 0         0 $row = sprintf '
204             }
205 51         432 push @table_rows, $row;
206             }
207            
208 52         227 my $table = sprintf '%s' x @table_rows, @table_rows;
209 52         175 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 1076 my $headers = {};
225 65         170 map { $headers->{ $_->lc_text }++ } $_[0]->all_headers;
  172         847  
226 65         159 return $headers;
227             }
228              
229             sub header_exists {
230 63     63 1 212 my ( $self, @headers ) = @_;
231              
232 63         189 my $headers_spec = $self->headers_spec;
233 63 100       146 for (@headers) { return 1 if $headers_spec->{ lc $_ } }
  79         405  
234 12         51 return 0;
235             }
236              
237             sub get_col {
238 19     19 1 64 my %args = ( header => $_[1] );
239 19         59 return $_[0]->get_header_column(%args);
240             }
241              
242             sub get_col_text {
243 2     2 1 774 my %args = ( header => $_[1] );
244 2         13 return $_[0]->get_header_column_text(%args);
245             }
246              
247             sub get_header_column {
248 26     26 1 77 my ( $self, %args ) = @_;
249              
250 26         52 my @cells = ();
251 26         51 my $column = $args{header};
252 26         64 foreach my $header ( $self->all_headers ) {
253 52 100       289 if ( $header->lc_text =~ m{$column}ixms ) {
254 26         92 for ( $header->all_cells ) {
255 53         254 push @cells, $_;
256             }
257             }
258             }
259              
260 26 100       77 if ( defined $args{dedupe} ) {
261 3         16 @cells = $self->_dedupe_object_array_not_losing_order(@cells);
262             }
263              
264 26         115 return \@cells;
265             }
266              
267             sub parse_to_column {
268 5243     5243 1 11027 my ($self, $cell) = @_;
269            
270 5243         10803 my $row = $self->get_last_row;
271              
272 5243         29476 my $header;
273 5243 100       13355 if ( my $row_header = $row->header ) {
274 39         57 $header = $row_header;
275             }
276             else {
277 5204         13494 my $cell_index = $row->cell_count;
278 5204         87206 $header = $self->headers->[$cell_index - 1];
279             }
280              
281 5243 100       35721 return unless $header;
282              
283 5071         10487 $cell->header($header);
284 5071         7129 push @{ $header->cells }, $cell;
  5071         66963  
285              
286 5071         32618 return 1;
287             }
288              
289             sub get_header_column_text {
290 7     7 1 527 my ( $self, %args ) = @_;
291 7         30 my $cells = $self->get_header_column(%args);
292 7         13 my @cell_text = map { $_->text } @{$cells};
  13         32  
  7         16  
293 7         41 return \@cell_text;
294             }
295              
296             sub has_nested_table_column {
297 21     21 1 44 my $self = shift;
298 21         60 for my $header ( $self->all_headers ) {
299 41         259 for ( $header->all_cells ) {
300 63 100       371 return 1 if $_->has_nested;
301             }
302             }
303 2         10 return 0;
304             }
305              
306             sub nested_column_headers {
307 19     19 1 38 my $self = shift;
308              
309 19         36 my $columns = {};
310 19         47 for my $header ( $self->all_headers ) {
311 39         219 my $cell = $header->get_first_cell;
312 39 100       276 if ( $cell->has_nested ) {
313 19         61 $columns->{ $header->lc_text }++;
314             }
315             }
316 19         85 return $columns;
317             }
318              
319             sub _dedupe_object_array_not_losing_order {
320 3     3   11 my ( $self, @items ) = @_;
321              
322             # someone could probably do this in one line :)
323 3         7 my %args;
324 3         6 my @new_items = ();
325 3         8 foreach my $item (@items) {
326 8 100       23 if ( !defined $args{ $item->text } ) {
327 5         18 $args{ $item->text }++;
328 5         13 push @new_items, $item;
329             }
330             }
331              
332 3         12 return @new_items;
333             }
334              
335             sub _filter_headers {
336 49     49   157 my ( $self, @headers ) = @_;
337              
338 49         103 my $headers = [];
339 49         122 foreach my $header ( $self->all_headers ) {
340 130         516 for (@headers) {
341 266 100       663 if ( $header->lc_text =~ m/$_/ims ) {
342 89         467 push @{$headers}, $header;
  89         214  
343             }
344             }
345             }
346              
347 49         807 $self->headers($headers);
348              
349 49         361 foreach my $row ( $self->all_rows ) {
350 130         1049 $row->_filter_headers($headers);
351             }
352              
353 49         349 return 1;
354             }
355              
356             sub clear_column {
357 2     2 0 1071 my ($self, @headers) = @_;
358              
359 2         5 my @remove_cell;
360 2         8 for my $index (0 .. $self->header_count - 1) {
361 4         24 for (@headers) {
362 4 100       57 if ( $self->headers->[$index]->lc_text =~ m/$_/ims ){
363 2         7 $self->clear_header($index);
364 2         15 push @remove_cell, $index;
365             }
366             }
367             }
368              
369 2         8 foreach my $row ($self->all_rows ) {
370 62         431 for (@remove_cell) {
371 62         143 $row->clear_cell($_);
372             }
373             }
374              
375 2         22 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__