POD conversion
authorJamie Cameron <jcameron@webmin.com>
Mon, 12 Jan 2009 07:20:44 +0000 (07:20 +0000)
committerJamie Cameron <jcameron@webmin.com>
Mon, 12 Jan 2009 07:20:44 +0000 (07:20 +0000)
ui-lib.pl

index 8ca2865..12b226f 100644 (file)
--- a/ui-lib.pl
+++ b/ui-lib.pl
@@ -1,11 +1,45 @@
-# ui-lib.pl
-# Common functions for generating HTML for Webmin user interface elements
+=head1 ui-lib.pl
+
+Common functions for generating HTML for Webmin user interface elements.
+Some example code :
+
+ require '../ui-lib.pl';
+ ui_print_header(undef, 'My Module', '');
+
+ print ui_form_start('save.cgi');
+ print ui_table_start('My form', undef, 2);
+
+ print ui_table_row('Enter your name',
+       ui_textbox('name', undef, 40));
+
+ print ui_table_end();
+ print ui_form_end([ [ undef, 'Save' ] ]);
+
+ ui_print_footer('/', 'Webmin index');
+
+=cut
 
 ####################### table generation functions
 
-# ui_table_start(heading, [tabletags], [cols], [&default-tds],
-#               [right-heading])
-# A table with a heading and table inside
+=head2 ui_table_start(heading, [tabletags], [cols], [&default-tds], [right-heading])
+
+Returns HTML for the start of a form block into which labelled inputs can
+be placed. By default this is implemented as a table with another table inside
+it, but themes may override this with their own layout.
+
+The parameters are :
+
+=item heading - Text to show at the top of the form.
+
+=item tabletags - HTML attributes to put in the outer <table>, typically something like width=100%.
+
+=item cols - Desired number of columns for labels and fields. Defaults to 4, but can be 2 for forms with lots of wide inputs.
+
+=item default-tds - An optional array reference of HTML attributes for the <td> tags in each row of the table.
+
+=item right-heading - HTML to appear in the heading, aligned to the right.
+
+=cut
 sub ui_table_start
 {
 return &theme_ui_table_start(@_) if (defined(&theme_ui_table_start));
@@ -38,8 +72,11 @@ $main::ui_table_default_tds = $tds;
 return $rv;
 }
 
-# ui_table_end()
-# The end of a table started by ui_table_start
+=head2 ui_table_end
+
+Returns HTML for the end of a block started by ui_table_start.
+
+=cut
 sub ui_table_end
 {
 return &theme_ui_table_end(@_) if (defined(&theme_ui_table_end));
@@ -62,8 +99,101 @@ $rv .= "</table></td></tr></table>\n";
 return $rv;
 }
 
-# ui_columns_start(&headings, [width-percent], [noborder], [&tdtags], [heading])
-# Returns HTML for a multi-column table, with the given headings
+=head2 ui_table_row(label, value, [cols], [&td-tags])
+
+Returns HTML for a row in a table started by ui_table_start, with a 1-column
+label and 1+ column value. The parameters are :
+
+=item label - Label for the input field. If this is undef, no label is displayed.
+
+=item value - HTML for the input part of the row.
+
+=item cols - Number of columns the value should take up, defaulting to 1.
+
+=item td-tags - Array reference of HTML attributes for the <td> tags in this row.
+
+=cut
+sub ui_table_row
+{
+return &theme_ui_table_row(@_) if (defined(&theme_ui_table_row));
+my ($label, $value, $cols, $tds) = @_;
+$cols ||= 1;
+$tds ||= $main::ui_table_default_tds;
+my $rv;
+if ($main::ui_table_pos+$cols+1 > $main::ui_table_cols &&
+    $main::ui_table_pos != 0) {
+       # If the requested number of cols won't fit in the number
+       # remaining, start a new row
+       $rv .= "</tr>\n";
+       $main::ui_table_pos = 0;
+       }
+$rv .= "<tr class='ui_table_row'>\n"
+       if ($main::ui_table_pos%$main::ui_table_cols == 0);
+$rv .= "<td valign=top $tds->[0] class='ui_label'><b>$label</b></td>\n"
+       if (defined($label));
+$rv .= "<td valign=top colspan=$cols $tds->[1] class='ui_value'>$value</td>\n";
+$main::ui_table_pos += $cols+(defined($label) ? 1 : 0);
+if ($main::ui_table_pos%$main::ui_table_cols == 0) {
+       $rv .= "</tr>\n";
+       $main::ui_table_pos = 0;
+       }
+return $rv;
+}
+
+=head2 ui_table_hr
+
+Returns HTML for a row in a block started by ui_table_row, with a horizontal
+line inside it to separate sections.
+
+=cut
+sub ui_table_hr
+{
+return &theme_ui_table_hr(@_) if (defined(&theme_ui_table_hr));
+my $rv;
+if ($ui_table_pos) {
+       $rv .= "</tr>\n";
+       $ui_table_pos = 0;
+       }
+$rv .= "<tr class='ui_table_hr'> ".
+       "<td colspan=$main::ui_table_cols><hr></td> </tr>\n";
+return $rv;
+}
+
+=head2 ui_table_span(text)
+
+Outputs a table row that spans the whole table, and contains the given text.
+
+=cut
+sub ui_table_span
+{
+my ($text) = @_;
+return &theme_ui_table_hr(@_) if (defined(&theme_ui_table_hr));
+my $rv;
+if ($ui_table_pos) {
+       $rv .= "</tr>\n";
+       $ui_table_pos = 0;
+       }
+$rv .= "<tr class='ui_table_span'> ".
+       "<td colspan=$main::ui_table_cols>$text</td> </tr>\n";
+return $rv;
+}
+
+=head2 ui_columns_start(&headings, [width-percent], [noborder], [&tdtags], [heading])
+
+Returns HTML for the start of a multi-column table, with the given headings.
+The parameters are :
+
+=item headings - An array reference of headers for the table's columns.
+
+=item width-percent - Desired width as a percentage, or undef to let the browser decide.
+
+=item noborder - Set to 1 if the table should not have a border.
+
+=item tdtags - An optional reference to an array of HTML attributes for the table's <td> tags.
+
+=item heading - An optional heading to put above the table.
+
+=cut
 sub ui_columns_start
 {
 return &theme_ui_columns_start(@_) if (defined(&theme_ui_columns_start));
@@ -85,8 +215,15 @@ $rv .= "</tr>\n";
 return $rv;
 }
 
