line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::XHTML_Table; |
2
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
122251
|
use strict; |
|
7
|
|
|
|
|
29
|
|
|
7
|
|
|
|
|
163
|
|
4
|
7
|
|
|
7
|
|
22
|
use warnings; |
|
7
|
|
|
|
|
6
|
|
|
7
|
|
|
|
|
228
|
|
5
|
|
|
|
|
|
|
our $VERSION = '1.49'; |
6
|
|
|
|
|
|
|
|
7
|
7
|
|
|
7
|
|
9201
|
use DBI; |
|
7
|
|
|
|
|
80885
|
|
|
7
|
|
|
|
|
350
|
|
8
|
7
|
|
|
7
|
|
45
|
use Carp; |
|
7
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
359
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# GLOBALS |
11
|
7
|
|
|
7
|
|
26
|
use vars qw(%ESCAPES $T $N); |
|
7
|
|
|
|
|
7
|
|
|
7
|
|
|
|
|
28163
|
|
12
|
|
|
|
|
|
|
($T,$N) = ("\t","\n"); |
13
|
|
|
|
|
|
|
%ESCAPES = ( |
14
|
|
|
|
|
|
|
'&' => '&', |
15
|
|
|
|
|
|
|
'<' => '<', |
16
|
|
|
|
|
|
|
'>' => '>', |
17
|
|
|
|
|
|
|
'"' => '"', |
18
|
|
|
|
|
|
|
); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
#################### CONSTRUCTOR ################################### |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# see POD for documentation |
23
|
|
|
|
|
|
|
sub new { |
24
|
2
|
|
|
2
|
1
|
169505
|
my $class = shift; |
25
|
2
|
|
|
|
|
7
|
my $self = { |
26
|
|
|
|
|
|
|
null_value => ' ', |
27
|
|
|
|
|
|
|
}; |
28
|
2
|
|
|
|
|
5
|
bless $self, $class; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# last arg might be GTCH (global table config hash) |
31
|
2
|
50
|
|
|
|
11
|
$self->{'global'} = pop if ref $_[$#_] eq 'HASH'; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# note: disconnected handles aren't caught :( |
34
|
|
|
|
|
|
|
|
35
|
2
|
50
|
|
|
|
14
|
if (UNIVERSAL::isa($_[0],'DBI::db')) { |
|
|
50
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# use supplied db handle |
37
|
0
|
|
|
|
|
0
|
$self->{'dbh'} = $_[0]; |
38
|
0
|
|
|
|
|
0
|
$self->{'keep_alive'} = 1; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
elsif (ref($_[0]) eq 'ARRAY') { |
41
|
|
|
|
|
|
|
# go ahead and accept a pre-built 2d array ref |
42
|
2
|
|
|
|
|
9
|
$self->_do_black_magic(@_); |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
else { |
45
|
|
|
|
|
|
|
# create my own db handle |
46
|
0
|
|
|
|
|
0
|
eval { $self->{'dbh'} = DBI->connect(@_) }; |
|
0
|
|
|
|
|
0
|
|
47
|
0
|
0
|
0
|
|
|
0
|
carp $@ and return undef if $@; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
2
|
|
|
|
|
6
|
return $self; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
#################### OBJECT METHODS ################################ |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub exec_query { |
56
|
0
|
|
|
0
|
1
|
0
|
my ($self,$sql,$vars) = @_; |
57
|
|
|
|
|
|
|
|
58
|
0
|
0
|
|
|
|
0
|
carp "can't call exec_query(): do database handle" unless $self->{'dbh'}; |
59
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
0
|
eval { |
61
|
|
|
|
|
|
|
$self->{'sth'} = (UNIVERSAL::isa($sql,'DBI::st')) |
62
|
|
|
|
|
|
|
? $sql |
63
|
0
|
0
|
|
|
|
0
|
: $self->{'dbh'}->prepare($sql) |
64
|
|
|
|
|
|
|
; |
65
|
0
|
|
|
|
|
0
|
$self->{'sth'}->execute(@$vars); |
66
|
|
|
|
|
|
|
}; |
67
|
0
|
0
|
0
|
|
|
0
|
carp $@ and return undef if $@; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# store the results |
70
|
0
|
|
|
|
|
0
|
$self->{'fields_arry'} = [ @{$self->{'sth'}->{'NAME'}} ]; |
|
0
|
|
|
|
|
0
|
|
71
|
0
|
|
|
|
|
0
|
$self->{'fields_hash'} = $self->_reset_fields_hash(); |
72
|
0
|
|
|
|
|
0
|
$self->{'rows'} = $self->{'sth'}->fetchall_arrayref(); |
73
|
0
|
0
|
|
|
|
0
|
carp "can't call exec_query(): no data was returned from query" unless @{$self->{'rows'}}; |
|
0
|
|
|
|
|
0
|
|
74
|
|
|
|
|
|
|
|
75
|
0
|
0
|
|
|
|
0
|
if (exists $self->{'pk'}) { |
76
|
|
|
|
|
|
|
# remove the primary key info from the arry and hash |
77
|
0
|
|
|
|
|
0
|
$self->{'pk_index'} = delete $self->{'fields_hash'}->{$self->{'pk'}}; |
78
|
0
|
0
|
|
|
|
0
|
splice(@{$self->{'fields_arry'}},$self->{'pk_index'},1) if defined $self->{'pk_index'}; |
|
0
|
|
|
|
|
0
|
|
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
0
|
return $self; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub output { |
85
|
7
|
|
|
7
|
1
|
697
|
my ($self,$config,$no_ws) = @_; |
86
|
7
|
50
|
0
|
|
|
22
|
carp "can't call output(): no data" and return '' unless $self->{'rows'}; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# have to deprecate old arguments ... |
89
|
7
|
50
|
|
|
|
14
|
if ($no_ws) { |
90
|
0
|
|
|
|
|
0
|
carp "scalar arguments to output() are deprecated, use hash reference"; |
91
|
0
|
|
|
|
|
0
|
$N = $T = ''; |
92
|
|
|
|
|
|
|
} |
93
|
7
|
50
|
33
|
|
|
38
|
if ($config and not ref $config) { |
|
|
50
|
|
|
|
|
|
94
|
0
|
|
|
|
|
0
|
carp "scalar arguments to output() are deprecated, use hash reference"; |
95
|
0
|
|
|
|
|
0
|
$self->{'no_head'} = $config; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
elsif ($config) { |
98
|
7
|
|
|
|
|
10
|
$self->{'no_head'} = $config->{'no_head'}; |
99
|
7
|
|
|
|
|
9
|
$self->{'no_ucfirst'} = $config->{'no_ucfirst'}; |
100
|
7
|
50
|
|
|
|
19
|
$N = $T = '' if $config->{'no_indent'}; |
101
|
7
|
50
|
|
|
|
13
|
if ($config->{'no_whitespace'}) { |
102
|
0
|
|
|
|
|
0
|
carp "no_whitespace attrib deprecated, use no_indent"; |
103
|
0
|
|
|
|
|
0
|
$N = $T = ''; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
7
|
|
|
|
|
15
|
return $self->_build_table(); |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub modify { |
111
|
1
|
|
|
1
|
1
|
4
|
my ($self,$tag,$attribs,$cols) = @_; |
112
|
1
|
|
|
|
|
3
|
$tag = lc $tag; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# apply attributes to specified columns |
115
|
1
|
50
|
|
|
|
6
|
if (ref $attribs eq 'HASH') { |
116
|
1
|
50
|
33
|
|
|
10
|
$cols = 'global' unless defined( $cols) && length( $cols ); |
117
|
1
|
|
|
|
|
8
|
$cols = $self->_refinate($cols); |
118
|
|
|
|
|
|
|
|
119
|
1
|
|
|
|
|
9
|
while (my($attr,$val) = each %$attribs) { |
120
|
1
|
|
|
|
|
37
|
$self->{lc $_}->{$tag}->{$attr} = $val for @$cols; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
# or handle a special case (e.g. |
124
|
|
|
|
|
|
|
else { |
125
|
|
|
|
|
|
|
# cols is really attribs now, attribs is just a scalar |
126
|
0
|
|
|
|
|
0
|
$self->{'global'}->{$tag} = $attribs; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# there is only one caption - no need to rotate attribs |
129
|
0
|
0
|
|
|
|
0
|
if (ref $cols->{'style'} eq 'HASH') { |
130
|
0
|
|
|
|
|
0
|
$cols->{'style'} = join('; ',map { "$_: ".$cols->{'style'}->{$_} } sort keys %{$cols->{'style'}}) . ';'; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
0
|
$self->{'global'}->{$tag."_attribs"} = $cols; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
1
|
|
|
|
|
22
|
return $self; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub map_cell { |
140
|
0
|
|
|
0
|
1
|
0
|
my ($self,$sub,$cols) = @_; |
141
|
|
|
|
|
|
|
|
142
|
0
|
0
|
0
|
|
|
0
|
carp "map_cell() is being ignored - no data" and return $self unless $self->{'rows'}; |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
0
|
$cols = $self->_refinate($cols); |
145
|
0
|
|
|
|
|
0
|
for (@$cols) { |
146
|
0
|
|
|
|
|
0
|
my $key; |
147
|
0
|
0
|
|
|
|
0
|
if (defined $self->{'fields_hash'}->{$_}) { |
|
|
0
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
$key = $_; |
149
|
|
|
|
|
|
|
} elsif( defined $self->{'fields_hash'}->{lc $_}) { |
150
|
0
|
|
|
|
|
0
|
$key = lc $_; |
151
|
|
|
|
|
|
|
} else { |
152
|
0
|
|
|
|
|
0
|
SEARCH: for my $k (sort keys %{ $self->{'fields_hash'} }) { |
|
0
|
|
|
|
|
0
|
|
153
|
0
|
0
|
|
|
|
0
|
if (lc( $k ) eq lc( $_ )) { |
154
|
0
|
|
|
|
|
0
|
$key = $k; |
155
|
0
|
|
|
|
|
0
|
last SEARCH; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
0
|
0
|
|
|
|
0
|
next unless $key; |
160
|
0
|
|
|
|
|
0
|
$self->{'map_cell'}->{$key} = $sub; |
161
|
|
|
|
|
|
|
} |
162
|
0
|
|
|
|
|
0
|
return $self; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub map_head { |
166
|
1
|
|
|
1
|
1
|
3
|
my ($self,$sub,$cols) = @_; |
167
|
|
|
|
|
|
|
|
168
|
1
|
50
|
0
|
|
|
3
|
carp "map_head() is being ignored - no data" and return $self unless $self->{'rows'}; |
169
|
|
|
|
|
|
|
|
170
|
1
|
|
|
|
|
4
|
$cols = $self->_refinate($cols); |
171
|
1
|
|
|
|
|
3
|
for (@$cols) { |
172
|
1
|
|
|
|
|
1
|
my $key; |
173
|
1
|
50
|
|
|
|
2
|
if (defined $self->{'fields_hash'}->{$_}) { |
|
|
0
|
|
|
|
|
|
174
|
1
|
|
|
|
|
2
|
$key = $_; |
175
|
|
|
|
|
|
|
} elsif( defined $self->{'fields_hash'}->{lc $_}) { |
176
|
0
|
|
|
|
|
0
|
$key = lc $_; |
177
|
|
|
|
|
|
|
} else { |
178
|
0
|
|
|
|
|
0
|
SEARCH: for my $k (sort keys %{ $self->{'fields_hash'} }) { |
|
0
|
|
|
|
|
0
|
|
179
|
0
|
0
|
|
|
|
0
|
if (lc( $k ) eq lc( $_ )) { |
180
|
0
|
|
|
|
|
0
|
$key = $k; |
181
|
0
|
|
|
|
|
0
|
last SEARCH; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} |
185
|
1
|
50
|
|
|
|
2
|
next unless $key; |
186
|
1
|
|
|
|
|
2
|
$self->{'map_head'}->{$key} = $sub; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
1
|
|
|
|
|
2
|
return $self; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub add_col_tag { |
193
|
0
|
|
|
0
|
1
|
0
|
my ($self,$attribs) = @_; |
194
|
0
|
0
|
|
|
|
0
|
$self->{'global'}->{'colgroup'} = {} unless $self->{'colgroups'}; |
195
|
0
|
|
|
|
|
0
|
push @{$self->{'colgroups'}}, $attribs; |
|
0
|
|
|
|
|
0
|
|
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
0
|
return $self; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub calc_totals { |
201
|
1
|
|
|
1
|
1
|
3
|
my ($self,$cols,$mask) = @_; |
202
|
1
|
50
|
|
|
|
4
|
return undef unless $self->{'rows'}; |
203
|
|
|
|
|
|
|
|
204
|
1
|
|
|
|
|
1
|
$self->{'totals_mask'} = $mask; |
205
|
1
|
|
|
|
|
2
|
$cols = $self->_refinate($cols); |
206
|
|
|
|
|
|
|
|
207
|
1
|
|
|
|
|
2
|
my @indexes; |
208
|
1
|
|
|
|
|
3
|
for (@$cols) { |
209
|
1
|
|
|
|
|
1
|
my $index; |
210
|
1
|
50
|
|
|
|
4
|
if (exists $self->{'fields_hash'}->{$_}) { |
|
|
0
|
|
|
|
|
|
211
|
1
|
|
|
|
|
2
|
$index = $self->{'fields_hash'}->{$_}; |
212
|
|
|
|
|
|
|
} elsif (exists $self->{'fields_hash'}->{lc $_}) { |
213
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{lc $_}; |
214
|
|
|
|
|
|
|
} else { |
215
|
0
|
|
|
|
|
0
|
SEARCH: for my $k (sort keys %{ $self->{'fields_hash'} }) { |
|
0
|
|
|
|
|
0
|
|
216
|
0
|
0
|
|
|
|
0
|
if (lc( $k ) eq lc( $_ )) { |
217
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{$k}; |
218
|
0
|
|
|
|
|
0
|
last SEARCH; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
} |
222
|
1
|
|
|
|
|
3
|
push @indexes, $index; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
1
|
|
|
|
|
3
|
$self->{'totals'} = $self->_total_chunk($self->{'rows'},\@indexes); |
226
|
|
|
|
|
|
|
|
227
|
1
|
|
|
|
|
2
|
return $self; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub calc_subtotals { |
231
|
0
|
|
|
0
|
1
|
0
|
my ($self,$cols,$mask,$nodups) = @_; |
232
|
0
|
0
|
|
|
|
0
|
return undef unless $self->{'rows'}; |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
0
|
$self->{'subtotals_mask'} = $mask; |
235
|
0
|
|
|
|
|
0
|
$cols = $self->_refinate($cols); |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
0
|
my @indexes; |
238
|
0
|
|
|
|
|
0
|
for (@$cols) { |
239
|
0
|
|
|
|
|
0
|
my $index; |
240
|
0
|
0
|
|
|
|
0
|
if (exists $self->{'fields_hash'}->{$_}) { |
|
|
0
|
|
|
|
|
|
241
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{$_}; |
242
|
|
|
|
|
|
|
} elsif (exists $self->{'fields_hash'}->{lc $_}) { |
243
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{lc $_}; |
244
|
|
|
|
|
|
|
} else { |
245
|
0
|
|
|
|
|
0
|
SEARCH: for my $k (sort keys %{ $self->{'fields_hash'} }) { |
|
0
|
|
|
|
|
0
|
|
246
|
0
|
0
|
|
|
|
0
|
if (lc( $k ) eq lc( $_ )) { |
247
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{$k}; |
248
|
0
|
|
|
|
|
0
|
last SEARCH; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
0
|
|
|
|
|
0
|
push @indexes, $index; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
0
|
my $beg = 0; |
256
|
0
|
|
|
|
|
0
|
foreach my $end (@{$self->{'body_breaks'}}) { |
|
0
|
|
|
|
|
0
|
|
257
|
0
|
|
|
|
|
0
|
my $chunk = ([@{$self->{'rows'}}[$beg..$end]]); |
|
0
|
|
|
|
|
0
|
|
258
|
0
|
|
|
|
|
0
|
push @{$self->{'sub_totals'}}, $self->_total_chunk($chunk,\@indexes); |
|
0
|
|
|
|
|
0
|
|
259
|
0
|
|
|
|
|
0
|
$beg = $end + 1; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
0
|
return $self; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub set_row_colors { |
266
|
0
|
|
|
0
|
1
|
0
|
my ($self,$colors,$myattrib) = @_; |
267
|
|
|
|
|
|
|
|
268
|
0
|
0
|
|
|
|
0
|
return $self unless ref $colors eq 'ARRAY'; |
269
|
0
|
0
|
|
|
|
0
|
return $self unless $#$colors >= 1; |
270
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
0
|
my $ref = ($myattrib) |
272
|
|
|
|
|
|
|
? { $myattrib => [@$colors] } |
273
|
|
|
|
|
|
|
: { style => {background => [@$colors]} } |
274
|
|
|
|
|
|
|
; |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
0
|
$self->modify(tr => $ref, 'body'); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# maybe that should be global? |
279
|
|
|
|
|
|
|
#$self->modify(tr => $ref); |
280
|
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
0
|
return $self; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub set_col_colors { |
285
|
0
|
|
|
0
|
1
|
0
|
my ($self,$colors,$myattrib) = @_; |
286
|
|
|
|
|
|
|
|
287
|
0
|
0
|
|
|
|
0
|
return $self unless ref $colors eq 'ARRAY'; |
288
|
0
|
0
|
|
|
|
0
|
return $self unless $#$colors >= 1; |
289
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
0
|
my $cols = $self->_refinate(); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# trick #1: truncate colors to cols |
293
|
0
|
0
|
|
|
|
0
|
$#$colors = $#$cols if $#$colors > $#$cols; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# trick #2: keep adding colors |
296
|
|
|
|
|
|
|
#unless ($#$cols % 2 and $#$colors % 2) { |
297
|
0
|
|
|
|
|
0
|
my $temp = [@$colors]; |
298
|
0
|
|
|
|
|
0
|
push(@$colors,_rotate($temp)) until $#$colors == $#$cols; |
299
|
|
|
|
|
|
|
#} |
300
|
|
|
|
|
|
|
|
301
|
0
|
0
|
|
|
|
0
|
my $ref = ($myattrib) |
302
|
|
|
|
|
|
|
? { $myattrib => [@$colors] } |
303
|
|
|
|
|
|
|
: { style => {background => [@$colors]} } |
304
|
|
|
|
|
|
|
; |
305
|
|
|
|
|
|
|
|
306
|
0
|
|
|
|
|
0
|
$self->modify(td => $ref, $_) for @$cols; |
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
return $self; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub set_group { |
312
|
1
|
|
|
1
|
1
|
3
|
my ($self,$group,$nodup,$value) = @_; |
313
|
1
|
50
|
33
|
|
|
7
|
$self->{'nodup'} = $value || $self->{'null_value'} if $nodup; |
314
|
|
|
|
|
|
|
|
315
|
1
|
|
|
|
|
1
|
my $index; |
316
|
1
|
50
|
|
|
|
7
|
if ($group =~ /^\d+$/) { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
317
|
0
|
|
|
|
|
0
|
$index = $group; |
318
|
|
|
|
|
|
|
} elsif (exists $self->{'fields_hash'}->{$group}) { |
319
|
1
|
|
|
|
|
2
|
$index = $self->{'fields_hash'}->{$group}; |
320
|
1
|
|
|
|
|
3
|
$self->{'group'} = $group; |
321
|
|
|
|
|
|
|
} elsif (exists $self->{'fields_hash'}->{lc $group}) { |
322
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{lc $group}; |
323
|
0
|
|
|
|
|
0
|
$self->{'group'} = lc $group; |
324
|
|
|
|
|
|
|
} else { |
325
|
0
|
|
|
|
|
0
|
SEARCH: for my $k (sort keys %{ $self->{'fields_hash'} }) { |
|
0
|
|
|
|
|
0
|
|
326
|
0
|
0
|
|
|
|
0
|
if (lc( $k ) eq lc( $group )) { |
327
|
0
|
|
|
|
|
0
|
$index = $self->{'fields_hash'}->{$k}; |
328
|
0
|
|
|
|
|
0
|
$self->{'group'} = $k; |
329
|
0
|
|
|
|
|
0
|
last SEARCH; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# initialize the first 'repetition' |
335
|
1
|
|
|
|
|
2
|
my $rep = $self->{'rows'}->[0]->[$index]; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# loop through the whole rows array, storing |
338
|
|
|
|
|
|
|
# the points at which a new group starts |
339
|
1
|
|
|
|
|
2
|
for my $i (0..$self->get_row_count - 1) { |
340
|
2
|
|
|
|
|
2
|
my $new = $self->{'rows'}->[$i]->[$index]; |
341
|
2
|
50
|
|
|
|
6
|
push @{$self->{'body_breaks'}}, $i - 1 unless ($rep eq $new); |
|
0
|
|
|
|
|
0
|
|
342
|
2
|
|
|
|
|
3
|
$rep = $new; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
1
|
|
|
|
|
2
|
push @{$self->{'body_breaks'}}, $self->get_row_count - 1; |
|
1
|
|
|
|
|
4
|
|
346
|
|
|
|
|
|
|
|
347
|
1
|
|
|
|
|
6
|
return $self; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub set_pk { |
351
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
352
|
0
|
|
0
|
|
|
0
|
my $pk = shift || 'id'; |
353
|
0
|
0
|
0
|
|
|
0
|
$pk = $pk =~ /^\d+$/ ? $self->_lookup_name($pk) || $pk : $pk; |
354
|
0
|
0
|
|
|
|
0
|
carp "can't call set_pk(): too late to set primary key" if exists $self->{'rows'}; |
355
|
0
|
|
|
|
|
0
|
$self->{'pk'} = $pk; |
356
|
|
|
|
|
|
|
|
357
|
0
|
|
|
|
|
0
|
return $self; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub set_null_value { |
361
|
0
|
|
|
0
|
1
|
0
|
my ($self,$value) = @_; |
362
|
0
|
|
|
|
|
0
|
$self->{'null_value'} = $value; |
363
|
0
|
|
|
|
|
0
|
return $self; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub get_col_count { |
367
|
1
|
|
|
1
|
1
|
1
|
my ($self) = @_; |
368
|
1
|
|
|
|
|
1
|
my $count = scalar @{$self->{'fields_arry'}}; |
|
1
|
|
|
|
|
3
|
|
369
|
1
|
|
|
|
|
2
|
return $count; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub get_row_count { |
373
|
6
|
|
|
6
|
1
|
6
|
my ($self) = @_; |
374
|
6
|
|
|
|
|
6
|
my $count = scalar @{$self->{'rows'}}; |
|
6
|
|
|
|
|
8
|
|
375
|
6
|
|
|
|
|
12
|
return $count; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub get_current_row { |
379
|
0
|
|
|
0
|
1
|
0
|
return shift->{'current_row'}; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub get_current_col { |
383
|
0
|
|
|
0
|
1
|
0
|
return shift->{'current_col'}; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub reset { |
387
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub add_cols { |
391
|
0
|
|
|
0
|
1
|
0
|
my ($self,$config) = @_; |
392
|
0
|
0
|
|
|
|
0
|
$config = [$config] unless ref $config eq 'ARRAY'; |
393
|
|
|
|
|
|
|
|
394
|
0
|
|
|
|
|
0
|
foreach (@$config) { |
395
|
0
|
0
|
|
|
|
0
|
next unless ref $_ eq 'HASH'; |
396
|
0
|
|
|
|
|
0
|
my ($name,$data,$pos) = @$_{(qw(name data before))}; |
397
|
0
|
|
|
|
|
0
|
my $max_pos = $self->get_col_count(); |
398
|
|
|
|
|
|
|
|
399
|
0
|
0
|
0
|
|
|
0
|
$pos = $self->_lookup_index(ucfirst $pos || '') || $max_pos unless defined $pos && $pos =~ /^\d+$/; |
|
|
|
0
|
|
|
|
|
400
|
0
|
0
|
|
|
|
0
|
$pos = $max_pos if $pos > $max_pos; |
401
|
0
|
0
|
|
|
|
0
|
$data = [$data] unless ref $data eq 'ARRAY'; |
402
|
|
|
|
|
|
|
|
403
|
0
|
|
|
|
|
0
|
splice(@{$self->{'fields_arry'}},$pos,0,$name); |
|
0
|
|
|
|
|
0
|
|
404
|
0
|
|
|
|
|
0
|
$self->_reset_fields_hash(); |
405
|
0
|
|
|
|
|
0
|
splice(@$_,$pos,0,_rotate($data)) for (@{$self->{rows}}); |
|
0
|
|
|
|
|
0
|
|
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
0
|
return $self; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub drop_cols { |
412
|
0
|
|
|
0
|
1
|
0
|
my ($self,$cols) = @_; |
413
|
0
|
|
|
|
|
0
|
$cols = $self->_refinate($cols); |
414
|
|
|
|
|
|
|
|
415
|
0
|
|
|
|
|
0
|
foreach my $col (@$cols) { |
416
|
0
|
|
|
|
|
0
|
my $index = delete $self->{'fields_hash'}->{$col}; |
417
|
0
|
|
|
|
|
0
|
splice(@{$self->{'fields_arry'}},$index,1); |
|
0
|
|
|
|
|
0
|
|
418
|
0
|
|
|
|
|
0
|
$self->_reset_fields_hash(); |
419
|
0
|
|
|
|
|
0
|
splice(@$_,$index,1) for (@{$self->{'rows'}}); |
|
0
|
|
|
|
|
0
|
|
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
0
|
|
|
|
|
0
|
return $self; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
###################### DEPRECATED ################################## |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub get_table { |
428
|
0
|
|
|
0
|
1
|
0
|
carp "get_table() is deprecated. Use output() instead"; |
429
|
0
|
|
|
|
|
0
|
output(@_); |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub modify_tag { |
433
|
0
|
|
|
0
|
1
|
0
|
carp "modify_tag() is deprecated. Use modify() instead"; |
434
|
0
|
|
|
|
|
0
|
modify(@_); |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub map_col { |
438
|
0
|
|
|
0
|
1
|
0
|
carp "map_col() is deprecated. Use map_cell() instead"; |
439
|
0
|
|
|
|
|
0
|
map_cell(@_); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
#################### UNDER THE HOOD ################################ |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# repeat: it only looks complicated |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub _build_table { |
447
|
7
|
|
|
7
|
|
11
|
my ($self) = @_; |
448
|
7
|
|
|
|
|
16
|
my $attribs = $self->{'global'}->{'table'}; |
449
|
|
|
|
|
|
|
|
450
|
7
|
|
|
|
|
5
|
my ($head,$body,$foot); |
451
|
7
|
|
|
|
|
13
|
$head = $self->_build_head; |
452
|
7
|
50
|
|
|
|
20
|
$body = $self->{'rows'} ? $self->_build_body : ''; |
453
|
7
|
100
|
|
|
|
15
|
$foot = $self->{'totals'} ? $self->_build_foot : ''; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# w3c says tfoot comes before tbody ... |
456
|
7
|
|
|
|
|
11
|
my $cdata = $head . $foot . $body; |
457
|
|
|
|
|
|
|
|
458
|
7
|
|
|
|
|
11
|
return _tag_it('table', $attribs, $cdata) . $N; |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub _build_head { |
462
|
7
|
|
|
7
|
|
7
|
my ($self) = @_; |
463
|
7
|
|
|
|
|
5
|
my ($attribs,$cdata,$caption); |
464
|
7
|
|
|
|
|
6
|
my $output = ''; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# build the |
493
|
7
|
|
|
|
|
11
|
$output .= $N.$T . _tag_it('thead', $attribs, $cdata) . $N; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub _build_head_colgroups { |
497
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
498
|
0
|
|
|
|
|
0
|
my (@cols,$output); |
499
|
|
|
|
|
|
|
|
500
|
0
|
0
|
|
|
|
0
|
return unless $self->{'colgroups'}; |
501
|
0
|
0
|
|
|
|
0
|
return undef unless @cols = @{$self->{'colgroups'}}; |
|
0
|
|
|
|
|
0
|
|
502
|
|
|
|
|
|
|
|
503
|
0
|
|
|
|
|
0
|
foreach (@cols) { |
504
|
0
|
|
|
|
|
0
|
$output .= $N.$T.$T . _tag_it('col', $_); |
505
|
|
|
|
|
|
|
} |
506
|
0
|
|
|
|
|
0
|
$output .= $N.$T; |
507
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
0
|
return $output; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub _build_head_row { |
512
|
7
|
|
|
7
|
|
5
|
my ($self) = @_; |
513
|
7
|
|
|
|
|
7
|
my $output = $N; |
514
|
7
|
|
|
|
|
8
|
my @copy = @{$self->{'fields_arry'}}; |
|
7
|
|
|
|
|
14
|
|
515
|
|
|
|
|
|
|
|
516
|
7
|
|
|
|
|
9
|
foreach my $field (@copy) { |
517
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
518
|
|
|
|
|
|
|
$self->{$field}->{'th'} || $self->{'head'}->{'th'}, |
519
|
14
|
|
33
|
|
|
58
|
$self->{'global'}->{'th'} || $self->{'head'}->{'th'}, |
|
|
|
33
|
|
|
|
|
520
|
|
|
|
|
|
|
); |
521
|
|
|
|
|
|
|
|
522
|
14
|
100
|
|
|
|
35
|
if (my $sub = $self->{'map_head'}->{$field}) { |
|
|
50
|
|
|
|
|
|
523
|
4
|
|
|
|
|
6
|
$field = $sub->($field); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
elsif (!$self->{'no_ucfirst'}) { |
526
|
10
|
|
|
|
|
19
|
$field = ucfirst( lc( $field ) ); |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# bug 21761 "Special XML characters should be expressed as entities" |
530
|
14
|
100
|
|
|
|
63
|
$field = $self->_xml_encode( $field ) if $self->{'encode_cells'}; |
531
|
|
|
|
|
|
|
|
532
|
14
|
|
|
|
|
25
|
$output .= $T.$T . _tag_it('th', $attribs, $field) . $N; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
7
|
|
|
|
|
12
|
return $output . $T; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub _build_body { |
539
|
|
|
|
|
|
|
|
540
|
7
|
|
|
7
|
|
7
|
my ($self) = @_; |
541
|
7
|
|
|
|
|
5
|
my $beg = 0; |
542
|
7
|
|
|
|
|
5
|
my $output; |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
# if a group was not set via set_group(), then use the entire 2-d array |
545
|
|
|
|
|
|
|
my @indicies = exists $self->{'body_breaks'} |
546
|
7
|
100
|
|
|
|
19
|
? @{$self->{'body_breaks'}} |
|
3
|
|
|
|
|
4
|
|
547
|
|
|
|
|
|
|
: ($self->get_row_count - 1); |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# the skinny here is to grab a slice of the rows, one for each group |
550
|
7
|
|
|
|
|
10
|
foreach my $end (@indicies) { |
551
|
7
|
|
50
|
|
|
14
|
my $body_group = $self->_build_body_group([@{$self->{'rows'}}[$beg..$end]]) || ''; |
552
|
7
|
|
33
|
|
|
23
|
my $attribs = $self->{'global'}->{'tbody'} || $self->{'body'}->{'tbody'}; |
553
|
7
|
|
|
|
|
9
|
my $cdata = $N . $body_group . $T; |
554
|
|
|
|
|
|
|
|
555
|
7
|
|
|
|
|
14
|
$output .= $T . _tag_it('tbody',$attribs,$cdata) . $N; |
556
|
7
|
|
|
|
|
14
|
$beg = $end + 1; |
557
|
|
|
|
|
|
|
} |
558
|
7
|
|
|
|
|
10
|
return $output; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub _build_body_group { |
562
|
|
|
|
|
|
|
|
563
|
7
|
|
|
7
|
|
25
|
my ($self,$chunk) = @_; |
564
|
7
|
|
|
|
|
6
|
my ($output,$cdata); |
565
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
566
|
7
|
|
|
|
|
17
|
$self->{'body'}->{'tr'}, $self->{'global'}->{'tr'} |
567
|
|
|
|
|
|
|
); |
568
|
7
|
|
|
|
|
10
|
my $pk_col = ''; |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# build the rows |
571
|
7
|
|
|
|
|
16
|
for my $i (0..$#$chunk) { |
572
|
14
|
|
|
|
|
10
|
my @row = @{$chunk->[$i]}; |
|
14
|
|
|
|
|
23
|
|
573
|
14
|
50
|
|
|
|
24
|
$pk_col = splice(@row,$self->{'pk_index'},1) if defined $self->{'pk_index'}; |
574
|
14
|
|
100
|
|
|
64
|
$cdata = $self->_build_body_row(\@row, ($i and $self->{'nodup'} or 0), $pk_col); |
575
|
14
|
|
|
|
|
20
|
$output .= $T . _tag_it('tr',$attribs,$cdata) . $N; |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# build the subtotal row if applicable |
579
|
7
|
50
|
|
|
|
7
|
if (my $subtotals = shift @{$self->{'sub_totals'}}) { |
|
7
|
|
|
|
|
18
|
|
580
|
0
|
|
|
|
|
0
|
$cdata = $self->_build_body_subtotal($subtotals); |
581
|
0
|
|
|
|
|
0
|
$output .= $T . _tag_it('tr',$attribs,$cdata) . $N; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
7
|
|
|
|
|
17
|
return $output; |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub _build_body_row { |
588
|
14
|
|
|
14
|
|
19
|
my ($self,$row,$nodup,$pk) = @_; |
589
|
|
|
|
|
|
|
|
590
|
14
|
|
|
|
|
12
|
my $group = $self->{'group'}; |
591
|
14
|
100
|
|
|
|
27
|
my $index = $self->_lookup_index($group) if $group; |
592
|
14
|
|
|
|
|
11
|
my $output = $N; |
593
|
|
|
|
|
|
|
|
594
|
14
|
|
|
|
|
14
|
$self->{'current_row'} = $pk; |
595
|
|
|
|
|
|
|
|
596
|
14
|
|
|
|
|
23
|
for (0..$#$row) { |
597
|
28
|
|
|
|
|
36
|
my $name = $self->_lookup_name($_); |
598
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
599
|
|
|
|
|
|
|
$self->{$name}->{'td'} || $self->{'body'}->{'td'}, |
600
|
28
|
|
33
|
|
|
105
|
$self->{'global'}->{'td'} || $self->{'body'}->{'td'}, |
|
|
|
33
|
|
|
|
|
601
|
|
|
|
|
|
|
); |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# suppress warnings AND keep 0 from becoming |
604
|
28
|
50
|
|
|
|
45
|
$row->[$_] = '' unless defined($row->[$_]); |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# bug 21761 "Special XML characters should be expressed as entities" |
607
|
28
|
100
|
|
|
|
45
|
$row->[$_] = $self->_xml_encode( $row->[$_] ) if $self->{'encode_cells'}; |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
my $cdata = ($row->[$_] =~ /^\s+$/) |
610
|
28
|
50
|
|
|
|
58
|
? $self->{'null_value'} |
611
|
|
|
|
|
|
|
: $row->[$_] |
612
|
|
|
|
|
|
|
; |
613
|
|
|
|
|
|
|
|
614
|
28
|
|
|
|
|
27
|
$self->{'current_col'} = $name; |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
$cdata = ($nodup and $index == $_) |
617
|
|
|
|
|
|
|
? $self->{'nodup'} |
618
|
28
|
100
|
100
|
|
|
88
|
: _map_it($self->{'map_cell'}->{$name},$cdata) |
619
|
|
|
|
|
|
|
; |
620
|
|
|
|
|
|
|
|
621
|
28
|
|
|
|
|
50
|
$output .= $T.$T . _tag_it('td', $attribs, $cdata) . $N; |
622
|
|
|
|
|
|
|
} |
623
|
14
|
|
|
|
|
26
|
return $output . $T; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
sub _build_body_subtotal { |
627
|
0
|
|
|
0
|
|
0
|
my ($self,$row) = @_; |
628
|
0
|
|
|
|
|
0
|
my $output = $N; |
629
|
|
|
|
|
|
|
|
630
|
0
|
0
|
|
|
|
0
|
return '' unless $row; |
631
|
|
|
|
|
|
|
|
632
|
0
|
|
|
|
|
0
|
for (0..$#$row) { |
633
|
0
|
|
|
|
|
0
|
my $name = $self->_lookup_name($_); |
634
|
0
|
|
|
|
|
0
|
my $sum = ($row->[$_]); |
635
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
636
|
|
|
|
|
|
|
$self->{$name}->{'th'} || $self->{'body'}->{'th'}, |
637
|
0
|
|
0
|
|
|
0
|
$self->{'global'}->{'th'} || $self->{'body'}->{'th'}, |
|
|
|
0
|
|
|
|
|
638
|
|
|
|
|
|
|
); |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# use sprintf if mask was supplied |
641
|
0
|
0
|
0
|
|
|
0
|
if ($self->{'subtotals_mask'} and defined $sum) { |
642
|
0
|
|
|
|
|
0
|
$sum = sprintf($self->{'subtotals_mask'},$sum); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
else { |
645
|
0
|
0
|
|
|
|
0
|
$sum = (defined $sum) ? $sum : $self->{'null_value'}; |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
|
648
|
0
|
|
|
|
|
0
|
$output .= $T.$T . _tag_it('th', $attribs, $sum) . $N; |
649
|
|
|
|
|
|
|
} |
650
|
0
|
|
|
|
|
0
|
return $output . $T; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub _build_foot { |
654
|
2
|
|
|
2
|
|
3
|
my ($self) = @_; |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my $tr_attribs = _merge_attribs( |
657
|
|
|
|
|
|
|
# notice that foot is 1st and global 2nd - different than rest |
658
|
2
|
|
|
|
|
5
|
$self->{'foot'}->{'tr'}, $self->{'global'}->{'tr'} |
659
|
|
|
|
|
|
|
); |
660
|
2
|
|
|
|
|
6
|
my $tr_cdata = $self->_build_foot_row(); |
661
|
|
|
|
|
|
|
|
662
|
2
|
|
33
|
|
|
6
|
my $attribs = $self->{'foot'}->{'tfoot'} || $self->{'global'}->{'tfoot'}; |
663
|
2
|
|
|
|
|
4
|
my $cdata = $N.$T . _tag_it('tr', $tr_attribs, $tr_cdata) . $N.$T; |
664
|
|
|
|
|
|
|
|
665
|
2
|
|
|
|
|
3
|
return $T . _tag_it('tfoot',$attribs,$cdata) . $N; |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub _build_foot_row { |
669
|
2
|
|
|
2
|
|
1
|
my ($self) = @_; |
670
|
|
|
|
|
|
|
|
671
|
2
|
|
|
|
|
3
|
my $output = $N; |
672
|
2
|
|
|
|
|
2
|
my $row = $self->{'totals'}; |
673
|
|
|
|
|
|
|
|
674
|
2
|
|
|
|
|
11
|
for (0..$#$row) { |
675
|
4
|
|
|
|
|
5
|
my $name = $self->_lookup_name($_); |
676
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
677
|
|
|
|
|
|
|
$self->{$name}->{'th'} || $self->{'foot'}->{'th'}, |
678
|
4
|
|
33
|
|
|
17
|
$self->{'global'}->{'th'} || $self->{'foot'}->{'th'}, |
|
|
|
33
|
|
|
|
|
679
|
|
|
|
|
|
|
); |
680
|
4
|
|
|
|
|
4
|
my $sum = ($row->[$_]); |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
# use sprintf if mask was supplied |
683
|
4
|
50
|
33
|
|
|
10
|
if ($self->{'totals_mask'} and defined $sum) { |
684
|
0
|
|
|
|
|
0
|
$sum = sprintf($self->{'totals_mask'},$sum) |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
else { |
687
|
4
|
100
|
|
|
|
6
|
$sum = defined $sum ? $sum : $self->{'null_value'}; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
4
|
|
|
|
|
6
|
$output .= $T.$T . _tag_it('th', $attribs, $sum) . $N; |
691
|
|
|
|
|
|
|
} |
692
|
2
|
|
|
|
|
3
|
return $output . $T; |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# builds a tag and it's enclosed data |
696
|
|
|
|
|
|
|
sub _tag_it { |
697
|
92
|
|
|
92
|
|
82
|
my ($name,$attribs,$cdata) = @_; |
698
|
92
|
|
|
|
|
91
|
my $text = "<\L$name\E"; |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# build the attributes if any - skip blank vals |
701
|
92
|
|
|
|
|
57
|
for my $k (sort keys %{$attribs}) { |
|
92
|
|
|
|
|
158
|
|
702
|
4
|
|
|
|
|
5
|
my $v = $attribs->{$k}; |
703
|
4
|
50
|
|
|
|
8
|
if (ref $v eq 'HASH') { |
704
|
|
|
|
|
|
|
$v = join('; ', map { |
705
|
4
|
|
|
|
|
6
|
my $attrib = $_; |
|
4
|
|
|
|
|
4
|
|
706
|
|
|
|
|
|
|
my $value = (ref $v->{$_} eq 'ARRAY') |
707
|
|
|
|
|
|
|
? _rotate($v->{$_}) |
708
|
4
|
50
|
|
|
|
12
|
: $v->{$_}; |
709
|
4
|
|
50
|
|
|
13
|
join(': ',$attrib,$value||''); |
710
|
|
|
|
|
|
|
} sort keys %$v) . ';'; |
711
|
|
|
|
|
|
|
} |
712
|
4
|
50
|
|
|
|
8
|
$v = _rotate($v) if (ref $v eq 'ARRAY'); |
713
|
4
|
50
|
|
|
|
13
|
$text .= qq| \L$k\E="$v"| unless $v =~ /^$/; |
714
|
|
|
|
|
|
|
} |
715
|
92
|
50
|
|
|
|
351
|
$text .= (defined $cdata) ? ">$cdata" : '/>'; |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# used by map_cell() and map_head() |
719
|
|
|
|
|
|
|
sub _map_it { |
720
|
25
|
|
|
25
|
|
27
|
my ($sub,$datum) = @_; |
721
|
25
|
50
|
|
|
|
50
|
return $datum unless $sub; |
722
|
0
|
|
|
|
|
0
|
return $datum = $sub->($datum); |
723
|
|
|
|
|
|
|
} |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
# used by calc_totals() and calc_subtotals() |
726
|
|
|
|
|
|
|
sub _total_chunk { |
727
|
1
|
|
|
1
|
|
1
|
my ($self,$chunk,$indexes) = @_; |
728
|
1
|
|
|
|
|
2
|
my %totals; |
729
|
|
|
|
|
|
|
|
730
|
1
|
|
|
|
|
1
|
foreach my $row (@$chunk) { |
731
|
2
|
|
|
|
|
3
|
foreach (@$indexes) { |
732
|
2
|
50
|
|
|
|
11
|
$totals{$_} += $row->[$_] if $row->[$_] =~ /^[-0-9\.]+$/; |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
|
736
|
1
|
100
|
|
|
|
5
|
return [ map { defined $totals{$_} ? $totals{$_} : undef } (0 .. $self->get_col_count() - 1) ]; |
|
2
|
|
|
|
|
7
|
|
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
# uses %ESCAPES to convert the '4 Horsemen' of XML |
740
|
|
|
|
|
|
|
# big thanks to Matt Sergeant |
741
|
|
|
|
|
|
|
sub _xml_encode { |
742
|
6
|
|
|
6
|
|
6
|
my ($self,$str) = @_; |
743
|
6
|
|
|
|
|
13
|
$str =~ s/([&<>"])/$ESCAPES{$1}/ge; |
|
24
|
|
|
|
|
42
|
|
744
|
6
|
|
|
|
|
10
|
return $str; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# returns value of and moves first element to last |
748
|
|
|
|
|
|
|
sub _rotate { |
749
|
4
|
|
|
4
|
|
2
|
my $ref = shift; |
750
|
4
|
|
|
|
|
3
|
my $next = shift @$ref; |
751
|
4
|
|
|
|
|
5
|
push @$ref, $next; |
752
|
4
|
|
|
|
|
5
|
return $next; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
# always returns an array ref |
756
|
|
|
|
|
|
|
sub _refinate { |
757
|
3
|
|
|
3
|
|
4
|
my ($self,$ref) = @_; |
758
|
3
|
50
|
33
|
|
|
9
|
$ref = undef if ref($ref) eq 'ARRAY' && scalar( @$ref ) < 1; |
759
|
3
|
50
|
|
|
|
6
|
$ref = [@{$self->{'fields_arry'}}] unless defined $ref; |
|
0
|
|
|
|
|
0
|
|
760
|
3
|
50
|
|
|
|
9
|
$ref = [$ref] unless ref $ref eq 'ARRAY'; |
761
|
3
|
50
|
0
|
|
|
5
|
return [map {$_ =~ /^\d+$/ ? $self->_lookup_name($_) || $_ : $_} @$ref]; |
|
3
|
|
|
|
|
18
|
|
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
sub _merge_attribs { |
765
|
62
|
|
|
62
|
|
57
|
my ($hash1,$hash2) = @_; |
766
|
|
|
|
|
|
|
|
767
|
62
|
100
|
|
|
|
98
|
return $hash1 unless $hash2; |
768
|
4
|
50
|
|
|
|
10
|
return $hash2 unless $hash1; |
769
|
|
|
|
|
|
|
|
770
|
4
|
|
|
|
|
10
|
return {%$hash2,%$hash1}; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
sub _lookup_name { |
774
|
32
|
|
|
32
|
|
38
|
my ($self,$index) = @_; |
775
|
32
|
|
|
|
|
41
|
return $self->{'fields_arry'}->[$index]; |
776
|
|
|
|
|
|
|
} |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub _lookup_index { |
779
|
6
|
|
|
6
|
|
5
|
my ($self,$name) = @_; |
780
|
6
|
|
|
|
|
8
|
return $self->{'fields_hash'}->{$name}; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
sub _reset_fields_hash { |
784
|
2
|
|
|
2
|
|
2
|
my $self = shift; |
785
|
2
|
|
|
|
|
3
|
my $i = 0; |
786
|
2
|
|
|
|
|
2
|
$self->{fields_hash} = { map { $_ => $i++ } @{$self->{fields_arry}} }; |
|
4
|
|
|
|
|
13
|
|
|
2
|
|
|
|
|
5
|
|
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
# assigns a non-DBI supplied data table (2D array ref) |
790
|
|
|
|
|
|
|
sub _do_black_magic { |
791
|
2
|
|
|
2
|
|
3
|
my ($self,$ref,$headers) = @_; |
792
|
2
|
50
|
|
|
|
10
|
croak "bad data" unless ref( $ref->[0] ) eq 'ARRAY'; |
793
|
2
|
50
|
|
|
|
22
|
$self->{'fields_arry'} = $headers ? [@$headers] : [ @{ shift @$ref } ]; |
|
2
|
|
|
|
|
11
|
|
794
|
2
|
|
|
|
|
11
|
$self->{'fields_hash'} = $self->_reset_fields_hash(); |
795
|
2
|
|
|
|
|
5
|
$self->{'rows'} = $ref; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# disconnect database handle if i created it |
799
|
|
|
|
|
|
|
sub DESTROY { |
800
|
2
|
|
|
2
|
|
864
|
my ($self) = @_; |
801
|
2
|
50
|
|
|
|
12
|
unless ($self->{'keep_alive'}) { |
802
|
2
|
50
|
|
|
|
82
|
$self->{'dbh'}->disconnect if defined $self->{'dbh'}; |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
1; |
807
|
|
|
|
|
|
|
__END__ |