fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-11-07 10:30 am

[dw-free] move cgi-bin/lj*.pl files into proper modules (in cgi-bin/LJ)

[commit: http://hg.dwscoalition.org/dw-free/rev/54cc18fd5a38]

http://bugs.dwscoalition.org/show_bug.cgi?id=1726

htmlcontrols.pl => LJ/HTMLControls.pm (no package changes)

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/LJ/Comment.pm
  • cgi-bin/LJ/HTMLControls.pm
  • cgi-bin/htmlcontrols.pl
  • cgi-bin/modperl_subs.pl
  • t/settings.t
  • t/talklib-mail-migrate.t
--------------------------------------------------------------------------------
diff -r b9043502017a -r 54cc18fd5a38 cgi-bin/LJ/Comment.pm
--- a/cgi-bin/LJ/Comment.pm	Mon Nov 07 18:28:01 2011 +0800
+++ b/cgi-bin/LJ/Comment.pm	Mon Nov 07 18:31:09 2011 +0800
@@ -20,11 +20,9 @@
 
 use strict;
 use Carp qw/ croak /;
+
 use LJ::Entry;
-
-use lib "$LJ::HOME/cgi-bin";
-
-require "htmlcontrols.pl";
+use LJ::HTMLControls;
 use LJ::Talk;
 
 =head1 NAME
diff -r b9043502017a -r 54cc18fd5a38 cgi-bin/LJ/HTMLControls.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/LJ/HTMLControls.pm	Mon Nov 07 18:31:09 2011 +0800
@@ -0,0 +1,503 @@
+#!/usr/bin/perl
+#
+# This code was forked from the LiveJournal project owned and operated
+# by Live Journal, Inc. The code has been modified and expanded by
+# Dreamwidth Studios, LLC. These files were originally licensed under
+# the terms of the license supplied by Live Journal, Inc, which can
+# currently be found at:
+#
+# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
+#
+# In accordance with the original license, this code and all its
+# modifications are provided under the GNU General Public License.
+# A copy of that license can be found in the LICENSE file included as
+# part of this distribution.
+
+package LJ;
+
+use strict;
+
+# <LJFUNC>
+# name: LJ::html_datetime
+# class: component
+# des: Creates date and time control HTML form elements.
+# info: Parse output later with [func[LJ::html_datetime_decode]].
+# args:
+# des-:
+# returns:
+# </LJFUNC>
+sub html_datetime
+{
+    my $opts = shift;
+    my $lang = $opts->{'lang'} || "EN";
+    my ($yyyy, $mm, $dd, $hh, $nn, $ss);
+    my $ret;
+    my $name = $opts->{name} || '';
+    my $id = $opts->{id} || '';
+    my $disabled = $opts->{'disabled'} ? 1 : 0;
+
+    my %extra_opts;
+    foreach (grep { ! /^(name|id|disabled|seconds|notime|lang|default)$/ } keys %$opts) {
+        $extra_opts{$_} = $opts->{$_};
+    }
+
+    if ($opts->{'default'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?/) {
+        ( $yyyy, $mm, $dd, $hh, $nn, $ss ) = ( $1 > 0 ? $1   : "",
+                                               $2+0,
+                                               $3 > 0 ? $3+0 : "",
+                                 defined $4 && $4 > 0 ? $4   : "",
+                                 defined $5 && $5 > 0 ? $5   : "",
+                                 defined $6 && $6 > 0 ? $6   : "" );
+    }
+    $ret .= html_select( { name => "${name}_mm",
+                           id => "${id}_mm",
+                           selected => $mm,
+                           class => 'select',
+                           title => 'month',
+                           disabled => $disabled, %extra_opts,
+                         },
+                         map { $_, LJ::Lang::month_long_ml($_) } (1..12) );
+    $ret .= html_text( { name => "${name}_dd",
+                         id => "${id}_dd",
+                         size => '2',
+                         class => 'text',
+                         maxlength => '2',
+                         value => $dd,
+                         title => 'day',
+                         disabled => $disabled, %extra_opts,
+                       } );
+    $ret .= html_text( { name => "${name}_yyyy",
+                         id => "${id}_yyyy",
+                         size => '4',
+                         class => 'text',
+                         maxlength => '4',
+                         value => $yyyy,
+                         title => 'year',
+                         disabled => $disabled, %extra_opts,
+                       } );
+    unless ( $opts->{notime} ) {
+        $ret .= ' ';
+        $ret .= html_text( { name => "${name}_hh",
+                             id => "${id}_hh",
+                             size => '2',
+                             maxlength => '2',
+                             value => $hh,
+                             title => 'hour',
+                             disabled => $disabled,
+                           } ) . ':';
+        $ret .= html_text( { name => "${name}_nn",
+                             id => "${id}_nn",
+                             size => '2',
+                             maxlength => '2',
+                             value => $nn,
+                             title => 'minutes',
+                             disabled => $disabled,
+                           } );
+        if ( $opts->{seconds} ) {
+            $ret .= ':';
+            $ret .= html_text( { name => "${name}_ss",
+                                 id => "${id}_ss",
+                                 size => '2',
+                                 maxlength => '2',
+                                 value => $ss,
+                                 title => 'seconds',
+                                 disabled => $disabled,
+                               } );
+        }
+    }
+
+    return $ret;
+}
+
+# <LJFUNC>
+# name: LJ::html_datetime_decode
+# class: component
+# des: Parses output of HTML form controls generated by [func[LJ::html_datetime]].
+# info:
+# args:
+# des-:
+# returns:
+# </LJFUNC>
+sub html_datetime_decode
+{
+    my $opts = shift;
+    my $hash = shift;
+    my $name = $opts->{name} || '';
+    return sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+                   $hash->{"${name}_yyyy"} || 0,
+                   $hash->{"${name}_mm"}   || 0,
+                   $hash->{"${name}_dd"}   || 0,
+                   $hash->{"${name}_hh"}   || 0,
+                   $hash->{"${name}_nn"}   || 0,
+                   $hash->{"${name}_ss"}   || 0 );
+}
+
+# <LJFUNC>
+# name: LJ::html_select
+# class: component
+# des: Creates a drop-down box or listbox HTML form element (the <select> tag).
+# info:
+# args: opts
+# A hashref with an attribute of optgroup will be treated as a list of of value => text pairs within an optgroup
+# des-opts: A hashref of options. Special options are:
+#           'raw' - inserts value unescaped into select tag;
+#           'noescape' - won't escape key values if set to 1;
+#           'disabled' - disables the element;
+#           'include_ids' - bool. If true, puts id attributes on each element in the drop-down.
+#           ids are off by default, to reduce page sizes with large drop-down menus;
+#           'multiple' - creates a drop-down if 0, a multi-select listbox if 1;
+#           'selected' - if multiple, an arrayref of selected values; otherwise,
+#           a scalar equalling the selected value;
+#           All other options will be treated as HTML attribute/value pairs.
+# returns: The generated HTML.
+# </LJFUNC>
+sub html_select
+{
+    my $opts = shift;
+    my @items = @_;
+    my $ehtml = $opts->{'noescape'} ? 0 : 1;
+    my $ret;
+    $ret .= "<select";
+    $ret .= " $opts->{'raw'}" if $opts->{'raw'};
+    $ret .= " disabled='disabled'" if $opts->{'disabled'};
+    $ret .= " multiple='multiple'" if $opts->{'multiple'};
+    foreach (grep { ! /^(raw|disabled|selected|noescape|multiple)$/ } keys %$opts) {
+        my $opt = $opts->{$_} || '';
+        $ret .= " $_=\"" . ( $ehtml ? ehtml( $opt ) : $opt ) . "\"";
+    }
+    $ret .= ">\n";
+
+    # build hashref from arrayref if multiple selected
+    my $selref;
+    $selref = { map { $_, 1 } @{$opts->{'selected'}} }
+        if $opts->{'multiple'} && ref $opts->{'selected'} eq 'ARRAY';
+
+    my $did_sel = 0;
+    while (defined (my $value = shift @items)) {
+
+        # items can be either pairs of $value, $text or a list of $it hashrefs (or a mix)
+        my $it = {};
+        my $text;
+        if (ref $value) {
+            $it = $value;
+            $value = $it->{value};
+            $text = $it->{text};
+        } else {
+            $text = shift @items;
+        }
+
+        if ( $it->{optgroup} ) {
+            $ret .= "<optgroup label='$it->{optgroup}'>";
+            $ret .= LJ::_html_option( $_->{value}, $_->{text}, {}, $opts, $ehtml, $selref, \$did_sel )
+                foreach @{$it->{items} || []};
+            $ret .= "</optgroup>";
+        } else {
+            $ret .= LJ::_html_option( $value, $text, $it, $opts, $ehtml,
+                $selref, \$did_sel );
+        }
+    }
+    $ret .= "</select>";
+    return $ret;
+}
+
+sub _html_option {
+    my ( $value, $text, $item, $opts, $ehtml, $selref, $did_sel ) = @_;
+
+    my $sel = "";
+    # multiple-mode or single-mode?
+    if ( $selref && ( ref $selref eq 'HASH' ) && $selref->{$value} ||
+        $opts->{selected} && ( $opts->{selected} eq $value ) && ! $$did_sel++ ) {
+
+        $sel = " selected='selected'";
+    }
+    $value  = $ehtml ? ehtml( $value ) : $value;
+
+    my $id = '';
+    if ( $opts->{include_ids} && $opts->{name} ne "" && $value ne "" ) {
+        $id = " id='$opts->{'name'}_$value'";
+    }
+
+    # is this individual option disabled?
+    my $dis = $item->{disabled} ? " disabled='disabled' style='color: #999;'" : '';
+
+    return "<option value=\"$value\"$id$sel$dis>" .
+             ( $ehtml ? ehtml( $text ) : $text ) . "</option>\n";
+}
+
+# <LJFUNC>
+# name: LJ::html_check
+# class: component
+# des: Creates HTML checkbox button, and radio button controls.
+# info: Labels for checkboxes are through LJ::labelfy.
+#       It does this safely, by not including any HTML elements in the label tag.
+# args: type, opts
+# des-type: Valid types are 'radio' or 'checkbox'.
+# des-opts: A hashref of options. Special options are:
+#           'disabled' - disables the element;
+#           'selected' - if multiple, an arrayref of selected values; otherwise,
+#           a scalar equalling the selected value;
+#           'raw' - inserts value unescaped into select tag;
+#           'noescape' - won't escape key values if set to 1;
+#           'label' - label for checkbox;
+# returns:
+# </LJFUNC>
+sub html_check
+{
+    my $opts = shift;
+
+    my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : "";
+    my $ehtml = $opts->{'noescape'} ? 0 : 1;
+    my $ret;
+    if ( $opts->{type} && $opts->{type} eq "radio" ) {
+        $ret .= "<input type='radio'";
+    } else {
+        $ret .= "<input type='checkbox'";
+    }
+    if ($opts->{'selected'}) { $ret .= " checked='checked'"; }
+    if ($opts->{'raw'}) { $ret .= " $opts->{'raw'}"; }
+    foreach (grep { ! /^(disabled|type|selected|raw|noescape|label)$/ } keys %$opts) {
+        $ret .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
+    }
+    $ret .= "$disabled />";
+    my $e_label = ($ehtml ? ehtml($opts->{'label'}) : $opts->{'label'});
+    $e_label = LJ::labelfy($opts->{id}, $e_label);
+    $ret .= $e_label if $opts->{'label'};
+    return $ret;
+}
+
+# given a string and an id, return the string
+# in a label, respecting HTML
+sub labelfy {
+    my ($id, $text, $class) = @_;
+    $id = '' unless defined $id;
+    $text = '' unless defined $text;
+
+    $class = LJ::ehtml( $class || "" );
+    $class = qq{class="$class"} if $class;
+
+    $text =~ s!
+        ^([^<]+)
+        !
+        <label for="$id" $class>
+            $1
+        </label>
+        !x;
+
+    return $text;
+}
+
+# <LJFUNC>
+# name: LJ::html_text
+# class: component
+# des: Creates a text input field, for single-line input.
+# info: Allows 'type' =&gt; 'password' flag.
+# args:
+# des-:
+# returns: The generated HTML.
+# </LJFUNC>
+sub html_text
+{
+    my $opts = shift;
+
+    my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : "";
+    my $ehtml = $opts->{'noescape'} ? 0 : 1;
+    my $type = 'text';
+    $type = $opts->{type} if $opts->{type} &&
+                             ( $opts->{type} eq 'password' ||
+                               $opts->{type} eq 'search' );
+    my $ret = '';
+    $ret .= "<input type=\"$type\"";
+    foreach (grep { ! /^(type|disabled|raw|noescape)$/ } keys %$opts) {
+        my $val = defined $opts->{$_} ? $opts->{$_} : '';
+        $ret .= " $_=\"" . ( $ehtml ? LJ::ehtml( $val ) : $val ) . "\"";
+    }
+    if ($opts->{'raw'}) { $ret .= " $opts->{'raw'}"; }
+    $ret .= "$disabled />";
+    return $ret;
+}
+
+# <LJFUNC>
+# name: LJ::html_textarea
+# class: component
+# des: Creates a text box for multi-line input (the <textarea> tag).
+# info:
+# args:
+# des-:
+# returns: The generated HTML.
+# </LJFUNC>
+sub html_textarea
+{
+    my $opts = $_[0];
+
+    my $disabled = $opts->{disabled} ? " disabled='disabled'" : "";
+    my $ehtml = $opts->{noescape} ? 0 : 1;
+    my $value = $opts->{value} || '';
+    $value = ehtml( $value ) if $ehtml;
+
+    my $ret = "<textarea";
+    foreach (grep { ! /^(disabled|raw|value|noescape)$/ } keys %$opts) {
+        $ret .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
+    }
+    $ret .= " $opts->{raw}" if $opts->{raw};
+    $ret .= "$disabled>$value</textarea>";
+    return $ret;
+}
+
+# <LJFUNC>
+# name: LJ::html_color
+# class: component
+# des: A text field with attached color preview and button to choose a color.
+# info: Depends on the client-side Color Picker.
+# args: opts
+# des-opts: Valid options are: 'onchange' argument, which happens when
+#           color picker button is clicked, or when focus is changed to text box;
+#           'disabled'; and 'raw' , for unescaped input.
+# returns:
+# </LJFUNC>
+sub html_color
+{
+    my $opts = shift;
+
+    my $htmlname = ehtml($opts->{'name'});
+    my $des = ehtml($opts->{'des'}) || "Pick a Color";
+    my $ret;
+
+    ## Output the preview box and picker button with script so that
+    ## they don't appear when JavaScript is unavailable.
+    $ret .= "<script language=\"JavaScript\"><!--\n".
+            "document.write('<span style=\"border: 1px solid #000000; ".
+            "padding-left: 2em; background-color: " . ehtml($opts->{'default'} || "") . ";\" ".
+            "id=\"${htmlname}_disp\"";
+            if ($opts->{no_btn}) {
+                $ret .= " onclick=\"spawnPicker(findel(\\'${htmlname}\\')," .
+                        "findel(\\'${htmlname}_disp\\'),\\'$des\\'); " .
+                        LJ::ejs($opts->{'onchange'}) .
+                        " return false;\"";
+            }
+    $ret .= ">&nbsp;</span>'); ".
+            "\n--></script>\n";
+
+    # 'onchange' argument happens when color picker button is clicked,
+    # or when focus is changed to text box
+
+    $ret .= html_text({ 'size' => 8, 'maxlength' => 7, 'name' => $htmlname, 'id' => $htmlname,
+                        'onchange' => "setBGColorWithId(findel('${htmlname}_disp'),'${htmlname}');",
+                        'onfocus' => $opts->{'onchange'},
+                        'disabled' => $opts->{'disabled'}, 'value' => $opts->{'default'},
+                        'noescape' => 1, 'raw' => $opts->{'raw'},
+                      });
+
+    unless ($opts->{no_btn}) {
+        my $disabled = $opts->{'disabled'} ? "disabled=\'disabled\'" : '';
+        $ret .= "<script language=\"JavaScript\"><!--\n".
+                "document.write('<button ".
+                "onclick=\"spawnPicker(findel(\\'${htmlname}\\')," .
+                "findel(\\'${htmlname}_disp\\'),\\'$des\\'); " .
+                LJ::ejs($opts->{'onchange'}) .
+                " return false;\"$disabled>Choose...</button>'); ".
+                "\n--></script>\n";
+    }
+
+    # A little help for the non-JavaScript folks
+    $ret .= "<noscript> (#<var>rr</var><var>gg</var><var>bb</var>)</noscript>";
+
+    return $ret;
+}
+
+# <LJFUNC>
+# name: LJ::html_hidden
+# class: component
+# des: Makes the HTML for a hidden form element.
+# args: name, val, opts
+# des-name: Name of form element (will be HTML escaped).
+# des-val: Value of form element (will be HTML escaped).
+# des-opts: Can optionally take arguments that are hashrefs
+#           and extract name/value/other standard keys from that. Can also be
+#           mixed with the older style key/value array calling format.
+# returns: HTML
+# </LJFUNC>
+sub html_hidden
+{
+    my $ret;
+
+    while (@_) {
+        my $name = shift;
+        my $val;
+        my $ehtml = 1;
+        my $extra = '';
+        if (ref $name eq 'HASH') {
+            my $opts = $name;
+
+            $val = $opts->{value};
+            $name = $opts->{name};
+
+            $ehtml = $opts->{'noescape'} ? 0 : 1;
+            foreach (grep { ! /^(name|value|raw|noescape)$/ } keys %$opts) {
+                $extra .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
+            }
+
+            $extra .= " $opts->{'raw'}" if $opts->{'raw'};
+
+        } else {
+            $val = shift;
+        }
+
+        $ret .= "<input type='hidden'";
+        # allow override of these in 'raw'
+        $ret .= " name=\"" . ($ehtml ? ehtml($name) : $name) . "\"" if $name;
+        $ret .= " value=\"" . ($ehtml ? ehtml($val) : $val) . "\"" if defined $val;
+        $ret .= "$extra />";
+    }
+    return $ret;
+}
+
+# <LJFUNC>
+# name: LJ::html_submit
+# class: component
+# des: Makes the HTML for a submit button.
+# info: If only one argument is given it is
+#       assumed LJ::html_submit(undef, 'value') was meant.
+# args: name, val, opts?, type
+# des-name: Name of form element (will be HTML escaped).
+# des-val: Value of form element, and label of button (will be HTML escaped).
+# des-opts: Optional hashref of additional tag attributes.
+#           A hashref of options. Special options are:
+#           'raw' - inserts value unescaped into select tag;
+#           'disabled' - disables the element;
+#           'noescape' - won't escape key values if set to 1;
+# des-type: Optional. Value format is type =&gt; (submit|reset). Defaults to submit.
+# returns: HTML
+# </LJFUNC>
+sub html_submit
+{
+    my ($name, $val, $opts) = @_;
+
+    # if one argument, assume (undef, $val)
+    if (@_ == 1) {
+        $val = $name;
+        $name = undef;
+    }
+
+    my ( $eopts, $disabled, $raw ) = ( '','','' );
+    my $type = 'submit';
+
+    my $ehtml;
+    if ($opts && ref $opts eq 'HASH') {
+        $disabled = " disabled='disabled'" if $opts->{'disabled'};
+        $raw = " $opts->{'raw'}" if $opts->{'raw'};
+        $type = 'reset' if $opts->{type} && $opts->{type} eq 'reset';
+
+        $ehtml = $opts->{'noescape'} ? 0 : 1;
+        foreach (grep { ! /^(raw|disabled|noescape|type)$/ } keys %$opts) {
+            $eopts .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
+        }
+    }
+    my $ret = "<input type='$type'";
+    # allow override of these in 'raw'
+    $ret .= " name=\"" . ($ehtml ? ehtml($name) : $name) . "\"" if $name;
+    $ret .= " value=\"" . ($ehtml ? ehtml($val) : $val) . "\"" if defined $val;
+    $ret .= "$eopts$raw$disabled />";
+    return $ret;
+}
+
+1;
diff -r b9043502017a -r 54cc18fd5a38 cgi-bin/htmlcontrols.pl
--- a/cgi-bin/htmlcontrols.pl	Mon Nov 07 18:28:01 2011 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,503 +0,0 @@
-#!/usr/bin/perl
-#
-# This code was forked from the LiveJournal project owned and operated
-# by Live Journal, Inc. The code has been modified and expanded by
-# Dreamwidth Studios, LLC. These files were originally licensed under
-# the terms of the license supplied by Live Journal, Inc, which can
-# currently be found at:
-#
-# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
-#
-# In accordance with the original license, this code and all its
-# modifications are provided under the GNU General Public License.
-# A copy of that license can be found in the LICENSE file included as
-# part of this distribution.
-
-package LJ;
-
-use strict;
-
-# <LJFUNC>
-# name: LJ::html_datetime
-# class: component
-# des: Creates date and time control HTML form elements.
-# info: Parse output later with [func[LJ::html_datetime_decode]].
-# args:
-# des-:
-# returns:
-# </LJFUNC>
-sub html_datetime
-{
-    my $opts = shift;
-    my $lang = $opts->{'lang'} || "EN";
-    my ($yyyy, $mm, $dd, $hh, $nn, $ss);
-    my $ret;
-    my $name = $opts->{name} || '';
-    my $id = $opts->{id} || '';
-    my $disabled = $opts->{'disabled'} ? 1 : 0;
-
-    my %extra_opts;
-    foreach (grep { ! /^(name|id|disabled|seconds|notime|lang|default)$/ } keys %$opts) {
-        $extra_opts{$_} = $opts->{$_};
-    }
-
-    if ($opts->{'default'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?/) {
-        ( $yyyy, $mm, $dd, $hh, $nn, $ss ) = ( $1 > 0 ? $1   : "",
-                                               $2+0,
-                                               $3 > 0 ? $3+0 : "",
-                                 defined $4 && $4 > 0 ? $4   : "",
-                                 defined $5 && $5 > 0 ? $5   : "",
-                                 defined $6 && $6 > 0 ? $6   : "" );
-    }
-    $ret .= html_select( { name => "${name}_mm",
-                           id => "${id}_mm",
-                           selected => $mm,
-                           class => 'select',
-                           title => 'month',
-                           disabled => $disabled, %extra_opts,
-                         },
-                         map { $_, LJ::Lang::month_long_ml($_) } (1..12) );
-    $ret .= html_text( { name => "${name}_dd",
-                         id => "${id}_dd",
-                         size => '2',
-                         class => 'text',
-                         maxlength => '2',
-                         value => $dd,
-                         title => 'day',
-                         disabled => $disabled, %extra_opts,
-                       } );
-    $ret .= html_text( { name => "${name}_yyyy",
-                         id => "${id}_yyyy",
-                         size => '4',
-                         class => 'text',
-                         maxlength => '4',
-                         value => $yyyy,
-                         title => 'year',
-                         disabled => $disabled, %extra_opts,
-                       } );
-    unless ( $opts->{notime} ) {
-        $ret .= ' ';
-        $ret .= html_text( { name => "${name}_hh",
-                             id => "${id}_hh",
-                             size => '2',
-                             maxlength => '2',
-                             value => $hh,
-                             title => 'hour',
-                             disabled => $disabled,
-                           } ) . ':';
-        $ret .= html_text( { name => "${name}_nn",
-                             id => "${id}_nn",
-                             size => '2',
-                             maxlength => '2',
-                             value => $nn,
-                             title => 'minutes',
-                             disabled => $disabled,
-                           } );
-        if ( $opts->{seconds} ) {
-            $ret .= ':';
-            $ret .= html_text( { name => "${name}_ss",
-                                 id => "${id}_ss",
-                                 size => '2',
-                                 maxlength => '2',
-                                 value => $ss,
-                                 title => 'seconds',
-                                 disabled => $disabled,
-                               } );
-        }
-    }
-
-    return $ret;
-}
-
-# <LJFUNC>
-# name: LJ::html_datetime_decode
-# class: component
-# des: Parses output of HTML form controls generated by [func[LJ::html_datetime]].
-# info:
-# args:
-# des-:
-# returns:
-# </LJFUNC>
-sub html_datetime_decode
-{
-    my $opts = shift;
-    my $hash = shift;
-    my $name = $opts->{name} || '';
-    return sprintf("%04d-%02d-%02d %02d:%02d:%02d",
-                   $hash->{"${name}_yyyy"} || 0,
-                   $hash->{"${name}_mm"}   || 0,
-                   $hash->{"${name}_dd"}   || 0,
-                   $hash->{"${name}_hh"}   || 0,
-                   $hash->{"${name}_nn"}   || 0,
-                   $hash->{"${name}_ss"}   || 0 );
-}
-
-# <LJFUNC>
-# name: LJ::html_select
-# class: component
-# des: Creates a drop-down box or listbox HTML form element (the <select> tag).
-# info:
-# args: opts
-# A hashref with an attribute of optgroup will be treated as a list of of value => text pairs within an optgroup
-# des-opts: A hashref of options. Special options are:
-#           'raw' - inserts value unescaped into select tag;
-#           'noescape' - won't escape key values if set to 1;
-#           'disabled' - disables the element;
-#           'include_ids' - bool. If true, puts id attributes on each element in the drop-down.
-#           ids are off by default, to reduce page sizes with large drop-down menus;
-#           'multiple' - creates a drop-down if 0, a multi-select listbox if 1;
-#           'selected' - if multiple, an arrayref of selected values; otherwise,
-#           a scalar equalling the selected value;
-#           All other options will be treated as HTML attribute/value pairs.
-# returns: The generated HTML.
-# </LJFUNC>
-sub html_select
-{
-    my $opts = shift;
-    my @items = @_;
-    my $ehtml = $opts->{'noescape'} ? 0 : 1;
-    my $ret;
-    $ret .= "<select";
-    $ret .= " $opts->{'raw'}" if $opts->{'raw'};
-    $ret .= " disabled='disabled'" if $opts->{'disabled'};
-    $ret .= " multiple='multiple'" if $opts->{'multiple'};
-    foreach (grep { ! /^(raw|disabled|selected|noescape|multiple)$/ } keys %$opts) {
-        my $opt = $opts->{$_} || '';
-        $ret .= " $_=\"" . ( $ehtml ? ehtml( $opt ) : $opt ) . "\"";
-    }
-    $ret .= ">\n";
-
-    # build hashref from arrayref if multiple selected
-    my $selref;
-    $selref = { map { $_, 1 } @{$opts->{'selected'}} }
-        if $opts->{'multiple'} && ref $opts->{'selected'} eq 'ARRAY';
-
-    my $did_sel = 0;
-    while (defined (my $value = shift @items)) {
-
-        # items can be either pairs of $value, $text or a list of $it hashrefs (or a mix)
-        my $it = {};
-        my $text;
-        if (ref $value) {
-            $it = $value;
-            $value = $it->{value};
-            $text = $it->{text};
-        } else {
-            $text = shift @items;
-        }
-
-        if ( $it->{optgroup} ) {
-            $ret .= "<optgroup label='$it->{optgroup}'>";
-            $ret .= LJ::_html_option( $_->{value}, $_->{text}, {}, $opts, $ehtml, $selref, \$did_sel )
-                foreach @{$it->{items} || []};
-            $ret .= "</optgroup>";
-        } else {
-            $ret .= LJ::_html_option( $value, $text, $it, $opts, $ehtml,
-                $selref, \$did_sel );
-        }
-    }
-    $ret .= "</select>";
-    return $ret;
-}
-
-sub _html_option {
-    my ( $value, $text, $item, $opts, $ehtml, $selref, $did_sel ) = @_;
-
-    my $sel = "";
-    # multiple-mode or single-mode?
-    if ( $selref && ( ref $selref eq 'HASH' ) && $selref->{$value} ||
-        $opts->{selected} && ( $opts->{selected} eq $value ) && ! $$did_sel++ ) {
-
-        $sel = " selected='selected'";
-    }
-    $value  = $ehtml ? ehtml( $value ) : $value;
-
-    my $id = '';
-    if ( $opts->{include_ids} && $opts->{name} ne "" && $value ne "" ) {
-        $id = " id='$opts->{'name'}_$value'";
-    }
-
-    # is this individual option disabled?
-    my $dis = $item->{disabled} ? " disabled='disabled' style='color: #999;'" : '';
-
-    return "<option value=\"$value\"$id$sel$dis>" .
-             ( $ehtml ? ehtml( $text ) : $text ) . "</option>\n";
-}
-
-# <LJFUNC>
-# name: LJ::html_check
-# class: component
-# des: Creates HTML checkbox button, and radio button controls.
-# info: Labels for checkboxes are through LJ::labelfy.
-#       It does this safely, by not including any HTML elements in the label tag.
-# args: type, opts
-# des-type: Valid types are 'radio' or 'checkbox'.
-# des-opts: A hashref of options. Special options are:
-#           'disabled' - disables the element;
-#           'selected' - if multiple, an arrayref of selected values; otherwise,
-#           a scalar equalling the selected value;
-#           'raw' - inserts value unescaped into select tag;
-#           'noescape' - won't escape key values if set to 1;
-#           'label' - label for checkbox;
-# returns:
-# </LJFUNC>
-sub html_check
-{
-    my $opts = shift;
-
-    my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : "";
-    my $ehtml = $opts->{'noescape'} ? 0 : 1;
-    my $ret;
-    if ( $opts->{type} && $opts->{type} eq "radio" ) {
-        $ret .= "<input type='radio'";
-    } else {
-        $ret .= "<input type='checkbox'";
-    }
-    if ($opts->{'selected'}) { $ret .= " checked='checked'"; }
-    if ($opts->{'raw'}) { $ret .= " $opts->{'raw'}"; }
-    foreach (grep { ! /^(disabled|type|selected|raw|noescape|label)$/ } keys %$opts) {
-        $ret .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
-    }
-    $ret .= "$disabled />";
-    my $e_label = ($ehtml ? ehtml($opts->{'label'}) : $opts->{'label'});
-    $e_label = LJ::labelfy($opts->{id}, $e_label);
-    $ret .= $e_label if $opts->{'label'};
-    return $ret;
-}
-
-# given a string and an id, return the string
-# in a label, respecting HTML
-sub labelfy {
-    my ($id, $text, $class) = @_;
-    $id = '' unless defined $id;
-    $text = '' unless defined $text;
-
-    $class = LJ::ehtml( $class || "" );
-    $class = qq{class="$class"} if $class;
-
-    $text =~ s!
-        ^([^<]+)
-        !
-        <label for="$id" $class>
-            $1
-        </label>
-        !x;
-
-    return $text;
-}
-
-# <LJFUNC>
-# name: LJ::html_text
-# class: component
-# des: Creates a text input field, for single-line input.
-# info: Allows 'type' =&gt; 'password' flag.
-# args:
-# des-:
-# returns: The generated HTML.
-# </LJFUNC>
-sub html_text
-{
-    my $opts = shift;
-
-    my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : "";
-    my $ehtml = $opts->{'noescape'} ? 0 : 1;
-    my $type = 'text';
-    $type = $opts->{type} if $opts->{type} &&
-                             ( $opts->{type} eq 'password' ||
-                               $opts->{type} eq 'search' );
-    my $ret = '';
-    $ret .= "<input type=\"$type\"";
-    foreach (grep { ! /^(type|disabled|raw|noescape)$/ } keys %$opts) {
-        my $val = defined $opts->{$_} ? $opts->{$_} : '';
-        $ret .= " $_=\"" . ( $ehtml ? LJ::ehtml( $val ) : $val ) . "\"";
-    }
-    if ($opts->{'raw'}) { $ret .= " $opts->{'raw'}"; }
-    $ret .= "$disabled />";
-    return $ret;
-}
-
-# <LJFUNC>
-# name: LJ::html_textarea
-# class: component
-# des: Creates a text box for multi-line input (the <textarea> tag).
-# info:
-# args:
-# des-:
-# returns: The generated HTML.
-# </LJFUNC>
-sub html_textarea
-{
-    my $opts = $_[0];
-
-    my $disabled = $opts->{disabled} ? " disabled='disabled'" : "";
-    my $ehtml = $opts->{noescape} ? 0 : 1;
-    my $value = $opts->{value} || '';
-    $value = ehtml( $value ) if $ehtml;
-
-    my $ret = "<textarea";
-    foreach (grep { ! /^(disabled|raw|value|noescape)$/ } keys %$opts) {
-        $ret .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
-    }
-    $ret .= " $opts->{raw}" if $opts->{raw};
-    $ret .= "$disabled>$value</textarea>";
-    return $ret;
-}
-
-# <LJFUNC>
-# name: LJ::html_color
-# class: component
-# des: A text field with attached color preview and button to choose a color.
-# info: Depends on the client-side Color Picker.
-# args: opts
-# des-opts: Valid options are: 'onchange' argument, which happens when
-#           color picker button is clicked, or when focus is changed to text box;
-#           'disabled'; and 'raw' , for unescaped input.
-# returns:
-# </LJFUNC>
-sub html_color
-{
-    my $opts = shift;
-
-    my $htmlname = ehtml($opts->{'name'});
-    my $des = ehtml($opts->{'des'}) || "Pick a Color";
-    my $ret;
-
-    ## Output the preview box and picker button with script so that
-    ## they don't appear when JavaScript is unavailable.
-    $ret .= "<script language=\"JavaScript\"><!--\n".
-            "document.write('<span style=\"border: 1px solid #000000; ".
-            "padding-left: 2em; background-color: " . ehtml($opts->{'default'} || "") . ";\" ".
-            "id=\"${htmlname}_disp\"";
-            if ($opts->{no_btn}) {
-                $ret .= " onclick=\"spawnPicker(findel(\\'${htmlname}\\')," .
-                        "findel(\\'${htmlname}_disp\\'),\\'$des\\'); " .
-                        LJ::ejs($opts->{'onchange'}) .
-                        " return false;\"";
-            }
-    $ret .= ">&nbsp;</span>'); ".
-            "\n--></script>\n";
-
-    # 'onchange' argument happens when color picker button is clicked,
-    # or when focus is changed to text box
-
-    $ret .= html_text({ 'size' => 8, 'maxlength' => 7, 'name' => $htmlname, 'id' => $htmlname,
-                        'onchange' => "setBGColorWithId(findel('${htmlname}_disp'),'${htmlname}');",
-                        'onfocus' => $opts->{'onchange'},
-                        'disabled' => $opts->{'disabled'}, 'value' => $opts->{'default'},
-                        'noescape' => 1, 'raw' => $opts->{'raw'},
-                      });
-
-    unless ($opts->{no_btn}) {
-        my $disabled = $opts->{'disabled'} ? "disabled=\'disabled\'" : '';
-        $ret .= "<script language=\"JavaScript\"><!--\n".
-                "document.write('<button ".
-                "onclick=\"spawnPicker(findel(\\'${htmlname}\\')," .
-                "findel(\\'${htmlname}_disp\\'),\\'$des\\'); " .
-                LJ::ejs($opts->{'onchange'}) .
-                " return false;\"$disabled>Choose...</button>'); ".
-                "\n--></script>\n";
-    }
-
-    # A little help for the non-JavaScript folks
-    $ret .= "<noscript> (#<var>rr</var><var>gg</var><var>bb</var>)</noscript>";
-
-    return $ret;
-}
-
-# <LJFUNC>
-# name: LJ::html_hidden
-# class: component
-# des: Makes the HTML for a hidden form element.
-# args: name, val, opts
-# des-name: Name of form element (will be HTML escaped).
-# des-val: Value of form element (will be HTML escaped).
-# des-opts: Can optionally take arguments that are hashrefs
-#           and extract name/value/other standard keys from that. Can also be
-#           mixed with the older style key/value array calling format.
-# returns: HTML
-# </LJFUNC>
-sub html_hidden
-{
-    my $ret;
-
-    while (@_) {
-        my $name = shift;
-        my $val;
-        my $ehtml = 1;
-        my $extra = '';
-        if (ref $name eq 'HASH') {
-            my $opts = $name;
-
-            $val = $opts->{value};
-            $name = $opts->{name};
-
-            $ehtml = $opts->{'noescape'} ? 0 : 1;
-            foreach (grep { ! /^(name|value|raw|noescape)$/ } keys %$opts) {
-                $extra .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
-            }
-
-            $extra .= " $opts->{'raw'}" if $opts->{'raw'};
-
-        } else {
-            $val = shift;
-        }
-
-        $ret .= "<input type='hidden'";
-        # allow override of these in 'raw'
-        $ret .= " name=\"" . ($ehtml ? ehtml($name) : $name) . "\"" if $name;
-        $ret .= " value=\"" . ($ehtml ? ehtml($val) : $val) . "\"" if defined $val;
-        $ret .= "$extra />";
-    }
-    return $ret;
-}
-
-# <LJFUNC>
-# name: LJ::html_submit
-# class: component
-# des: Makes the HTML for a submit button.
-# info: If only one argument is given it is
-#       assumed LJ::html_submit(undef, 'value') was meant.
-# args: name, val, opts?, type
-# des-name: Name of form element (will be HTML escaped).
-# des-val: Value of form element, and label of button (will be HTML escaped).
-# des-opts: Optional hashref of additional tag attributes.
-#           A hashref of options. Special options are:
-#           'raw' - inserts value unescaped into select tag;
-#           'disabled' - disables the element;
-#           'noescape' - won't escape key values if set to 1;
-# des-type: Optional. Value format is type =&gt; (submit|reset). Defaults to submit.
-# returns: HTML
-# </LJFUNC>
-sub html_submit
-{
-    my ($name, $val, $opts) = @_;
-
-    # if one argument, assume (undef, $val)
-    if (@_ == 1) {
-        $val = $name;
-        $name = undef;
-    }
-
-    my ( $eopts, $disabled, $raw ) = ( '','','' );
-    my $type = 'submit';
-
-    my $ehtml;
-    if ($opts && ref $opts eq 'HASH') {
-        $disabled = " disabled='disabled'" if $opts->{'disabled'};
-        $raw = " $opts->{'raw'}" if $opts->{'raw'};
-        $type = 'reset' if $opts->{type} && $opts->{type} eq 'reset';
-
-        $ehtml = $opts->{'noescape'} ? 0 : 1;
-        foreach (grep { ! /^(raw|disabled|noescape|type)$/ } keys %$opts) {
-            $eopts .= " $_=\"" . ($ehtml ? ehtml($opts->{$_}) : $opts->{$_}) . "\"";
-        }
-    }
-    my $ret = "<input type='$type'";
-    # allow override of these in 'raw'
-    $ret .= " name=\"" . ($ehtml ? ehtml($name) : $name) . "\"" if $name;
-    $ret .= " value=\"" . ($ehtml ? ehtml($val) : $val) . "\"" if defined $val;
-    $ret .= "$eopts$raw$disabled />";
-    return $ret;
-}
-
-1;
diff -r b9043502017a -r 54cc18fd5a38 cgi-bin/modperl_subs.pl
--- a/cgi-bin/modperl_subs.pl	Mon Nov 07 18:28:01 2011 +0800
+++ b/cgi-bin/modperl_subs.pl	Mon Nov 07 18:31:09 2011 +0800
@@ -81,7 +81,7 @@
 use LJ::Lang;
 use LJ::Links;
 use LJ::Syn;
-require "htmlcontrols.pl";
+use LJ::HTMLControls;
 require "weblib.pl";
 use LJ::Support;
 use LJ::CleanHTML;
diff -r b9043502017a -r 54cc18fd5a38 t/settings.t
--- a/t/settings.t	Mon Nov 07 18:28:01 2011 +0800
+++ b/t/settings.t	Mon Nov 07 18:31:09 2011 +0800
@@ -1,17 +1,14 @@
-# -*-perl-*-
+#!/usr/bin/perl
 
 use strict;
 use Test::More;
 use lib "$ENV{LJHOME}/cgi-bin";
 require 'ljlib.pl';
 use LJ::Lang;
+use LJ::HTMLControls;
 
 plan tests => 9;
 
-package LJ;
-require 'htmlcontrols.pl';
-package main;
-
 
 use LJ::Setting::Gender;
 use LJ::Setting::Name;
diff -r b9043502017a -r 54cc18fd5a38 t/talklib-mail-migrate.t
--- a/t/talklib-mail-migrate.t	Mon Nov 07 18:28:01 2011 +0800
+++ b/t/talklib-mail-migrate.t	Mon Nov 07 18:31:09 2011 +0800
@@ -11,7 +11,7 @@
 package LJ;
 
 require 'ljlib.pl';
-require 'htmlcontrols.pl';
+use LJ::HTMLControls;
 use LJ::Talk;
 
 use LJ::Test qw(temp_user memcache_stress);
--------------------------------------------------------------------------------