-# ui_columns_row(&columns, &tdtags)
-# Returns HTML for a row in a multi-column table
+=head2 ui_columns_row(&columns, &tdtags)
+
+Returns HTML for a row in a multi-column table. The parameters are :
+
+=item columns - Reference to an array containing the HTML to show in the columns for this row.
+
+=item tdtags - An optional array reference containing HTML attributes for the row's <td> tags.
+
+=cut
 sub ui_columns_row
 {
 return &theme_ui_columns_row(@_) if (defined(&theme_ui_columns_row));
@@ -102,8 +239,12 @@ $rv .= "</tr>\n";
 return $rv;
 }
 
-# ui_columns_header(&columns, &tdtags)
-# Returns HTML for a row in a multi-column table, with a header background
+=head2 ui_columns_header(&columns, &tdtags)
+
+Returns HTML for a row in a multi-column table, styled as a header. Parameters
+are the same as ui_columns_row.
+
+=cut
 sub ui_columns_header
 {
 return &theme_ui_columns_header(@_) if (defined(&theme_ui_columns_header));
@@ -119,10 +260,13 @@ $rv .= "</tr>\n";
 return $rv;
 }
 
-# ui_checked_columns_row(&columns, &tdtags, checkname, checkvalue, [checked?],
-#                       [disabled])
-# Returns HTML for a row in a multi-column table, in which the first
-# column is a checkbox
+=head2 ui_checked_columns_row(&columns, &tdtags, checkname, checkvalue, [checked?], [disabled])
+
+Returns HTML for a row in a multi-column table, in which the first column 
+contains a checkbox.
+XXX
+
+=cut
 sub ui_checked_columns_row
 {
 return &theme_ui_checked_columns_row(@_) if (defined(&theme_ui_checked_columns_row));
@@ -149,10 +293,12 @@ $rv .= "</tr>\n";
 return $rv;
 }
 
-# ui_radio_columns_row(&columns, &tdtags, checkname, checkvalue, [checked],
-#                     [disabled])
-# Returns HTML for a row in a multi-column table, in which the first
-# column is a radio button
+=head2 ui_radio_columns_row(&columns, &tdtags, checkname, checkvalue, [checked], [disabled])
+
+Returns HTML for a row in a multi-column table, in which the first
+column is a radio button
+
+=cut
 sub ui_radio_columns_row
 {
 return &theme_ui_radio_columns_row(@_) if (defined(&theme_ui_radio_columns_row));
@@ -178,32 +324,37 @@ $rv .= "</tr>\n";
 return $rv;
 }
 
-# ui_columns_end()
-# Returns HTML to end a table started by ui_columns_start
+=head2 ui_columns_end
+
+Returns HTML to end a table started by ui_columns_start
+
+=cut
 sub ui_columns_end
 {
 return &theme_ui_columns_end(@_) if (defined(&theme_ui_columns_end));
 return "</table>\n";
 }
 
-# ui_columns_table(&headings, width-percent, &data, &types, no-sort, title,
-#                 empty-msg)
-# Returns HTML for a complete table.
-# headings - An array ref of heading HTML
-# width-percent - Preferred total width
-# data - A 2x2 array ref of table contents. Each can either be a simple string,
-#        or a hash ref like :
-#          { 'type' => 'group', 'desc' => 'Some section title' }
-#          { 'type' => 'string', 'value' => 'Foo', 'colums' => 3,
-#           'nowrap' => 1 }
-#          { 'type' => 'checkbox', 'name' => 'd', 'value' => 'foo',
-#            'label' => 'Yes', 'checked' => 1, 'disabled' => 1 }
-#          { 'type' => 'radio', 'name' => 'd', 'value' => 'foo', ... }
-# types - An array ref of data types, such as 'string', 'number', 'bytes'
-#         or 'date'
-# no-sort - Set to 1 to disable sorting by theme
-# title - Text to appear above the table
-# empty-msg - Message to display if no data
+=head2 ui_columns_table(&headings, width-percent, &data, &types, no-sort, title, empty-msg)
+
+Returns HTML for a complete table.
+headings - An array ref of heading HTML
+width-percent - Preferred total width
+data - A 2x2 array ref of table contents. Each can either be a simple string,
+or a hash ref like :
+{ 'type' => 'group', 'desc' => 'Some section title' }
+{ 'type' => 'string', 'value' => 'Foo', 'colums' => 3,
+'nowrap' => 1 }
+{ 'type' => 'checkbox', 'name' => 'd', 'value' => 'foo',
+'label' => 'Yes', 'checked' => 1, 'disabled' => 1 }
+{ 'type' => 'radio', 'name' => 'd', 'value' => 'foo', ... }
+types - An array ref of data types, such as 'string', 'number', 'bytes'
+or 'date'
+no-sort - Set to 1 to disable sorting by theme
+title - Text to appear above the table
+empty-msg - Message to display if no data
+
+=cut
 sub ui_columns_table
 {
 return &theme_ui_columns_table(@_) if (defined(&theme_ui_columns_table));
@@ -303,20 +454,21 @@ $rv .= &ui_columns_end();
 return $rv;
 }
 
