| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package DBIx::XHTML_Table; |
|
2
|
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
54272
|
use strict; |
|
|
7
|
|
|
|
|
11
|
|
|
|
7
|
|
|
|
|
198
|
|
|
4
|
7
|
|
|
7
|
|
27
|
use warnings; |
|
|
7
|
|
|
|
|
9
|
|
|
|
7
|
|
|
|
|
254
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '1.46'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
7
|
|
|
7
|
|
10227
|
use DBI; |
|
|
7
|
|
|
|
|
99550
|
|
|
|
7
|
|
|
|
|
432
|
|
|
8
|
7
|
|
|
7
|
|
62
|
use Carp; |
|
|
7
|
|
|
|
|
9
|
|
|
|
7
|
|
|
|
|
463
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# GLOBALS |
|
11
|
7
|
|
|
7
|
|
32
|
use vars qw(%ESCAPES $T $N); |
|
|
7
|
|
|
|
|
7
|
|
|
|
7
|
|
|
|
|
36995
|
|
|
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
|
97
|
my $class = shift; |
|
25
|
2
|
|
|
|
|
9
|
my $self = { |
|
26
|
|
|
|
|
|
|
null_value => ' ', |
|
27
|
|
|
|
|
|
|
}; |
|
28
|
2
|
|
|
|
|
6
|
bless $self, $class; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# last arg might be GTCH (global table config hash) |
|
31
|
2
|
50
|
|
|
|
13
|
$self->{'global'} = pop if ref $_[$#_] eq 'HASH'; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# note: disconnected handles aren't caught :( |
|
34
|
|
|
|
|
|
|
|
|
35
|
2
|
50
|
|
|
|
18
|
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
|
|
|
|
|
12
|
$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
|
|
|
|
|
7
|
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
|
354
|
my ($self,$config,$no_ws) = @_; |
|
86
|
7
|
50
|
0
|
|
|
25
|
carp "can't call output(): no data" and return '' unless $self->{'rows'}; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# have to deprecate old arguments ... |
|
89
|
7
|
50
|
|
|
|
15
|
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
|
|
|
49
|
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
|
|
|
|
|
18
|
$self->{'no_head'} = $config->{'no_head'}; |
|
99
|
7
|
|
|
|
|
11
|
$self->{'no_ucfirst'} = $config->{'no_ucfirst'}; |
|
100
|
7
|
50
|
|
|
|
23
|
$N = $T = '' if $config->{'no_indent'}; |
|
101
|
7
|
50
|
|
|
|
19
|
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
|
|
|
|
|
17
|
return $self->_build_table(); |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub modify { |
|
111
|
1
|
|
|
1
|
1
|
2
|
my ($self,$tag,$attribs,$cols) = @_; |
|
112
|
1
|
|
|
|
|
2
|
$tag = lc $tag; |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# apply attributes to specified columns |
|
115
|
1
|
50
|
|
|
|
4
|
if (ref $attribs eq 'HASH') { |
|
116
|
1
|
50
|
33
|
|
|
6
|
$cols = 'global' unless defined( $cols) && length( $cols ); |
|
117
|
1
|
|
|
|
|
2
|
$cols = $self->_refinate($cols); |
|
118
|
|
|
|
|
|
|
|
|
119
|
1
|
|
|
|
|
6
|
while (my($attr,$val) = each %$attribs) { |
|
120
|
1
|
|
|
|
|
7
|
$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
|
|
|
|
|
2
|
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
|
|
|
|
|
5
|
$cols = $self->_refinate($cols); |
|
171
|
1
|
|
|
|
|
3
|
for (@$cols) { |
|
172
|
1
|
|
|
|
|
1
|
my $key; |
|
173
|
1
|
50
|
|
|
|
4
|
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
|
|
|
|
|
3
|
$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
|
|
|
|
|
3
|
$cols = $self->_refinate($cols); |
|
206
|
|
|
|
|
|
|
|
|
207
|
1
|
|
|
|
|
1
|
my @indexes; |
|
208
|
1
|
|
|
|
|
3
|
for (@$cols) { |
|
209
|
1
|
|
|
|
|
1
|
my $index; |
|
210
|
1
|
50
|
|
|
|
3
|
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
|
|
|
|
|
4
|
$self->{'totals'} = $self->_total_chunk($self->{'rows'},\@indexes); |
|
226
|
|
|
|
|
|
|
|
|
227
|
1
|
|
|
|
|
3
|
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
|
2
|
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
|
|
|
|
8
|
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
|
|
|
|
|
2
|
$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
|
|
|
|
|
3
|
for my $i (0..$self->get_row_count - 1) { |
|
340
|
2
|
|
|
|
|
3
|
my $new = $self->{'rows'}->[$i]->[$index]; |
|
341
|
2
|
50
|
|
|
|
4
|
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
|
|
|
|
|
3
|
|
|
346
|
|
|
|
|
|
|
|
|
347
|
1
|
|
|
|
|
2
|
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
|
2
|
my ($self) = @_; |
|
368
|
1
|
|
|
|
|
2
|
my $count = scalar @{$self->{'fields_arry'}}; |
|
|
1
|
|
|
|
|
2
|
|
|
369
|
1
|
|
|
|
|
3
|
return $count; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub get_row_count { |
|
373
|
6
|
|
|
6
|
1
|
7
|
my ($self) = @_; |
|
374
|
6
|
|
|
|
|
7
|
my $count = scalar @{$self->{'rows'}}; |
|
|
6
|
|
|
|
|
12
|
|
|
375
|
6
|
|
|
|
|
14
|
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
|
|
10
|
my ($self) = @_; |
|
448
|
7
|
|
|
|
|
12
|
my $attribs = $self->{'global'}->{'table'}; |
|
449
|
|
|
|
|
|
|
|
|
450
|
7
|
|
|
|
|
7
|
my ($head,$body,$foot); |
|
451
|
7
|
|
|
|
|
16
|
$head = $self->_build_head; |
|
452
|
7
|
50
|
|
|
|
38
|
$body = $self->{'rows'} ? $self->_build_body : ''; |
|
453
|
7
|
100
|
|
|
|
18
|
$foot = $self->{'totals'} ? $self->_build_foot : ''; |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# w3c says tfoot comes before tbody ... |
|
456
|
7
|
|
|
|
|
14
|
my $cdata = $head . $foot . $body; |
|
457
|
|
|
|
|
|
|
|
|
458
|
7
|
|
|
|
|
18
|
return _tag_it('table', $attribs, $cdata) . $N; |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub _build_head { |
|
462
|
7
|
|
|
7
|
|
10
|
my ($self) = @_; |
|
463
|
7
|
|
|
|
|
6
|
my ($attribs,$cdata,$caption); |
|
464
|
7
|
|
|
|
|
9
|
my $output = ''; |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# build the tag if applicable |
|
467
|
7
|
50
|
|
|
|
19
|
if ($caption = $self->{'global'}->{'caption'}) { |
|
468
|
0
|
|
|
|
|
0
|
$attribs = $self->{'global'}->{'caption_attribs'}; |
|
469
|
0
|
0
|
|
|
|
0
|
$cdata = $self->{'encode_cells'} ? $self->_xml_encode($caption) : $caption; |
|
470
|
0
|
|
|
|
|
0
|
$output .= $N.$T . _tag_it('caption', $attribs, $cdata); |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# build the tags if applicable |
|
474
|
7
|
50
|
|
|
|
15
|
if ($attribs = $self->{'global'}->{'colgroup'}) { |
|
475
|
0
|
|
|
|
|
0
|
$cdata = $self->_build_head_colgroups(); |
|
476
|
0
|
|
|
|
|
0
|
$output .= $N.$T . _tag_it('colgroup', $attribs, $cdata); |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# go ahead and stop if they don't want the head |
|
480
|
7
|
50
|
|
|
|
18
|
return "$output\n" if $self->{'no_head'}; |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# prepare |
tag info
|
483
|
|
|
|
|
|
|
my $tr_attribs = _merge_attribs( |
|
484
|
7
|
|
|
|
|
30
|
$self->{'head'}->{'tr'}, $self->{'global'}->{'tr'} |
|
485
|
|
|
|
|
|
|
); |
|
486
|
7
|
|
|
|
|
24
|
my $tr_cdata = $self->_build_head_row(); |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# prepare the tag info
|
489
|
7
|
|
33
|
|
|
34
|
$attribs = $self->{'head'}->{'thead'} || $self->{'global'}->{'thead'}; |
|
490
|
7
|
|
|
|
|
23
|
$cdata = $N.$T . _tag_it('tr', $tr_attribs, $tr_cdata) . $N.$T; |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# add the tag to the output
|
493
|
7
|
|
|
|
|
16
|
$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
|
|
9
|
my ($self) = @_; |
|
513
|
7
|
|
|
|
|
9
|
my $output = $N; |
|
514
|
7
|
|
|
|
|
8
|
my @copy = @{$self->{'fields_arry'}}; |
|
|
7
|
|
|
|
|
23
|
|
|
515
|
|
|
|
|
|
|
|
|
516
|
7
|
|
|
|
|
14
|
foreach my $field (@copy) { |
|
517
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
|
518
|
|
|
|
|
|
|
$self->{$field}->{'th'} || $self->{'head'}->{'th'}, |
|
519
|
14
|
|
33
|
|
|
68
|
$self->{'global'}->{'th'} || $self->{'head'}->{'th'}, |
|
|
|
|
33
|
|
|
|
|
|
520
|
|
|
|
|
|
|
); |
|
521
|
|
|
|
|
|
|
|
|
522
|
14
|
100
|
|
|
|
40
|
if (my $sub = $self->{'map_head'}->{$field}) { |
|
|
|
50
|
|
|
|
|
|
|
523
|
4
|
|
|
|
|
7
|
$field = $sub->($field); |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
elsif (!$self->{'no_ucfirst'}) { |
|
526
|
10
|
|
|
|
|
23
|
$field = ucfirst( lc( $field ) ); |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# bug 21761 "Special XML characters should be expressed as entities" |
|
530
|
14
|
100
|
|
|
|
74
|
$field = $self->_xml_encode( $field ) if $self->{'encode_cells'}; |
|
531
|
|
|
|
|
|
|
|
|
532
|
14
|
|
|
|
|
30
|
$output .= $T.$T . _tag_it('th', $attribs, $field) . $N; |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
|
|
535
|
7
|
|
|
|
|
21
|
return $output . $T; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub _build_body { |
|
539
|
|
|
|
|
|
|
|
|
540
|
7
|
|
|
7
|
|
10
|
my ($self) = @_; |
|
541
|
7
|
|
|
|
|
7
|
my $beg = 0; |
|
542
|
7
|
|
|
|
|
8
|
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
|
|
|
|
20
|
? @{$self->{'body_breaks'}} |
|
|
3
|
|
|
|
|
6
|
|
|
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
|
|
|
|
|
12
|
foreach my $end (@indicies) { |
|
551
|
7
|
|
50
|
|
|
14
|
my $body_group = $self->_build_body_group([@{$self->{'rows'}}[$beg..$end]]) || ''; |
|
552
|
7
|
|
33
|
|
|
28
|
my $attribs = $self->{'global'}->{'tbody'} || $self->{'body'}->{'tbody'}; |
|
553
|
7
|
|
|
|
|
13
|
my $cdata = $N . $body_group . $T; |
|
554
|
|
|
|
|
|
|
|
|
555
|
7
|
|
|
|
|
14
|
$output .= $T . _tag_it('tbody',$attribs,$cdata) . $N; |
|
556
|
7
|
|
|
|
|
15
|
$beg = $end + 1; |
|
557
|
|
|
|
|
|
|
} |
|
558
|
7
|
|
|
|
|
16
|
return $output; |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub _build_body_group { |
|
562
|
|
|
|
|
|
|
|
|
563
|
7
|
|
|
7
|
|
9
|
my ($self,$chunk) = @_; |
|
564
|
7
|
|
|
|
|
6
|
my ($output,$cdata); |
|
565
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
|
566
|
7
|
|
|
|
|
23
|
$self->{'body'}->{'tr'}, $self->{'global'}->{'tr'} |
|
567
|
|
|
|
|
|
|
); |
|
568
|
7
|
|
|
|
|
14
|
my $pk_col = ''; |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# build the rows |
|
571
|
7
|
|
|
|
|
22
|
for my $i (0..$#$chunk) { |
|
572
|
14
|
|
|
|
|
13
|
my @row = @{$chunk->[$i]}; |
|
|
14
|
|
|
|
|
30
|
|
|
573
|
14
|
50
|
|
|
|
34
|
$pk_col = splice(@row,$self->{'pk_index'},1) if defined $self->{'pk_index'}; |
|
574
|
14
|
|
100
|
|
|
81
|
$cdata = $self->_build_body_row(\@row, ($i and $self->{'nodup'} or 0), $pk_col); |
|
575
|
14
|
|
|
|
|
57
|
$output .= $T . _tag_it('tr',$attribs,$cdata) . $N; |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# build the subtotal row if applicable |
|
579
|
7
|
50
|
|
|
|
9
|
if (my $subtotals = shift @{$self->{'sub_totals'}}) { |
|
|
7
|
|
|
|
|
26
|
|
|
580
|
0
|
|
|
|
|
0
|
$cdata = $self->_build_body_subtotal($subtotals); |
|
581
|
0
|
|
|
|
|
0
|
$output .= $T . _tag_it('tr',$attribs,$cdata) . $N; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
7
|
|
|
|
|
21
|
return $output; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub _build_body_row { |
|
588
|
14
|
|
|
14
|
|
21
|
my ($self,$row,$nodup,$pk) = @_; |
|
589
|
|
|
|
|
|
|
|
|
590
|
14
|
|
|
|
|
13
|
my $group = $self->{'group'}; |
|
591
|
14
|
100
|
|
|
|
35
|
my $index = $self->_lookup_index($group) if $group; |
|
592
|
14
|
|
|
|
|
18
|
my $output = $N; |
|
593
|
|
|
|
|
|
|
|
|
594
|
14
|
|
|
|
|
23
|
$self->{'current_row'} = $pk; |
|
595
|
|
|
|
|
|
|
|
|
596
|
14
|
|
|
|
|
27
|
for (0..$#$row) { |
|
597
|
28
|
|
|
|
|
49
|
my $name = $self->_lookup_name($_); |
|
598
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
|
599
|
|
|
|
|
|
|
$self->{$name}->{'td'} || $self->{'body'}->{'td'}, |
|
600
|
28
|
|
33
|
|
|
129
|
$self->{'global'}->{'td'} || $self->{'body'}->{'td'}, |
|
|
|
|
33
|
|
|
|
|
|
601
|
|
|
|
|
|
|
); |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# suppress warnings AND keep 0 from becoming |
|
604
|
28
|
50
|
|
|
|
84
|
$row->[$_] = '' unless defined($row->[$_]); |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# bug 21761 "Special XML characters should be expressed as entities" |
|
607
|
28
|
100
|
|
|
|
55
|
$row->[$_] = $self->_xml_encode( $row->[$_] ) if $self->{'encode_cells'}; |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
my $cdata = ($row->[$_] =~ /^\s+$/) |
|
610
|
28
|
50
|
|
|
|
89
|
? $self->{'null_value'} |
|
611
|
|
|
|
|
|
|
: $row->[$_] |
|
612
|
|
|
|
|
|
|
; |
|
613
|
|
|
|
|
|
|
|
|
614
|
28
|
|
|
|
|
47
|
$self->{'current_col'} = $name; |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
$cdata = ($nodup and $index == $_) |
|
617
|
|
|
|
|
|
|
? $self->{'nodup'} |
|
618
|
28
|
100
|
100
|
|
|
110
|
: _map_it($self->{'map_cell'}->{$name},$cdata) |
|
619
|
|
|
|
|
|
|
; |
|
620
|
|
|
|
|
|
|
|
|
621
|
28
|
|
|
|
|
60
|
$output .= $T.$T . _tag_it('td', $attribs, $cdata) . $N; |
|
622
|
|
|
|
|
|
|
} |
|
623
|
14
|
|
|
|
|
39
|
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
|
|
8
|
my ($self) = @_; |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my $tr_attribs = _merge_attribs( |
|
657
|
|
|
|
|
|
|
# notice that foot is 1st and global 2nd - different than rest |
|
658
|
2
|
|
|
|
|
6
|
$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
|
|
2
|
my ($self) = @_; |
|
670
|
|
|
|
|
|
|
|
|
671
|
2
|
|
|
|
|
3
|
my $output = $N; |
|
672
|
2
|
|
|
|
|
3
|
my $row = $self->{'totals'}; |
|
673
|
|
|
|
|
|
|
|
|
674
|
2
|
|
|
|
|
4
|
for (0..$#$row) { |
|
675
|
4
|
|
|
|
|
6
|
my $name = $self->_lookup_name($_); |
|
676
|
|
|
|
|
|
|
my $attribs = _merge_attribs( |
|
677
|
|
|
|
|
|
|
$self->{$name}->{'th'} || $self->{'foot'}->{'th'}, |
|
678
|
4
|
|
33
|
|
|
18
|
$self->{'global'}->{'th'} || $self->{'foot'}->{'th'}, |
|
|
|
|
33
|
|
|
|
|
|
679
|
|
|
|
|
|
|
); |
|
680
|
4
|
|
|
|
|
5
|
my $sum = ($row->[$_]); |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
# use sprintf if mask was supplied |
|
683
|
4
|
50
|
33
|
|
|
12
|
if ($self->{'totals_mask'} and defined $sum) { |
|
684
|
0
|
|
|
|
|
0
|
$sum = sprintf($self->{'totals_mask'},$sum) |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
else { |
|
687
|
4
|
100
|
|
|
|
8
|
$sum = defined $sum ? $sum : $self->{'null_value'}; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
4
|
|
|
|
|
9
|
$output .= $T.$T . _tag_it('th', $attribs, $sum) . $N; |
|
691
|
|
|
|
|
|
|
} |
|
692
|
2
|
|
|
|
|
4
|
return $output . $T; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# builds a tag and it's enclosed data |
|
696
|
|
|
|
|
|
|
sub _tag_it { |
|
697
|
92
|
|
|
92
|
|
106
|
my ($name,$attribs,$cdata) = @_; |
|
698
|
92
|
|
|
|
|
108
|
my $text = "<\L$name\E"; |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# build the attributes if any - skip blank vals |
|
701
|
92
|
|
|
|
|
77
|
for my $k (sort keys %{$attribs}) { |
|
|
92
|
|
|
|
|
207
|
|
|
702
|
4
|
|
|
|
|
5
|
my $v = $attribs->{$k}; |
|
703
|
4
|
50
|
|
|
|
9
|
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
|
|
|
|
10
|
: $v->{$_}; |
|
709
|
4
|
|
50
|
|
|
19
|
join(': ',$attrib,$value||''); |
|
710
|
|
|
|
|
|
|
} sort keys %$v) . ';'; |
|
711
|
|
|
|
|
|
|
} |
|
712
|
4
|
50
|
|
|
|
7
|
$v = _rotate($v) if (ref $v eq 'ARRAY'); |
|
713
|
4
|
50
|
|
|
|
18
|
$text .= qq| \L$k\E="$v"| unless $v =~ /^$/; |
|
714
|
|
|
|
|
|
|
} |
|
715
|
92
|
50
|
|
|
|
457
|
$text .= (defined $cdata) ? ">$cdata\L$name\E>" : '/>'; |
|
716
|
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# used by map_cell() and map_head() |
|
719
|
|
|
|
|
|
|
sub _map_it { |
|
720
|
25
|
|
|
25
|
|
35
|
my ($sub,$datum) = @_; |
|
721
|
25
|
50
|
|
|
|
57
|
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
|
|
|
|
|
2
|
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
|
|
|
|
24
|
return [ map { defined $totals{$_} ? $totals{$_} : undef } (0 .. $self->get_col_count() - 1) ]; |
|
|
2
|
|
|
|
|
8
|
|
|
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
|
|
13
|
my ($self,$str) = @_; |
|
743
|
6
|
|
|
|
|
27
|
$str =~ s/([&<>"])/$ESCAPES{$1}/ge; |
|
|
24
|
|
|
|
|
87
|
|
|
744
|
6
|
|
|
|
|
17
|
return $str; |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# returns value of and moves first element to last |
|
748
|
|
|
|
|
|
|
sub _rotate { |
|
749
|
4
|
|
|
4
|
|
4
|
my $ref = shift; |
|
750
|
4
|
|
|
|
|
299
|
my $next = shift @$ref; |
|
751
|
4
|
|
|
|
|
15
|
push @$ref, $next; |
|
752
|
4
|
|
|
|
|
6
|
return $next; |
|
753
|
|
|
|
|
|
|
} |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
# always returns an array ref |
|
756
|
|
|
|
|
|
|
sub _refinate { |
|
757
|
3
|
|
|
3
|
|
3
|
my ($self,$ref) = @_; |
|
758
|
3
|
50
|
33
|
|
|
8
|
$ref = undef if ref($ref) eq 'ARRAY' && scalar( @$ref ) < 1; |
|
759
|
3
|
50
|
|
|
|
10
|
$ref = [@{$self->{'fields_arry'}}] unless defined $ref; |
|
|
0
|
|
|
|
|
0
|
|
|
760
|
3
|
50
|
|
|
|
9
|
$ref = [$ref] unless ref $ref eq 'ARRAY'; |
|
761
|
3
|
50
|
0
|
|
|
4
|
return [map {$_ =~ /^\d+$/ ? $self->_lookup_name($_) || $_ : $_} @$ref]; |
|
|
3
|
|
|
|
|
17
|
|
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
sub _merge_attribs { |
|
765
|
62
|
|
|
62
|
|
73
|
my ($hash1,$hash2) = @_; |
|
766
|
|
|
|
|
|
|
|
|
767
|
62
|
100
|
|
|
|
169
|
return $hash1 unless $hash2; |
|
768
|
4
|
50
|
|
|
|
6
|
return $hash2 unless $hash1; |
|
769
|
|
|
|
|
|
|
|
|
770
|
4
|
|
|
|
|
11
|
return {%$hash2,%$hash1}; |
|
771
|
|
|
|
|
|
|
} |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
sub _lookup_name { |
|
774
|
32
|
|
|
32
|
|
35
|
my ($self,$index) = @_; |
|
775
|
32
|
|
|
|
|
50
|
return $self->{'fields_arry'}->[$index]; |
|
776
|
|
|
|
|
|
|
} |
|
777
|
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub _lookup_index { |
|
779
|
6
|
|
|
6
|
|
4
|
my ($self,$name) = @_; |
|
780
|
6
|
|
|
|
|
19
|
return $self->{'fields_hash'}->{$name}; |
|
781
|
|
|
|
|
|
|
} |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
sub _reset_fields_hash { |
|
784
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
|
785
|
2
|
|
|
|
|
5
|
my $i = 0; |
|
786
|
2
|
|
|
|
|
3
|
$self->{fields_hash} = { map { $_ => $i++ } @{$self->{fields_arry}} }; |
|
|
4
|
|
|
|
|
21
|
|
|
|
2
|
|
|
|
|
6
|
|
|
787
|
|
|
|
|
|
|
} |
|
788
|
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
# assigns a non-DBI supplied data table (2D array ref) |
|
790
|
|
|
|
|
|
|
sub _do_black_magic { |
|
791
|
2
|
|
|
2
|
|
5
|
my ($self,$ref,$headers) = @_; |
|
792
|
2
|
50
|
|
|
|
11
|
croak "bad data" unless ref( $ref->[0] ) eq 'ARRAY'; |
|
793
|
2
|
50
|
|
|
|
7
|
$self->{'fields_arry'} = $headers ? [@$headers] : [ @{ shift @$ref } ]; |
|
|
2
|
|
|
|
|
16
|
|
|
794
|
2
|
|
|
|
|
10
|
$self->{'fields_hash'} = $self->_reset_fields_hash(); |
|
795
|
2
|
|
|
|
|
8
|
$self->{'rows'} = $ref; |
|
796
|
|
|
|
|
|
|
} |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# disconnect database handle if i created it |
|
799
|
|
|
|
|
|
|
sub DESTROY { |
|
800
|
2
|
|
|
2
|
|
931
|
my ($self) = @_; |
|
801
|
2
|
50
|
|
|
|
10
|
unless ($self->{'keep_alive'}) { |
|
802
|
2
|
50
|
|
|
|
87
|
$self->{'dbh'}->disconnect if defined $self->{'dbh'}; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
1; |
|
807
|
|
|
|
|
|
|
__END__ |
| |