Handle hostnames with upper-case letters
[webmin.git] / blue-theme / theme.pl
1 # Virtualmin Framed heme
2 # Icons copyright David Vignoni, all other theme elements copyright 2005-2007
3 # Virtualmin, Inc.
4
5 $default_domains_to_show = 10;
6
7 # Global state for wrapper
8 # if 0, wrapper isn't on, add one and open it, if 1 close it, if 2+, subtract
9 # but don't close
10 $WRAPPER_OPEN = 0;
11
12 # theme_ui_post_header([subtext])
13 # Returns HTML to appear directly after a standard header() call
14 sub theme_ui_post_header
15 {
16 local ($text) = @_;
17 my $rv;
18 $rv .= "<div class='ui_post_header'>$text</div>\n" if (defined($text));
19 #$rv .= "<div class='section'>\n";
20 $rv .= "<p>" if (!defined($text));
21 return $rv;
22 }
23
24 # theme_ui_pre_footer()
25 # Returns HTML to appear directly before a standard footer() call
26 sub theme_ui_pre_footer
27 {
28 my $rv;
29 $rv .= "</div><p>\n";
30 return $rv;
31 }
32
33 # ui_print_footer(args...)
34 # Print HTML for a footer with the pre-footer line. Args are the same as those
35 # passed to footer()
36 sub theme_ui_print_footer
37 {
38 local @args = @_;
39 print &ui_pre_footer();
40 &footer(@args);
41 }
42
43 sub theme_icons_table
44 {
45 my ($i, $need_tr);
46 my $cols = $_[3] ? $_[3] : 4;
47 my $per = int(100.0 / $cols);
48 print "<div class='wrapper'>\n";
49 print "<table class='icons_table' id='main' width=100% cellpadding=5>\n";
50 for($i=0; $i<@{$_[0]}; $i++) {
51         if ($i%$cols == 0) { print "<tr>\n"; }
52         print "<td width=$per% align=center valign=top>\n";
53         &generate_icon($_[2]->[$i], $_[1]->[$i], $_[0]->[$i],
54                        $_[4], $_[5], $_[6], $_[7]->[$i], $_[8]->[$i]);
55         print "</td>\n";
56         if ($i%$cols == $cols-1) { print "</tr>\n"; }
57         }
58 while($i++%$cols) { print "<td width=$per%></td>\n"; $need_tr++; }
59 print "</tr>\n" if ($need_tr);
60 print "</table>\n";
61 print "</div>\n";
62 }
63
64 sub theme_generate_icon
65 {
66 my $w = !defined($_[4]) ? "width=48" : $_[4] ? "width=$_[4]" : "";
67 my $h = !defined($_[5]) ? "height=48" : $_[5] ? "height=$_[5]" : "";
68 if ($tconfig{'noicons'}) {
69         if ($_[2]) {
70                 print "$_[6]<a href=\"$_[2]\" $_[3]>$_[1]</a>$_[7]\n";
71                 }
72         else {
73                 print "$_[6]$_[1]$_[7]\n";
74                 }
75         }
76 elsif ($_[2]) {
77         print "<table><tr><td width=48 height=48>\n",
78               "<a href=\"$_[2]\" $_[3]><img src=\"$_[0]\" alt=\"\" border=0 ",
79               "$w $h></a></td></tr></table>\n";
80         print "$_[6]<a href=\"$_[2]\" $_[3]>$_[1]</a>$_[7]\n";
81         }
82 else {
83         print "<table><tr><td width=48 height=48>\n",
84               "<img src=\"$_[0]\" alt=\"\" border=0 $w $h>",
85               "</td></tr></table>\n$_[6]$_[1]$_[7]\n";
86         }
87 }
88
89 sub theme_post_change_modules
90 {
91 print <<EOF;
92 <script>
93 var url = '' + top.left.location;
94 top.left.location = url;
95 </script>
96 EOF
97 }
98
99 sub theme_prebody
100 {
101 my $module_name = &get_module_name();
102 if ($script_name =~ /session_login.cgi/) {
103         # Generate CSS link
104         print "<link rel='stylesheet' type='text/css' href='$gconfig{'webprefix'}/unauthenticated/style.css'>\n";
105         }
106 }
107
108 sub theme_prehead
109 {
110 print "<link rel='stylesheet' type='text/css' href='$gconfig{'webprefix'}/unauthenticated/style.css' />\n";
111 print "<script type='text/javascript' src='$gconfig{'webprefix'}/unauthenticated/toggleview.js'></script>\n";
112 print "<script>\n";
113 print "var rowsel = new Array();\n";
114 print "</script>\n";
115 print "<script type='text/javascript' src='$gconfig{'webprefix'}/unauthenticated/sorttable.js'></script>\n";
116 }
117
118 sub theme_popup_prehead
119 {
120 return &theme_prehead();
121 }
122
123 # ui_table_start(heading, [tabletags], [cols], [&default-tds], [right-heading])
124 # A table with a heading and table inside
125 sub theme_ui_table_start
126 {
127 my ($heading, $tabletags, $cols, $tds, $rightheading) = @_;
128 if (! $tabletags =~ /width/) { $tabletages .= " width=100%"; }
129 if (defined($main::ui_table_cols)) {
130   # Push on stack, for nested call
131   push(@main::ui_table_cols_stack, $main::ui_table_cols);
132   push(@main::ui_table_pos_stack, $main::ui_table_pos);
133   push(@main::ui_table_default_tds_stack, $main::ui_table_default_tds);
134   }
135 my $rv;
136 my $colspan = 1;
137
138 if (!$WRAPPER_OPEN) {
139         $rv .= "<table class='shrinkwrapper' $tabletags>\n";
140         $rv .= "<tr><td>\n";
141         }
142 $WRAPPER_OPEN++;
143 $rv .= "<table class='ui_table' $tabletags>\n";
144 if (defined($heading) || defined($rightheading)) {
145         $rv .= "<thead><tr class='ui_table_head'>";
146         if (defined($heading)) {
147                 $rv .= "<td><b>$heading</b></td>"
148                 }
149         if (defined($rightheading)) {
150                 $rv .= "<td align=right>$rightheading</td>";
151                 $colspan++;
152                 }
153         $rv .= "</tr></thead>\n";
154         }
155 $rv .= "<tbody> <tr class='ui_table_body'> <td colspan=$colspan>".
156        "<table width=100%>\n";
157 $main::ui_table_cols = $cols || 4;
158 $main::ui_table_pos = 0;
159 $main::ui_table_default_tds = $tds;
160 return $rv;
161 }
162
163 # ui_table_end()
164 # The end of a table started by ui_table_start
165 sub theme_ui_table_end
166 {
167 my $rv;
168 if ($main::ui_table_cols == 4 && $main::ui_table_pos) {
169   # Add an empty block to balance the table
170   $rv .= &ui_table_row(" ", " ");
171   }
172 if (@main::ui_table_cols_stack) {
173   $main::ui_table_cols = pop(@main::ui_table_cols_stack);
174   $main::ui_table_pos = pop(@main::ui_table_pos_stack);
175   $main::ui_table_default_tds = pop(@main::ui_table_default_tds_stack);
176   }
177 else {
178   $main::ui_table_cols = undef;
179   $main::ui_table_pos = undef;
180   $main::ui_table_default_tds = undef;
181   }
182 $rv .= "</tbody></table></td></tr></table>\n";
183 if ($WRAPPER_OPEN==1) {
184         #$rv .= "</div>\n";
185         $rv .= "</td></tr>\n";
186         $rv .= "</table>\n";
187         }
188 $WRAPPER_OPEN--;
189 return $rv;
190 }
191
192 # theme_ui_tabs_start(&tabs, name, selected, show-border)
193 # Render a row of tabs from which one can be selected. Each tab is an array
194 # ref containing a name, title and link.
195 sub theme_ui_tabs_start
196 {
197 my ($tabs, $name, $sel, $border) = @_;
198 my $rv;
199 if (!$main::ui_hidden_start_donejs++) {
200   $rv .= &ui_hidden_javascript();
201   }
202
203 # Build list of tab titles and names
204 my $tabnames = "[".join(",", map { "\"".&quote_escape($_->[0])."\"" } @$tabs)."]";
205 my $tabtitles = "[".join(",", map { "\"".&quote_escape($_->[1])."\"" } @$tabs)."]";
206 $rv .= "<script>\n";
207 $rv .= "document.${name}_tabnames = $tabnames;\n";
208 $rv .= "document.${name}_tabtitles = $tabtitles;\n";
209 $rv .= "</script>\n";
210
211 # Output the tabs
212 my $imgdir = "$gconfig{'webprefix'}/images";
213 $rv .= &ui_hidden($name, $sel)."\n";
214 $rv .= "<table border=0 cellpadding=0 cellspacing=0 class='ui_tabs'>\n";
215 $rv .= "<tr><td bgcolor=#ffffff colspan=".(scalar(@$tabs)*2+1).">";
216 if ($ENV{'HTTP_USER_AGENT'} !~ /msie/i) {
217         # For some reason, the 1-pixel space above the tabs appears huge on IE!
218         $rv .= "<img src=$imgdir/1x1.gif>";
219         }
220 $rv .= "</td></tr>\n";
221 $rv .= "<tr>\n";
222 $rv .= "<td bgcolor=#ffffff width=1><img src=$imgdir/1x1.gif></td>\n";
223 foreach my $t (@$tabs) {
224         if ($t ne $tabs[0]) {
225                 # Spacer
226                 $rv .= "<td width=2 bgcolor=#ffffff class='ui_tab_spacer'>".
227                        "<img src=$imgdir/1x1.gif></td>\n";
228                 }
229         my $tabid = "tab_".$t->[0];
230         $rv .= "<td id=${tabid} class='ui_tab'>";
231         $rv .= "<table cellpadding=0 cellspacing=0 border=0><tr>";
232         if ($t->[0] eq $sel) {
233                 # Selected tab
234                 $rv .= "<td valign=top class='tabSelected'>".
235                        "<img src=$imgdir/lc2.gif alt=\"\"></td>";
236                 $rv .= "<td class='tabSelected' nowrap>".
237                        "&nbsp;<b>$t->[1]</b>&nbsp;</td>";
238                 $rv .= "<td valign=top class='tabSelected'>".
239                        "<img src=$imgdir/rc2.gif alt=\"\"></td>";
240                 }
241         else {
242                 # Other tab (which has a link)
243                 $rv .= "<td valign=top class='tabUnselected'>".
244                        "<img src=$imgdir/lc1.gif alt=\"\"></td>";
245                 $rv .= "<td class='tabUnselected' nowrap>".
246                        "&nbsp;<a href='$t->[2]' ".
247                        "onClick='return select_tab(\"$name\", \"$t->[0]\")'>".
248                        "$t->[1]</a>&nbsp;</td>";
249                 $rv .= "<td valign=top class='tabUnselected'>".
250                        "<img src=$imgdir/rc1.gif ".
251                        "alt=\"\"></td>";
252                 $rv .= "</td>\n";
253                 }
254         $rv .= "</tr></table>";
255         $rv .= "</td>\n";
256         }
257 $rv .= "<td bgcolor=#ffffff width=1><img src=$imgdir/1x1.gif></td>\n";
258 $rv .= "</table>\n";
259
260 if ($border) {
261         # All tabs are within a grey box
262         $rv .= "<table width=100% cellpadding=0 cellspacing=0 ".
263                "class='ui_tabs_box'>\n";
264         $rv .= "<tr> <td bgcolor=#ffffff rowspan=3 width=1><img src=$imgdir/1x1.gif></td>\n";
265         $rv .= "<td $cb colspan=3 height=2><img src=$imgdir/1x1.gif></td> </tr>\n";
266         $rv .= "<tr> <td $cb width=2><img src=$imgdir/1x1.gif></td>\n";
267         $rv .= "<td valign=top>";
268         }
269 $main::ui_tabs_selected = $sel;
270 return $rv;
271 }
272
273 # theme_ui_columns_start(&headings, [width-percent], [noborder], [&tdtags], [heading])
274 # Returns HTML for a multi-column table, with the given headings
275 sub theme_ui_columns_start
276 {
277 my ($heads, $width, $noborder, $tdtags, $heading) = @_;
278 my ($href) = grep { $_ =~ /<a\s+href/i } @$heads;
279 my $rv;
280 $theme_ui_columns_row_toggle = 0;
281 if (!$noborder && !$WRAPPER_OPEN) {
282         $rv .= "<table class='wrapper' width="
283              . ($width ? $width : "100")
284              . "% class='ui_columns'>\n";
285         $rv .= "<tr><td>\n";
286         }
287 if (!$noborder) {
288         $WRAPPER_OPEN++;
289         }
290 local @classes;
291 push(@classes, "ui_table") if (!$noborder);
292 push(@classes, "sortable") if (!$href);
293 $rv .= "<table".(@classes ? " class='".join(" ", @classes)."'" : "").
294     (defined($width) ? " width=$width%" : "").">\n";
295 if ($heading) {
296   $rv .= "<thead> <tr $tb title='ui_columns_heading'>".
297          "<td colspan=".scalar(@$heads).
298          "><b>$heading</b></td></tr> </thead>\n";
299   }
300 $rv .= "<thead> <tr $tb class='ui_columns_heads'>\n";
301 my $i;
302 for($i=0; $i<@$heads; $i++) {
303   $rv .= "<td ".$tdtags->[$i]."><b>".
304          ($heads->[$i] eq "" ? "<br>" : $heads->[$i])."</b></td>\n";
305   }
306 $rv .= "</tr></thead> <tbody>\n";
307 $theme_ui_columns_count++;
308 return $rv;
309 }
310
311 # theme_ui_columns_row(&columns, &tdtags)
312 # Returns HTML for a row in a multi-column table
313 sub theme_ui_columns_row
314 {
315 $theme_ui_columns_row_toggle = $theme_ui_columns_row_toggle ? '0' : '1';
316 local ($cols, $tdtags) = @_;
317 my $rv;
318 $rv .= "<tr class='ui_columns row$theme_ui_columns_row_toggle' onMouseOver=\"this.className='mainhigh'\" onMouseOut=\"this.className='mainbody row$theme_ui_columns_row_toggle'\">\n";
319 my $i;
320 for($i=0; $i<@$cols; $i++) {
321         $rv .= "<td ".$tdtags->[$i].">".
322                ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i])."</td>\n";
323         }
324 $rv .= "</tr>\n";
325 return $rv;
326 }
327
328 # theme_ui_columns_end()
329 # Returns HTML to end a table started by ui_columns_start
330 sub theme_ui_columns_end
331 {
332 my $rv;
333 $rv = "</tbody> </table>\n";
334 if ($WRAPPER_OPEN == 1) { # Last wrapper
335         $rv .= "</td> </tr> </table>\n";
336         }
337 $WRAPPER_OPEN--;
338 return $rv;
339 }
340
341 # theme_ui_grid_table(&elements, columns, [width-percent], [tds], [tabletags],
342 #   [title])
343 # Given a list of HTML elements, formats them into a table with the given
344 # number of columns. However, themes are free to override this to use fewer
345 # columns where space is limited.
346 sub theme_ui_grid_table
347 {
348 my ($elements, $cols, $width, $tds, $tabletags, $title) = @_;
349 return "" if (!@$elements);
350         
351 my $rv = "<table class='wrapper' " 
352        . ($width ? " width=$width%" : " width=100%")
353        . ($tabletags ? " ".$tabletags : "")
354        . "><tr><td>\n";
355 $rv .= "<table class='ui_grid_table'"
356      . ($width ? " width=$width%" : "")
357      . ($tabletags ? " ".$tabletags : "")
358      . ">\n";
359 if ($title) {
360         $rv .= "<thead><tr $tb class='ui_grid_heading'> ".
361                "<td colspan=$cols><b>$title</b></td> </tr></thead>\n";
362         }
363 $rv .= "<tbody>\n";
364 my $i;
365 for($i=0; $i<@$elements; $i++) {
366   $rv .= "<tr class='ui_grid_row'>" if ($i%$cols == 0);
367   $rv .= "<td ".$tds->[$i%$cols]." valign=top class='ui_grid_cell'>".
368          $elements->[$i]."</td>\n";
369   $rv .= "</tr>" if ($i%$cols == $cols-1);
370   }
371 if ($i%$cols) {
372   while($i%$cols) {
373     $rv .= "<td ".$tds->[$i%$cols]." class='ui_grid_cell'><br></td>\n";
374     $i++;
375     }
376   $rv .= "</tr>\n";
377   }
378 $rv .= "</table>\n";
379 $rv .= "</tbody>\n";
380 $rv .= "</td></tr></table>\n"; # wrapper
381 return $rv;
382 }
383
384 # theme_ui_hidden_table_start(heading, [tabletags], [cols], name, status,
385 #                             [&default-tds], [rightheading])
386 # A table with a heading and table inside, and which is collapsible
387 sub theme_ui_hidden_table_start
388 {
389 my ($heading, $tabletags, $cols, $name, $status, $tds, $rightheading) = @_;
390 my $rv;
391 if (!$main::ui_hidden_start_donejs++) {
392   $rv .= &ui_hidden_javascript();
393   }
394 my $divid = "hiddendiv_$name";
395 my $openerid = "hiddenopener_$name";
396 my $defimg = $status ? "open.gif" : "closed.gif";
397 my $defclass = $status ? 'opener_shown' : 'opener_hidden';
398 my $text = defined($tconfig{'cs_text'}) ? $tconfig{'cs_text'} :
399         defined($gconfig{'cs_text'}) ? $gconfig{'cs_text'} : "000000";
400 if (!$WRAPPER_OPEN) { # If we're not already inside of a wrapper, wrap it
401         $rv .= "<table class='shrinkwrapper ui_table' $tabletags>\n";
402         $rv .= "<tr><td>\n";
403         }
404 $WRAPPER_OPEN++;
405 my $colspan = 1;
406 $rv .= "<table class='ui_table' $tabletags>\n";
407 if (defined($heading) || defined($rightheading)) {
408         $rv .= "<thead><tr class='ui_table_head'>";
409         if (defined($heading)) {
410                 $rv .= "<td><a href=\"javascript:hidden_opener('$divid', '$openerid')\" id='$openerid'><img border=0 src='$gconfig{'webprefix'}/images/$defimg'></a> <a href=\"javascript:hidden_opener('$divid', '$openerid')\"><b><font color=#ffffff>$heading</font></b></a></td>";
411                 }
412         if (defined($rightheading)) {
413                 $rv .= "<td align=right>$rightheading</td>";
414                 $colspan++;
415                 }
416         $rv .= "</tr> </thead>\n";
417         }
418 $rv .= "<tbody><tr> <td colspan=$colspan><div class='$defclass' id='$divid'><table width=100%>\n";
419 $main::ui_table_cols = $cols || 4;
420 $main::ui_table_pos = 0;
421 $main::ui_table_default_tds = $tds;
422 return $rv;
423 }
424
425 # ui_hidden_table_end(name)
426 # Returns HTML for the end of table with hiding, as started by
427 # ui_hidden_table_start
428 sub theme_ui_hidden_table_end
429 {
430 my ($name) = @_;
431 local $rv = "</table></div></td></tr></tbody></table>\n";
432 if ( $WRAPPER_OPEN == 1 ) {
433         $WRAPPER_OPEN--;
434         #$rv .= "</div>\n";
435         $rv .= "</td></tr></table>\n";
436         }
437 elsif ($WRAPPER_OPEN) { $WRAPPER_OPEN--; }
438 return $rv;
439 }
440
441 # theme_select_all_link(field, form, text)
442 # Adds support for row highlighting to the normal select all
443 sub theme_select_all_link
444 {
445 local ($field, $form, $text) = @_;
446 $form = int($form);
447 $text ||= $text{'ui_selall'};
448 return "<a class='select_all' href='#' onClick='f = document.forms[$form]; ff = f.$field; ff.checked = true; r = document.getElementById(\"row_\"+ff.id); if (r) { r.className = \"mainsel\" }; for(i=0; i<f.$field.length; i++) { ff = f.${field}[i]; if (!ff.disabled) { ff.checked = true; r = document.getElementById(\"row_\"+ff.id); if (r) { r.className = \"mainsel\" } } } return false'>$text</a>";
449 }
450
451 # theme_select_invert_link(field, form, text)
452 # Adds support for row highlighting to the normal invert selection
453 sub theme_select_invert_link
454 {
455 local ($field, $form, $text) = @_;
456 $form = int($form);
457 $text ||= $text{'ui_selinv'};
458 return "<a class='select_invert' href='#' onClick='f = document.forms[$form]; ff = f.$field; ff.checked = !f.$field.checked; r = document.getElementById(\"row_\"+ff.id); if (r) { r.className = ff.checked ? \"mainsel\" : \"mainbody\" }; for(i=0; i<f.$field.length; i++) { ff = f.${field}[i]; if (!ff.disabled) { ff.checked = !ff.checked; r = document.getElementById(\"row_\"+ff.id); if (r) { r.className = ff.checked ? \"mainsel\" : \"mainbody row\"+((i+1)%2) } } } return false'>$text</a>";
459 }
460
461 sub theme_select_rows_link
462 {
463 local ($field, $form, $text, $rows) = @_;
464 $form = int($form);
465 my $js = "var sel = { ".join(",", map { "\"".&quote_escape($_)."\":1" } @$rows)." }; ";
466 $js .= "for(var i=0; i<document.forms[$form].${field}.length; i++) { var ff = document.forms[$form].${field}[i]; var r = document.getElementById(\"row_\"+ff.id); ff.checked = sel[ff.value]; if (r) { r.className = ff.checked ? \"mainsel\" : \"mainbody row\"+((i+1)%2) } } ";
467 $js .= "return false;";
468 return "<a class='select_rows' href='#' onClick='$js'>$text</a>";
469 }
470
471 sub theme_ui_checked_columns_row
472 {
473 $theme_ui_columns_row_toggle = $theme_ui_columns_row_toggle ? '0' : '1';
474 local ($cols, $tdtags, $checkname, $checkvalue, $checked, $disabled, $tags) = @_;
475 my $rv;
476 my $cbid = &quote_escape(quotemeta("${checkname}_${checkvalue}"));
477 my $rid = &quote_escape(quotemeta("row_${checkname}_${checkvalue}"));
478 my $ridtr = &quote_escape("row_${checkname}_${checkvalue}");
479 my $mycb = $cb;
480 if ($checked) {
481         $mycb =~ s/mainbody/mainsel/g;
482         }
483 $mycb =~ s/class='/class='row$theme_ui_columns_row_toggle ui_checked_columns /;
484 $rv .= "<tr id=\"$ridtr\" $mycb onMouseOver=\"this.className = document.getElementById('$cbid').checked ? 'mainhighsel' : 'mainhigh'\" onMouseOut=\"this.className = document.getElementById('$cbid').checked ? 'mainsel' : 'mainbody row$theme_ui_columns_row_toggle'\">\n";
485 $rv .= "<td ".$tdtags->[0]." class='ui_checked_checkbox'>".
486        &ui_checkbox($checkname, $checkvalue, undef, $checked, $tags." "."onClick=\"document.getElementById('$rid').className = this.checked ? 'mainhighsel' : 'mainhigh';\"", $disabled).
487        "</td>\n";
488 my $i;
489 for($i=0; $i<@$cols; $i++) {
490         $rv .= "<td ".$tdtags->[$i+1].">";
491         if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) {
492                 $rv .= "<label for=\"".
493                         &quote_escape("${checkname}_${checkvalue}")."\">";
494                 }
495         $rv .= ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i]);
496         if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) {
497                 $rv .= "</label>";
498                 }
499         $rv .= "</td>\n";
500         }
501 $rv .= "</tr>\n";
502 return $rv;
503 }
504
505 sub theme_ui_radio_columns_row
506 {
507 local ($cols, $tdtags, $checkname, $checkvalue, $checked, $tags) = @_;
508 my $rv;
509 my $cbid = &quote_escape(quotemeta("${checkname}_${checkvalue}"));
510 my $rid = &quote_escape(quotemeta("row_${checkname}_${checkvalue}"));
511 my $ridtr = &quote_escape("row_${checkname}_${checkvalue}");
512 my $mycb = $cb;
513 if ($checked) {
514         $mycb =~ s/mainbody/mainsel/g;
515         }
516
517 $mycb =~ s/class='/class='ui_radio_columns /;
518 $rv .= "<tr $mycb id=\"$ridtr\" onMouseOver=\"this.className = document.getElementById('$cbid').checked ? 'mainhighsel' : 'mainhigh'\" onMouseOut=\"this.className = document.getElementById('$cbid').checked ? 'mainsel' : 'mainbody'\">\n";
519 $rv .= "<td ".$tdtags->[0]." class='ui_radio_radio'>".
520        &ui_oneradio($checkname, $checkvalue, undef, $checked, $tags." "."onClick=\"for(i=0; i<form.$checkname.length; i++) { ff = form.${checkname}[i]; r = document.getElementById('row_'+ff.id); if (r) { r.className = 'mainbody' } } document.getElementById('$rid').className = this.checked ? 'mainhighsel' : 'mainhigh';\"").
521        "</td>\n";
522 my $i;
523 for($i=0; $i<@$cols; $i++) {
524         $rv .= "<td ".$tdtags->[$i+1].">";
525         if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) {
526                 $rv .= "<label for=\"".
527                         &quote_escape("${checkname}_${checkvalue}")."\">";
528                 }
529         $rv .= ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i]);
530         if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) {
531                 $rv .= "</label>";
532                 }
533         $rv .= "</td>\n";
534         }
535 $rv .= "</tr>\n";
536 return $rv;
537 }
538
539 # theme_ui_nav_link(direction, url, disabled)
540 # Returns an arrow icon linking to provided url
541 sub theme_ui_nav_link
542 {
543 my ($direction, $url, $disabled) = @_;
544 my $alt = $direction eq "left" ? '<-' : '->';
545 if ($disabled) {
546   return "<img alt=\"$alt\" align=\"middle\""
547        . "src=\"$gconfig{'webprefix'}/images/$direction-grey.gif\">\n";
548   }
549 else {
550   return "<a href=\"$url\"><img alt=\"$alt\" align=\"top\""
551        . "src=\"$gconfig{'webprefix'}/images/$direction.gif\"></a>\n";
552   }
553 }
554
555 # theme_footer([page, name]+, [noendbody])
556 # Output a footer for returning to some page
557 sub theme_footer
558 {
559 my $i;
560 my $count = 0;
561 my $module_name = &get_module_name();
562 my %module_info = &get_module_info($module_name);
563 for($i=0; $i+1<@_; $i+=2) {
564         local $url = $_[$i];
565         if ($url ne '/' || !$tconfig{'noindex'}) {
566                 if ($url eq '/') {
567                         $url = "/?cat=$module_info{'category'}";
568                         }
569                 elsif ($url eq '' && $module_name) {
570                         $url = "/$module_name/$module_info{'index_link'}";
571                         }
572                 elsif ($url =~ /^\?/ && $module_name) {
573                         $url = "/$module_name/$url";
574                         }
575                 $url = "$gconfig{'webprefix'}$url" if ($url =~ /^\//);
576                 if ($count++ == 0) {
577                         print theme_ui_nav_link("left", $url);
578                         }
579                 else {
580                         print "&nbsp;|\n";
581                         }
582                 print "&nbsp;<a href=\"$url\">",&text('main_return', $_[$i+1]),"</a>\n";
583                 }
584         }
585 print "<br>\n";
586 if (!$_[$i]) {
587         print "</body></html>\n";
588         }
589 }
590
591 # theme_ui_hidden_javascript()
592 # Returns <script> and <style> sections for hiding functions and CSS
593 sub theme_ui_hidden_javascript
594 {
595 my $rv;
596 my $imgdir = "$gconfig{'webprefix'}/images";
597
598 return <<EOF;
599 <style>
600 .opener_shown {display:inline}
601 .opener_hidden {display:none}
602 </style>
603 <script>
604 // Open or close a hidden section
605 function hidden_opener(divid, openerid)
606 {
607 var divobj = document.getElementById(divid);
608 var openerobj = document.getElementById(openerid);
609 if (divobj.className == 'opener_shown') {
610   divobj.className = 'opener_hidden';
611   openerobj.innerHTML = '<img border=0 src=$imgdir/closed.gif>';
612   }
613 else {
614   divobj.className = 'opener_shown';
615   openerobj.innerHTML = '<img border=0 src=$imgdir/open.gif>';
616   }
617 }
618
619 // Show a tab
620 function select_tab(name, tabname, form)
621 {
622 var tabnames = document[name+'_tabnames'];
623 var tabtitles = document[name+'_tabtitles'];
624 for(var i=0; i<tabnames.length; i++) {
625   var tabobj = document.getElementById('tab_'+tabnames[i]);
626   var divobj = document.getElementById('div_'+tabnames[i]);
627   var title = tabtitles[i];
628   if (tabnames[i] == tabname) {
629     // Selected table
630     tabobj.innerHTML = '<table cellpadding=0 cellspacing=0><tr>'+
631                        '<td valign=top class=\\'tabSelected\\'>'+
632                        '<img src=$imgdir/lc2.gif alt=""></td>'+
633                        '<td class=\\'tabSelected\\' nowrap>'+
634                        '&nbsp;<b>'+title+'</b>&nbsp;</td>'+
635                        '<td valign=top class=\\'tabSelected\\'>'+
636                        '<img src=$imgdir/rc2.gif alt=""></td>'+
637                        '</tr></table>';
638     divobj.className = 'opener_shown';
639     }
640   else {
641     // Non-selected tab
642     tabobj.innerHTML = '<table cellpadding=0 cellspacing=0><tr>'+
643                        '<td valign=top class=\\'tabUnselected\\'>'+
644                        '<img src=$imgdir/lc1.gif alt=""></td>'+
645                        '<td class=\\'tabUnselected\\' nowrap>'+
646                        '&nbsp;<a href=\\'\\' onClick=\\'return select_tab("'+
647                        name+'", "'+tabnames[i]+'")\\'>'+title+'</a>&nbsp;</td>'+
648                        '<td valign=top class=\\'tabUnselected\\'>'+
649                        '<img src=$imgdir/rc1.gif alt=""></td>'+
650                        '</tr></table>';
651     divobj.className = 'opener_hidden';
652     }
653   }
654 if (document.forms[0] && document.forms[0][name]) {
655   document.forms[0][name].value = tabname;
656   }
657 return false;
658 }
659 </script>
660 EOF
661 }
662
663 1;
664