-# ui_form_columns_table(cgi, &buttons, select-all, &otherlinks, &hiddens,
-#                      &headings, width-percent, &data, &types, no-sort, title,
-#                      empty-msg)
-# Similar to ui_columns_table, but wrapped in a form. Args are :
-# cgi - URL to submit the form to
-# buttons - An array ref of buttons at the end of the form, similar to
-#           that taken by ui_form_end
-# select-all - If set to 1, include select all / invert links
-# otherslinks - An array ref of other links to put at the top of the table,
-#               each of which is a 3-element hash ref of url, text and
-#              alignment (left or right)
-# hiddens - An array ref of hidden fields, each of which is a 2-element array
-#           ref containing the name and value
-# All other parameters are the same as ui_columns_table
+=head2 ui_form_columns_table(cgi, &buttons, select-all, &otherlinks, &hiddens, &headings, width-percent, &data, &types, no-sort, title, empty-msg)
+
+Similar to ui_columns_table, but wrapped in a form. Args are :
+cgi - URL to submit the form to
+buttons - An array ref of buttons at the end of the form, similar to
+that taken by ui_form_end
+select-all - If set to 1, include select all / invert links
+otherslinks - An array ref of other links to put at the top of the table,
+each of which is a 3-element hash ref of url, text and
+alignment (left or right)
+hiddens - An array ref of hidden fields, each of which is a 2-element array
+ref containing the name and value
+All other parameters are the same as ui_columns_table
+
+=cut
 sub ui_form_columns_table
 {
 return &theme_ui_form_columns_table(@_)
@@ -385,8 +537,11 @@ return $rv;
 
 ####################### form generation functions
 
-# ui_form_start(script, method, [target], [tags])
-# Returns HTML for a form that submits to some script
+=head2 ui_form_start(script, method, [target], [tags])
+
+Returns HTML for a form that submits to some script
+
+=cut
 sub ui_form_start
 {
 return &theme_ui_form_start(@_) if (defined(&theme_ui_form_start));
@@ -403,8 +558,11 @@ $rv .= "<form class='ui_form' action='".&html_escape($script)."' ".
 return $rv;
 }
 
-# ui_form_end([&buttons], [width])
-# Returns HTML for the end of a form, optionally with a row of submit buttons
+=head2 ui_form_end([&buttons], [width])
+
+Returns HTML for the end of a form, optionally with a row of submit buttons
+
+=cut
 sub ui_form_end
 {
 return &theme_ui_form_end(@_) if (defined(&theme_ui_form_end));
@@ -435,8 +593,11 @@ $rv .= "</form>\n";
 return $rv;
 }
 
-# ui_textbox(name, value, size, [disabled?], [maxlength], [tags])
-# Returns HTML for a text input
+=head2 ui_textbox(name, value, size, [disabled?], [maxlength], [tags])
+
+Returns HTML for a text input
+
+=cut
 sub ui_textbox
 {
 return &theme_ui_textbox(@_) if (defined(&theme_ui_textbox));
@@ -450,8 +611,11 @@ return "<input class='ui_textbox' name=\"".&quote_escape($name)."\" ".
        ">";
 }
 
-# ui_filebox(name, value, size, [disabled?], [maxlength], [tags], [dir-only])
-# Returns HTML for a text box for choosing a file
+=head2 ui_filebox(name, value, size, [disabled?], [maxlength], [tags], [dir-only])
+
+Returns HTML for a text box for choosing a file
+
+=cut
 sub ui_filebox
 {
 return &theme_ui_filebox(@_) if (defined(&theme_ui_filebox));
@@ -460,9 +624,12 @@ return &ui_textbox($name, $value, $size, $dis, $max, $tags)."&nbsp;".
        &file_chooser_button($name, $dironly);
 }
 
-# ui_bytesbox(name, bytes, [size], [disabled?])
-# Returns HTML for entering a number of bytes, but with friendly kB/MB/GB
-# options. May truncate values to 2 decimal points!
+=head2 ui_bytesbox(name, bytes, [size], [disabled?])
+
+Returns HTML for entering a number of bytes, but with friendly kB/MB/GB
+options. May truncate values to 2 decimal points!
+
+=cut
 sub ui_bytesbox
 {
 my ($name, $bytes, $size, $dis) = @_;
@@ -496,8 +663,11 @@ return &ui_textbox($name, $bytes, $size, $dis)." ".
                   [ 1024*1024*1024*1024, "TB" ] ], undef, undef, undef, $dis);
 }
 
-# ui_upload(name, size, [disabled?], [tags])
-# Returns HTML for a file upload input
+=head2 ui_upload(name, size, [disabled?], [tags])
+
+Returns HTML for a file upload input
+
+=cut
 sub ui_upload
 {
 return &theme_ui_upload(@_) if (defined(&theme_ui_upload));
@@ -509,8 +679,11 @@ return "<input class='ui_upload' type=file name=\"".&quote_escape($name)."\" ".
        ($tags ? " ".$tags : "").">";
 }
 
-# ui_password(name, value, size, [disabled?], [maxlength])
-# Returns HTML for a password text input
+=head2 ui_password(name, value, size, [disabled?], [maxlength])
+
+Returns HTML for a password text input
+
+=cut
 sub ui_password
 {
 return &theme_ui_password(@_) if (defined(&theme_ui_password));
@@ -524,8 +697,11 @@ return "<input class='ui_password' ".
        ">";
 }
 
