Filename | /var/www/foswiki11/lib/Foswiki/Plugins/TablePlugin/Core.pm |
Statements | Executed 625 statements in 9.43ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
5 | 1 | 1 | 671µs | 1.77ms | handler | Foswiki::Plugins::TablePlugin::Core::
6 | 2 | 1 | 205µs | 205µs | _mergeHashes | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 174µs | 311µs | _parseAttributes | Foswiki::Plugins::TablePlugin::Core::
5 | 1 | 1 | 110µs | 327µs | _resetReusedVariables | Foswiki::Plugins::TablePlugin::Core::
13 | 5 | 1 | 65µs | 96µs | _debug | Foswiki::Plugins::TablePlugin::Core::
23 | 23 | 1 | 47µs | 47µs | _storeAttribute | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 29µs | 383µs | _initDefaults | Foswiki::Plugins::TablePlugin::Core::
6 | 6 | 1 | 22µs | 22µs | _arrayRefFromParam | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 21µs | 155µs | BEGIN@11 | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 16µs | 29µs | BEGIN@3 | Foswiki::Plugins::TablePlugin::
1 | 1 | 1 | 14µs | 30µs | BEGIN@1728 | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 12µs | 23µs | BEGIN@1730 | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 11µs | 11µs | BEGIN@10 | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 11µs | 327µs | _parseDefaultAttributes | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 10µs | 17µs | BEGIN@4 | Foswiki::Plugins::TablePlugin::
2 | 2 | 1 | 9µs | 16µs | _debugData | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 6µs | 6µs | BEGIN@8 | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 6µs | 14µs | _init | Foswiki::Plugins::TablePlugin::Core::
2 | 2 | 1 | 4µs | 4µs | _cleanParamValue | Foswiki::Plugins::TablePlugin::Core::
1 | 1 | 1 | 4µs | 4µs | BEGIN@9 | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | __ANON__[:1012] | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | __ANON__[:481] | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | __ANON__[:486] | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _addDefaultStyles | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _addHeadStyles | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendColNumberCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendFirstColumnCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendLastColumnCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendLastRowCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendRowNumberCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendSortedAscendingCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendSortedCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendSortedDescendingCssClass | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _appendToClassList | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _convertStringToDate | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _convertStringToNumber | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _createCssStyles | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _getCurrentSortDirection | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _getDefaultSortDirection | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _getImageTextForSorting | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _getIncludeParams | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _getNewSortDirection | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _headerRowCount | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _parseTableSpecificTableAttributes | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _processTableRow | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _setSortTypeForCells | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _stripHtml | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | _writeStyleToHead | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | addDefaultSizeUnit | Foswiki::Plugins::TablePlugin::Core::
0 | 0 | 0 | 0s | 0s | emitTable | Foswiki::Plugins::TablePlugin::Core::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | 2 | 31µs | 2 | 43µs | # spent 29µs (16+14) within Foswiki::Plugins::TablePlugin::BEGIN@3 which was called:
# once (16µs+14µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 3 # spent 29µs making 1 call to Foswiki::Plugins::TablePlugin::BEGIN@3
# spent 14µs making 1 call to strict::import |
4 | 2 | 34µs | 2 | 24µs | # spent 17µs (10+7) within Foswiki::Plugins::TablePlugin::BEGIN@4 which was called:
# once (10µs+7µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 4 # spent 17µs making 1 call to Foswiki::Plugins::TablePlugin::BEGIN@4
# spent 7µs making 1 call to warnings::import |
5 | |||||
6 | package Foswiki::Plugins::TablePlugin::Core; | ||||
7 | |||||
8 | 2 | 26µs | 1 | 6µs | # spent 6µs within Foswiki::Plugins::TablePlugin::Core::BEGIN@8 which was called:
# once (6µs+0s) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 8 # spent 6µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@8 |
9 | 2 | 22µs | 1 | 4µs | # spent 4µs within Foswiki::Plugins::TablePlugin::Core::BEGIN@9 which was called:
# once (4µs+0s) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 9 # spent 4µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@9 |
10 | 2 | 43µs | 1 | 11µs | # spent 11µs within Foswiki::Plugins::TablePlugin::Core::BEGIN@10 which was called:
# once (11µs+0s) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 10 # spent 11µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@10 |
11 | 2 | 6.57ms | 2 | 290µs | # spent 155µs (21+135) within Foswiki::Plugins::TablePlugin::Core::BEGIN@11 which was called:
# once (21µs+135µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 11 # spent 155µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@11
# spent 135µs making 1 call to Error::import |
12 | |||||
13 | 1 | 300ns | my @curTable; | ||
14 | 1 | 100ns | my $translationToken; | ||
15 | 1 | 100ns | my $insideTABLE; | ||
16 | 1 | 100ns | my $currTablePre; | ||
17 | 1 | 100ns | my $didWriteDefaultStyle; | ||
18 | 1 | 100ns | my $defaultAttrs; # to write generic table CSS | ||
19 | 1 | 100ns | my $tableSpecificAttrs; # to write table specific table CSS | ||
20 | 1 | 100ns | my $combinedTableAttrs; # default and specific table attributes | ||
21 | 1 | 800ns | my $styles = {}; # hash of default and specific styles | ||
22 | 1 | 600ns | my @messages = (); | ||
23 | |||||
24 | # not yet refactored: | ||||
25 | 1 | 100ns | my $tableCount; | ||
26 | 1 | 100ns | my $sortCol; | ||
27 | 1 | 100ns | my $MAX_SORT_COLS; | ||
28 | 1 | 100ns | my $requestedTable; | ||
29 | 1 | 100ns | my $up; | ||
30 | 1 | 100ns | my $sortTablesInText; | ||
31 | 1 | 100ns | my $sortAttachments; | ||
32 | 1 | 100ns | my $sortColFromUrl; | ||
33 | 1 | 0s | my $url; | ||
34 | 1 | 0s | my $currentSortDirection; | ||
35 | 1 | 0s | my @rowspan; | ||
36 | |||||
37 | 1 | 500ns | my $HEAD_ID_DEFAULT_STYLE = | ||
38 | 'TABLEPLUGIN_default'; # this name is part of the API, do not change | ||||
39 | 1 | 200ns | my $HEAD_ID_SPECIFIC_STYLE = | ||
40 | 'TABLEPLUGIN_specific'; # this name is part of the API, do not change | ||||
41 | |||||
42 | 1 | 2µs | my $PATTERN_TABLE = qr/%TABLE(?:{(.*?)})?%/; | ||
43 | 1 | 4µs | 1 | 3µs | my $URL_ICON = # spent 3µs making 1 call to Foswiki::Func::getPubUrlPath |
44 | Foswiki::Func::getPubUrlPath() . '/' | ||||
45 | . $Foswiki::cfg{SystemWebName} | ||||
46 | . '/DocumentGraphics/'; | ||||
47 | 1 | 9µs | 1 | 237µs | my $GIF_TABLE_SORT_ASCENDING = CGI::img( # spent 237µs making 1 call to CGI::AUTOLOAD |
48 | { | ||||
49 | src => $URL_ICON . 'tablesortup.gif', | ||||
50 | border => 0, | ||||
51 | width => 11, | ||||
52 | height => 13, | ||||
53 | alt => 'Sorted ascending', | ||||
54 | title => 'Sorted ascending' | ||||
55 | } | ||||
56 | ); | ||||
57 | |||||
58 | 1 | 5µs | 1 | 59µs | my $GIF_TABLE_SORT_DESCENDING = CGI::img( # spent 59µs making 1 call to CGI::img |
59 | { | ||||
60 | src => $URL_ICON . 'tablesortdown.gif', | ||||
61 | border => 0, | ||||
62 | width => 11, | ||||
63 | height => 13, | ||||
64 | alt => 'Sorted descending', | ||||
65 | title => 'Sorted descending' | ||||
66 | } | ||||
67 | ); | ||||
68 | |||||
69 | 1 | 4µs | 1 | 57µs | my $GIF_TABLE_SORT_BOTH = CGI::img( # spent 57µs making 1 call to CGI::img |
70 | { | ||||
71 | src => $URL_ICON . 'tablesortdiamond.gif', | ||||
72 | border => 0, | ||||
73 | width => 11, | ||||
74 | height => 13, | ||||
75 | alt => 'Sort', | ||||
76 | title => 'Sort' | ||||
77 | } | ||||
78 | ); | ||||
79 | 1 | 4µs | 1 | 210µs | my $CHAR_SORT_ASCENDING = CGI::span( { class => 'tableSortIcon tableSortUp' }, # spent 210µs making 1 call to CGI::AUTOLOAD |
80 | $GIF_TABLE_SORT_ASCENDING ); | ||||
81 | 1 | 3µs | 1 | 36µs | my $CHAR_SORT_DESCENDING = # spent 36µs making 1 call to CGI::span |
82 | CGI::span( { class => 'tableSortIcon tableSortDown' }, | ||||
83 | $GIF_TABLE_SORT_DESCENDING ); | ||||
84 | 1 | 2µs | 1 | 28µs | my $CHAR_SORT_BOTH = # spent 28µs making 1 call to CGI::span |
85 | CGI::span( { class => 'tableSortIcon tableSortUp' }, $GIF_TABLE_SORT_BOTH ); | ||||
86 | |||||
87 | 1 | 1µs | my $SORT_DIRECTION = { | ||
88 | 'ASCENDING' => 0, | ||||
89 | 'DESCENDING' => 1, | ||||
90 | 'NONE' => 2, | ||||
91 | }; | ||||
92 | |||||
93 | 1 | 2µs | my $PATTERN_ATTRIBUTE_SIZE = qr'([0-9]+)(px|%)*'o; | ||
94 | |||||
95 | 1 | 300ns | my $TABLE_RULES = {}; | ||
96 | 1 | 3µs | $TABLE_RULES->{all}->{TD} = $TABLE_RULES->{all}->{TH} = | ||
97 | $TABLE_RULES->{data_all}->{TD} = $TABLE_RULES->{header_all}->{TH} = | ||||
98 | 'border-style:solid'; | ||||
99 | 1 | 2µs | $TABLE_RULES->{none}->{TD} = $TABLE_RULES->{none}->{TH} = | ||
100 | $TABLE_RULES->{data_none}->{TD} = $TABLE_RULES->{header_none}->{TH} = | ||||
101 | 'border-style:none'; | ||||
102 | 1 | 2µs | $TABLE_RULES->{cols}->{TD} = $TABLE_RULES->{cols}->{TH} = | ||
103 | $TABLE_RULES->{data_cols}->{TD} = $TABLE_RULES->{header_cols}->{TH} = | ||||
104 | 'border-style:none solid'; | ||||
105 | 1 | 2µs | $TABLE_RULES->{rows}->{TD} = $TABLE_RULES->{rows}->{TH} = | ||
106 | $TABLE_RULES->{data_rows}->{TD} = $TABLE_RULES->{header_rows}->{TH} = | ||||
107 | 'border-style:solid none'; | ||||
108 | 1 | 500ns | $TABLE_RULES->{groups}->{TD} = 'border-style:none'; | ||
109 | 1 | 500ns | $TABLE_RULES->{groups}->{TH} = 'border-style:solid none'; | ||
110 | |||||
111 | 1 | 200ns | my $TABLE_FRAME = {}; | ||
112 | 1 | 400ns | $TABLE_FRAME->{void} = 'border-style:none'; | ||
113 | 1 | 400ns | $TABLE_FRAME->{above} = 'border-style:solid none none none'; | ||
114 | 1 | 300ns | $TABLE_FRAME->{below} = 'border-style:none none solid none'; | ||
115 | 1 | 400ns | $TABLE_FRAME->{lhs} = 'border-style:none none none solid'; | ||
116 | 1 | 300ns | $TABLE_FRAME->{rhs} = 'border-style:none solid none none'; | ||
117 | 1 | 300ns | $TABLE_FRAME->{hsides} = 'border-style:solid none solid none'; | ||
118 | 1 | 300ns | $TABLE_FRAME->{vsides} = 'border-style:none solid none solid'; | ||
119 | 1 | 700ns | $TABLE_FRAME->{box} = 'border-style:solid'; | ||
120 | 1 | 300ns | $TABLE_FRAME->{border} = 'border-style:solid'; | ||
121 | |||||
122 | # spent 14µs (6+8) within Foswiki::Plugins::TablePlugin::Core::_init which was called:
# once (6µs+8µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 70 of /var/www/foswiki11/lib/Foswiki/Plugins/TablePlugin.pm | ||||
123 | 1 | 900ns | 1 | 8µs | _debug("_init"); # spent 8µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_debug |
124 | 1 | 400ns | $translationToken = "\0"; | ||
125 | |||||
126 | # the maximum number of columns we will handle | ||||
127 | 1 | 200ns | $MAX_SORT_COLS = 10000; | ||
128 | 1 | 200ns | $didWriteDefaultStyle = 0; | ||
129 | 1 | 100ns | $tableCount = 0; | ||
130 | 1 | 200ns | $currTablePre = ''; | ||
131 | 1 | 300ns | $combinedTableAttrs = {}; | ||
132 | 1 | 200ns | $tableSpecificAttrs = {}; | ||
133 | 1 | 3µs | $styles = {}; | ||
134 | } | ||||
135 | |||||
136 | # called one time | ||||
137 | # spent 383µs (29+355) within Foswiki::Plugins::TablePlugin::Core::_initDefaults which was called:
# once (29µs+355µs) by Foswiki::Plugins::TablePlugin::Core::handler at line 1896 | ||||
138 | 1 | 800ns | 1 | 5µs | _debug('_initDefaults'); # spent 5µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_debug |
139 | 1 | 2µs | $defaultAttrs = { | ||
140 | headerrows => 0, | ||||
141 | footerrows => 0, | ||||
142 | sort => 1, | ||||
143 | class => 'foswikiTable', | ||||
144 | sortAllTables => $sortTablesInText, | ||||
145 | }; | ||||
146 | 1 | 17µs | 1 | 327µs | _parseDefaultAttributes( # spent 327µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_parseDefaultAttributes |
147 | %{Foswiki::Plugins::TablePlugin::pluginAttributes} ); | ||||
148 | |||||
149 | 1 | 5µs | 1 | 22µs | $combinedTableAttrs = _mergeHashes( {}, $defaultAttrs ); # spent 22µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_mergeHashes |
150 | } | ||||
151 | |||||
152 | sub _addDefaultStyles { | ||||
153 | return if $Foswiki::Plugins::TablePlugin::writtenToHead; | ||||
154 | $Foswiki::Plugins::TablePlugin::writtenToHead = 1; | ||||
155 | |||||
156 | # create CSS styles tables in general | ||||
157 | my ( $id, @styles ) = _createCssStyles( 1, $defaultAttrs ); | ||||
158 | _addHeadStyles( $HEAD_ID_DEFAULT_STYLE, @styles ) if scalar @styles; | ||||
159 | } | ||||
160 | |||||
161 | # spent 327µs (110+217) within Foswiki::Plugins::TablePlugin::Core::_resetReusedVariables which was called 5 times, avg 65µs/call:
# 5 times (110µs+217µs) by Foswiki::Plugins::TablePlugin::Core::handler at line 1934, avg 65µs/call | ||||
162 | 5 | 6µs | 5 | 35µs | _debug('_resetReusedVariables'); # spent 35µs making 5 calls to Foswiki::Plugins::TablePlugin::Core::_debug, avg 7µs/call |
163 | 5 | 2µs | $currTablePre = ''; | ||
164 | 5 | 56µs | 5 | 182µs | $combinedTableAttrs = _mergeHashes( {}, $defaultAttrs ); # spent 182µs making 5 calls to Foswiki::Plugins::TablePlugin::Core::_mergeHashes, avg 36µs/call |
165 | 5 | 3µs | $tableSpecificAttrs = {}; | ||
166 | 5 | 1µs | $sortCol = 0; | ||
167 | 5 | 21µs | @messages = (); | ||
168 | } | ||||
169 | |||||
170 | =pod | ||||
171 | |||||
172 | =cut | ||||
173 | |||||
174 | # spent 47µs within Foswiki::Plugins::TablePlugin::Core::_storeAttribute which was called 23 times, avg 2µs/call:
# once (5µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 379
# once (3µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 384
# once (3µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 319
# once (3µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 253
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 364
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 359
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 274
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 376
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 382
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 365
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 320
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 324
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 366
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 361
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 377
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 322
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 378
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 323
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 367
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 392
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 370
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 389
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 373 | ||||
175 | 23 | 12µs | my ( $inAttrName, $inValue, $inCollection ) = @_; | ||
176 | |||||
177 | 23 | 3µs | if ( !$inCollection ) { | ||
178 | _debug('_storeAttribute -- missing inCollection!'); | ||||
179 | return; | ||||
180 | } | ||||
181 | 23 | 50µs | return if !defined $inValue; | ||
182 | 10 | 2µs | return if !defined $inAttrName || $inAttrName eq ''; | ||
183 | 10 | 39µs | $inCollection->{$inAttrName} = $inValue; | ||
184 | } | ||||
185 | |||||
186 | =pod | ||||
187 | |||||
188 | =cut | ||||
189 | |||||
190 | # spent 327µs (11+316) within Foswiki::Plugins::TablePlugin::Core::_parseDefaultAttributes which was called:
# once (11µs+316µs) by Foswiki::Plugins::TablePlugin::Core::_initDefaults at line 146 | ||||
191 | 1 | 4µs | my (%params) = @_; | ||
192 | |||||
193 | 1 | 900ns | 1 | 5µs | _debug('_parseDefaultAttributes'); # spent 5µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_debug |
194 | |||||
195 | 1 | 4µs | 1 | 311µs | _parseAttributes( 0, $defaultAttrs, \%params ); # spent 311µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_parseAttributes |
196 | } | ||||
197 | |||||
198 | =pod | ||||
199 | |||||
200 | =cut | ||||
201 | |||||
202 | sub _parseTableSpecificTableAttributes { | ||||
203 | my (%params) = @_; | ||||
204 | |||||
205 | _debug('_parseTableSpecificTableAttributes'); | ||||
206 | |||||
207 | _parseAttributes( 1, $tableSpecificAttrs, \%params ); | ||||
208 | |||||
209 | # remove default values from hash | ||||
210 | while ( my ( $key, $value ) = each %{$tableSpecificAttrs} ) { | ||||
211 | delete $tableSpecificAttrs->{$key} | ||||
212 | if $defaultAttrs->{$key} && $value eq $defaultAttrs->{$key}; | ||||
213 | } | ||||
214 | $combinedTableAttrs = | ||||
215 | _mergeHashes( $combinedTableAttrs, $tableSpecificAttrs ); | ||||
216 | _debugData( 'combinedTableAttrs', $combinedTableAttrs ); | ||||
217 | |||||
218 | # create CSS styles for this table only | ||||
219 | my ( $id, @styles ) = _createCssStyles( 0, $tableSpecificAttrs ); | ||||
220 | _debugData( "after _createCssStyles, id=$id; styles", \@styles ); | ||||
221 | |||||
222 | _addHeadStyles( $id, @styles ) if scalar @styles; | ||||
223 | |||||
224 | return $currTablePre . '<nop>'; | ||||
225 | } | ||||
226 | |||||
227 | =pod | ||||
228 | |||||
229 | =cut | ||||
230 | |||||
231 | # spent 311µs (174+137) within Foswiki::Plugins::TablePlugin::Core::_parseAttributes which was called:
# once (174µs+137µs) by Foswiki::Plugins::TablePlugin::Core::_parseDefaultAttributes at line 195 | ||||
232 | 1 | 500ns | my ( $isTableSpecific, $inCollection, $inParams ) = @_; | ||
233 | |||||
234 | 1 | 2µs | 1 | 11µs | _debugData( "isTableSpecific=$isTableSpecific; _parseAttributes=", # spent 11µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_debugData |
235 | $inParams ); | ||||
236 | |||||
237 | # include topic to read definitions | ||||
238 | 1 | 400ns | if ( $inParams->{include} ) { | ||
239 | my ( $includeParams, $message ) = | ||||
240 | _getIncludeParams( $inParams->{include} ); | ||||
241 | |||||
242 | if ($includeParams) { | ||||
243 | $inParams = $includeParams; | ||||
244 | } | ||||
245 | if ($message) { | ||||
246 | push( @messages, $message ); | ||||
247 | } | ||||
248 | } | ||||
249 | |||||
250 | # table attributes | ||||
251 | # some will be used for css styling as well | ||||
252 | |||||
253 | 1 | 4µs | 2 | 19µs | _storeAttribute( 'generateInlineMarkup', # spent 16µs making 1 call to Foswiki::Func::isTrue
# spent 3µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
254 | Foswiki::Func::isTrue( $inParams->{inlinemarkup} ), | ||||
255 | $inCollection ) | ||||
256 | if defined $inParams->{inlinemarkup}; | ||||
257 | |||||
258 | # sort attributes | ||||
259 | 1 | 400ns | if ( defined $inParams->{sort} ) { | ||
260 | my $sort = Foswiki::Func::isTrue( $inParams->{sort} ); | ||||
261 | _storeAttribute( 'sort', $sort, $inCollection ); | ||||
262 | _storeAttribute( 'sortAllTables', $sort, $inCollection ); | ||||
263 | } | ||||
264 | 1 | 300ns | if ( defined( $inParams->{initsort} ) | ||
265 | and int( $inParams->{initsort} ) > 0 ) | ||||
266 | { | ||||
267 | _storeAttribute( 'initSort', $inParams->{initsort}, $inCollection ); | ||||
268 | |||||
269 | # override sort attribute: we are sorting after all | ||||
270 | _storeAttribute( 'sort', 1, $inCollection ); | ||||
271 | } | ||||
272 | |||||
273 | 1 | 600ns | if ( $inParams->{initdirection} ) { | ||
274 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'initDirection', $SORT_DIRECTION->{'ASCENDING'}, # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
275 | $inCollection ) | ||||
276 | if $inParams->{initdirection} =~ /^down$/i; | ||||
277 | 1 | 1µs | _storeAttribute( 'initDirection', $SORT_DIRECTION->{'DESCENDING'}, | ||
278 | $inCollection ) | ||||
279 | if $inParams->{initdirection} =~ /^up$/i; | ||||
280 | } | ||||
281 | |||||
282 | # Don't allow sort requests when rendering for static use. | ||||
283 | # Force sort=off but allow initsort / initdirection | ||||
284 | 1 | 2µs | 1 | 5µs | my $context = Foswiki::Func::getContext(); # spent 5µs making 1 call to Foswiki::Func::getContext |
285 | 1 | 400ns | if ( $context->{static} ) { | ||
286 | delete $inCollection->{sortAllTables}; | ||||
287 | } | ||||
288 | |||||
289 | # If EditTablePlugin is installed and we are editing a table, | ||||
290 | # the CGI parameter 'sort' is defined as "off" to disable all | ||||
291 | # header sorting ((Item5135) | ||||
292 | 1 | 1µs | 1 | 6µs | my $cgi = Foswiki::Func::getCgiQuery(); # spent 6µs making 1 call to Foswiki::Func::getCgiQuery |
293 | 1 | 2µs | 1 | 16µs | my $urlParamSort = $cgi->param('sort'); # spent 16µs making 1 call to Foswiki::Request::param |
294 | 1 | 200ns | if ( $urlParamSort && $urlParamSort =~ /^off$/oi ) { | ||
295 | delete $inCollection->{sortAllTables}; | ||||
296 | } | ||||
297 | |||||
298 | # If EditTablePlugin is installed and we are editing a table, the | ||||
299 | # 'disableallsort' TABLE parameter is added to disable initsort and header | ||||
300 | # sorting in the table that is being edited. (Item5135) | ||||
301 | 1 | 2µs | 1 | 5µs | if ( Foswiki::Func::isTrue( $inParams->{disableallsort} ) ) { # spent 5µs making 1 call to Foswiki::Func::isTrue |
302 | $inCollection->{sortAllTables} = 0; | ||||
303 | delete $inCollection->{initSort}; | ||||
304 | } | ||||
305 | |||||
306 | 1 | 200ns | if ($isTableSpecific) { | ||
307 | |||||
308 | _storeAttribute( 'summary', $inParams->{summary}, $inCollection ); | ||||
309 | my $id = | ||||
310 | defined $inParams->{id} | ||||
311 | ? $inParams->{id} | ||||
312 | : 'table' | ||||
313 | . $Foswiki::Plugins::TablePlugin::topic | ||||
314 | . ( $tableCount + 1 ); | ||||
315 | _storeAttribute( 'id', $id, $inCollection ); | ||||
316 | _storeAttribute( 'headerrows', $inParams->{headerrows}, $inCollection ); | ||||
317 | _storeAttribute( 'footerrows', $inParams->{footerrows}, $inCollection ); | ||||
318 | } | ||||
319 | 1 | 1µs | 1 | 3µs | _storeAttribute( 'border', $inParams->{tableborder}, $inCollection ); # spent 3µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
320 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'tableBorderColor', $inParams->{tablebordercolor}, # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
321 | $inCollection ); | ||||
322 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'cellpadding', $inParams->{cellpadding}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
323 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'cellspacing', $inParams->{cellspacing}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
324 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'frame', $inParams->{tableframe}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
325 | |||||
326 | # tablerules css settings | ||||
327 | 1 | 400ns | my @tableRulesList = (); | ||
328 | 1 | 500ns | if ( $inParams->{tablerules} ) { | ||
329 | |||||
330 | # store tablerules as array, so that headerrules and datarules | ||||
331 | # can be appended to that list | ||||
332 | 1 | 2µs | 1 | 2µs | my $param = _cleanParamValue( $inParams->{tablerules} ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_cleanParamValue |
333 | 1 | 900ns | if ($param) { | ||
334 | push( @tableRulesList, $param ); | ||||
335 | } | ||||
336 | } | ||||
337 | 1 | 600ns | if ( $inParams->{headerrules} ) { | ||
338 | 1 | 1µs | 1 | 2µs | my $param = _cleanParamValue( $inParams->{headerrules} ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_cleanParamValue |
339 | 1 | 400ns | if ($param) { | ||
340 | 1 | 700ns | $param = "header_$param"; | ||
341 | 1 | 400ns | push( @tableRulesList, $param ); | ||
342 | } | ||||
343 | } | ||||
344 | 1 | 400ns | if ( $inParams->{datarules} ) { | ||
345 | my $param = _cleanParamValue( $inParams->{datarules} ); | ||||
346 | if ($param) { | ||||
347 | $param = "data_$param"; | ||||
348 | push( @tableRulesList, $param ); | ||||
349 | } | ||||
350 | } | ||||
351 | 1 | 800ns | $inCollection->{tableRules} = \@tableRulesList if scalar @tableRulesList; | ||
352 | |||||
353 | # use 'rules' as table attribute only (not to define css styles) | ||||
354 | # but set to | ||||
355 | 1 | 600ns | my $rules = | ||
356 | ( defined $inParams->{headerrules} || defined $inParams->{datarules} ) | ||||
357 | ? 'none' | ||||
358 | : $inParams->{tablerules}; | ||||
359 | 1 | 900ns | 1 | 2µs | _storeAttribute( 'rules', $rules, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
360 | |||||
361 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'width', $inParams->{tablewidth}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
362 | |||||
363 | # css attributes | ||||
364 | 1 | 1µs | 1 | 2µs | _storeAttribute( 'headerColor', $inParams->{headercolor}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
365 | 1 | 1µs | 1 | 2µs | _storeAttribute( 'headerBg', $inParams->{headerbg}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
366 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'cellBorder', $inParams->{cellborder}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
367 | 1 | 3µs | 2 | 3µs | _storeAttribute( 'headerAlignListRef', # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
368 | _arrayRefFromParam( $inParams->{headeralign} ), | ||||
369 | $inCollection ); | ||||
370 | 1 | 2µs | 2 | 3µs | _storeAttribute( 'dataAlignListRef', # spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
371 | _arrayRefFromParam( $inParams->{dataalign} ), | ||||
372 | $inCollection ); | ||||
373 | 1 | 2µs | 2 | 2µs | _storeAttribute( 'columnWidthsListRef', # spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
374 | _arrayRefFromParam( $inParams->{columnwidths} ), | ||||
375 | $inCollection ); | ||||
376 | 1 | 1µs | 1 | 2µs | _storeAttribute( 'vAlign', $inParams->{valign} || 'top', $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
377 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'dataVAlign', $inParams->{datavalign}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
378 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'headerVAlign', $inParams->{headervalign}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
379 | 1 | 1µs | 1 | 5µs | _storeAttribute( 'headerBgSorted', # spent 5µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
380 | $inParams->{headerbgsorted} || $inParams->{headerbg}, | ||||
381 | $inCollection ); | ||||
382 | 1 | 2µs | 2 | 6µs | _storeAttribute( 'dataBgListRef', _arrayRefFromParam( $inParams->{databg} ), # spent 4µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
383 | $inCollection ); | ||||
384 | 1 | 2µs | 2 | 15µs | _storeAttribute( # spent 12µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 3µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
385 | 'dataBgSortedListRef', | ||||
386 | _arrayRefFromParam( $inParams->{databgsorted} || $inParams->{databg} ), | ||||
387 | $inCollection | ||||
388 | ); | ||||
389 | 1 | 3µs | 2 | 3µs | _storeAttribute( 'dataColorListRef', # spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam
# spent 1µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
390 | _arrayRefFromParam( $inParams->{datacolor} ), | ||||
391 | $inCollection ); | ||||
392 | 1 | 2µs | 1 | 2µs | _storeAttribute( 'tableCaption', $inParams->{caption}, $inCollection ); # spent 2µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_storeAttribute |
393 | |||||
394 | # remove empty attributes | ||||
395 | 1 | 15µs | while ( my ( $key, $value ) = each %{$inCollection} ) { | ||
396 | delete $inCollection->{$key} if !defined $value || $value eq ''; | ||||
397 | } | ||||
398 | |||||
399 | 1 | 5µs | 1 | 5µs | _debugData( '_parseAttributes result:', $inCollection ); # spent 5µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_debugData |
400 | } | ||||
401 | |||||
402 | =pod | ||||
403 | |||||
404 | _getIncludeParams( $includeTopic ) -> \%params | ||||
405 | |||||
406 | From $includeTopic read the first TABLE tag and return its parameters. | ||||
407 | |||||
408 | =cut | ||||
409 | |||||
410 | sub _getIncludeParams { | ||||
411 | my ($inIncludeTopic) = @_; | ||||
412 | |||||
413 | my ( $includeWeb, $includeTopic ) = | ||||
414 | Foswiki::Func::normalizeWebTopicName( $Foswiki::Plugins::TablePlugin::web, | ||||
415 | $inIncludeTopic ); | ||||
416 | |||||
417 | _debug("_getIncludeParams:$inIncludeTopic"); | ||||
418 | _debug("\t includeTopic=$includeTopic") if $includeTopic; | ||||
419 | |||||
420 | if ( !Foswiki::Func::topicExists( $includeWeb, $includeTopic ) ) { | ||||
421 | _debug("TablePlugin: included topic $inIncludeTopic does not exist."); | ||||
422 | return ( undef, | ||||
423 | '%MAKETEXT{"Warning: \'include\' topic <nop>[_1] does not exist!" args="' | ||||
424 | . "$includeWeb.$includeTopic" | ||||
425 | . '"}%' ); | ||||
426 | } | ||||
427 | else { | ||||
428 | |||||
429 | my $text = Foswiki::Func::readTopicText( $includeWeb, $includeTopic ); | ||||
430 | |||||
431 | if ( $text =~ m/$PATTERN_TABLE/s ) { | ||||
432 | _debug("\t PATTERN_TABLE=$PATTERN_TABLE; 1=$1"); | ||||
433 | my $paramString = $1; | ||||
434 | |||||
435 | if ( $includeWeb ne $Foswiki::Plugins::TablePlugin::web | ||||
436 | || $includeTopic ne $Foswiki::Plugins::TablePlugin::topic ) | ||||
437 | { | ||||
438 | |||||
439 | # expand common vars, except oneself to prevent recursion | ||||
440 | $paramString = | ||||
441 | Foswiki::Func::expandCommonVariables( $paramString, | ||||
442 | $includeTopic, $includeWeb ); | ||||
443 | } | ||||
444 | my %params = Foswiki::Func::extractParameters($paramString); | ||||
445 | return ( \%params, undef ); | ||||
446 | } | ||||
447 | else { | ||||
448 | return ( undef, | ||||
449 | '%MAKETEXT{"Warning: table definition in \'include\' topic [_1] does not exist!" args="' | ||||
450 | . "$includeWeb.$includeTopic" | ||||
451 | . '"}%' ); | ||||
452 | } | ||||
453 | } | ||||
454 | } | ||||
455 | |||||
456 | =pod | ||||
457 | |||||
458 | _convertStringToDate ( $text ) -> $number | ||||
459 | |||||
460 | Convert text to number if syntactically possible, otherwise return undef. | ||||
461 | Assumes that the text has been stripped from HTML markup. | ||||
462 | |||||
463 | =cut | ||||
464 | |||||
465 | sub _convertStringToDate { | ||||
466 | my ($text) = @_; | ||||
467 | |||||
468 | return undef if !defined $text; | ||||
469 | return undef if $text eq ''; | ||||
470 | return undef if ( $text =~ /^\s*$/ ); | ||||
471 | |||||
472 | my $date = undef; | ||||
473 | |||||
474 | if ( $text =~ /^\s*-?[0-9]+(\.[0-9])*\s*$/ ) { | ||||
475 | _debug("\t this is a number"); | ||||
476 | } | ||||
477 | else { | ||||
478 | try { | ||||
479 | $date = Foswiki::Time::parseTime($text); | ||||
480 | _debug("\t is a date"); | ||||
481 | } | ||||
482 | catch Error::Simple with { | ||||
483 | |||||
484 | # nope, wasn't a date | ||||
485 | _debug("\t $text is not a date"); | ||||
486 | }; | ||||
487 | } | ||||
488 | |||||
489 | return $date; | ||||
490 | } | ||||
491 | |||||
492 | =pod | ||||
493 | |||||
494 | _convertStringToNumber ( $text ) -> $number | ||||
495 | |||||
496 | Convert text to number if syntactically possible, otherwise return undef. | ||||
497 | Assumes that the text has been stripped from HTML markup. | ||||
498 | |||||
499 | =cut | ||||
500 | |||||
501 | sub _convertStringToNumber { | ||||
502 | my ($text) = @_; | ||||
503 | |||||
504 | return undef if !defined $text; | ||||
505 | return undef if $text eq ''; | ||||
506 | return undef if ( $text =~ /^\s*$/ ); | ||||
507 | |||||
508 | # very course testing on IP (could in fact be anything with n.n. syntax | ||||
509 | if ( | ||||
510 | $text =~ m/ | ||||
511 | ^ | ||||
512 | \s* # any space | ||||
513 | (?: # don't need to capture | ||||
514 | [0-9]+ # digits | ||||
515 | \. # dot | ||||
516 | ) # | ||||
517 | {2,} # repeat more than once: exclude decimal numbers | ||||
518 | .*? # any string | ||||
519 | $ | ||||
520 | /x | ||||
521 | ) | ||||
522 | { | ||||
523 | _debug("\t $text looks like an IP address, or something similar"); | ||||
524 | |||||
525 | # should be sorted by text | ||||
526 | return undef; | ||||
527 | } | ||||
528 | |||||
529 | if ( | ||||
530 | $text =~ m/ | ||||
531 | ^ | ||||
532 | \s* # any space | ||||
533 | ( # | ||||
534 | -* # possible minus | ||||
535 | [0-9]+ # digits | ||||
536 | \.* # possible decimal | ||||
537 | [0-9]* # possible fracture digits | ||||
538 | ) # end capture of number | ||||
539 | .*$ # any string | ||||
540 | /x | ||||
541 | ) | ||||
542 | { | ||||
543 | |||||
544 | _debug("\t $1 is a number"); | ||||
545 | |||||
546 | # make sure to return a number, not a string | ||||
547 | return $1 * 1.0; | ||||
548 | } | ||||
549 | return undef; | ||||
550 | } | ||||
551 | |||||
552 | sub _processTableRow { | ||||
553 | my ( $thePre, $theRow ) = @_; | ||||
554 | |||||
555 | $currTablePre = $thePre || ''; | ||||
556 | my $span = 0; | ||||
557 | my $l1 = 0; | ||||
558 | my $l2 = 0; | ||||
559 | |||||
560 | if ( !$insideTABLE ) { | ||||
561 | @curTable = (); | ||||
562 | @rowspan = (); | ||||
563 | |||||
564 | $tableCount++; | ||||
565 | $currentSortDirection = $SORT_DIRECTION->{'NONE'}; | ||||
566 | |||||
567 | if ( defined $requestedTable | ||||
568 | && $requestedTable == $tableCount | ||||
569 | && defined $sortColFromUrl ) | ||||
570 | { | ||||
571 | $sortCol = $sortColFromUrl; | ||||
572 | $sortCol = 0 unless ( $sortCol =~ m/^[0-9]+$/ ); | ||||
573 | $sortCol = $MAX_SORT_COLS if ( $sortCol > $MAX_SORT_COLS ); | ||||
574 | $currentSortDirection = _getCurrentSortDirection($up); | ||||
575 | } | ||||
576 | elsif ( defined $combinedTableAttrs->{initSort} ) { | ||||
577 | $sortCol = $combinedTableAttrs->{initSort} - 1; | ||||
578 | $sortCol = $MAX_SORT_COLS if ( $sortCol > $MAX_SORT_COLS ); | ||||
579 | $currentSortDirection = | ||||
580 | _getCurrentSortDirection( $combinedTableAttrs->{initDirection} ); | ||||
581 | } | ||||
582 | } | ||||
583 | |||||
584 | $theRow =~ s/\t/ /go; # change tabs to space | ||||
585 | $theRow =~ s/\s*$//o; # remove trailing spaces | ||||
586 | $theRow =~ | ||||
587 | s/(\|\|+)/'colspan'.$translationToken.length($1)."\|"/geo; # calc COLSPAN | ||||
588 | my $colCount = 0; | ||||
589 | my @row = (); | ||||
590 | $span = 0; | ||||
591 | my $value = ''; | ||||
592 | |||||
593 | foreach ( split( /\|/, $theRow ) ) { | ||||
594 | my $attr = {}; | ||||
595 | $span = 1; | ||||
596 | |||||
597 | #AS 25-5-01 Fix to avoid matching also single columns | ||||
598 | if (s/colspan$translationToken([0-9]+)//) { | ||||
599 | $span = $1; | ||||
600 | $attr->{colspan} = $span; | ||||
601 | } | ||||
602 | s/^\s+$/ /o; | ||||
603 | ( $l1, $l2 ) = ( 0, 0 ); | ||||
604 | if (/^(\s*).*?(\s*)$/) { | ||||
605 | $l1 = length($1); | ||||
606 | $l2 = length($2); | ||||
607 | } | ||||
608 | if ( $l1 >= 2 ) { | ||||
609 | if ( $l2 <= 1 ) { | ||||
610 | $attr->{align} = 'right'; | ||||
611 | } | ||||
612 | else { | ||||
613 | $attr->{align} = 'center'; | ||||
614 | } | ||||
615 | } | ||||
616 | if ( $span <= 2 ) { | ||||
617 | $attr->{class} = | ||||
618 | _appendColNumberCssClass( $attr->{class}, $colCount ); | ||||
619 | } | ||||
620 | |||||
621 | # html attribute: (column) width | ||||
622 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
623 | && defined $combinedTableAttrs->{columnWidthsListRef} ) | ||||
624 | { | ||||
625 | my @columnWidths = @{ $combinedTableAttrs->{columnWidthsListRef} }; | ||||
626 | if ( defined $columnWidths[$colCount] | ||||
627 | && $columnWidths[$colCount] | ||||
628 | && $span <= 2 ) | ||||
629 | { | ||||
630 | $attr->{width} = $columnWidths[$colCount]; | ||||
631 | } | ||||
632 | } | ||||
633 | |||||
634 | # END html attribute | ||||
635 | |||||
636 | if (/^(\s|<[^>]*>)*\^(\s|<[^>]*>)*$/) { # row span above | ||||
637 | $rowspan[$colCount]++; | ||||
638 | push @row, { text => $value, type => 'Y' }; | ||||
639 | } | ||||
640 | else { | ||||
641 | for ( my $col = $colCount ; $col < ( $colCount + $span ) ; $col++ ) | ||||
642 | { | ||||
643 | if ( defined( $rowspan[$col] ) && $rowspan[$col] ) { | ||||
644 | my $nRows = scalar(@curTable); | ||||
645 | my $rspan = $rowspan[$col] + 1; | ||||
646 | if ( $rspan > 1 ) { | ||||
647 | $curTable[ $nRows - $rspan ][$col]->{attrs}->{rowspan} | ||||
648 | = $rspan; | ||||
649 | } | ||||
650 | undef( $rowspan[$col] ); | ||||
651 | } | ||||
652 | } | ||||
653 | |||||
654 | if ( | ||||
655 | ( | ||||
656 | ( | ||||
657 | defined $requestedTable | ||||
658 | && $requestedTable == $tableCount | ||||
659 | ) | ||||
660 | || defined $combinedTableAttrs->{initSort} | ||||
661 | ) | ||||
662 | && defined $sortCol | ||||
663 | && $colCount == $sortCol | ||||
664 | ) | ||||
665 | { | ||||
666 | |||||
667 | # CSS class name | ||||
668 | if ( $currentSortDirection == $SORT_DIRECTION->{'ASCENDING'} ) { | ||||
669 | $attr->{class} = | ||||
670 | _appendSortedAscendingCssClass( $attr->{class} ); | ||||
671 | } | ||||
672 | if ( $currentSortDirection == $SORT_DIRECTION->{'DESCENDING'} ) | ||||
673 | { | ||||
674 | $attr->{class} = | ||||
675 | _appendSortedDescendingCssClass( $attr->{class} ); | ||||
676 | } | ||||
677 | } | ||||
678 | |||||
679 | my $type = ''; | ||||
680 | if (/^\s*\*(.*)\*\s*$/) { | ||||
681 | $value = $1; | ||||
682 | $type = 'th'; | ||||
683 | |||||
684 | # html attribute: align | ||||
685 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
686 | && defined $combinedTableAttrs->{headerAlignListRef} ) | ||||
687 | { | ||||
688 | my @headerAlign = | ||||
689 | @{ $combinedTableAttrs->{headerAlignListRef} }; | ||||
690 | if (@headerAlign) { | ||||
691 | my $align = | ||||
692 | @headerAlign[ $colCount % ( $#headerAlign + 1 ) ]; | ||||
693 | $attr->{align} = $align; | ||||
694 | } | ||||
695 | } | ||||
696 | |||||
697 | # END html attribute | ||||
698 | |||||
699 | # html attribute: valign | ||||
700 | if ( $combinedTableAttrs->{generateInlineMarkup} ) { | ||||
701 | if ( defined $combinedTableAttrs->{headerVAlign} ) { | ||||
702 | $attr->{valign} = $combinedTableAttrs->{headerVAlign}; | ||||
703 | } | ||||
704 | elsif ( defined $combinedTableAttrs->{vAlign} ) { | ||||
705 | $attr->{valign} = $combinedTableAttrs->{vAlign}; | ||||
706 | } | ||||
707 | } | ||||
708 | |||||
709 | # END html attribute | ||||
710 | } | ||||
711 | else { | ||||
712 | if (/^\s*(.*?)\s*$/) { # strip white spaces | ||||
713 | $_ = $1; | ||||
714 | } | ||||
715 | $value = $_; | ||||
716 | $type = 'td'; | ||||
717 | |||||
718 | # html attribute: align | ||||
719 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
720 | && defined $combinedTableAttrs->{dataAlignListRef} ) | ||||
721 | { | ||||
722 | my @dataAlign = | ||||
723 | @{ $combinedTableAttrs->{dataAlignListRef} }; | ||||
724 | if (@dataAlign) { | ||||
725 | my $align = | ||||
726 | @dataAlign[ $colCount % ( $#dataAlign + 1 ) ]; | ||||
727 | $attr->{align} = $align; | ||||
728 | } | ||||
729 | } | ||||
730 | |||||
731 | # END html attribute | ||||
732 | |||||
733 | # html attribute: valign | ||||
734 | if ( $combinedTableAttrs->{generateInlineMarkup} ) { | ||||
735 | if ( defined $combinedTableAttrs->{dataVAlign} ) { | ||||
736 | $attr->{valign} = $combinedTableAttrs->{dataVAlign}; | ||||
737 | } | ||||
738 | elsif ( defined $combinedTableAttrs->{vAlign} ) { | ||||
739 | $attr->{valign} = $combinedTableAttrs->{vAlign}; | ||||
740 | } | ||||
741 | } | ||||
742 | |||||
743 | # END html attribute | ||||
744 | } | ||||
745 | |||||
746 | push @row, { text => $value, attrs => $attr, type => $type }; | ||||
747 | } | ||||
748 | while ( $span > 1 ) { | ||||
749 | push @row, { text => $value, type => 'X' }; | ||||
750 | $colCount++; | ||||
751 | $span--; | ||||
752 | } | ||||
753 | $colCount++; | ||||
754 | } | ||||
755 | push @curTable, \@row; | ||||
756 | return $currTablePre | ||||
757 | . '<nop>'; # Avoid Foswiki converting empty lines to new paras | ||||
758 | } | ||||
759 | |||||
760 | sub _headerRowCount { | ||||
761 | my ($table) = @_; | ||||
762 | |||||
763 | my $headerCount = 0; | ||||
764 | my $footerCount = 0; | ||||
765 | my $endheader = 0; | ||||
766 | |||||
767 | # All cells in header are headings? | ||||
768 | foreach my $row (@$table) { | ||||
769 | my $isHeader = 1; | ||||
770 | foreach my $cell (@$row) { | ||||
771 | if ( $cell->{type} ne 'th' ) { | ||||
772 | $isHeader = 0; | ||||
773 | $endheader = 1; | ||||
774 | $footerCount = 0 if $footerCount; | ||||
775 | } | ||||
776 | } | ||||
777 | unless ($endheader) { | ||||
778 | $headerCount++ if $isHeader; | ||||
779 | } | ||||
780 | else { | ||||
781 | $footerCount++ if $isHeader; | ||||
782 | } | ||||
783 | } | ||||
784 | |||||
785 | # Some cells came after the footer - so there isn't one. | ||||
786 | $footerCount = 0 if ( $endheader > 1 ); | ||||
787 | |||||
788 | return ( $headerCount, $footerCount ); | ||||
789 | } | ||||
790 | |||||
791 | =pod | ||||
792 | |||||
793 | _setSortTypeForCells ( $col, \@table ) | ||||
794 | |||||
795 | Sets a sort key for each cell. | ||||
796 | |||||
797 | =cut | ||||
798 | |||||
799 | sub _setSortTypeForCells { | ||||
800 | my ( $col, $table ) = @_; | ||||
801 | |||||
802 | foreach my $row ( @{$table} ) { | ||||
803 | |||||
804 | my $rowText = _stripHtml( $row->[$col]->{text} ); | ||||
805 | |||||
806 | my $num = _convertStringToNumber($rowText); | ||||
807 | my $date = _convertStringToDate($rowText); | ||||
808 | |||||
809 | $row->[$col]->{sortText} = ''; | ||||
810 | $row->[$col]->{number} = 0; | ||||
811 | $row->[$col]->{dateString} = ''; | ||||
812 | |||||
813 | if ( defined $date ) { | ||||
814 | |||||
815 | # date has just converted to a number | ||||
816 | $row->[$col]->{number} = $date; | ||||
817 | |||||
818 | # add dateString value in case dates are equal | ||||
819 | $row->[$col]->{dateString} = $rowText; | ||||
820 | } | ||||
821 | elsif ( defined $num ) { | ||||
822 | $row->[$col]->{number} = $num; | ||||
823 | |||||
824 | # when sorting mixed numbers and text, make the text sort value as low as possible | ||||
825 | $row->[$col]->{sortText} = ' '; | ||||
826 | } | ||||
827 | else { | ||||
828 | $row->[$col]->{sortText} = lc $rowText; | ||||
829 | } | ||||
830 | |||||
831 | } | ||||
832 | } | ||||
833 | |||||
834 | # Remove HTML from text so it can be sorted | ||||
835 | sub _stripHtml { | ||||
836 | my ($text) = @_; | ||||
837 | |||||
838 | return undef if !defined $text; | ||||
839 | $text =~ | ||||
840 | s/\[\[[^\]]+\]\[([^\]]+)\]\]/$1/go; # extract label from [[...][...]] link | ||||
841 | |||||
842 | my $orgtext = | ||||
843 | $text; # in case we will have removed all contents with stripping html | ||||
844 | $text =~ s/<[^>]+>//go; # strip HTML | ||||
845 | $text =~ s/\ / /go; | ||||
846 | $text = _getImageTextForSorting($orgtext) if ( $text eq '' ); | ||||
847 | $text =~ s/[\[\]\*\|=_\&\<\>]/ /g; # remove Wiki formatting chars | ||||
848 | $text =~ s/^ *//go; # strip leading space space | ||||
849 | |||||
850 | return $text; | ||||
851 | } | ||||
852 | |||||
853 | =pod | ||||
854 | |||||
855 | Retrieve text data from an image html tag to be used for sorting. | ||||
856 | First try the alt tag string. If not available, return the url string. | ||||
857 | If not available, return the original string. | ||||
858 | |||||
859 | =cut | ||||
860 | |||||
861 | sub _getImageTextForSorting { | ||||
862 | my ($text) = @_; | ||||
863 | |||||
864 | # try to see _if_ there is any img data for sorting | ||||
865 | my $hasImageTag = ( $text =~ m/\<\s*img([^>]+)>/ ); | ||||
866 | return $text if ( !$hasImageTag ); | ||||
867 | |||||
868 | # first try to get the alt text | ||||
869 | my $key = 'alt'; | ||||
870 | $text =~ m/$key=\s*[\"\']([^\"\']*)/; | ||||
871 | return $1 if ( $1 ne '' ); | ||||
872 | |||||
873 | # else | ||||
874 | |||||
875 | # no alt text; use the url | ||||
876 | $key = 'url'; | ||||
877 | $text =~ m/$key=\s*[\"\']([^\"\']*)/; | ||||
878 | return $1 if ( $1 ne '' ); | ||||
879 | |||||
880 | # else | ||||
881 | |||||
882 | return $text; | ||||
883 | } | ||||
884 | |||||
885 | =pod | ||||
886 | |||||
887 | Appends $className to $classList, separated by a space. | ||||
888 | |||||
889 | =cut | ||||
890 | |||||
891 | sub _appendToClassList { | ||||
892 | my ( $classList, $className ) = @_; | ||||
893 | $classList = $classList ? $classList .= ' ' : ''; | ||||
894 | $classList .= $className; | ||||
895 | return $classList; | ||||
896 | } | ||||
897 | |||||
898 | sub _appendSortedCssClass { | ||||
899 | my ($classList) = @_; | ||||
900 | |||||
901 | return _appendToClassList( $classList, 'foswikiSortedCol' ); | ||||
902 | } | ||||
903 | |||||
904 | sub _appendRowNumberCssClass { | ||||
905 | my ( $classList, $colListName, $rowNum ) = @_; | ||||
906 | |||||
907 | my $rowClassName = 'foswikiTableRow' . $colListName . $rowNum; | ||||
908 | return _appendToClassList( $classList, $rowClassName ); | ||||
909 | } | ||||
910 | |||||
911 | sub _appendColNumberCssClass { | ||||
912 | my ( $classList, $colNum ) = @_; | ||||
913 | |||||
914 | my $colClassName = 'foswikiTableCol' . $colNum; | ||||
915 | return _appendToClassList( $classList, $colClassName ); | ||||
916 | } | ||||
917 | |||||
918 | sub _appendFirstColumnCssClass { | ||||
919 | my ($classList) = @_; | ||||
920 | |||||
921 | return _appendToClassList( $classList, 'foswikiFirstCol' ); | ||||
922 | } | ||||
923 | |||||
924 | sub _appendLastColumnCssClass { | ||||
925 | my ($classList) = @_; | ||||
926 | |||||
927 | return _appendToClassList( $classList, 'foswikiLastCol' ); | ||||
928 | } | ||||
929 | |||||
930 | sub _appendLastRowCssClass { | ||||
931 | my ($classList) = @_; | ||||
932 | |||||
933 | return _appendToClassList( $classList, 'foswikiLast' ); | ||||
934 | } | ||||
935 | |||||
936 | sub _appendSortedAscendingCssClass { | ||||
937 | my ($classList) = @_; | ||||
938 | |||||
939 | return _appendToClassList( $classList, 'foswikiSortedAscendingCol' ); | ||||
940 | } | ||||
941 | |||||
942 | sub _appendSortedDescendingCssClass { | ||||
943 | my ($classList) = @_; | ||||
944 | |||||
945 | return _appendToClassList( $classList, 'foswikiSortedDescendingCol' ); | ||||
946 | } | ||||
947 | |||||
948 | # The default sort direction. | ||||
949 | sub _getDefaultSortDirection { | ||||
950 | return $SORT_DIRECTION->{'ASCENDING'}; | ||||
951 | } | ||||
952 | |||||
953 | # Gets the current sort direction. | ||||
954 | sub _getCurrentSortDirection { | ||||
955 | my ($currentDirection) = @_; | ||||
956 | $currentDirection = $SORT_DIRECTION->{'ASCENDING'} | ||||
957 | unless defined $currentDirection && $currentDirection =~ m/[0-2]+/; | ||||
958 | $currentDirection ||= _getDefaultSortDirection(); | ||||
959 | return $currentDirection; | ||||
960 | } | ||||
961 | |||||
962 | # Gets the new sort direction (needed for sort button) based on the current sort | ||||
963 | # direction. | ||||
964 | sub _getNewSortDirection { | ||||
965 | my ($currentDirection) = @_; | ||||
966 | if ( !defined $currentDirection ) { | ||||
967 | return _getDefaultSortDirection(); | ||||
968 | } | ||||
969 | my $newDirection; | ||||
970 | if ( $currentDirection == $SORT_DIRECTION->{'ASCENDING'} ) { | ||||
971 | $newDirection = $SORT_DIRECTION->{'DESCENDING'}; | ||||
972 | } | ||||
973 | elsif ( $currentDirection == $SORT_DIRECTION->{'DESCENDING'} ) { | ||||
974 | $newDirection = $SORT_DIRECTION->{'NONE'}; | ||||
975 | } | ||||
976 | elsif ( $currentDirection == $SORT_DIRECTION->{'NONE'} ) { | ||||
977 | $newDirection = $SORT_DIRECTION->{'ASCENDING'}; | ||||
978 | } | ||||
979 | else { | ||||
980 | $newDirection = _getDefaultSortDirection(); | ||||
981 | } | ||||
982 | |||||
983 | return $newDirection; | ||||
984 | } | ||||
985 | |||||
986 | =pod | ||||
987 | |||||
988 | _createCssStyles( $writeDefaults, $inAttrs ) -> ($id, @styles) | ||||
989 | |||||
990 | Explicitly set styles override html styling (in this file marked with comment '# html attribute'). | ||||
991 | |||||
992 | =cut | ||||
993 | |||||
994 | sub _createCssStyles { | ||||
995 | my ( $writeDefaults, $inAttrs ) = @_; | ||||
996 | |||||
997 | _debug("_createCssStyles; writeDefaults=$writeDefaults"); | ||||
998 | |||||
999 | my $_styles = {}; | ||||
1000 | my $setAttribute = sub { | ||||
1001 | my ( $tableSelector, $type, $rule ) = @_; | ||||
1002 | |||||
1003 | return if !$rule; | ||||
1004 | $type ||= '#'; # for table selector only, if no type | ||||
1005 | my $storedType = $_styles->{$tableSelector}->{$type} || ''; | ||||
1006 | if ( !defined $storedType ) { | ||||
1007 | @{ $_styles->{$tableSelector}->{$type} } = (); | ||||
1008 | } | ||||
1009 | if ( $rule ne $storedType ) { | ||||
1010 | push @{ $_styles->{$tableSelector}->{$type} }, $rule; | ||||
1011 | } | ||||
1012 | }; | ||||
1013 | |||||
1014 | if ( $writeDefaults && !$didWriteDefaultStyle ) { | ||||
1015 | my $tableSelector = '.foswikiTable'; | ||||
1016 | my $attr = 'padding-left:.3em; vertical-align:text-bottom'; | ||||
1017 | &$setAttribute( $tableSelector, '.tableSortIcon img', $attr ); | ||||
1018 | |||||
1019 | if ( $inAttrs->{cellpadding} ) { | ||||
1020 | my $attr = | ||||
1021 | 'padding:' . addDefaultSizeUnit( $inAttrs->{cellpadding} ); | ||||
1022 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1023 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1024 | } | ||||
1025 | } | ||||
1026 | |||||
1027 | my $tableSelector; | ||||
1028 | my $id; | ||||
1029 | if ($writeDefaults) { | ||||
1030 | $id = 'default'; | ||||
1031 | $tableSelector = ".foswikiTable"; | ||||
1032 | } | ||||
1033 | else { | ||||
1034 | $id = $inAttrs->{id}; | ||||
1035 | $tableSelector = ".foswikiTable#$id"; | ||||
1036 | } | ||||
1037 | |||||
1038 | # tablerules | ||||
1039 | if ( $inAttrs->{tableRules} ) { | ||||
1040 | my @rules = @{ $inAttrs->{tableRules} }; | ||||
1041 | |||||
1042 | my $attr_td; | ||||
1043 | my $attr_th; | ||||
1044 | foreach my $rule (@rules) { | ||||
1045 | $attr_td = $TABLE_RULES->{$rule}->{TD} | ||||
1046 | if $TABLE_RULES->{$rule}->{TD}; | ||||
1047 | $attr_th = $TABLE_RULES->{$rule}->{TH} | ||||
1048 | if $TABLE_RULES->{$rule}->{TH}; | ||||
1049 | } | ||||
1050 | &$setAttribute( $tableSelector, 'th', $attr_th ); | ||||
1051 | &$setAttribute( $tableSelector, 'td', $attr_td ); | ||||
1052 | } | ||||
1053 | |||||
1054 | # tableframe | ||||
1055 | if ( $inAttrs->{frame} ) { | ||||
1056 | my $attr = $TABLE_FRAME->{ $inAttrs->{frame} }; | ||||
1057 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1058 | } | ||||
1059 | |||||
1060 | # tableborder | ||||
1061 | if ( defined $inAttrs->{border} ) { | ||||
1062 | my $tableBorderWidth = $inAttrs->{border} || 0; | ||||
1063 | my $attr = 'border-width:' . addDefaultSizeUnit($tableBorderWidth); | ||||
1064 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1065 | } | ||||
1066 | |||||
1067 | # tableBorderColor | ||||
1068 | if ( defined $inAttrs->{tableBorderColor} ) { | ||||
1069 | my $attr; | ||||
1070 | $attr = 'border-color:' . $inAttrs->{tableBorderColor}; | ||||
1071 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1072 | $attr = 'border-top-color:' . $inAttrs->{tableBorderColor}; | ||||
1073 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1074 | $attr = 'border-bottom-color:' . $inAttrs->{tableBorderColor}; | ||||
1075 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1076 | $attr = 'border-left-color:' . $inAttrs->{tableBorderColor}; | ||||
1077 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1078 | $attr = 'border-right-color:' . $inAttrs->{tableBorderColor}; | ||||
1079 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1080 | } | ||||
1081 | |||||
1082 | # cellSpacing | ||||
1083 | if ( defined $inAttrs->{cellspacing} ) { | ||||
1084 | |||||
1085 | # do not use border-collapse:collapse | ||||
1086 | my $attr = 'border-collapse:separate'; | ||||
1087 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1088 | } | ||||
1089 | |||||
1090 | # cellpadding | ||||
1091 | if ( defined $inAttrs->{cellpadding} ) { | ||||
1092 | my $attr = 'padding:' . addDefaultSizeUnit( $inAttrs->{cellpadding} ); | ||||
1093 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1094 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1095 | } | ||||
1096 | |||||
1097 | # cellborder | ||||
1098 | if ( defined $inAttrs->{cellBorder} ) { | ||||
1099 | my $cellBorderWidth = $inAttrs->{cellBorder} || 0; | ||||
1100 | my $attr = 'border-width:' . addDefaultSizeUnit($cellBorderWidth); | ||||
1101 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1102 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1103 | } | ||||
1104 | |||||
1105 | # tablewidth | ||||
1106 | if ( defined $inAttrs->{width} ) { | ||||
1107 | my $width = addDefaultSizeUnit( $inAttrs->{width} ); | ||||
1108 | my $attr = 'width:' . $width; | ||||
1109 | &$setAttribute( $tableSelector, '', $attr ); | ||||
1110 | } | ||||
1111 | |||||
1112 | # valign | ||||
1113 | if ( defined $inAttrs->{vAlign} ) { | ||||
1114 | my $attr = 'vertical-align:' . $inAttrs->{vAlign}; | ||||
1115 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1116 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1117 | } | ||||
1118 | |||||
1119 | # headerVAlign | ||||
1120 | if ( defined $inAttrs->{headerVAlign} ) { | ||||
1121 | my $attr = 'vertical-align:' . $inAttrs->{headerVAlign}; | ||||
1122 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1123 | } | ||||
1124 | |||||
1125 | # dataVAlign | ||||
1126 | if ( defined $inAttrs->{dataVAlign} ) { | ||||
1127 | my $attr = 'vertical-align:' . $inAttrs->{dataVAlign}; | ||||
1128 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1129 | } | ||||
1130 | |||||
1131 | # headerbg | ||||
1132 | if ( defined $inAttrs->{headerBg} ) { | ||||
1133 | my $color = | ||||
1134 | ( $inAttrs->{headerBg} =~ /none/i ) | ||||
1135 | ? 'transparent' | ||||
1136 | : $inAttrs->{headerBg}; | ||||
1137 | my $attr = 'background-color:' . $color; | ||||
1138 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1139 | } | ||||
1140 | |||||
1141 | # headerbgsorted | ||||
1142 | if ( defined $inAttrs->{headerBgSorted} ) { | ||||
1143 | my $color = | ||||
1144 | ( $inAttrs->{headerBgSorted} =~ /none/i ) | ||||
1145 | ? 'transparent' | ||||
1146 | : $inAttrs->{headerBgSorted}; | ||||
1147 | my $attr = 'background-color:' . $color; | ||||
1148 | &$setAttribute( $tableSelector, 'th.foswikiSortedCol', $attr ); | ||||
1149 | } | ||||
1150 | |||||
1151 | # headercolor | ||||
1152 | if ( defined $inAttrs->{headerColor} ) { | ||||
1153 | my $attr = 'color:' . $inAttrs->{headerColor}; | ||||
1154 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1155 | &$setAttribute( $tableSelector, 'th a:link', $attr ); | ||||
1156 | &$setAttribute( $tableSelector, 'th a:visited', $attr ); | ||||
1157 | &$setAttribute( $tableSelector, 'th a:xhover', $attr ) | ||||
1158 | ; # just to make sorting work: hover should be last. below we will remove the x again. | ||||
1159 | if ( defined $inAttrs->{headerBg} ) { | ||||
1160 | my $hoverBackgroundColor = $inAttrs->{headerBg}; | ||||
1161 | $attr = 'background-color:' . $hoverBackgroundColor; | ||||
1162 | &$setAttribute( $tableSelector, 'th a:xhover', $attr ); | ||||
1163 | } | ||||
1164 | } | ||||
1165 | |||||
1166 | # databg (array) | ||||
1167 | if ( defined $inAttrs->{dataBgListRef} ) { | ||||
1168 | my @dataBg = @{ $inAttrs->{dataBgListRef} }; | ||||
1169 | my $noneColor = ( $dataBg[0] =~ /none/i ) ? 'transparent' : ''; | ||||
1170 | my $count = 0; | ||||
1171 | foreach my $color (@dataBg) { | ||||
1172 | $color = $noneColor if $noneColor; | ||||
1173 | next if !$color; | ||||
1174 | my $rowSelector = 'foswikiTableRow' . 'dataBg' . $count; | ||||
1175 | my $attr = "background-color:$color"; | ||||
1176 | &$setAttribute( $tableSelector, "tr.$rowSelector td", $attr ); | ||||
1177 | $count++; | ||||
1178 | } | ||||
1179 | } | ||||
1180 | |||||
1181 | # databgsorted (array) | ||||
1182 | if ( defined $inAttrs->{dataBgSortedListRef} ) { | ||||
1183 | my @dataBgSorted = @{ $inAttrs->{dataBgSortedListRef} }; | ||||
1184 | my $noneColor = ( $dataBgSorted[0] =~ /none/i ) ? 'transparent' : ''; | ||||
1185 | my $count = 0; | ||||
1186 | foreach my $color (@dataBgSorted) { | ||||
1187 | $color = $noneColor if $noneColor; | ||||
1188 | next if !$color; | ||||
1189 | my $rowSelector = 'foswikiTableRow' . 'dataBg' . $count; | ||||
1190 | my $attr = "background-color:$color"; | ||||
1191 | &$setAttribute( $tableSelector, | ||||
1192 | "tr.$rowSelector td.foswikiSortedCol", $attr ); | ||||
1193 | $count++; | ||||
1194 | } | ||||
1195 | } | ||||
1196 | |||||
1197 | # datacolor (array) | ||||
1198 | if ( defined $inAttrs->{dataColorListRef} ) { | ||||
1199 | my @dataColor = @{ $inAttrs->{dataColorListRef} }; | ||||
1200 | unless ( $dataColor[0] =~ /none/i ) { | ||||
1201 | my $count = 0; | ||||
1202 | foreach my $color (@dataColor) { | ||||
1203 | next if !$color; | ||||
1204 | my $rowSelector = 'foswikiTableRow' . 'dataColor' . $count; | ||||
1205 | my $attr = "color:$color"; | ||||
1206 | &$setAttribute( $tableSelector, "tr.$rowSelector td", $attr ); | ||||
1207 | $count++; | ||||
1208 | } | ||||
1209 | } | ||||
1210 | } | ||||
1211 | |||||
1212 | # columnwidths | ||||
1213 | if ( defined $inAttrs->{columnWidthsListRef} ) { | ||||
1214 | my @columnWidths = @{ $inAttrs->{columnWidthsListRef} }; | ||||
1215 | my $count = 0; | ||||
1216 | foreach my $width (@columnWidths) { | ||||
1217 | next if !$width; | ||||
1218 | $width = addDefaultSizeUnit($width); | ||||
1219 | my $colSelector = 'foswikiTableCol'; | ||||
1220 | $colSelector .= $count; | ||||
1221 | my $attr = 'width:' . $width; | ||||
1222 | &$setAttribute( $tableSelector, "td.$colSelector", $attr ); | ||||
1223 | &$setAttribute( $tableSelector, "th.$colSelector", $attr ); | ||||
1224 | $count++; | ||||
1225 | } | ||||
1226 | } | ||||
1227 | |||||
1228 | # headeralign | ||||
1229 | if ( defined $inAttrs->{headerAlignListRef} ) { | ||||
1230 | my @headerAlign = @{ $inAttrs->{headerAlignListRef} }; | ||||
1231 | if ( scalar @headerAlign == 1 ) { | ||||
1232 | my $align = $headerAlign[0]; | ||||
1233 | my $attr = 'text-align:' . $align; | ||||
1234 | &$setAttribute( $tableSelector, 'th', $attr ); | ||||
1235 | } | ||||
1236 | else { | ||||
1237 | my $count = 0; | ||||
1238 | foreach my $align (@headerAlign) { | ||||
1239 | next if !$align; | ||||
1240 | my $colSelector = 'foswikiTableCol'; | ||||
1241 | $colSelector .= $count; | ||||
1242 | my $attr = 'text-align:' . $align; | ||||
1243 | &$setAttribute( $tableSelector, "th.$colSelector", $attr ); | ||||
1244 | $count++; | ||||
1245 | } | ||||
1246 | } | ||||
1247 | } | ||||
1248 | |||||
1249 | # dataAlign | ||||
1250 | if ( defined $inAttrs->{dataAlignListRef} ) { | ||||
1251 | my @dataAlign = @{ $inAttrs->{dataAlignListRef} }; | ||||
1252 | if ( scalar @dataAlign == 1 ) { | ||||
1253 | my $align = $dataAlign[0]; | ||||
1254 | my $attr = 'text-align:' . $align; | ||||
1255 | &$setAttribute( $tableSelector, 'td', $attr ); | ||||
1256 | } | ||||
1257 | else { | ||||
1258 | my $count = 0; | ||||
1259 | foreach my $align (@dataAlign) { | ||||
1260 | next if !$align; | ||||
1261 | my $colSelector = 'foswikiTableCol'; | ||||
1262 | $colSelector .= $count; | ||||
1263 | my $attr = 'text-align:' . $align; | ||||
1264 | &$setAttribute( $tableSelector, "td.$colSelector", $attr ); | ||||
1265 | $count++; | ||||
1266 | } | ||||
1267 | } | ||||
1268 | } | ||||
1269 | |||||
1270 | my @styles = (); | ||||
1271 | foreach my $tableSelector ( sort keys %{$_styles} ) { | ||||
1272 | foreach my $selector ( sort keys %{ $_styles->{$tableSelector} } ) { | ||||
1273 | my $selectors = | ||||
1274 | join( '; ', @{ $_styles->{$tableSelector}->{$selector} } ); | ||||
1275 | $selector =~ s/xhover/hover/go; # remove sorting hack | ||||
1276 | # TODO: optimize by combining identical rules | ||||
1277 | if ( $selector eq '#' ) { | ||||
1278 | push @styles, "$tableSelector {$selectors}"; | ||||
1279 | } | ||||
1280 | else { | ||||
1281 | push @styles, "$tableSelector $selector {$selectors}"; | ||||
1282 | } | ||||
1283 | } | ||||
1284 | } | ||||
1285 | |||||
1286 | return ( $id, @styles ); | ||||
1287 | } | ||||
1288 | |||||
1289 | sub _addHeadStyles { | ||||
1290 | my ( $inId, @inStyles ) = @_; | ||||
1291 | |||||
1292 | return if !scalar @inStyles; | ||||
1293 | |||||
1294 | $styles->{seendIds}->{$inId} = 1; | ||||
1295 | if ( $inId eq $HEAD_ID_DEFAULT_STYLE ) { | ||||
1296 | $styles->{$HEAD_ID_DEFAULT_STYLE}->{'default'} = \@inStyles; | ||||
1297 | _writeStyleToHead( $HEAD_ID_DEFAULT_STYLE, | ||||
1298 | $styles->{$HEAD_ID_DEFAULT_STYLE} ); | ||||
1299 | } | ||||
1300 | else { | ||||
1301 | $styles->{$HEAD_ID_SPECIFIC_STYLE}->{$inId} = \@inStyles; | ||||
1302 | _writeStyleToHead( $HEAD_ID_SPECIFIC_STYLE, | ||||
1303 | $styles->{$HEAD_ID_SPECIFIC_STYLE} ); | ||||
1304 | } | ||||
1305 | } | ||||
1306 | |||||
1307 | sub _writeStyleToHead { | ||||
1308 | my ( $inId, $inStyles ) = @_; | ||||
1309 | |||||
1310 | my @allStyles = (); | ||||
1311 | foreach my $id ( sort keys %{$inStyles} ) { | ||||
1312 | push @allStyles, @{ $inStyles->{$id} }; | ||||
1313 | } | ||||
1314 | my $styleText = join( "\n", @allStyles ); | ||||
1315 | |||||
1316 | my $header = <<EOS; | ||||
1317 | <style type="text/css" media="all"> | ||||
1318 | $styleText | ||||
1319 | </style> | ||||
1320 | EOS | ||||
1321 | $header =~ s/(.*?)\s*$/$1/; # remove last newline | ||||
1322 | Foswiki::Func::addToHEAD( $inId, $header, $HEAD_ID_DEFAULT_STYLE ); | ||||
1323 | } | ||||
1324 | |||||
1325 | =pod | ||||
1326 | |||||
1327 | StaticMethod addDefaultSizeUnit ($text) -> $text | ||||
1328 | |||||
1329 | Adds size unit 'px' if this is missing from the size text. | ||||
1330 | |||||
1331 | =cut | ||||
1332 | |||||
1333 | sub addDefaultSizeUnit { | ||||
1334 | my ($inSize) = @_; | ||||
1335 | |||||
1336 | my $unit = ''; | ||||
1337 | if ( $inSize =~ m/$PATTERN_ATTRIBUTE_SIZE/ ) { | ||||
1338 | $unit = 'px' if !$2; | ||||
1339 | } | ||||
1340 | return "$inSize$unit"; | ||||
1341 | } | ||||
1342 | |||||
1343 | sub emitTable { | ||||
1344 | |||||
1345 | _addDefaultStyles(); | ||||
1346 | |||||
1347 | _debug('emitTable'); | ||||
1348 | |||||
1349 | #Validate headerrows/footerrows and modify if out of range | ||||
1350 | if ( $combinedTableAttrs->{headerrows} > scalar @curTable ) { | ||||
1351 | $combinedTableAttrs->{headerrows} = | ||||
1352 | scalar @curTable; # limit header to size of table! | ||||
1353 | } | ||||
1354 | if ( $combinedTableAttrs->{headerrows} + $combinedTableAttrs->{footerrows} > | ||||
1355 | @curTable ) | ||||
1356 | { | ||||
1357 | $combinedTableAttrs->{footerrows} = scalar @curTable - | ||||
1358 | $combinedTableAttrs->{headerrows}; # and footer to whatever is left | ||||
1359 | } | ||||
1360 | |||||
1361 | my $sortThisTable = | ||||
1362 | ( !defined $combinedTableAttrs->{sortAllTables} | ||||
1363 | || $combinedTableAttrs->{sortAllTables} == 0 ) | ||||
1364 | ? 0 | ||||
1365 | : $combinedTableAttrs->{sort}; | ||||
1366 | |||||
1367 | if ( $combinedTableAttrs->{headerrows} == 0 ) { | ||||
1368 | my ( $headerRowCount, $footerRowCount ) = _headerRowCount( \@curTable ); | ||||
1369 | |||||
1370 | # override default setting with calculated header count | ||||
1371 | $combinedTableAttrs->{headerrows} = $headerRowCount; | ||||
1372 | $combinedTableAttrs->{footerrows} = $footerRowCount; | ||||
1373 | } | ||||
1374 | |||||
1375 | my $tableTagAttributes = {}; | ||||
1376 | $tableTagAttributes->{class} = $combinedTableAttrs->{class}; | ||||
1377 | $tableTagAttributes->{border} = $combinedTableAttrs->{border}; | ||||
1378 | $tableTagAttributes->{cellspacing} = $combinedTableAttrs->{cellspacing}; | ||||
1379 | $tableTagAttributes->{cellpadding} = $combinedTableAttrs->{cellpadding}; | ||||
1380 | $tableTagAttributes->{id} = $combinedTableAttrs->{id} | ||||
1381 | || undef; | ||||
1382 | $tableTagAttributes->{summary} = $combinedTableAttrs->{summary}; | ||||
1383 | $tableTagAttributes->{frame} = $combinedTableAttrs->{frame}; | ||||
1384 | $tableTagAttributes->{rules} = $combinedTableAttrs->{rules}; | ||||
1385 | $tableTagAttributes->{width} = $combinedTableAttrs->{width}; | ||||
1386 | |||||
1387 | # remove empty attributes | ||||
1388 | while ( my ( $key, $value ) = each %{$tableTagAttributes} ) { | ||||
1389 | delete $tableTagAttributes->{$key} if !defined $value || $value eq ''; | ||||
1390 | } | ||||
1391 | |||||
1392 | my $text = $currTablePre . CGI::start_table($tableTagAttributes); | ||||
1393 | $text .= $currTablePre . CGI::caption( $combinedTableAttrs->{tableCaption} ) | ||||
1394 | if $combinedTableAttrs->{tableCaption}; | ||||
1395 | |||||
1396 | # count the number of cols to prevent looping over non-existing columns | ||||
1397 | my $maxCols = 0; | ||||
1398 | |||||
1399 | # Flush out any remaining rowspans | ||||
1400 | for ( my $i = 0 ; $i < @rowspan ; $i++ ) { | ||||
1401 | if ( defined( $rowspan[$i] ) && $rowspan[$i] ) { | ||||
1402 | my $nRows = scalar(@curTable); | ||||
1403 | my $rspan = $rowspan[$i] + 1; | ||||
1404 | my $r = $nRows - $rspan; | ||||
1405 | $curTable[$r][$i]->{attrs} ||= {}; | ||||
1406 | if ( $rspan > 1 ) { | ||||
1407 | $curTable[$r][$i]->{attrs}->{rowspan} = $rspan; | ||||
1408 | } | ||||
1409 | } | ||||
1410 | } | ||||
1411 | |||||
1412 | if ( | ||||
1413 | ( | ||||
1414 | $sortThisTable | ||||
1415 | && defined $sortCol | ||||
1416 | && defined $requestedTable | ||||
1417 | && $requestedTable == $tableCount | ||||
1418 | ) | ||||
1419 | || defined $combinedTableAttrs->{initSort} | ||||
1420 | ) | ||||
1421 | { | ||||
1422 | |||||
1423 | # DG 08 Aug 2002: Allow multi-line headers | ||||
1424 | my @header = splice( @curTable, 0, $combinedTableAttrs->{headerrows} ); | ||||
1425 | |||||
1426 | # DG 08 Aug 2002: Skip sorting any trailers as well | ||||
1427 | my @trailer = (); | ||||
1428 | if ( $combinedTableAttrs->{footerrows} | ||||
1429 | && scalar(@curTable) > $combinedTableAttrs->{footerrows} ) | ||||
1430 | { | ||||
1431 | @trailer = splice( @curTable, -$combinedTableAttrs->{footerrows} ); | ||||
1432 | } | ||||
1433 | |||||
1434 | # Count the maximum number of columns of this table | ||||
1435 | for my $row ( 0 .. $#curTable ) { | ||||
1436 | my $thisRowMaxColCount = 0; | ||||
1437 | for my $col ( 0 .. $#{ $curTable[$row] } ) { | ||||
1438 | $thisRowMaxColCount++; | ||||
1439 | } | ||||
1440 | $maxCols = $thisRowMaxColCount | ||||
1441 | if ( $thisRowMaxColCount > $maxCols ); | ||||
1442 | } | ||||
1443 | |||||
1444 | # Handle multi-row labels by killing rowspans in sorted tables | ||||
1445 | for my $row ( 0 .. $#curTable ) { | ||||
1446 | for my $col ( 0 .. $#{ $curTable[$row] } ) { | ||||
1447 | |||||
1448 | # SMELL: why do we need to specify a rowspan of 1? | ||||
1449 | $curTable[$row][$col]->{attrs}->{rowspan} = 1; | ||||
1450 | if ( $curTable[$row][$col]->{type} eq 'Y' ) { | ||||
1451 | $curTable[$row][$col]->{text} = | ||||
1452 | $curTable[ $row - 1 ][$col]->{text}; | ||||
1453 | $curTable[$row][$col]->{type} = 'td'; | ||||
1454 | } | ||||
1455 | } | ||||
1456 | } | ||||
1457 | |||||
1458 | # url requested sort on column beyond end of table. Force to last column | ||||
1459 | $sortCol = 0 unless ( $sortCol =~ m/^[0-9]+$/ ); | ||||
1460 | $sortCol = $maxCols - 1 if ( $sortCol >= $maxCols ); | ||||
1461 | |||||
1462 | # only get the column type if within bounds | ||||
1463 | if ( $sortCol < $maxCols ) { | ||||
1464 | _setSortTypeForCells( $sortCol, \@curTable ); | ||||
1465 | } | ||||
1466 | |||||
1467 | _debug("currentSortDirection:$currentSortDirection"); | ||||
1468 | |||||
1469 | if ( $combinedTableAttrs->{sort} | ||||
1470 | && $currentSortDirection == $SORT_DIRECTION->{'ASCENDING'} ) | ||||
1471 | { | ||||
1472 | @curTable = sort { | ||||
1473 | $a->[$sortCol]->{sortText} cmp $b->[$sortCol]->{sortText} | ||||
1474 | || $a->[$sortCol]->{number} <=> $b->[$sortCol]->{number} | ||||
1475 | || $a->[$sortCol]->{dateString} | ||||
1476 | cmp $b->[$sortCol]->{dateString} | ||||
1477 | } @curTable; | ||||
1478 | } | ||||
1479 | elsif ($combinedTableAttrs->{sort} | ||||
1480 | && $currentSortDirection == $SORT_DIRECTION->{'DESCENDING'} ) | ||||
1481 | { | ||||
1482 | @curTable = sort { | ||||
1483 | $b->[$sortCol]->{sortText} cmp $a->[$sortCol]->{sortText} | ||||
1484 | || $b->[$sortCol]->{number} <=> $a->[$sortCol]->{number} | ||||
1485 | || $b->[$sortCol]->{dateString} | ||||
1486 | cmp $a->[$sortCol]->{dateString} | ||||
1487 | } @curTable; | ||||
1488 | } | ||||
1489 | |||||
1490 | # DG 08 Aug 2002: Cleanup after the header/trailer splicing | ||||
1491 | # this is probably awfully inefficient - but how big is a table? | ||||
1492 | @curTable = ( @header, @curTable, @trailer ); | ||||
1493 | } # if defined $sortCol ... | ||||
1494 | |||||
1495 | my $rowCount = 0; | ||||
1496 | my $numberOfRows = scalar(@curTable); | ||||
1497 | my $dataColorCount = 0; | ||||
1498 | |||||
1499 | my @headerRowList = (); | ||||
1500 | my @bodyRowList = (); | ||||
1501 | my @footerRowList = (); | ||||
1502 | |||||
1503 | my $isPastHeaderRows = 0; | ||||
1504 | my $singleIndent = "\n\t"; | ||||
1505 | my $doubleIndent = "\n\t\t"; | ||||
1506 | my $tripleIndent = "\n\t\t\t"; | ||||
1507 | |||||
1508 | # Only *one* row of the table has sort links, and it will either | ||||
1509 | # be the last row in the header or the first row in the footer. | ||||
1510 | my $sortLinksWritten = 0; | ||||
1511 | |||||
1512 | foreach my $row (@curTable) { | ||||
1513 | my $rowtext = ''; | ||||
1514 | my $colCount = 0; | ||||
1515 | |||||
1516 | # keep track of header cells: if all cells are header cells, do not | ||||
1517 | # update the data color count | ||||
1518 | my $headerCellCount = 0; | ||||
1519 | my $numberOfCols = scalar(@$row); | ||||
1520 | my $writingSortLinks = 0; | ||||
1521 | |||||
1522 | foreach my $fcell (@$row) { | ||||
1523 | |||||
1524 | # check if cell exists | ||||
1525 | next if ( !$fcell || !$fcell->{type} ); | ||||
1526 | |||||
1527 | my $tableAnchor = ''; | ||||
1528 | next | ||||
1529 | if ( $fcell->{type} eq 'X' ) | ||||
1530 | ; # data was there so sort could work with col spanning | ||||
1531 | my $type = $fcell->{type}; | ||||
1532 | my $cell = $fcell->{text}; | ||||
1533 | my $attr = $fcell->{attrs} || {}; | ||||
1534 | |||||
1535 | my $newDirection; | ||||
1536 | my $isSorted = 0; | ||||
1537 | |||||
1538 | if ( | ||||
1539 | $currentSortDirection != $SORT_DIRECTION->{'NONE'} | ||||
1540 | && defined $sortCol | ||||
1541 | && $colCount == $sortCol | ||||
1542 | |||||
1543 | # Removing the line below hides the marking of sorted columns | ||||
1544 | # until the user clicks on a header (KJL) | ||||
1545 | # && defined $requestedTable && $requestedTable == $tableCount | ||||
1546 | # && $sortType ne '' | ||||
1547 | ) | ||||
1548 | { | ||||
1549 | $isSorted = 1; | ||||
1550 | $newDirection = _getNewSortDirection($currentSortDirection); | ||||
1551 | } | ||||
1552 | else { | ||||
1553 | $newDirection = _getDefaultSortDirection(); | ||||
1554 | } | ||||
1555 | |||||
1556 | if ( $type eq 'th' ) { | ||||
1557 | $headerCellCount++; | ||||
1558 | |||||
1559 | # html attribute: bgcolor | ||||
1560 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
1561 | && defined $combinedTableAttrs->{headerBg} ) | ||||
1562 | { | ||||
1563 | $attr->{bgcolor} = $combinedTableAttrs->{headerBg} | ||||
1564 | unless ( $combinedTableAttrs->{headerBg} =~ /none/i ); | ||||
1565 | } | ||||
1566 | |||||
1567 | # END html attribute | ||||
1568 | |||||
1569 | if ($isSorted) { | ||||
1570 | if ( $currentSortDirection == | ||||
1571 | $SORT_DIRECTION->{'ASCENDING'} ) | ||||
1572 | { | ||||
1573 | $tableAnchor = $CHAR_SORT_ASCENDING; | ||||
1574 | } | ||||
1575 | if ( $currentSortDirection == | ||||
1576 | $SORT_DIRECTION->{'DESCENDING'} ) | ||||
1577 | { | ||||
1578 | $tableAnchor = $CHAR_SORT_DESCENDING; | ||||
1579 | } | ||||
1580 | |||||
1581 | # html attribute: (sorted header cell) bgcolor | ||||
1582 | # overrides earlier set bgcolor | ||||
1583 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
1584 | && defined $combinedTableAttrs->{headerBgSorted} ) | ||||
1585 | { | ||||
1586 | $attr->{bgcolor} = $combinedTableAttrs->{headerBgSorted} | ||||
1587 | unless ( | ||||
1588 | $combinedTableAttrs->{headerBgSorted} =~ /none/i ); | ||||
1589 | } | ||||
1590 | |||||
1591 | # END html attribute | ||||
1592 | } | ||||
1593 | |||||
1594 | if ( | ||||
1595 | defined $sortCol | ||||
1596 | && $colCount == $sortCol | ||||
1597 | && defined $requestedTable | ||||
1598 | && $requestedTable == $tableCount | ||||
1599 | && ( $combinedTableAttrs->{headerrows} | ||||
1600 | || $combinedTableAttrs->{footerrows} ) | ||||
1601 | ) | ||||
1602 | { | ||||
1603 | |||||
1604 | $tableAnchor = | ||||
1605 | CGI::a( { name => 'sorted_table' }, '<!-- -->' ) | ||||
1606 | . $tableAnchor; | ||||
1607 | } | ||||
1608 | |||||
1609 | # html attribute: headercolor (font style) | ||||
1610 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
1611 | && defined $combinedTableAttrs->{headerColor} ) | ||||
1612 | { | ||||
1613 | my $fontStyle = | ||||
1614 | { color => $combinedTableAttrs->{headerColor} }; | ||||
1615 | $cell = CGI::font( $fontStyle, $cell ); | ||||
1616 | } | ||||
1617 | |||||
1618 | # END html attribute | ||||
1619 | |||||
1620 | if ( | ||||
1621 | $sortThisTable | ||||
1622 | && ( | ||||
1623 | ( $rowCount == $combinedTableAttrs->{headerrows} - 1 ) | ||||
1624 | || ( !$combinedTableAttrs->{headerrows} | ||||
1625 | && $rowCount == | ||||
1626 | $numberOfRows - $combinedTableAttrs->{footerrows} ) | ||||
1627 | ) | ||||
1628 | && ( $writingSortLinks || !$sortLinksWritten ) | ||||
1629 | ) | ||||
1630 | { | ||||
1631 | $writingSortLinks = 1; | ||||
1632 | my $linkAttributes = { | ||||
1633 | href => $url | ||||
1634 | . 'sortcol=' | ||||
1635 | . $colCount | ||||
1636 | . ';table=' | ||||
1637 | . $tableCount . ';up=' | ||||
1638 | . $newDirection | ||||
1639 | . '#sorted_table', | ||||
1640 | rel => 'nofollow', | ||||
1641 | title => 'Sort by this column' | ||||
1642 | }; | ||||
1643 | |||||
1644 | if ( $cell =~ /\[\[|href/o ) { | ||||
1645 | $cell .= CGI::a( $linkAttributes, $CHAR_SORT_BOTH ) | ||||
1646 | . $tableAnchor; | ||||
1647 | } | ||||
1648 | else { | ||||
1649 | $cell = CGI::a( $linkAttributes, $cell ) . $tableAnchor; | ||||
1650 | } | ||||
1651 | } | ||||
1652 | |||||
1653 | } | ||||
1654 | else { | ||||
1655 | |||||
1656 | $type = 'td' unless $type eq 'Y'; | ||||
1657 | |||||
1658 | # html attribute: bgcolor | ||||
1659 | if ( $combinedTableAttrs->{generateInlineMarkup} ) { | ||||
1660 | if ( $isSorted | ||||
1661 | && defined $combinedTableAttrs->{dataBgSortedListRef} ) | ||||
1662 | { | ||||
1663 | my @dataBg = | ||||
1664 | @{ $combinedTableAttrs->{dataBgSortedListRef} }; | ||||
1665 | |||||
1666 | unless ( $dataBg[0] =~ /none/ ) { | ||||
1667 | $attr->{bgcolor} = | ||||
1668 | $dataBg[ $dataColorCount % ( $#dataBg + 1 ) ]; | ||||
1669 | } | ||||
1670 | } | ||||
1671 | elsif ( defined $combinedTableAttrs->{dataBgListRef} ) { | ||||
1672 | my @dataBg = @{ $combinedTableAttrs->{dataBgListRef} }; | ||||
1673 | unless ( $dataBg[0] =~ /none/i ) { | ||||
1674 | $attr->{bgcolor} = | ||||
1675 | $dataBg[ $dataColorCount % ( $#dataBg + 1 ) ]; | ||||
1676 | } | ||||
1677 | } | ||||
1678 | } | ||||
1679 | |||||
1680 | # END html attribute | ||||
1681 | |||||
1682 | # html attribute: datacolor (font style) | ||||
1683 | if ( $combinedTableAttrs->{generateInlineMarkup} | ||||
1684 | && defined $combinedTableAttrs->{dataColorListRef} ) | ||||
1685 | { | ||||
1686 | my @dataColor = | ||||
1687 | @{ $combinedTableAttrs->{dataColorListRef} }; | ||||
1688 | my $color = | ||||
1689 | $dataColor[ $dataColorCount % ( $#dataColor + 1 ) ]; | ||||
1690 | unless ( $color =~ /^(none)$/i ) { | ||||
1691 | my $cellAttrs = { color => $color }; | ||||
1692 | $cell = CGI::font( $cellAttrs, ' ' . $cell . ' ' ); | ||||
1693 | } | ||||
1694 | } | ||||
1695 | |||||
1696 | # END html attribute | ||||
1697 | |||||
1698 | } ###if( $type eq 'th' ) | ||||
1699 | |||||
1700 | if ($isSorted) { | ||||
1701 | $attr->{class} = _appendSortedCssClass( $attr->{class} ); | ||||
1702 | } | ||||
1703 | |||||
1704 | if ($writingSortLinks) { | ||||
1705 | $sortLinksWritten = 1; | ||||
1706 | } | ||||
1707 | |||||
1708 | my $isLastRow = ( $rowCount == $numberOfRows - 1 ); | ||||
1709 | if ( $attr->{rowspan} ) { | ||||
1710 | $isLastRow = | ||||
1711 | ( ( $rowCount + ( $attr->{rowspan} - 1 ) ) == | ||||
1712 | $numberOfRows - 1 ); | ||||
1713 | } | ||||
1714 | |||||
1715 | # CSS class name | ||||
1716 | $attr->{class} = _appendFirstColumnCssClass( $attr->{class} ) | ||||
1717 | if $colCount == 0; | ||||
1718 | my $isLastCol = ( $colCount == $numberOfCols - 1 ); | ||||
1719 | $attr->{class} = _appendLastColumnCssClass( $attr->{class} ) | ||||
1720 | if $isLastCol; | ||||
1721 | |||||
1722 | $attr->{class} = _appendLastRowCssClass( $attr->{class} ) | ||||
1723 | if $isLastRow; | ||||
1724 | |||||
1725 | $colCount++; | ||||
1726 | next if ( $type eq 'Y' ); | ||||
1727 | my $fn = 'CGI::' . $type; | ||||
1728 | 2 | 54µs | 2 | 46µs | # spent 30µs (14+16) within Foswiki::Plugins::TablePlugin::Core::BEGIN@1728 which was called:
# once (14µs+16µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 1728 # spent 30µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@1728
# spent 16µs making 1 call to strict::unimport |
1729 | $rowtext .= "$tripleIndent" . &$fn( $attr, " $cell " ); | ||||
1730 | 2 | 1.27ms | 2 | 33µs | # spent 23µs (12+10) within Foswiki::Plugins::TablePlugin::Core::BEGIN@1730 which was called:
# once (12µs+10µs) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 1730 # spent 23µs making 1 call to Foswiki::Plugins::TablePlugin::Core::BEGIN@1730
# spent 10µs making 1 call to strict::import |
1731 | } # foreach my $fcell ( @$row ) | ||||
1732 | |||||
1733 | # assign css class names to tr | ||||
1734 | # based on settings: dataBg, dataBgSorted | ||||
1735 | my $trClassName = ''; | ||||
1736 | |||||
1737 | # just 2 css names is too limited, but we will keep it for compatibility | ||||
1738 | # with existing style sheets | ||||
1739 | my $rowTypeName = | ||||
1740 | ( $rowCount % 2 ) ? 'foswikiTableEven' : 'foswikiTableOdd'; | ||||
1741 | $trClassName = _appendToClassList( $trClassName, $rowTypeName ); | ||||
1742 | |||||
1743 | if ( $combinedTableAttrs->{dataBgSortedListRef} ) { | ||||
1744 | my @dataBgSorted = @{ $combinedTableAttrs->{dataBgSortedListRef} }; | ||||
1745 | my $modRowNum = $dataColorCount % ( $#dataBgSorted + 1 ); | ||||
1746 | $trClassName = | ||||
1747 | _appendRowNumberCssClass( $trClassName, 'dataBgSorted', | ||||
1748 | $modRowNum ); | ||||
1749 | } | ||||
1750 | if ( $combinedTableAttrs->{dataBgListRef} ) { | ||||
1751 | my @dataBg = @{ $combinedTableAttrs->{dataBgListRef} }; | ||||
1752 | my $modRowNum = $dataColorCount % ( $#dataBg + 1 ); | ||||
1753 | $trClassName = | ||||
1754 | _appendRowNumberCssClass( $trClassName, 'dataBg', $modRowNum ); | ||||
1755 | } | ||||
1756 | if ( $combinedTableAttrs->{dataColorListRef} ) { | ||||
1757 | my @dataColor = @{ $combinedTableAttrs->{dataColorListRef} }; | ||||
1758 | my $modRowNum = $dataColorCount % ( $#dataColor + 1 ); | ||||
1759 | $trClassName = | ||||
1760 | _appendRowNumberCssClass( $trClassName, 'dataColor', $modRowNum ); | ||||
1761 | } | ||||
1762 | $rowtext .= $doubleIndent; | ||||
1763 | my $rowHTML = | ||||
1764 | $doubleIndent . CGI::Tr( { class => $trClassName }, $rowtext ); | ||||
1765 | |||||
1766 | my $isHeaderRow = | ||||
1767 | $rowCount < | ||||
1768 | $combinedTableAttrs->{headerrows}; #( $headerCellCount == $colCount ); | ||||
1769 | my $isFooterRow = | ||||
1770 | ( ( $numberOfRows - $rowCount ) <= | ||||
1771 | $combinedTableAttrs->{footerrows} ); | ||||
1772 | |||||
1773 | if ( !$isHeaderRow && !$isFooterRow ) { | ||||
1774 | |||||
1775 | # don't include non-adjacent header rows to the top block of header rows | ||||
1776 | $isPastHeaderRows = 1; | ||||
1777 | } | ||||
1778 | |||||
1779 | if ($isFooterRow) { | ||||
1780 | push @footerRowList, $rowHTML; | ||||
1781 | } | ||||
1782 | elsif ( $isHeaderRow && !$isPastHeaderRows ) { | ||||
1783 | push( @headerRowList, $rowHTML ); | ||||
1784 | } | ||||
1785 | else { | ||||
1786 | push @bodyRowList, $rowHTML; | ||||
1787 | $dataColorCount++; | ||||
1788 | } | ||||
1789 | |||||
1790 | if ($isHeaderRow) { | ||||
1791 | |||||
1792 | # reset data color count to start with first color after | ||||
1793 | # each table heading | ||||
1794 | $dataColorCount = 0; | ||||
1795 | } | ||||
1796 | |||||
1797 | $rowCount++; | ||||
1798 | } # foreach my $row ( @curTable ) | ||||
1799 | |||||
1800 | my $thead = | ||||
1801 | "$singleIndent<thead>" | ||||
1802 | . join( "", @headerRowList ) | ||||
1803 | . "$singleIndent</thead>"; | ||||
1804 | $text .= $currTablePre . $thead if scalar @headerRowList; | ||||
1805 | |||||
1806 | my $tfoot = | ||||
1807 | "$singleIndent<tfoot>" | ||||
1808 | . join( "", @footerRowList ) | ||||
1809 | . "$singleIndent</tfoot>"; | ||||
1810 | $text .= $currTablePre . $tfoot if scalar @footerRowList; | ||||
1811 | |||||
1812 | my $tbody; | ||||
1813 | if ( scalar @bodyRowList ) { | ||||
1814 | $tbody = | ||||
1815 | "$singleIndent<tbody>" | ||||
1816 | . join( "", @bodyRowList ) | ||||
1817 | . "$singleIndent</tbody>"; | ||||
1818 | } | ||||
1819 | else { | ||||
1820 | |||||
1821 | # A HTML table requires a body, which cannot be empty (Item8991). | ||||
1822 | # So we provide one, but prevent it from being displayed. | ||||
1823 | $tbody = | ||||
1824 | "$singleIndent<tbody>$doubleIndent<tr style=\"display:none;\">$tripleIndent<td></td>$doubleIndent</tr>$singleIndent</tbody>\n"; | ||||
1825 | } | ||||
1826 | |||||
1827 | if ( scalar @messages ) { | ||||
1828 | $text = | ||||
1829 | '<span class="foswikiAlert">' | ||||
1830 | . Foswiki::Func::expandCommonVariables( join( "\n", @messages ) ) | ||||
1831 | . '</span>' . "\n" | ||||
1832 | . $text; | ||||
1833 | } | ||||
1834 | |||||
1835 | $text .= $currTablePre . $tbody; | ||||
1836 | $text .= $currTablePre . CGI::end_table() . "\n"; | ||||
1837 | |||||
1838 | return $text; | ||||
1839 | } | ||||
1840 | |||||
1841 | # spent 1.77ms (671µs+1.09) within Foswiki::Plugins::TablePlugin::Core::handler which was called 5 times, avg 353µs/call:
# 5 times (671µs+1.09ms) by Foswiki::Plugins::TablePlugin::preRenderingHandler at line 73 of /var/www/foswiki11/lib/Foswiki/Plugins/TablePlugin.pm, avg 353µs/call | ||||
1842 | ### my ( $text, $removed ) = @_; | ||||
1843 | |||||
1844 | 5 | 14µs | 5 | 44µs | _debug('handler'); # spent 44µs making 5 calls to Foswiki::Plugins::TablePlugin::Core::_debug, avg 9µs/call |
1845 | |||||
1846 | 5 | 2µs | unless ($Foswiki::Plugins::TablePlugin::initialised) { | ||
1847 | 1 | 200ns | $insideTABLE = 0; | ||
1848 | |||||
1849 | # Even if $tableCount is initialized already at plugin init | ||||
1850 | # we need to reset it again each time preRenderingHandler | ||||
1851 | # calls this handler sub. Important for initialiseWhenRender API | ||||
1852 | 1 | 200ns | $tableCount = 0; | ||
1853 | |||||
1854 | 1 | 1µs | 1 | 8µs | my $cgi = Foswiki::Func::getCgiQuery(); # spent 8µs making 1 call to Foswiki::Func::getCgiQuery |
1855 | 1 | 200ns | return unless $cgi; | ||
1856 | |||||
1857 | # Copy existing values | ||||
1858 | 1 | 300ns | my ( @origSort, @origTable, @origUp ); | ||
1859 | 1 | 3µs | 1 | 22µs | @origSort = $cgi->param('sortcol'); # spent 22µs making 1 call to Foswiki::Request::param |
1860 | 1 | 2µs | 1 | 13µs | @origTable = $cgi->param('table'); # spent 13µs making 1 call to Foswiki::Request::param |
1861 | 1 | 1µs | 1 | 12µs | @origUp = $cgi->param('up'); # NOTE: internal parameter # spent 12µs making 1 call to Foswiki::Request::param |
1862 | 1 | 5µs | 1 | 9µs | $cgi->delete( 'sortcol', 'table', 'up' ); # spent 9µs making 1 call to Foswiki::Request::delete |
1863 | 1 | 5µs | 1 | 151µs | $url = $cgi->url( -absolute => 1, -path => 1 ) . '?'; # spent 151µs making 1 call to Foswiki::Request::url |
1864 | 1 | 2µs | 1 | 62µs | my $queryString = $cgi->query_string(); # spent 62µs making 1 call to Foswiki::Request::queryString |
1865 | 1 | 700ns | $url .= $queryString . ';' if $queryString; | ||
1866 | |||||
1867 | # Restore parameters, so we don't interfere on the remaining execution | ||||
1868 | 1 | 300ns | $cgi->param( -name => 'sortcol', -value => \@origSort ) if @origSort; | ||
1869 | 1 | 200ns | $cgi->param( -name => 'table', -value => \@origTable ) if @origTable; | ||
1870 | 1 | 200ns | $cgi->param( -name => 'up', -value => \@origUp ) if @origUp; | ||
1871 | |||||
1872 | 1 | 1µs | 1 | 13µs | $sortColFromUrl = # spent 13µs making 1 call to Foswiki::Request::param |
1873 | $cgi->param('sortcol'); # zero based: 0 is first column | ||||
1874 | 1 | 100ns | if ( defined $sortColFromUrl && $sortColFromUrl !~ m/^[0-9]+$/ ) { | ||
1875 | $sortColFromUrl = 0; | ||||
1876 | } | ||||
1877 | |||||
1878 | 1 | 1µs | 1 | 12µs | $requestedTable = $cgi->param('table'); # spent 12µs making 1 call to Foswiki::Request::param |
1879 | 1 | 400ns | $requestedTable = 0 | ||
1880 | unless ( defined $requestedTable && $requestedTable =~ m/^[0-9]+$/ ); | ||||
1881 | |||||
1882 | 1 | 1µs | 1 | 11µs | $up = $cgi->param('up'); # spent 11µs making 1 call to Foswiki::Request::param |
1883 | |||||
1884 | 1 | 200ns | $sortTablesInText = 0; | ||
1885 | 1 | 100ns | $sortAttachments = 0; | ||
1886 | 1 | 2µs | 1 | 28µs | my $tmp = Foswiki::Func::getPreferencesValue('TABLEPLUGIN_SORT') # spent 28µs making 1 call to Foswiki::Func::getPreferencesValue |
1887 | || 'all'; | ||||
1888 | 1 | 2µs | if ( !$tmp || $tmp =~ /^all$/oi ) { | ||
1889 | 1 | 200ns | $sortTablesInText = 1; | ||
1890 | 1 | 300ns | $sortAttachments = 1; | ||
1891 | } | ||||
1892 | elsif ( $tmp =~ /^attachments$/oi ) { | ||||
1893 | $sortAttachments = 1; | ||||
1894 | } | ||||
1895 | |||||
1896 | 1 | 2µs | 1 | 383µs | _initDefaults(); # first time # spent 383µs making 1 call to Foswiki::Plugins::TablePlugin::Core::_initDefaults |
1897 | 1 | 1µs | $Foswiki::Plugins::TablePlugin::initialised = 1; | ||
1898 | } | ||||
1899 | |||||
1900 | 5 | 4µs | $insideTABLE = 0; | ||
1901 | |||||
1902 | 5 | 7µs | my $defaultSort = $combinedTableAttrs->{sortAllTables}; | ||
1903 | |||||
1904 | 5 | 2µs | my $acceptable = $combinedTableAttrs->{sortAllTables}; | ||
1905 | 5 | 222µs | my @lines = split( /\r?\n/, $_[0] ); | ||
1906 | 5 | 6µs | for (@lines) { | ||
1907 | 223 | 240µs | if ( | ||
1908 | s/$PATTERN_TABLE/_parseTableSpecificTableAttributes(Foswiki::Func::extractParameters($1))/se | ||||
1909 | ) | ||||
1910 | { | ||||
1911 | $acceptable = 1; | ||||
1912 | } | ||||
1913 | elsif (s/^(\s*)\|(.*\|\s*)$/_processTableRow($1,$2)/eo) { | ||||
1914 | $insideTABLE = 1; | ||||
1915 | } | ||||
1916 | elsif ($insideTABLE) { | ||||
1917 | $_ = emitTable() . $_; | ||||
1918 | $insideTABLE = 0; | ||||
1919 | |||||
1920 | $combinedTableAttrs->{sortAllTables} = $defaultSort; | ||||
1921 | $acceptable = $defaultSort; | ||||
1922 | |||||
1923 | # prepare for next table | ||||
1924 | _resetReusedVariables(); | ||||
1925 | } | ||||
1926 | } | ||||
1927 | 5 | 62µs | $_[0] = join( "\n", @lines ); | ||
1928 | |||||
1929 | 5 | 2µs | if ($insideTABLE) { | ||
1930 | $_[0] .= emitTable(); | ||||
1931 | } | ||||
1932 | |||||
1933 | # prepare for next table | ||||
1934 | 5 | 47µs | 5 | 327µs | _resetReusedVariables(); # spent 327µs making 5 calls to Foswiki::Plugins::TablePlugin::Core::_resetReusedVariables, avg 65µs/call |
1935 | } | ||||
1936 | |||||
1937 | =pod | ||||
1938 | |||||
1939 | _mergeHashes (\%a, \%b ) -> \%merged | ||||
1940 | |||||
1941 | Merges 2 hash references. | ||||
1942 | |||||
1943 | =cut | ||||
1944 | |||||
1945 | # spent 205µs within Foswiki::Plugins::TablePlugin::Core::_mergeHashes which was called 6 times, avg 34µs/call:
# 5 times (182µs+0s) by Foswiki::Plugins::TablePlugin::Core::_resetReusedVariables at line 164, avg 36µs/call
# once (22µs+0s) by Foswiki::Plugins::TablePlugin::Core::_initDefaults at line 149 | ||||
1946 | 6 | 4µs | my ( $A, $B ) = @_; | ||
1947 | |||||
1948 | 6 | 6µs | my %merged = (); | ||
1949 | 6 | 11µs | while ( my ( $k, $v ) = each(%$A) ) { | ||
1950 | $merged{$k} = $v; | ||||
1951 | } | ||||
1952 | 6 | 169µs | while ( my ( $k, $v ) = each(%$B) ) { | ||
1953 | $merged{$k} = $v; | ||||
1954 | } | ||||
1955 | 6 | 27µs | return \%merged; | ||
1956 | } | ||||
1957 | |||||
1958 | =pod | ||||
1959 | |||||
1960 | =cut | ||||
1961 | |||||
1962 | sub _cleanParamValue { | ||||
1963 | 2 | 700ns | my ($inValue) = @_; | ||
1964 | |||||
1965 | 2 | 200ns | return undef if !$inValue; | ||
1966 | |||||
1967 | 2 | 800ns | $inValue =~ s/ //go; # remove spaces | ||
1968 | 2 | 10µs | return $inValue; | ||
1969 | } | ||||
1970 | |||||
1971 | =pod | ||||
1972 | |||||
1973 | =cut | ||||
1974 | |||||
1975 | # spent 22µs within Foswiki::Plugins::TablePlugin::Core::_arrayRefFromParam which was called 6 times, avg 4µs/call:
# once (12µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 384
# once (4µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 382
# once (2µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 367
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 370
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 389
# once (1µs+0s) by Foswiki::Plugins::TablePlugin::Core::_parseAttributes at line 373 | ||||
1976 | 6 | 3µs | my ($inValue) = @_; | ||
1977 | |||||
1978 | 6 | 12µs | return undef if !$inValue; | ||
1979 | |||||
1980 | 2 | 600ns | $inValue =~ s/ //go; # remove spaces | ||
1981 | 2 | 13µs | my @list = split( /,/, $inValue ); | ||
1982 | 2 | 7µs | return \@list; | ||
1983 | } | ||||
1984 | |||||
1985 | =pod | ||||
1986 | |||||
1987 | Shorthand debugging call. | ||||
1988 | |||||
1989 | =cut | ||||
1990 | |||||
1991 | # spent 96µs (65+31) within Foswiki::Plugins::TablePlugin::Core::_debug which was called 13 times, avg 7µs/call:
# 5 times (32µs+12µs) by Foswiki::Plugins::TablePlugin::Core::handler at line 1844, avg 9µs/call
# 5 times (22µs+13µs) by Foswiki::Plugins::TablePlugin::Core::_resetReusedVariables at line 162, avg 7µs/call
# once (5µs+3µs) by Foswiki::Plugins::TablePlugin::Core::_init at line 123
# once (3µs+2µs) by Foswiki::Plugins::TablePlugin::Core::_initDefaults at line 138
# once (3µs+2µs) by Foswiki::Plugins::TablePlugin::Core::_parseDefaultAttributes at line 193 | ||||
1992 | 13 | 57µs | 13 | 31µs | return Foswiki::Plugins::TablePlugin::debug( 'TablePlugin::Core', @_ ); # spent 31µs making 13 calls to Foswiki::Plugins::TablePlugin::debug, avg 2µs/call |
1993 | } | ||||
1994 | |||||
1995 | sub _debugData { | ||||
1996 | 2 | 9µs | 2 | 8µs | return Foswiki::Plugins::TablePlugin::debugData( 'TablePlugin::Core', @_ ); # spent 8µs making 2 calls to Foswiki::Plugins::TablePlugin::debugData, avg 4µs/call |
1997 | } | ||||
1998 | |||||
1999 | 1 | 25µs | 1; | ||
2000 | __END__ |