-# ui_hidden(name, value)
-# Returns HTML for a hidden field
+=head2 ui_hidden(name, value)
+
+Returns HTML for a hidden field
+
+=cut
 sub ui_hidden
 {
 return &theme_ui_hidden(@_) if (defined(&theme_ui_hidden));
@@ -535,9 +711,11 @@ return "<input class='ui_hidden' type=hidden ".
        "value=\"".&quote_escape($value)."\">\n";
 }
 
-# ui_select(name, value|&values, &options, [size], [multiple],
-#          [add-if-missing], [disabled?], [javascript])
-# Returns HTML for a drop-down menu or multiple selection list
+=head2 ui_select(name, value|&values, &options, [size], [multiple], [add-if-missing], [disabled?], [javascript])
+
+Returns HTML for a drop-down menu or multiple selection list
+
+=cut
 sub ui_select
 {
 return &theme_ui_select(@_) if (defined(&theme_ui_select));
@@ -566,11 +744,13 @@ $rv .= "</select>\n";
 return $rv;
 }
 
-# ui_multi_select(name, &values, &options, size, [add-if-missing], [disabled?],
-#                 [options-title, values-title], [width])
-# Returns HTML for selecting many of many from a list. By default, this is
-# implemented using two <select> lists and Javascript buttons to move elements
-# between them. The resulting input value is \n separated.
+=head2 ui_multi_select(name, &values, &options, size, [add-if-missing], [disabled?], [options-title, values-title], [width])
+
+Returns HTML for selecting many of many from a list. By default, this is
+implemented using two <select> lists and Javascript buttons to move elements
+between them. The resulting input value is \n separated.
+
+=cut
 sub ui_multi_select
 {
 return &theme_ui_multi_select(@_) if (defined(&theme_ui_multi_select));
@@ -611,8 +791,11 @@ $rv .= &ui_hidden($name, join("\n", map { $_->[0] } @$values));
 return $rv;
 }
 
-# ui_multi_select_javascript()
-# Returns <script> section for left/right select boxes
+=head2 ui_multi_select_javascript
+
+Returns <script> section for left/right select boxes
+
+=cut
 sub ui_multi_select_javascript
 {
 return &theme_ui_multiselect_javascript()
@@ -652,8 +835,11 @@ if (hid) {
 EOF
 }
 
-# ui_radio(name, value, &options, [disabled?])
-# Returns HTML for a series of radio buttons
+=head2 ui_radio(name, value, &options, [disabled?])
+
+Returns HTML for a series of radio buttons
+
+=cut
 sub ui_radio
 {
 return &theme_ui_radio(@_) if (defined(&theme_ui_radio));
@@ -680,8 +866,11 @@ foreach $o (@$opts) {
 return $rv;
 }
 
-# ui_yesno_radio(name, value, [yes], [no], [disabled?])
-# Like ui_yesno, but always displays just two inputs (yes and no)
+=head2 ui_yesno_radio(name, value, [yes], [no], [disabled?])
+
+Like ui_yesno, but always displays just two inputs (yes and no)
+
+=cut
 sub ui_yesno_radio
 {
 my ($name, $value, $yes, $no, $dis) = @_;
@@ -693,8 +882,11 @@ return &ui_radio($name, $value, [ [ $yes, $text{'yes'} ],
                                  [ $no, $text{'no'} ] ], $dis);
 }
 
-# ui_checkbox(name, value, label, selected?, [tags], [disabled?])
-# Returns HTML for a single checkbox
+=head2 ui_checkbox(name, value, label, selected?, [tags], [disabled?])
+
+Returns HTML for a single checkbox
+
+=cut
 sub ui_checkbox
 {
 return &theme_ui_checkbox(@_) if (defined(&theme_ui_checkbox));
@@ -715,8 +907,11 @@ return "<input class='ui_checkbox' type=checkbox ".
         "\">$label</label>$after")."\n";
 }
 
-# ui_oneradio(name, value, label, selected?, [tags], [disabled?])
-# Returns HTML for a single radio button
+=head2 ui_oneradio(name, value, label, selected?, [tags], [disabled?])
+
+Returns HTML for a single radio button
+
+=cut
 sub ui_oneradio
 {
 return &theme_ui_oneradio(@_) if (defined(&theme_ui_oneradio));
@@ -734,8 +929,11 @@ return "<input class='ui_radio' type=radio name=\"".&quote_escape($name)."\" ".
        " $tags> <label for=\"$id\">$label</label>$after\n";
 }
 
-# ui_textarea(name, value, rows, cols, [wrap], [disabled?], [tags])
-# Returns HTML for a multi-line text input
+=head2 ui_textarea(name, value, rows, cols, [wrap], [disabled?], [tags])
+
+Returns HTML for a multi-line text input
+
+=cut
 sub ui_textarea
 {
 return &theme_ui_textarea(@_) if (defined(&theme_ui_textarea));
@@ -749,8 +947,11 @@ return "<textarea class='ui_textarea' name=\"".&quote_escape($name)."\" ".
        "</textarea>";
 }
 
-# ui_user_textbox(name, value, [form], [disabled?], [tags])
-# Returns HTML for a Unix user input
+=head2 ui_user_textbox(name, value, [form], [disabled?], [tags])
+
+Returns HTML for a Unix user input
+
+=cut
 sub ui_user_textbox
 {
 return &theme_ui_user_textbox(@_) if (defined(&theme_ui_user_textbox));
@@ -758,8 +959,11 @@ return &ui_textbox($_[0], $_[1], 13, $_[3], undef, $_[4])." ".
        &user_chooser_button($_[0], 0, $_[2]);
 }
 
-# ui_group_textbox(name, value, [form], [disabled?], [tags])
-# Returns HTML for a Unix group input
+=head2 ui_group_textbox(name, value, [form], [disabled?], [tags])
+
+Returns HTML for a Unix group input
+
+=cut
 sub ui_group_textbox
 {
 return &theme_ui_group_textbox(@_) if (defined(&theme_ui_group_textbox));
@@ -767,9 +971,11 @@ return &ui_textbox($_[0], $_[1], 13, $_[3], undef, $_[4])." ".
        &group_chooser_button($_[0], 0, $_[2]);
 }
 
-# ui_opt_textbox(name, value, size, option1, [option2], [disabled?],
-#               [&extra-fields], [max])
-# Returns HTML for a text field that is optional
+=head2 ui_opt_textbox(name, value, size, option1, [option2], [disabled?], [&extra-fields], [max])
+
+Returns HTML for a text field that is optional
+
+=cut
 sub ui_opt_textbox
 {
 return &theme_ui_opt_textbox(@_) if (defined(&theme_ui_opt_textbox));
@@ -788,8 +994,11 @@ $rv .= "<input class='ui_opt_textbox' name=\"".&quote_escape($name)."\" ".
 return $rv;
 }
 
-# ui_submit(label, [name], [disabled?], [tags])
-# Returns HTML for a form submit button
+=head2 ui_submit(label, [name], [disabled?], [tags])
+
+Returns HTML for a form submit button
+
+=cut
 sub ui_submit
 {
 return &theme_ui_submit(@_) if (defined(&theme_ui_submit));
@@ -802,8 +1011,11 @@ return "<input class='ui_submit' type=submit".
                        
 }
 
-# ui_reset(label, [disabled?])
-# Returns HTML for a form reset button
+=head2 ui_reset(label, [disabled?])
+
+Returns HTML for a form reset button
+
+=cut
 sub ui_reset
 {
 return &theme_ui_reset(@_) if (defined(&theme_ui_reset));
@@ -813,8 +1025,11 @@ return "<input type=reset value=\"".&quote_escape($label)."\"".
                        
 }
 
-# ui_button(label, [name], [disabled?], [tags])
-# Returns HTML for a form button
+=head2 ui_button(label, [name], [disabled?], [tags])
+
+Returns HTML for a form button
+
+=cut
 sub ui_button
 {
 return &theme_ui_button(@_) if (defined(&theme_ui_button));
@@ -826,8 +1041,11 @@ return "<input type=button".
        ($tags ? " ".$tags : "").">\n";
 }
 
-# ui_date_input(day, month, year, day-name, month-name, year-name, [disabled?])
-# Returns HTML for a date-selection field
+=head2 ui_date_input(day, month, year, day-name, month-name, year-name, [disabled?])
+
+Returns HTML for a date-selection field
+
+=cut
 sub ui_date_input
 {
 my ($day, $month, $year, $dayname, $monthname, $yearname, $dis) = @_;
@@ -844,82 +1062,23 @@ $rv .= "</span>";
 return $rv;
 }
 
-# ui_table_row(label, value, [cols], [&td-tags])
-# Returns HTML for a row in a table started by ui_table_start, with a 1-column
-# label and 1+ column value.
-sub ui_table_row
-{
-return &theme_ui_table_row(@_) if (defined(&theme_ui_table_row));
-my ($label, $value, $cols, $tds) = @_;
-$cols ||= 1;
-$tds ||= $main::ui_table_default_tds;
-my $rv;
-if ($main::ui_table_pos+$cols+1 > $main::ui_table_cols &&
-    $main::ui_table_pos != 0) {
-       # If the requested number of cols won't fit in the number
-       # remaining, start a new row
-       $rv .= "</tr>\n";
-       $main::ui_table_pos = 0;
-       }
-$rv .= "<tr class='ui_table_row'>\n"
-       if ($main::ui_table_pos%$main::ui_table_cols == 0);
-$rv .= "<td valign=top $tds->[0] class='ui_label'><b>$label</b></td>\n"
-       if (defined($label));
-$rv .= "<td valign=top colspan=$cols $tds->[1] class='ui_value'>$value</td>\n";
-$main::ui_table_pos += $cols+(defined($label) ? 1 : 0);
-if ($main::ui_table_pos%$main::ui_table_cols == 0) {
-       $rv .= "</tr>\n";
-       $main::ui_table_pos = 0;
-       }
-return $rv;
-}
-
-# ui_table_hr()
-sub ui_table_hr
-{
-return &theme_ui_table_hr(@_) if (defined(&theme_ui_table_hr));
-my $rv;
-if ($ui_table_pos) {
-       $rv .= "</tr>\n";
-       $ui_table_pos = 0;
-       }
-$rv .= "<tr class='ui_table_hr'> ".
-       "<td colspan=$main::ui_table_cols><hr></td> </tr>\n";
-return $rv;
-}
-
-# ui_table_span(text)
-# Outputs a table row that spans the whole table, and contains the given text
-sub ui_table_span
-{
-my ($text) = @_;
-return &theme_ui_table_hr(@_) if (defined(&theme_ui_table_hr));
-my $rv;
-if ($ui_table_pos) {
-       $rv .= "</tr>\n";
-       $ui_table_pos = 0;
-       }
-$rv .= "<tr class='ui_table_span'> ".
-       "<td colspan=$main::ui_table_cols>$text</td> </tr>\n";
-return $rv;
-}
-
-# ui_buttons_start()
 sub ui_buttons_start
 {
 return &theme_ui_buttons_start(@_) if (defined(&theme_ui_buttons_start));
 return "<table width=100% class='ui_buttons_table'>\n";
 }
 
-# ui_buttons_end()
 sub ui_buttons_end
 {
 return &theme_ui_buttons_end(@_) if (defined(&theme_ui_buttons_end));
 return "</table>\n";
 }
 
-# ui_buttons_row(script, button-label, description, [hiddens], [after-submit],
-#               [before-submit]) 
+=head2 ui_buttons_row(script, button-label, description, [hiddens], [after-submit], [before-submit]) 
+
+MISSING DOCUMENTATION
+
+=cut
 sub ui_buttons_row
 {
 return &theme_ui_buttons_row(@_) if (defined(&theme_ui_buttons_row));
@@ -935,7 +1094,11 @@ return "<form action=$script class='ui_buttons_form'>\n".
        "</form>\n";
 }
 
-# ui_buttons_hr([title])
+=head2 ui_buttons_hr([title])
+
+MISSING DOCUMENTATION
+
+=cut
 sub ui_buttons_hr
 {
 my ($title) = @_;
@@ -950,8 +1113,11 @@ else {
 
 ####################### header and footer functions
 
-# ui_post_header([subtext])
-# Returns HTML to appear directly after a standard header() call
+=head2 ui_post_header([subtext])
+
+Returns HTML to appear directly after a standard header() call
+
+=cut
 sub ui_post_header
 {
 return &theme_ui_post_header(@_) if (defined(&theme_ui_post_header));
@@ -964,8 +1130,11 @@ if (!$tconfig{'nohr'} && !$tconfig{'notophr'}) {
 return $rv;
 }
 
-# ui_pre_footer()
-# Returns HTML to appear directly before a standard footer() call
+=head2 ui_pre_footer
+
+Returns HTML to appear directly before a standard footer() call
+
+=cut
 sub ui_pre_footer
 {
 return &theme_ui_pre_footer(@_) if (defined(&theme_ui_pre_footer));
@@ -976,9 +1145,12 @@ if (!$tconfig{'nohr'} && !$tconfig{'nobottomhr'}) {
 return $rv;
 }
 
-# ui_print_header(subtext, args...)
-# Print HTML for a header with the post-header line. The args are the same
-# as those passed to header()
+=head2 ui_print_header(subtext, args...)
+
+Print HTML for a header with the post-header line. The args are the same
+as those passed to header()
+
+=cut
 sub ui_print_header
 {
 &load_theme_library();
@@ -988,9 +1160,12 @@ my ($text, @args) = @_;
 print &ui_post_header($text);
 }
 
-# ui_print_unbuffered_header(subtext, args...)
-# Like ui_print_header, but ensures that output for this page is not buffered
-# or contained in a table.
+=head2 ui_print_unbuffered_header(subtext, args...)
+
+Like ui_print_header, but ensures that output for this page is not buffered
+or contained in a table.
+
+=cut
 sub ui_print_unbuffered_header
 {
 &load_theme_library();
@@ -1000,9 +1175,12 @@ $theme_no_table = 1;
 &ui_print_header(@_);
 }
 
-# ui_print_footer(args...)
-# Print HTML for a footer with the pre-footer line. Args are the same as those
-# passed to footer()
+=head2 ui_print_footer(args...)
+
+Print HTML for a footer with the pre-footer line. Args are the same as those
+passed to footer()
+
+=cut
 sub ui_print_footer
 {
 return &theme_ui_print_footer(@_) if (defined(&theme_ui_print_footer));
@@ -1011,9 +1189,12 @@ print &ui_pre_footer();
 &footer(@args);
 }
 
-# ui_config_link(text, &subs)
-# Returns HTML for a module config link. The first non-null sub will be
-# replaced with the appropriate URL.
+=head2 ui_config_link(text, &subs)
+
+Returns HTML for a module config link. The first non-null sub will be
+replaced with the appropriate URL.
+
+=cut
 sub ui_config_link
 {
 return &theme_ui_config_link(@_) if (defined(&theme_ui_config_link));
@@ -1023,9 +1204,12 @@ my @subs = map { $_ || "../config.cgi?$module_name" }
 return "<p>".&text($text, @subs)."<p>\n";
 }
 
-# ui_print_endpage(text)
-# Prints HTML for an error message followed by a page footer with a link to
-# /, then exits. Good for main page error messages.
+=head2 ui_print_endpage(text)
+
+Prints HTML for an error message followed by a page footer with a link to
+/, then exits. Good for main page error messages.
+
+=cut
 sub ui_print_endpage
 {
 return &theme_ui_print_endpage(@_) if (defined(&theme_ui_print_endpage));
@@ -1036,16 +1220,22 @@ print "</p>\n";
 exit;
 }
 
-# ui_subheading(text, ...)
-# Returns HTML for a section heading
+=head2 ui_subheading(text, ...)
+
+Returns HTML for a section heading
+
+=cut
 sub ui_subheading
 {
 return &theme_ui_subheading(@_) if (defined(&theme_ui_subheading));
 return "<h3 class='ui_subheading'>".join("", @_)."</h3>\n";
 }
 
-# ui_links_row(&links)
-# Returns HTML for a row of links, like select all / invert selection / add..
+=head2 ui_links_row(&links)
+
+Returns HTML for a row of links, like select all / invert selection / add..
+
+=cut
 sub ui_links_row
 {
 return &theme_ui_links_row(@_) if (defined(&theme_ui_links_row));
@@ -1056,8 +1246,11 @@ return @$links ? join("\n|\n", @$links)."<br>\n"
 
 ########################### collapsible section / tab functions
 
-# ui_hidden_javascript()
-# Returns <script> and <style> sections for hiding functions and CSS
+=head2 ui_hidden_javascript
+
+Returns <script> and <style> sections for hiding functions and CSS
+
+=cut
 sub ui_hidden_javascript
 {
 return &theme_ui_hidden_javascript(@_)
@@ -1133,9 +1326,12 @@ return false;
 EOF
 }
 
-# ui_hidden_start(title, name, status, thisurl)
-# Returns HTML for the start of a collapsible hidden section, such as for
-# advanced options.
+=head2 ui_hidden_start(title, name, status, thisurl)
+
+Returns HTML for the start of a collapsible hidden section, such as for
+advanced options.
+
+=cut
 sub ui_hidden_start
 {
 return &theme_ui_hidden_start(@_) if (defined(&theme_ui_hidden_start));
@@ -1154,8 +1350,11 @@ $rv .= "<div class='$defclass' id='$divid'>\n";
 return $rv;
 }
 
-# ui_hidden_end(name)
-# Returns HTML for the end of a hidden section
+=head2 ui_hidden_end(name)
+
+Returns HTML for the end of a hidden section
+
+=cut
 sub ui_hidden_end
 {
 return &theme_ui_hidden_end(@_) if (defined(&theme_ui_hidden_end));
@@ -1163,9 +1362,12 @@ my ($name) = @_;
 return "</div>\n";
 }
 
-# ui_hidden_table_row_start(title, name, status, thisurl)
-# Similar to ui_hidden_start, but for use within a table started with
-# ui_table_start
+=head2 ui_hidden_table_row_start(title, name, status, thisurl)
+
+Similar to ui_hidden_start, but for use within a table started with
+ui_table_start
+
+=cut
 sub ui_hidden_table_row_start
 {
 return &theme_ui_hidden_table_row_start(@_)
@@ -1188,7 +1390,11 @@ $rv .= "<table width=100%>\n";
 return $rv;
 }
 
-# ui_hidden_table_row_end(name)
+=head2 ui_hidden_table_row_end(name)
+
+MISSING DOCUMENTATION
+
+=cut
 sub ui_hidden_table_row_end
 {
 return &theme_ui_hidden_table_row_end(@_)
@@ -1197,9 +1403,11 @@ my ($name) = @_;
 return "</table></div><table width=100%>\n";
 }
 
-# ui_hidden_table_start(heading, [tabletags], [cols], name, status,
-#                      [&default-tds], [rightheading])
-# A table with a heading and table inside, and which is collapsible
+=head2 ui_hidden_table_start(heading, [tabletags], [cols], name, status, [&default-tds], [rightheading])
+
+A table with a heading and table inside, and which is collapsible
+
+=cut
 sub ui_hidden_table_start
 {
 return &theme_ui_hidden_table_start(@_)
@@ -1235,9 +1443,12 @@ $main::ui_table_default_tds = $tds;
 return $rv;
 }
 
-# ui_hidden_table_end(name)
-# Returns HTML for the end of table with hiding, as started by
-# ui_hidden_table_start
+=head2 ui_hidden_table_end(name)
+
+Returns HTML for the end of table with hiding, as started by
+ui_hidden_table_start
+
+=cut
 sub ui_hidden_table_end
 {
 my ($name) = @_;
@@ -1245,9 +1456,12 @@ return &theme_ui_hidden_table_end(@_) if (defined(&theme_ui_hidden_table_end));
 return "</table></div></td></tr></table>\n";
 }
 
-# ui_tabs_start(&tabs, name, selected, show-border)
-# Render a row of tabs from which one can be selected. Each tab is an array
-# ref containing a name, title and link.
+=head2 ui_tabs_start(&tabs, name, selected, show-border)
+
+Render a row of tabs from which one can be selected. Each tab is an array
+ref containing a name, title and link.
+
+=cut
 sub ui_tabs_start
 {
 return &theme_ui_tabs_start(@_) if (defined(&theme_ui_tabs_start));
@@ -1327,7 +1541,11 @@ $main::ui_tabs_selected = $sel;
 return $rv;
 }
 
-# ui_tabs_end(border)
+=head2 ui_tabs_end(border)
+
+MISSING DOCUMENTATION
+
+=cut
 sub ui_tabs_end
 {
 return &theme_ui_tabs_end(@_) if (defined(&theme_ui_tabs_end));
@@ -1344,8 +1562,11 @@ if ($border) {
 return $rv;
 }
 
-# ui_tabs_start_tab(name, tab)
-# Must be called before outputting the HTML for the named tab
+=head2 ui_tabs_start_tab(name, tab)
+
+Must be called before outputting the HTML for the named tab
+
+=cut
 sub ui_tabs_start_tab
 {
 return &theme_ui_tabs_start_tab(@_) if (defined(&theme_ui_tabs_start_tab));
@@ -1356,8 +1577,11 @@ my $rv = "<div id='div_$tab' class='$defclass ui_tabs_start'>\n";
 return $rv;
 }
 
-# ui_tabs_start_tabletab(name, tab)
-# Behaves like ui_tabs_start_tab, but for use within a ui_table_start block
+=head2 ui_tabs_start_tabletab(name, tab)
+
+Behaves like ui_tabs_start_tab, but for use within a ui_table_start block
+
+=cut
 sub ui_tabs_start_tabletab
 {
 return &theme_ui_tabs_start_tabletab(@_)
@@ -1379,8 +1603,11 @@ return &theme_ui_tabs_end_tabletab(@_)
 return "</table></div><table width=100%>\n";
 }
 
-# ui_max_text_width(width, [text-area?])
-# Returns a new width for a text field, based on theme settings
+=head2 ui_max_text_width(width, [text-area?])
+
+Returns a new width for a text field, based on theme settings
+
+=cut
 sub ui_max_text_width
 {
 my ($w, $ta) = @_;
@@ -1390,10 +1617,13 @@ return $max && $w > $max ? $max : $w;
 
 ####################### radio hidden functions
 
-# ui_radio_selector(&opts, name, selected)
-# Returns HTML for a set of radio buttons, each of which shows a different
-# block of HTML when selected. &opts is an array ref to arrays containing
-# [ value, label, html ]
+=head2 ui_radio_selector(&opts, name, selected)
+
+Returns HTML for a set of radio buttons, each of which shows a different
+block of HTML when selected. &opts is an array ref to arrays containing
+[ value, label, html ]
+
+=cut
 sub ui_radio_selector
 {
 return &theme_ui_radio_selector(@_) if (defined(&theme_ui_radio_selector));
@@ -1438,11 +1668,13 @@ EOF
 
 ####################### grid layout functions
 
-# ui_grid_table(&elements, columns, [width-percent], [tds], [tabletags],
-#              [title])
-# Given a list of HTML elements, formats them into a table with the given
-# number of columns. However, themes are free to override this to use fewer
-# columns where space is limited.
+=head2 ui_grid_table(&elements, columns, [width-percent], [tds], [tabletags], [title])
+
+Given a list of HTML elements, formats them into a table with the given
+number of columns. However, themes are free to override this to use fewer
+columns where space is limited.
+
+=cut
 sub ui_grid_table
 {
 return &theme_ui_grid_table(@_) if (defined(&theme_ui_grid_table));
@@ -1478,9 +1710,12 @@ if (defined($title)) {
 return $rv;
 }
 
-# ui_radio_table(name, selected, &rows)
-# Returns HTML for a table of radio buttons, each of which has a label and
-# some associated inputs to the right.
+=head2 ui_radio_table(name, selected, &rows)
+
+Returns HTML for a table of radio buttons, each of which has a label and
+some associated inputs to the right.
+
+=cut
 sub ui_radio_table
 {
 return &theme_ui_radio_table(@_) if (defined(&theme_ui_radio_table));
@@ -1502,8 +1737,11 @@ $rv .= "</table>\n";
 return $rv;
 }
 
-# ui_up_down_arrows(uplink, downlink, up-show, down-show)
-# Returns HTML for moving some objects in a table up or down
+=head2 ui_up_down_arrows(uplink, downlink, up-show, down-show)
+
+Returns HTML for moving some objects in a table up or down
+
+=cut
 sub ui_up_down_arrows
 {
 return &theme_ui_up_down_arrows(@_) if (defined(&theme_ui_up_down_arrows));
@@ -1527,16 +1765,22 @@ else {
 return $mover;
 }
 
-# ui_hr()
-# Returns a horizontal row tag
+=head2 ui_hr
+
+Returns a horizontal row tag
+
+=cut
 sub ui_hr
 {
 return &theme_ui_hr() if (defined(&theme_ui_hr));
 return "<hr>\n";
 }
 
-# ui_nav_link(direction, url, disabled)
-# Returns an arrow icon linking to provided url
+=head2 ui_nav_link(direction, url, disabled)
+
+Returns an arrow icon linking to provided url
+
+=cut
 sub ui_nav_link
 {
 return &theme_ui_nav_link(@_) if (defined(&theme_ui_nav_link));
@@ -1552,10 +1796,12 @@ else {
        }
 }
 
-# ui_confirmation_form(cgi, message, &hiddens, [&buttons], [&otherinputs],
-#                      [extra-warning])
-# Returns HTML for a form asking for confirmation before performing some
-# action, such as deleting a user.
+=head2 ui_confirmation_form(cgi, message, &hiddens, [&buttons], [&otherinputs], [extra-warning])
+
+Returns HTML for a form asking for confirmation before performing some
+action, such as deleting a user.
+
+=cut
 sub ui_confirmation_form
 {
 my ($cgi, $message, $hiddens, $buttons, $others, $warning) = @_;
@@ -1579,8 +1825,12 @@ return $rv;
 
 ####################### javascript functions
 
-# js_disable_input(&disable-inputs, &enable-inputs, [tag])
-# Returns Javascript to disable some form elements and enable others
+=head2 js_disable_inputs
+
+js_disable_input(&disable-inputs, &enable-inputs, [tag])
+Returns Javascript to disable some form elements and enable others
+
+=cut
 sub js_disable_inputs
 {
 my $rv;
@@ -1604,11 +1854,13 @@ foreach $f (@{$_[1]}) {
 return $_[2] ? "$_[2]='$rv'" : $rv;
 }
 
-# ui_page_flipper(message, [inputs, cgi], left-link, right-link,
-#                 [far-left-link], [far-right-link], [below])
-# Returns HTML for moving left and right in some large list, such as an inbox
-# or database table. If only 5 parameters are given, no far links are included.
-# If any link is undef, that array will be greyed out.
+=head2 ui_page_flipper(message, [inputs, cgi], left-link, right-link, [far-left-link], [far-right-link], [below])
+
+Returns HTML for moving left and right in some large list, such as an inbox
+or database table. If only 5 parameters are given, no far links are included.
+If any link is undef, that array will be greyed out.
+
+=cut
 sub ui_page_flipper
 {
 return &theme_ui_page_flipper(@_) if (defined(&theme_ui_page_flipper));
@@ -1670,7 +1922,11 @@ $rv .= "</center>\n";
 return $rv;
 }
 
-# js_checkbox_disable(name, &checked-disable, &checked-enable, [tag])
+=head2 js_checkbox_disable(name, &checked-disable, &checked-enable, [tag])
+
+MISSING DOCUMENTATION
+
+=cut
 sub js_checkbox_disable
 {
 my $rv;
@@ -1684,8 +1940,11 @@ foreach $f (@{$_[2]}) {
 return $_[3] ? "$_[3]='$rv'" : $rv;
 }
 
-# js_redirect(url, [window-object])
-# Returns HTML to trigger a redirect to some URL
+=head2 js_redirect(url, [window-object])
+
+Returns HTML to trigger a redirect to some URL
+
+=cut
 sub js_redirect
 {
 my ($url, $window) = @_;