[Rt-commit] r18188 - in rt/3.8/branches/html-css-cleanup: . etc lib/RT lib/RT/Crypt lib/RT/I18N lib/RT/Interface/Email/Auth lib/RT/Report share/html/Admin/Tools share/html/Dashboards share/html/Elements share/html/Elements/RT__Ticket share/html/Install share/html/NoAuth/css/web2 share/html/NoAuth/js/prototype share/html/NoAuth/js/scriptaculous share/html/Search share/html/SelfService/Elements share/html/Ticket share/html/Ticket/Elements share/html/User/Groups t/api t/data/gnupg/emails t/mail

elacour at bestpractical.com elacour at bestpractical.com
Wed Feb 4 10:23:57 EST 2009


Author: elacour
Date: Wed Feb  4 10:23:40 2009
New Revision: 18188

Added:
   rt/3.8/branches/html-css-cleanup/share/html/Ticket/Elements/ShowParents
      - copied unchanged from r18155, /rt/3.8/trunk/share/html/Ticket/Elements/ShowParents
   rt/3.8/branches/html-css-cleanup/t/data/gnupg/emails/19-signed-inline-plain-nested.txt
      - copied unchanged from r18155, /rt/3.8/trunk/t/data/gnupg/emails/19-signed-inline-plain-nested.txt
Modified:
   rt/3.8/branches/html-css-cleanup/   (props changed)
   rt/3.8/branches/html-css-cleanup/README
   rt/3.8/branches/html-css-cleanup/README.Oracle
   rt/3.8/branches/html-css-cleanup/etc/RT_Config.pm.in
   rt/3.8/branches/html-css-cleanup/lib/RT/Config.pm
   rt/3.8/branches/html-css-cleanup/lib/RT/Crypt/GnuPG.pm
   rt/3.8/branches/html-css-cleanup/lib/RT/Date.pm
   rt/3.8/branches/html-css-cleanup/lib/RT/I18N/ru.po
   rt/3.8/branches/html-css-cleanup/lib/RT/Interface/Email/Auth/GnuPG.pm
   rt/3.8/branches/html-css-cleanup/lib/RT/Queue_Overlay.pm
   rt/3.8/branches/html-css-cleanup/lib/RT/Report/Tickets.pm
   rt/3.8/branches/html-css-cleanup/share/html/Admin/Tools/Configuration.html
   rt/3.8/branches/html-css-cleanup/share/html/Dashboards/Queries.html
   rt/3.8/branches/html-css-cleanup/share/html/Elements/EditCustomFieldAutocomplete
   rt/3.8/branches/html-css-cleanup/share/html/Elements/RT__Ticket/ColumnMap
   rt/3.8/branches/html-css-cleanup/share/html/Elements/ShowLinks
   rt/3.8/branches/html-css-cleanup/share/html/Install/Basics.html
   rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseDetails.html
   rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseType.html
   rt/3.8/branches/html-css-cleanup/share/html/Install/Initialize.html
   rt/3.8/branches/html-css-cleanup/share/html/Install/Sendmail.html
   rt/3.8/branches/html-css-cleanup/share/html/Install/index.html
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/msie6.css
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/ticket.css
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/prototype/prototype.js
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/controls.js
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/effects.js
   rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/scriptaculous.js
   rt/3.8/branches/html-css-cleanup/share/html/Search/Bulk.html
   rt/3.8/branches/html-css-cleanup/share/html/SelfService/Elements/Tabs
   rt/3.8/branches/html-css-cleanup/share/html/Ticket/Elements/ShowTransactionAttachments
   rt/3.8/branches/html-css-cleanup/share/html/Ticket/ModifyAll.html
   rt/3.8/branches/html-css-cleanup/share/html/User/Elements/DelegateRights
   rt/3.8/branches/html-css-cleanup/share/html/User/Groups/index.html
   rt/3.8/branches/html-css-cleanup/t/api/date.t
   rt/3.8/branches/html-css-cleanup/t/mail/gnupg-realmail.t
   rt/3.8/branches/html-css-cleanup/t/mail/gnupg-reverification.t

Log:
Merge from trunk (up to r18155 included).


Modified: rt/3.8/branches/html-css-cleanup/README
==============================================================================
--- rt/3.8/branches/html-css-cleanup/README	(original)
+++ rt/3.8/branches/html-css-cleanup/README	Wed Feb  4 10:23:40 2009
@@ -15,13 +15,13 @@
      March, 2005
 
 
-REQUIRED PACKAGES:
-------------------
+REQUIRED PACKAGES
+-----------------
 
 o   Perl 5.8.3 or later (http://www.perl.com).
 
-       Perl versions prior to 5.8.3 contain bugs that could result
-       in data corruption. RT won't start on older versions.
+        Perl versions prior to 5.8.3 contain bugs that could result
+        in data corruption. RT won't start on older versions.
 
 o   A supported SQL database
 
@@ -30,21 +30,21 @@
                               Oracle 9iR2 or later.
                               SQLite 3.0. (Not recommended for production)
 
-o   Apache version 1.3.x or 2.x (http://httpd.apache.org) 
-        with mod_perl -- (http://perl.apache.org ) 
+o   Apache version 1.3.x or 2.x (http://httpd.apache.org)
+        with mod_perl -- (http://perl.apache.org )
         or with FastCGI -- (www.fastcgi.com)
         or other webserver with FastCGI support
 
         RT's FastCGI handler needs to access RT's configuration file.
 
-o    Various and sundry perl modules
-	A tool included with RT takes care of the installation of
-	most of these automatically during the install process.
-
-	The tool supplied with RT uses Perl's CPAN system
-	(http://www.cpan.org) to install modules. Some operating
-	systems package all or some of the modules required, and
-	you may be better off installing the modules that way.
+o   Various and sundry perl modules
+        A tool included with RT takes care of the installation of
+        most of these automatically during the install process.
+
+        The tool supplied with RT uses Perl's CPAN system
+        (http://www.cpan.org) to install modules. Some operating
+        systems package all or some of the modules required, and
+        you may be better off installing the modules that way.
 
 
 GENERAL INSTALLATION
@@ -61,7 +61,7 @@
 
        tar xzvf rt.tar.gz -C /tmp
 
-2   Run the "configure" script. 
+2   Run the "configure" script.
 
        ./configure --help to see the list of options
        ./configure (with the flags you want)
@@ -81,7 +81,7 @@
 
     Check for missing dependencies by running:
 
-       make testdeps        
+       make testdeps
 
 4   If the script reports any missing dependencies, install them by hand
     or run the following command as a user who has permission to install perl
@@ -93,29 +93,29 @@
     so it may be necessary to install them manually.
 
 5   Check to make sure everything was installed properly.
-     
+
        make testdeps
 
      It might sometimes be necessary to run "make fixdeps" several times
      to install all necessary perl modules.
 
 6   If this is a new installation:
-     
+
      As a user with permission to install RT in your chosen directory, type:
 
-       make install   
-                    
+       make install
+
      Set up etc/RT_SiteConfig.pm in your RT installation directory.
-     You'll need to add any values you need to change from the defaults 
+     You'll need to add any values you need to change from the defaults
      in etc/RT_Config.pm
 
      As a user with permission to read RT's configuration file, type:
-     
-       make initialize-database 
+
+       make initialize-database
 
      If the make fails, type:
-     
-       make dropdb 
+
+       make dropdb
 
      and start over from step 6
 
@@ -128,18 +128,18 @@
      new version of RT up and running smoothly.
 
      As a user with permission to install RT in your chosen installation
-     directory, type: 
+     directory, type:
 
-       make upgrade    
+       make upgrade
 
      This will install new binaries, config files and libraries without
-     overwriting your RT database. 
+     overwriting your RT database.
 
      Update etc/RT_SiteConfig.pm in your RT installation directory.
-     You'll need to add any new values you need to change from the defaults 
+     You'll need to add any new values you need to change from the defaults
      in etc/RT_Config.pm
 
-     You may also need to update RT's database.  You can do this with 
+     You may also need to update RT's database.  You can do this with
      the rt-setup-database tool.  Replace root with the name of the dba
      user on your database (root is the default for MySQL).
 
@@ -163,11 +163,11 @@
     release.  You can download it from CPAN here:
     http://search.cpan.org/dist/RT-Extension-RT2toRT3/
 
-9   Configure the email and web gateways, as described below. 
+9   Configure the email and web gateways, as described below.
 
-    NOTE: root's password for the web interface is "password" 
+    NOTE: root's password for the web interface is "password"
     (without the quotes).  Not changing this is a SECURITY risk!
- 
+
 10  Set up automated recurring tasks (cronjobs):
 
     To generate email digest messages, you must arrange for the provided
@@ -198,9 +198,9 @@
 RT's web interface is based around HTML::Mason, which works well with
 the mod_perl perl interpreter within Apache httpd and FastCGI.
 
-Once you've set up the web interface, consider setting up automatic 
+Once you've set up the web interface, consider setting up automatic
 logout for inactive sessions. For more information about how to do that,
-run 
+run
     perldoc /path/to/rt/sbin/rt-clean-sessions
 
 
@@ -274,7 +274,7 @@
 FastCGI
 -------
 
-Installation with FastCGI is a little bit more complex and is documented 
+Installation with FastCGI is a little bit more complex and is documented
 in detail at http://wiki.bestpractical.com/index.cgi?FastCGIConfiguration
 
 In the most basic configuration, you can set up your webserver to run
@@ -283,7 +283,7 @@
 implications of this configuration, which are discussed in the document
 mentioned above.
 
-To install RT with FastCGI, you'll need to add a few lines to your 
+To install RT with FastCGI, you'll need to add a few lines to your
 Apache configuration file telling it about RT:
 
 
@@ -304,7 +304,7 @@
 
 
 
-SETTING UP THE MAIL GATEWAY 
+SETTING UP THE MAIL GATEWAY
 ---------------------------
 
 To let email flow to your RT server, you need to add a few lines of
@@ -333,7 +333,7 @@
 <sales at bestpractical.com>.
 
 If you're interested in having RT extended or customized or would like more
-information about commercial support options, please send email to 
+information about commercial support options, please send email to
 <sales at bestpractical.com> to discuss rates and availability.
 
 
@@ -341,21 +341,21 @@
 RT WEBSITE
 ----------
 
-For current information about RT, check out the RT website at 
-     http://www.bestpractical.com/  
+For current information about RT, check out the RT website at
+     http://www.bestpractical.com/
 
-You'll find screenshots, a pointer to the current version of RT, contributed 
+You'll find screenshots, a pointer to the current version of RT, contributed
 patches, and lots of other great stuff.
 
 
 
 RT-USERS MAILING LIST
---------------------
+---------------------
 
 To keep up to date on the latest RT tips, techniques and extensions,
 you probably want to join the rt-users mailing list.  Send a message to:
 
-      rt-users-request at lists.bestpractical.com 
+      rt-users-request at lists.bestpractical.com
 
 with the body of the message consisting of only the word:
 

Modified: rt/3.8/branches/html-css-cleanup/README.Oracle
==============================================================================
--- rt/3.8/branches/html-css-cleanup/README.Oracle	(original)
+++ rt/3.8/branches/html-css-cleanup/README.Oracle	Wed Feb  4 10:23:40 2009
@@ -1,4 +1,4 @@
-Since RT 3.8.2 RT deployment on Oracle databaase has been simplified.
+Since RT 3.8.2 RT deployment on Oracle database has been simplified.
 
 You don't need to create anything beforehead. During installation
 an user is created and all RT's objects are created in his schema.

Modified: rt/3.8/branches/html-css-cleanup/etc/RT_Config.pm.in
==============================================================================
--- rt/3.8/branches/html-css-cleanup/etc/RT_Config.pm.in	(original)
+++ rt/3.8/branches/html-css-cleanup/etc/RT_Config.pm.in	Wed Feb  4 10:23:40 2009
@@ -600,12 +600,21 @@
 If you want to allow people to encrypt attachments inside the DB then
 set C<AllowEncryptDataInDB> to true
 
+Set C<RejectOnMissingPrivateKey> to false if you don't want to reject
+emails encrypted for key RT doesn't have and can not decrypt.
+
+Set C<RejectOnBadData> to false if you don't want to reject letters
+with incorrect GnuPG data.
+
 =cut
 
 Set( %GnuPG,
     Enable => @RT_GPG@,
     OutgoingMessagesFormat => 'RFC', # Inline
     AllowEncryptDataInDB   => 0,
+
+    RejectOnMissingPrivateKey => 1,
+    RejectOnBadData           => 1,
 );
 
 =item C<%GnuPGOptions>
@@ -683,6 +692,11 @@
 traces for messages with level equal to or greater than
 specified.
 
+NOTICE: Stack traces include parameters that functions or methods
+were called wiht. It is possible for stack trace logging to reveal
+sensitive information such as passwords or ticket content in your
+logs.
+
 =cut
 
 Set($LogStackTraces, '');
@@ -930,7 +944,7 @@
 
 =item C<$WebFallbackToInternalAuth>
 
-If C<$WebFallbackToInternalAuth> is undefined, the user is allowed a chance
+If C<$WebFallbackToInternalAuth> is defined, the user is allowed a chance
 of fallback to the login screen, even if REMOTE_USER failed.
 
 =cut
@@ -1284,6 +1298,7 @@
 be overridden by users in their preferences.
 Some examples:
 
+C<Set($DateTimeFormat, 'LocalizedDateTime');>
 C<Set($DateTimeFormat, { Format => 'ISO', Seconds => 0 });>
 C<Set($DateTimeFormat, 'RFC2822');>
 C<Set($DateTimeFormat, { Format => 'RFC2822', Seconds => 0, DayOfWeek => 0 });>

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Config.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Config.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Config.pm	Wed Feb  4 10:23:40 2009
@@ -110,7 +110,7 @@
  Overridable - Can users change this option
  SortOrder   - Within a Section, how should the options be sorted
                for display to the user
- Widget      - Mason component path to widget that should be user 
+ Widget      - Mason component path to widget that should be used 
                to display this config option
  WidgetArguments - An argument hash passed to the WIdget
     Description - Friendly description to show the user
@@ -275,7 +275,7 @@
             Callback => sub { my $ret = { Values => [], ValuesLabel => {}};
                               my $date = new RT::Date($HTML::Mason::Commands::session{'CurrentUser'});
                               $date->Set;
-                              foreach my $value (qw(DefaultFormat RFC2822 ISO W3CDTF)) { #loc_qw
+                              foreach my $value ($date->Formatters) {
                                  push @{$ret->{Values}}, $value;
                                  $ret->{ValuesLabel}{$value} = $date->$value();
                               }
@@ -447,8 +447,13 @@
     my $self = shift;
     my %args = ( File => '', @_ );
 
-    my $is_ext = $args{'File'} !~ /^RT_(?:Site)?Config/ ? 1 : 0;
-    my $is_site = $args{'File'} =~ /SiteConfig/ ? 1 : 0;
+    my ($is_ext, $is_site);
+    if ( $args{'File'} eq ($ENV{RT_SITE_CONFIG}||'') ) {
+        ($is_ext, $is_site) = ('', 1);
+    } else {
+        $is_ext = $args{'File'} =~ /^(?!RT_)(?:(.*)_)(?:Site)?Config/ ? $1 : '';
+        $is_site = $args{'File'} =~ /SiteConfig/ ? 1 : 0;
+    }
 
     eval {
         package RT;
@@ -555,10 +560,12 @@
 
 Takes name of the option as argument and returns its current value.
 
-In the case of a user-overridable option, first checks the user's preferences before looking for site-wide configuration.
-
-Returns values from RT_SiteConfig, RT_Config and then the %META hash of configuration variables's "Default" for this config variable, in that order.
+In the case of a user-overridable option, first checks the user's
+preferences before looking for site-wide configuration.
 
+Returns values from RT_SiteConfig, RT_Config and then the %META hash
+of configuration variables's "Default" for this config variable,
+in that order.
 
 Returns different things in scalar and array contexts. For scalar
 options it's not that important, however for arrays and hash it's.
@@ -614,7 +621,7 @@
         { no warnings 'once'; no strict 'refs'; @{"RT::$name"} = (@_); }
     } elsif ( $type eq 'HASH' ) {
         $OPTIONS{$name} = {@_};
-        { no warnings 'once';  no strict 'refs'; %{"RT::$name"} = (@_); }
+        { no warnings 'once'; no strict 'refs'; %{"RT::$name"} = (@_); }
     } else {
         $OPTIONS{$name} = shift;
         {no warnings 'once'; no strict 'refs'; ${"RT::$name"} = $OPTIONS{$name}; }
@@ -664,7 +671,41 @@
         $type = $META{$name}->{'Type'} || 'SCALAR';
     }
 
-    return 1 if exists $OPTIONS{$name} && !$args{'SiteConfig'};
+    # if option is already set we have to check where
+    # it comes from and may be ignore it
+    if ( exists $OPTIONS{$name} ) {
+        if ( $args{'SiteConfig'} && $args{'Extension'} ) {
+            # if it's site config of an extension then it can only
+            # override options that came from its main config
+            if ( $args{'Extension'} ne $META{$name}->{'Source'}{'Extension'} ) {
+                my %source = %{ $META{$name}->{'Source'} };
+                warn
+                    "Change of config option '$name' at $args{'File'} line $args{'Line'} has been ignored."
+                    ." This option earlier has been set in $source{'File'} line $source{'Line'}."
+                    ." To overide this option use ". ($source{'Extension'}||'RT')
+                    ." site config."
+                ;
+                return 1;
+            }
+        } elsif ( !$args{'SiteConfig'} && $META{$name}->{'Source'}{'SiteConfig'} ) {
+            # if it's core config then we can override any option that came from another
+            # core config, but not site config
+
+            my %source = %{ $META{$name}->{'Source'} };
+            if ( $source{'Extension'} ne $args{'Extension'} ) {
+                # as a site config is loaded earlier then its base config
+                # then we warn only on different extensions, for example
+                # RTIR's options is set in main site config or RTFM's
+                warn
+                    "Change of config option '$name' at $args{'File'} line $args{'Line'} has been ignored."
+                    ." It's may be ok, but we want you to be aware."
+                    ." This option earlier has been set in $source{'File'} line $source{'Line'}."
+                ;
+            }
+
+            return 1;
+        }
+    }
 
     $META{$name}->{'Type'} = $type;
     foreach (qw(Package File Line SiteConfig Extension)) {
@@ -757,31 +798,28 @@
 
 sub Options {
     my $self = shift;
-    my %args = ( Section => undef, Overridable => 1, @_ );
-    my @res  = sort { ($META{$a}->{SortOrder}||9999) <=> ($META{$b}->{SortOrder}||9999)
-                       || $a cmp $b } keys %META;
+    my %args = ( Section => undef, Overridable => 1, Sorted => 1, @_ );
+    my @res  = keys %META;
+    
     @res = grep( ( $META{$_}->{'Section'} || 'General' ) eq $args{'Section'},
-        @res )
-        if defined $args{'Section'};
+        @res 
+    ) if defined $args{'Section'};
+
     if ( defined $args{'Overridable'} ) {
         @res
             = grep( ( $META{$_}->{'Overridable'} || 0 ) == $args{'Overridable'},
             @res );
     }
-    return @res;
-}
 
-=head3 Type
-
-=cut
-
-sub Type {
-    my $self = shift;
-    my $name = shift;
+    if ( $args{'Sorted'} ) {
+        @res = sort {
+            ($META{$a}->{SortOrder}||9999) <=> ($META{$b}->{SortOrder}||9999)
+            || $a cmp $b 
+        } @res;
+    } else {
+        @res = sort { $a cmp $b } @res;
+    }
+    return @res;
 }
 
-=head3 IsOverridable
-
-=cut
-
 1;

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Crypt/GnuPG.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Crypt/GnuPG.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Crypt/GnuPG.pm	Wed Feb  4 10:23:40 2009
@@ -1269,6 +1269,9 @@
     while ( defined(my $str = $io->getline) ) {
         if ( $in_block && $str =~ /-----END PGP (?:MESSAGE|SIGNATURE)-----/ ) {
             print $block_fh $str;
+            $in_block--;
+            next if $in_block > 0;
+
             seek $block_fh, 0, 0;
 
             my ($res_fh, $res_fn);
@@ -1289,8 +1292,11 @@
             binmode $block_fh, ':raw';
             $in_block = 0;
         }
-        elsif ( $in_block || $str =~ /-----BEGIN PGP (SIGNED )?MESSAGE-----/ ) {
-            $in_block = 1;
+        elsif ( $str =~ /-----BEGIN PGP (SIGNED )?MESSAGE-----/ ) {
+            $in_block++;
+            print $block_fh $str;
+        }
+        elsif ( $in_block ) {
             print $block_fh $str;
         }
         else {

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Date.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Date.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Date.pm	Wed Feb  4 10:23:40 2009
@@ -109,6 +109,18 @@
     'Sat', # loc
 );
 
+our @FORMATTERS = (
+    'DefaultFormat', # loc
+    'ISO',           # loc
+    'W3CDTF',        # loc
+    'RFC2822',       # loc
+    'RFC2616',       # loc
+    'iCal',          # loc
+);
+if ( eval 'use DateTime qw(); 1;' && eval 'use DateTime::Locale qw(); 1;' ) {
+    push @FORMATTERS, 'LocalizedDateTime'; # loc
+}
+
 =head2 new
 
 Object constructor takes one argument C<RT::CurrentUser> object.
@@ -554,6 +566,19 @@
 in RFC2822 format day of time in output is optional so it
 understand boolean argument C<DayOfTime>.
 
+=head3 Formatters
+
+Returns an array of available formatters.
+
+=cut
+
+sub Formatters
+{
+    my $self = shift;
+
+    return @FORMATTERS;
+}
+
 =head3 DefaultFormat
 
 =cut
@@ -564,6 +589,7 @@
     my %args = ( Date => 1,
                  Time => 1,
                  Timezone => '',
+                 Seconds => 1,
                  @_,
                );
     
@@ -578,11 +604,87 @@
         return $self->loc('[_1] [_2] [_3] [_4]',
                           $wday,$mon,$mday,$year);
     } elsif( !$args{'Date'} && $args{'Time'} ) {
-        return $self->loc('[_1]:[_2]:[_3]',
-                          $hour,$min,$sec);
+        if( $args{'Seconds'} ) {
+            return $self->loc('[_1]:[_2]:[_3]',
+                              $hour,$min,$sec);
+        } else {
+            return $self->loc('[_1]:[_2]',
+                              $hour,$min);
+        }
+    } else {
+        if( $args{'Seconds'} ) {
+            return $self->loc('[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]',
+                              $wday,$mon,$mday,$hour,$min,$sec,$year);
+        } else {
+            return $self->loc('[_1] [_2] [_3] [_4]:[_5] [_6]',
+                              $wday,$mon,$mday,$hour,$min,$year);
+        }
+    }
+}
+
+=head3 LocalizedDateTime
+
+Returns date and time as string, with user localization.
+
+Supports arguments: C<DateFormat> and C<TimeFormat> which may contains date and
+time format as specified in DateTime::Locale (default to full_date_format and
+medium_time_format), C<AbbrDay> and C<AbbrMonth> which may be set to 0 if
+you want full Day/Month names instead of abbreviated ones.
+
+Require optionnal DateTime::Locale module.
+
+=cut
+
+sub LocalizedDateTime
+{
+    my $self = shift;
+    my %args = ( Date => 1,
+                 Time => 1,
+                 Timezone => '',
+                 DateFormat => 'full_date_format',
+                 TimeFormat => 'medium_time_format',
+                 AbbrDay => 1,
+                 AbbrMonth => 1,
+                 @_,
+               );
+
+    return $self->loc("DateTime module missing") unless ( eval 'use DateTime qw(); 1;' );
+    return $self->loc("DateTime::Locale module missing") unless ( eval 'use DateTime::Locale qw(); 1;' );
+    my $date_format = $args{'DateFormat'};
+    my $time_format = $args{'TimeFormat'};
+
+    my $lang = $self->CurrentUser->UserObj->Lang || 'en';
+
+    my $formatter = DateTime::Locale->load($lang);
+    $date_format = $formatter->$date_format;
+    $time_format = $formatter->$time_format;
+    $date_format =~ s/\%A/\%a/g if ( $args{'AbbrDay'} );
+    $date_format =~ s/\%B/\%b/g if ( $args{'AbbrMonth'} );
+
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
+                            $self->Localtime($args{'Timezone'});
+    $mon++;
+    my $tz = $self->Timezone($args{'Timezone'});
+
+    # FIXME : another way to call this module without conflict with local
+    # DateTime method?
+    my $dt = new DateTime::( locale => $lang,
+                            time_zone => $tz,
+                            year => $year,
+                            month => $mon,
+                            day => $mday,
+                            hour => $hour,
+                            minute => $min,
+                            second => $sec,
+                            nanosecond => 0,
+                          );
+
+    if ( $args{'Date'} && !$args{'Time'} ) {
+        return $dt->strftime($date_format);
+    } elsif ( !$args{'Date'} && $args{'Time'} ) {
+        return $dt->strftime($time_format);
     } else {
-        return $self->loc('[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]',
-                          $wday,$mon,$mday,$hour,$min,$sec,$year);
+        return $dt->strftime($date_format) . " " . $dt->strftime($time_format);
     }
 }
 

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/I18N/ru.po
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/I18N/ru.po	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/I18N/ru.po	Wed Feb  4 10:23:40 2009
@@ -26,7 +26,7 @@
 
 #: NOT FOUND IN SOURCE
 msgid " %1 saved."
-msgstr " %1 сохранен."
+msgstr " %1 сохранён."
 
 #: share/html/Elements/RT__Ticket/ColumnMap:259
 msgid " (no pubkey!)"
@@ -776,7 +776,7 @@
 
 #: share/html/Ticket/Create.html:149 share/html/Ticket/Update.html:122
 msgid "Add More Files"
-msgstr "Добавить еще файлы"
+msgstr "Добавить ещё файлы"
 
 #: NOT FOUND IN SOURCE
 msgid "Add Next State"
@@ -2215,10 +2215,6 @@
 msgid "Current Links"
 msgstr "Текущие связи"
 
-#: NOT FOUND IN SOURCE
-msgid "Current Relationships"
-msgstr "Текущие взаимосвязи"
-
 #: share/html/Admin/Elements/EditScrips:53
 msgid "Current Scrips"
 msgstr "Текущие скриплеты"
@@ -2829,10 +2825,6 @@
 msgid "Edit Query"
 msgstr "Редактировать запрос"
 
-#: NOT FOUND IN SOURCE
-msgid "Edit Relationships"
-msgstr "Редактировать взаимосвязи"
-
 #: share/html/Ticket/Elements/Tabs:265
 msgid "Edit Search"
 msgstr "Редактировать запрос"
@@ -3098,10 +3090,6 @@
 msgid "Escalate tickets"
 msgstr "Эскалировать приоритет заявки"
 
-#: NOT FOUND IN SOURCE
-msgid "Estimate"
-msgstr "Расчётно"
-
 #: share/html/Ticket/Elements/ShowBasics:59
 msgid "Estimated"
 msgstr "Расчётное"
@@ -3429,7 +3417,7 @@
 #: share/html/Admin/Global/MyRT.html:95
 #. ($pane)
 msgid "Global portlet %1 saved."
-msgstr "Общий портлет %1 сохранен."
+msgstr "Общий портлет %1 сохранён."
 
 #: share/html/Admin/Elements/SelectTemplate:61
 #. (loc($Template->Name))
@@ -3807,10 +3795,6 @@
 msgid "Invalid Right"
 msgstr "Недопустимое право"
 
-#: NOT FOUND IN SOURCE
-msgid "Invalid Type"
-msgstr "Недопустимый тип"
-
 #: lib/RT/Record.pm:926
 msgid "Invalid data"
 msgstr "Недопустимые данные"
@@ -3823,10 +3807,6 @@
 msgid "Invalid owner object"
 msgstr "Неверный объект ответственного"
 
-#: NOT FOUND IN SOURCE
-msgid "Invalid owner. Defaulting to 'nobody'."
-msgstr "Несуществующий ответственный. Используем 'nobody'."
-
 #: lib/RT/CustomField_Overlay.pm:208 lib/RT/CustomField_Overlay.pm:597
 #. ($msg)
 msgid "Invalid pattern: %1"
@@ -3942,10 +3922,6 @@
 msgid "Language"
 msgstr "Язык"
 
-#: share/html/Install/index.html:56
-msgid "Language."
-msgstr "Язык."
-
 #: share/html/Search/Elements/EditFormat:76
 msgid "Large"
 msgstr "Большой"
@@ -3958,18 +3934,10 @@
 msgid "Last Contact"
 msgstr "Последний контакт"
 
-#: NOT FOUND IN SOURCE
-msgid "Last Contact</a>"
-msgstr "Последний контакт</a>"
-
 #: share/html/Elements/SelectDateType:52
 msgid "Last Contacted"
 msgstr "Последний контакт"
 
-#: NOT FOUND IN SOURCE
-msgid "Last Notified"
-msgstr "Последнее уведомление"
-
 #: share/html/Elements/ColumnMap:81 share/html/Elements/ColumnMap:86 share/html/Elements/SelectDateType:53
 msgid "Last Updated"
 msgstr "Последнее изменение"
@@ -4029,7 +3997,7 @@
 
 #: share/html/Install/index.html:86
 msgid "Let's go!"
-msgstr "Вперед!"
+msgstr "Вперёд!"
 
 #: NOT FOUND IN SOURCE
 msgid "Limiting owner to %1 %2"
@@ -4834,14 +4802,6 @@
 msgid "New Pending Approval"
 msgstr "Новое ожидающее подтверждение"
 
-#: NOT FOUND IN SOURCE
-msgid "New Query"
-msgstr "Новый запрос"
-
-#: NOT FOUND IN SOURCE
-msgid "New Relationships"
-msgstr "Новые связи"
-
 #: share/html/Ticket/Elements/Tabs:261
 msgid "New Search"
 msgstr "Новый поиск"
@@ -4931,18 +4891,6 @@
 msgid "Next"
 msgstr "Вперёд"
 
-#: NOT FOUND IN SOURCE
-msgid "Next Page"
-msgstr "Следующая страница"
-
-#: NOT FOUND IN SOURCE
-msgid "Next page"
-msgstr "Следующая страница"
-
-#: NOT FOUND IN SOURCE
-msgid "Next: "
-msgstr "Вперёд: "
-
 #: share/html/Elements/RT__User/ColumnMap:71
 msgid "NickName"
 msgstr "Псевдоним"
@@ -5186,10 +5134,6 @@
 msgid "Not found"
 msgstr "Не найдено"
 
-#: NOT FOUND IN SOURCE
-msgid "Not logged in"
-msgstr "Не вошли в систему"
-
 #: share/html/Elements/Header:97
 msgid "Not logged in."
 msgstr "Не вошли в систему."
@@ -5627,7 +5571,7 @@
 
 #: lib/RT/User_Overlay.pm:922
 msgid "Password: Permission Denied"
-msgstr "Пароль: доступ запрещен"
+msgstr "Пароль: доступ запрещён"
 
 #: etc/initialdata:427
 msgid "PasswordChange"
@@ -5669,10 +5613,6 @@
 msgid "Permission denied"
 msgstr "Доступ запрещён"
 
-#: NOT FOUND IN SOURCE
-msgid "Permissions denied"
-msgstr "Доступы запрещены"
-
 #: share/html/Dashboards/index.html:56
 msgid "Personal Dashboards"
 msgstr ""
@@ -5699,7 +5639,7 @@
 
 #: share/html/dhandler:51
 msgid "Please check the URL and try again."
-msgstr "Проверьте, пожалуйста, адрес и попробуйте еще раз."
+msgstr "Проверьте, пожалуйста, адрес и попробуйте ещё раз."
 
 #: share/html/Dashboards/Elements/HiddenSearches:68
 msgid "Possible hidden searches"
@@ -6016,10 +5956,6 @@
 msgid "RT Self Service"
 msgstr "Ваш RT"
 
-#: NOT FOUND IN SOURCE
-msgid "RT Self Service / Closed Tickets"
-msgstr "Ваш RT / Закрытые заявки"
-
 #: share/html/Admin/Tools/Configuration.html:94
 msgid "RT Variables"
 msgstr "Переменные RT"
@@ -6212,10 +6148,6 @@
 msgid "Refresh this page every %1 minutes."
 msgstr "Обновлять эту страницу каждые %1 минут."
 
-#: NOT FOUND IN SOURCE
-msgid "Relationships"
-msgstr "Взаимосвязи"
-
 #: lib/RT/Transaction_Overlay.pm:833
 #. ($ticket->Subject)
 msgid "Reminder '%1' added"
@@ -6781,10 +6713,6 @@
 msgid "Selections modified. Please save your changes"
 msgstr "Выбор изменён. Пожалуйста, сохраните ваши изменения"
 
-#: NOT FOUND IN SOURCE
-msgid "Self Service"
-msgstr "Ваш RT"
-
 #: lib/RT/Interface/Email.pm:655
 msgid "Send email successfully"
 msgstr "Почта успешно отправлена"
@@ -7854,18 +7782,10 @@
 msgid "Time Worked"
 msgstr "Время в работе"
 
-#: NOT FOUND IN SOURCE
-msgid "Time left"
-msgstr "Осталось времени"
-
 #: share/html/Elements/Footer:54
 msgid "Time to display"
 msgstr "Время генерации страницы"
 
-#: NOT FOUND IN SOURCE
-msgid "Time worked"
-msgstr "Время в работе"
-
 #: lib/RT/Graph/Tickets.pm:157 lib/RT/Tickets_Overlay.pm:107 share/html/Search/Elements/BuildFormatString:100
 msgid "TimeEstimated"
 msgstr "РасчётноеВремя"
@@ -8626,7 +8546,7 @@
 
 #: share/html/Install/index.html:79
 msgid "You're seeing this screen because you started up an RT server without a working database. Most likely, this is the first time you're running RT.  If you click <i>Let's go!</i> below, RT will guide you through setting up your RT server and database."
-msgstr "Вы видите этот экран так как сервер RT запущен без настроенной БД. Вероятнее всего это первый запуск RT. Если вы нажмёте <i>Поехали!</i> ниже, система установки RT проведёт Вас  через все шаги, необходимые для настройки сервера и БД RT."
+msgstr "Вы видите этот экран так как сервер RT запущен без настроенной БД. Вероятнее всего это первый запуск RT. Если вы нажмёте <i>Поехали!</i> ниже, система установки RT проведёт Вас через все шаги, необходимые для настройки сервера и БД RT."
 
 #: share/html/NoAuth/Logout.html:59
 msgid "You're welcome to login again"
@@ -8983,7 +8903,7 @@
 #: lib/RT/Group_Overlay.pm:150
 #. ($self->Name, $user->Name)
 msgid "personal group '%1' for user '%2'"
-msgstr "пользовательская группа '%1' для пользователя '%2'"
+msgstr "пользовательская группа '%1' пользователя '%2'"
 
 #: share/html/Search/Elements/SelectChartType:55
 msgid "pie"

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Interface/Email/Auth/GnuPG.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Interface/Email/Auth/GnuPG.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Interface/Email/Auth/GnuPG.pm	Wed Feb  4 10:23:40 2009
@@ -149,13 +149,13 @@
         unless ( $sent_once{'NoPrivateKey'} ) {
             unless ( CheckNoPrivateKey( Message => $args{'Message'}, Status => \@status ) ) {
                 $sent_once{'NoPrivateKey'}++;
-                $reject = 1;
+                $reject = 1 if RT->Config->Get('GnuPG')->{'RejectOnMissingPrivateKey'};
             }
         }
         unless ( $sent_once{'BadData'} ) {
             unless ( CheckBadData( Message => $args{'Message'}, Status => \@status ) ) {
                 $sent_once{'BadData'}++;
-                $reject = 1;
+                $reject = 1 if RT->Config->Get('GnuPG')->{'RejectOnBadData'};
             }
         }
     }

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Queue_Overlay.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Queue_Overlay.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Queue_Overlay.pm	Wed Feb  4 10:23:40 2009
@@ -399,7 +399,7 @@
     my $val = shift;
 
     $RT::Handle->BeginTransaction();
-    my $set_err = $self->PrincipalObj->SetDisabled($val);
+    my $set_err = $self->SUPER::SetDisabled($val);
     unless ($set_err) {
         $RT::Handle->Rollback();
         $RT::Logger->warning("Couldn't ".($val == 1) ? "disable" : "enable"." queue ".$self->PrincipalObj->Id);

Modified: rt/3.8/branches/html-css-cleanup/lib/RT/Report/Tickets.pm
==============================================================================
--- rt/3.8/branches/html-css-cleanup/lib/RT/Report/Tickets.pm	(original)
+++ rt/3.8/branches/html-css-cleanup/lib/RT/Report/Tickets.pm	Wed Feb  4 10:23:40 2009
@@ -190,24 +190,25 @@
     } elsif ( $field =~ /^(?:(Owner|Creator|LastUpdatedBy))(?:\.(.*))?$/ ) {
         my $type = $1 || '';
         my $column = $2 || 'Name';
-        my $u_alias = $self->Join(
-            TYPE   => 'LEFT',
-            ALIAS1 => 'main',
-            FIELD1 => $type,
-            TABLE2 => 'Users',
-            FIELD2 => 'id',
-        );
+        my $u_alias = $self->{"_sql_report_${type}_users_${column}"}
+            ||= $self->Join(
+                TYPE   => 'LEFT',
+                ALIAS1 => 'main',
+                FIELD1 => $type,
+                TABLE2 => 'Users',
+                FIELD2 => 'id',
+            );
         @args{qw(ALIAS FIELD)} = ($u_alias, $column);
     } elsif ( $field =~ /^(?:Watcher|(Requestor|Cc|AdminCc))(?:\.(.*))?$/ ) {
         my $type = $1 || '';
         my $column = $2 || 'Name';
-        if ( my $u_alias = $self->{"_sql_report_watcher_users_alias_$type"} ) {
-            @args{qw(ALIAS FIELD)} = ($u_alias, $column);
-        } else {
-            my ($g_alias, $gm_alias, $u_alias) = $self->_WatcherJoin( $type );
-            @args{qw(ALIAS FIELD)} = ($u_alias, $column);
+        my $u_alias = $self->{"_sql_report_watcher_users_alias_$type"};
+        unless ( $u_alias ) {
+            my ($g_alias, $gm_alias);
+            ($g_alias, $gm_alias, $u_alias) = $self->_WatcherJoin( $type );
             $self->{"_sql_report_watcher_users_alias_$type"} = $u_alias;
         }
+        @args{qw(ALIAS FIELD)} = ($u_alias, $column);
     }
     return %args;
 }

Modified: rt/3.8/branches/html-css-cleanup/share/html/Admin/Tools/Configuration.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Admin/Tools/Configuration.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Admin/Tools/Configuration.html	Wed Feb  4 10:23:40 2009
@@ -71,23 +71,41 @@
 
 <h2><&|/l&>RT Config</&></h2>
 <table>
-% foreach my $key ( RT->Config->Options( Overridable => undef ) ) {
-% my $val = RT->Config->Get( $key );
-% next unless defined $val;
-<tr><td><% $key %></td>
-<td>
+<%PERL>
+foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
+    my $val = RT->Config->Get( $key );
+    next unless defined $val;
+
+    my $meta = RT->Config->Meta( $key );
+    my $description = '';
+    if ( $meta->{'Source'}{'Extension'} && $meta->{'Source'}{'SiteConfig'} ) {
+        $description = loc("[_1] site config", $meta->{'Source'}{'Extension'});
+    }
+    elsif ( $meta->{'Source'}{'Extension'} ) {
+        $description = loc("[_1] core config", $meta->{'Source'}{'Extension'});
+    }
+    elsif ( $meta->{'Source'}{'SiteConfig'} ) {
+        $description = loc("site config");
+    }
+    else {
+        $description = loc("core config");
+    }
+</%PERL>
+<tr><th><% $key %></th>
+<td rowspan="2">\
 % if ( $key =~ /Password(?!Length)/i ) { 
-<em>Password not printed</em>
+<em>Password not printed</em>\
 % } elsif ( !ref $val ) {
-<% $val %>
+<% "$val" %>\
 % } elsif ( ref $val eq 'ARRAY' ) {
-<% join ', ', @$val %>
+<% join ', ', @$val %>\
 % } elsif ( ref $val eq 'HASH' ) {
-<% join ', ', %$val %>
+<% join ', ', %$val %>\
 % } else {
-<% ref $val %>
+<% ref $val %>\
 % }
 </td></tr>
+<tr><td><% $description %></td></tr>
 % }
 </table>
 
@@ -97,7 +115,7 @@
 % my %config_opt = map { $_ => 1 } RT->Config->Options( Overridable => undef );
 % foreach my $key ( sort keys %{*RT::} ) {
 % next if !${'RT::'.$key} || ref ${'RT::'.$key} || $config_opt{ $key };
-<tr><td>RT::<% $key %></td>
+<tr><th>RT::<% $key %></th>
 <td>
 % if ( $key =~ /Password(?!Length)/i ) { 
 <em>Password not printed</em>

Modified: rt/3.8/branches/html-css-cleanup/share/html/Dashboards/Queries.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Dashboards/Queries.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Dashboards/Queries.html	Wed Feb  4 10:23:40 2009
@@ -74,8 +74,6 @@
 % }
 </table>
 
-</form>
-
 <%INIT>
 
 my $current_subtab = 'Dashboards/Queries.html?id=' . $id;

Modified: rt/3.8/branches/html-css-cleanup/share/html/Elements/EditCustomFieldAutocomplete
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Elements/EditCustomFieldAutocomplete	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Elements/EditCustomFieldAutocomplete	Wed Feb  4 10:23:40 2009
@@ -51,7 +51,7 @@
 new Ajax.Autocompleter(
     "<% $name %>-Values",
     "<% $name %>-Choices",
-    "/Helpers/Autocomplete/CustomFieldValues",
+    <% RT->Config->Get('WebPath')%>"/Helpers/Autocomplete/CustomFieldValues",
     { tokens: [ '\n' ] }
 );
 % } else {
@@ -60,7 +60,7 @@
 new Ajax.Autocompleter(
     "<% $name %>-Value",
     "<% $name %>-Choices",
-    "/Helpers/Autocomplete/CustomFieldValues",
+    <% RT->Config->Get('WebPath')%>"/Helpers/Autocomplete/CustomFieldValues",
     {}
 );
 % }

Modified: rt/3.8/branches/html-css-cleanup/share/html/Elements/RT__Ticket/ColumnMap
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Elements/RT__Ticket/ColumnMap	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Elements/RT__Ticket/ColumnMap	Wed Feb  4 10:23:40 2009
@@ -175,7 +175,7 @@
         value     => sub { return $_[0]->Cc->MemberEmailAddressesAsString }
     },
     AdminCc => {
-        loc       => ('AdminCc'),
+        title     => 'AdminCc', # loc
         attribute => 'AdminCc.EmailAddress',
         value     => sub { return $_[0]->AdminCc->MemberEmailAddressesAsString }
     },

Modified: rt/3.8/branches/html-css-cleanup/share/html/Elements/ShowLinks
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Elements/ShowLinks	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Elements/ShowLinks	Wed Feb  4 10:23:40 2009
@@ -88,13 +88,7 @@
   </tr>
   <tr>
     <td class="labeltop"><&|/l&>Parents</&>: (<a href="<%$clone->{'MemberOf-new'}%>"><% loc('Create') %></a>)</td>
-    <td class="value">
-<ul>
-% while (my $Link = $Ticket->MemberOf->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &></li>
-% }
-</ul>
-    </td>
+    <td class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></td>
   </tr>
   <tr>
     <td class="labeltop"><&|/l&>Children</&>: (<a href="<%$clone->{'new-MemberOf'}%>"><% loc('Create') %></a>)</td>

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/Basics.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/Basics.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/Basics.html	Wed Feb  4 10:23:40 2009
@@ -60,7 +60,7 @@
 <input type="hidden" name="Run" value="1">
 <& /Elements/Submit,
     Label => loc('Next') .': '. loc('Customize Email Configuration'),
-    Back => 1, BackLabel => loc('Back') .': '. loc('Customize Database Details'),
+    Back => 1, BackLabel => loc('Back') .': '. loc('Check Database Credentials'),
 &>
 </form>
 </&>

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseDetails.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseDetails.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseDetails.html	Wed Feb  4 10:23:40 2009
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-<&| Elements/Wrapper, Title => loc('Step [_1] of [_2]', 2, 7 ) .': '. loc('Check your database credentials') &> 
+<&| Elements/Wrapper, Title => loc('Step [_1] of [_2]', 2, 7 ) .': '. loc('Check Database Credentials') &> 
 
 % if ( @errors ) {
 <& Elements/Errors, Errors => \@errors &>
@@ -86,7 +86,7 @@
                     &>
 <& /Elements/Submit,
     Label => loc('Check Database Connectivity'),
-    Back => 1, BackLabel => loc('Back') .': '. loc('Select Database Type'),
+    Back => 1, BackLabel => loc('Back') .': '. loc('Choose Database Engine'),
 &>
 % }
 

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseType.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseType.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/DatabaseType.html	Wed Feb  4 10:23:40 2009
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-<&|Elements/Wrapper, Title => loc('Step [_1] of [_2]', 1, 7 ) .': '. loc('Choose a database engine') &>
+<&|Elements/Wrapper, Title => loc('Step [_1] of [_2]', 1, 7 ) .': '. loc('Choose Database Engine') &>
 
 <div class="intro">
 <p>
@@ -67,7 +67,7 @@
     CurrentValue => RT::Installer->CurrentValues(@Types) &>
 
 <input type="hidden" name="Run" value="1">
-<& /Elements/Submit, Label => loc('Next') .': '. loc( 'Check your database credentials') &>
+<& /Elements/Submit, Label => loc('Next') .': '. loc( 'Check Database Credentials') &>
 </form>
 </&>
 <%init>

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/Initialize.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/Initialize.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/Initialize.html	Wed Feb  4 10:23:40 2009
@@ -59,7 +59,7 @@
 <input type="hidden" name="Run" value="1">
 <& /Elements/Submit,
     Label => loc('Initialize Database'),
-    Back => 1, BackLabel => loc('Back') .': '. loc('Customize Global'),
+    Back => 1, BackLabel => loc('Back') .': '. loc('Customize Email Addresses'),
 &>
 </form>
 </&>

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/Sendmail.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/Sendmail.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/Sendmail.html	Wed Feb  4 10:23:40 2009
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-<&|Elements/Wrapper, Title => loc('Step [_1] of [_2]', 4, 7 ) .': '. loc('Email Configuration') &> 
+<&|Elements/Wrapper, Title => loc('Step [_1] of [_2]', 4, 7 ) .': '. loc('Customize Email Configuration') &> 
 
 <& Elements/Errors, Errors => \@errors &>
 
@@ -60,7 +60,7 @@
 
 <input type="hidden" name="Run" value="1">
 <& /Elements/Submit,
-    Label => loc('Next') .': '. loc('Customize Global Defaults'),
+    Label => loc('Next') .': '. loc('Customize Email Addresses'),
     Back => 1, BackLabel => loc('Back' ) .': '. loc('Customize Basics'),
 &>
 </form>
@@ -88,8 +88,8 @@
 
     if ( ! $ARGS{OwnerEmail} || $ARGS{OwnerEmail} !~ /.+ at .+/ ) {
         push @errors,
-            loc("Invalid [_1]: that doesn't look like an email address",
-            'Administrator Email');
+            loc("Invalid [_1]: '[_2]' doesn't look like an email address",
+            'Administrator Email', $ARGS{OwnerEmail} );
     }
 
 

Modified: rt/3.8/branches/html-css-cleanup/share/html/Install/index.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Install/index.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Install/index.html	Wed Feb  4 10:23:40 2009
@@ -53,7 +53,7 @@
 
 <form method="post">
 
-<h1><% loc('Language.') %></h1>
+<h1><% loc('Language') %></h1>
 
 <div class="select-lang">
 <&|/l&>Select another language</&>:

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/msie6.css
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/msie6.css	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/msie6.css	Wed Feb  4 10:23:40 2009
@@ -75,3 +75,8 @@
     width: 100%;
 }
 
+.ticket-transaction .messagebody img {
+    /* ie6 does not support max-width */
+    width: expression(this.width > 401 ? 400 : true);
+}
+

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/ticket.css
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/ticket.css	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/css/web2/ticket.css	Wed Feb  4 10:23:40 2009
@@ -133,6 +133,10 @@
  min-height: 2.5em;
 }
 
+.ticket-transaction .messagebody img {
+ max-width: 100%;
+}
+
 div#ticket-history div.downloadattachment {
 float: right;
 clear: both;

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/prototype/prototype.js
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/prototype/prototype.js	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/prototype/prototype.js	Wed Feb  4 10:23:40 2009
@@ -1,5 +1,5 @@
-/*  Prototype JavaScript framework, version 1.6.0
- *  (c) 2005-2007 Sam Stephenson
+/*  Prototype JavaScript framework, version 1.6.0.3
+ *  (c) 2005-2008 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://www.prototypejs.org/
@@ -7,23 +7,26 @@
  *--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.6.0',
+  Version: '1.6.0.3',
 
   Browser: {
-    IE:     !!(window.attachEvent && !window.opera),
-    Opera:  !!window.opera,
+    IE:     !!(window.attachEvent &&
+      navigator.userAgent.indexOf('Opera') === -1),
+    Opera:  navigator.userAgent.indexOf('Opera') > -1,
     WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
-    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
+      navigator.userAgent.indexOf('KHTML') === -1,
     MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
   },
 
   BrowserFeatures: {
     XPath: !!document.evaluate,
+    SelectorsAPI: !!document.querySelector,
     ElementExtensions: !!window.HTMLElement,
     SpecificElementExtensions:
-      document.createElement('div').__proto__ &&
-      document.createElement('div').__proto__ !==
-        document.createElement('form').__proto__
+      document.createElement('div')['__proto__'] &&
+      document.createElement('div')['__proto__'] !==
+        document.createElement('form')['__proto__']
   },
 
   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
@@ -36,8 +39,6 @@
 if (Prototype.Browser.MobileSafari)
   Prototype.BrowserFeatures.SpecificElementExtensions = false;
 
-if (Prototype.Browser.WebKit)
-  Prototype.BrowserFeatures.XPath = false;
 
 /* Based on Alex Arnell's inheritance implementation. */
 var Class = {
@@ -85,12 +86,13 @@
       var property = properties[i], value = source[property];
       if (ancestor && Object.isFunction(value) &&
           value.argumentNames().first() == "$super") {
-        var method = value, value = Object.extend((function(m) {
+        var method = value;
+        value = (function(m) {
           return function() { return ancestor[m].apply(this, arguments) };
-        })(property).wrap(method), {
-          valueOf:  function() { return method },
-          toString: function() { return method.toString() }
-        });
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
       }
       this.prototype[property] = value;
     }
@@ -110,9 +112,9 @@
 Object.extend(Object, {
   inspect: function(object) {
     try {
-      if (object === undefined) return 'undefined';
+      if (Object.isUndefined(object)) return 'undefined';
       if (object === null) return 'null';
-      return object.inspect ? object.inspect() : object.toString();
+      return object.inspect ? object.inspect() : String(object);
     } catch (e) {
       if (e instanceof RangeError) return '...';
       throw e;
@@ -135,7 +137,7 @@
     var results = [];
     for (var property in object) {
       var value = Object.toJSON(object[property]);
-      if (value !== undefined)
+      if (!Object.isUndefined(value))
         results.push(property.toJSON() + ': ' + value);
     }
 
@@ -169,11 +171,12 @@
   },
 
   isElement: function(object) {
-    return object && object.nodeType == 1;
+    return !!(object && object.nodeType == 1);
   },
 
   isArray: function(object) {
-    return object && object.constructor === Array;
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
   },
 
   isHash: function(object) {
@@ -199,12 +202,13 @@
 
 Object.extend(Function.prototype, {
   argumentNames: function() {
-    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+      .replace(/\s+/g, '').split(',');
     return names.length == 1 && !names[0] ? [] : names;
   },
 
   bind: function() {
-    if (arguments.length < 2 && arguments[0] === undefined) return this;
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
     var __method = this, args = $A(arguments), object = args.shift();
     return function() {
       return __method.apply(object, args.concat($A(arguments)));
@@ -233,6 +237,11 @@
     }, timeout);
   },
 
+  defer: function() {
+    var args = [0.01].concat($A(arguments));
+    return this.delay.apply(this, args);
+  },
+
   wrap: function(wrapper) {
     var __method = this;
     return function() {
@@ -249,8 +258,6 @@
   }
 });
 
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
-
 Date.prototype.toJSON = function() {
   return '"' + this.getUTCFullYear() + '-' +
     (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
@@ -351,7 +358,7 @@
 
   sub: function(pattern, replacement, count) {
     replacement = this.gsub.prepareReplacement(replacement);
-    count = count === undefined ? 1 : count;
+    count = Object.isUndefined(count) ? 1 : count;
 
     return this.gsub(pattern, function(match) {
       if (--count < 0) return match[0];
@@ -366,7 +373,7 @@
 
   truncate: function(length, truncation) {
     length = length || 30;
-    truncation = truncation === undefined ? '...' : truncation;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
     return this.length > length ?
       this.slice(0, length - truncation.length) + truncation : String(this);
   },
@@ -486,7 +493,9 @@
   },
 
   isJSON: function() {
-    var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
     return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
   },
 
@@ -529,7 +538,7 @@
     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
   },
   unescapeHTML: function() {
-    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
   }
 });
 
@@ -546,7 +555,7 @@
   text: document.createTextNode('')
 });
 
-with (String.prototype.escapeHTML) div.appendChild(text);
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
 
 var Template = Class.create({
   initialize: function(template, pattern) {
@@ -565,7 +574,8 @@
       if (before == '\\') return match[2];
 
       var ctx = object, expr = match[3];
-      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
       if (match == null) return before;
 
       while (match != null) {
@@ -577,7 +587,7 @@
       }
 
       return before + String.interpret(ctx);
-    }.bind(this));
+    });
   }
 });
 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
@@ -587,10 +597,9 @@
 var Enumerable = {
   each: function(iterator, context) {
     var index = 0;
-    iterator = iterator.bind(context);
     try {
       this._each(function(value) {
-        iterator(value, index++);
+        iterator.call(context, value, index++);
       });
     } catch (e) {
       if (e != $break) throw e;
@@ -599,47 +608,46 @@
   },
 
   eachSlice: function(number, iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
     var index = -number, slices = [], array = this.toArray();
+    if (number < 1) return array;
     while ((index += number) < array.length)
       slices.push(array.slice(index, index+number));
     return slices.collect(iterator, context);
   },
 
   all: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result = true;
     this.each(function(value, index) {
-      result = result && !!iterator(value, index);
+      result = result && !!iterator.call(context, value, index);
       if (!result) throw $break;
     });
     return result;
   },
 
   any: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result = false;
     this.each(function(value, index) {
-      if (result = !!iterator(value, index))
+      if (result = !!iterator.call(context, value, index))
         throw $break;
     });
     return result;
   },
 
   collect: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push(iterator.call(context, value, index));
     });
     return results;
   },
 
   detect: function(iterator, context) {
-    iterator = iterator.bind(context);
     var result;
     this.each(function(value, index) {
-      if (iterator(value, index)) {
+      if (iterator.call(context, value, index)) {
         result = value;
         throw $break;
       }
@@ -648,17 +656,16 @@
   },
 
   findAll: function(iterator, context) {
-    iterator = iterator.bind(context);
     var results = [];
     this.each(function(value, index) {
-      if (iterator(value, index))
+      if (iterator.call(context, value, index))
         results.push(value);
     });
     return results;
   },
 
   grep: function(filter, iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var results = [];
 
     if (Object.isString(filter))
@@ -666,7 +673,7 @@
 
     this.each(function(value, index) {
       if (filter.match(value))
-        results.push(iterator(value, index));
+        results.push(iterator.call(context, value, index));
     });
     return results;
   },
@@ -686,7 +693,7 @@
   },
 
   inGroupsOf: function(number, fillWith) {
-    fillWith = fillWith === undefined ? null : fillWith;
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
     return this.eachSlice(number, function(slice) {
       while(slice.length < number) slice.push(fillWith);
       return slice;
@@ -694,9 +701,8 @@
   },
 
   inject: function(memo, iterator, context) {
-    iterator = iterator.bind(context);
     this.each(function(value, index) {
-      memo = iterator(memo, value, index);
+      memo = iterator.call(context, memo, value, index);
     });
     return memo;
   },
@@ -709,32 +715,32 @@
   },
 
   max: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = iterator(value, index);
-      if (result == undefined || value >= result)
+      value = iterator.call(context, value, index);
+      if (result == null || value >= result)
         result = value;
     });
     return result;
   },
 
   min: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = iterator(value, index);
-      if (result == undefined || value < result)
+      value = iterator.call(context, value, index);
+      if (result == null || value < result)
         result = value;
     });
     return result;
   },
 
   partition: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var trues = [], falses = [];
     this.each(function(value, index) {
-      (iterator(value, index) ?
+      (iterator.call(context, value, index) ?
         trues : falses).push(value);
     });
     return [trues, falses];
@@ -749,19 +755,20 @@
   },
 
   reject: function(iterator, context) {
-    iterator = iterator.bind(context);
     var results = [];
     this.each(function(value, index) {
-      if (!iterator(value, index))
+      if (!iterator.call(context, value, index))
         results.push(value);
     });
     return results;
   },
 
   sortBy: function(iterator, context) {
-    iterator = iterator.bind(context);
     return this.map(function(value, index) {
-      return {value: value, criteria: iterator(value, index)};
+      return {
+        value: value,
+        criteria: iterator.call(context, value, index)
+      };
     }).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
       return a < b ? -1 : a > b ? 1 : 0;
@@ -805,20 +812,24 @@
 function $A(iterable) {
   if (!iterable) return [];
   if (iterable.toArray) return iterable.toArray();
-  var length = iterable.length, results = new Array(length);
+  var length = iterable.length || 0, results = new Array(length);
   while (length--) results[length] = iterable[length];
   return results;
 }
 
 if (Prototype.Browser.WebKit) {
-  function $A(iterable) {
+  $A = function(iterable) {
     if (!iterable) return [];
-    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
-        iterable.toArray) return iterable.toArray();
-    var length = iterable.length, results = new Array(length);
+    // In Safari, only use the `toArray` method if it's not a NodeList.
+    // A NodeList is a function, has an function `item` property, and a numeric
+    // `length` property. Adapted from Google Doctype.
+    if (!(typeof iterable === 'function' && typeof iterable.length ===
+        'number' && typeof iterable.item === 'function') && iterable.toArray)
+      return iterable.toArray();
+    var length = iterable.length || 0, results = new Array(length);
     while (length--) results[length] = iterable[length];
     return results;
-  }
+  };
 }
 
 Array.from = $A;
@@ -904,7 +915,7 @@
     var results = [];
     this.each(function(object) {
       var value = Object.toJSON(object);
-      if (value !== undefined) results.push(value);
+      if (!Object.isUndefined(value)) results.push(value);
     });
     return '[' + results.join(', ') + ']';
   }
@@ -961,8 +972,8 @@
     return this + 1;
   },
 
-  times: function(iterator) {
-    $R(0, this, true).each(iterator);
+  times: function(iterator, context) {
+    $R(0, this, true).each(iterator, context);
     return this;
   },
 
@@ -984,34 +995,6 @@
 };
 
 var Hash = Class.create(Enumerable, (function() {
-  if (function() {
-    var i = 0, Test = function(value) { this.key = value };
-    Test.prototype.key = 'foo';
-    for (var property in new Test('bar')) i++;
-    return i > 1;
-  }()) {
-    function each(iterator) {
-      var cache = [];
-      for (var key in this._object) {
-        var value = this._object[key];
-        if (cache.include(key)) continue;
-        cache.push(key);
-        var pair = [key, value];
-        pair.key = key;
-        pair.value = value;
-        iterator(pair);
-      }
-    }
-  } else {
-    function each(iterator) {
-      for (var key in this._object) {
-        var value = this._object[key], pair = [key, value];
-        pair.key = key;
-        pair.value = value;
-        iterator(pair);
-      }
-    }
-  }
 
   function toQueryPair(key, value) {
     if (Object.isUndefined(value)) return key;
@@ -1023,14 +1006,23 @@
       this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
     },
 
-    _each: each,
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
 
     set: function(key, value) {
       return this._object[key] = value;
     },
 
     get: function(key) {
-      return this._object[key];
+      // simulating poorly supported hasOwnProperty
+      if (this._object[key] !== Object.prototype[key])
+        return this._object[key];
     },
 
     unset: function(key) {
@@ -1070,14 +1062,14 @@
     },
 
     toQueryString: function() {
-      return this.map(function(pair) {
+      return this.inject([], function(results, pair) {
         var key = encodeURIComponent(pair.key), values = pair.value;
 
         if (values && typeof values == 'object') {
           if (Object.isArray(values))
-            return values.map(toQueryPair.curry(key)).join('&');
-        }
-        return toQueryPair(key, values);
+            return results.concat(values.map(toQueryPair.curry(key)));
+        } else results.push(toQueryPair(key, values));
+        return results;
       }).join('&');
     },
 
@@ -1187,8 +1179,11 @@
     Object.extend(this.options, options || { });
 
     this.options.method = this.options.method.toLowerCase();
+
     if (Object.isString(this.options.parameters))
       this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
   }
 });
 
@@ -1315,7 +1310,7 @@
 
       var contentType = response.getHeader('Content-type');
       if (this.options.evalJS == 'force'
-          || (this.options.evalJS && contentType
+          || (this.options.evalJS && this.isSameOrigin() && contentType
           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
         this.evalResponse();
     }
@@ -1333,9 +1328,18 @@
     }
   },
 
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
   getHeader: function(name) {
     try {
-      return this.transport.getResponseHeader(name);
+      return this.transport.getResponseHeader(name) || null;
     } catch (e) { return null }
   },
 
@@ -1371,7 +1375,7 @@
 
     if(readyState == 4) {
       var xml = transport.responseXML;
-      this.responseXML  = xml === undefined ? null : xml;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
       this.responseJSON = this._getResponseJSON();
     }
   },
@@ -1408,7 +1412,8 @@
     if (!json) return null;
     json = decodeURIComponent(escape(json));
     try {
-      return json.evalJSON(this.request.options.sanitizeJSON);
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
     } catch (e) {
       this.request.dispatchException(e);
     }
@@ -1417,10 +1422,12 @@
   _getResponseJSON: function() {
     var options = this.request.options;
     if (!options.evalJSON || (options.evalJSON != 'force' &&
-      !(this.getHeader('Content-type') || '').include('application/json')))
-        return null;
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
     try {
-      return this.transport.responseText.evalJSON(options.sanitizeJSON);
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
     } catch (e) {
       this.request.dispatchException(e);
     }
@@ -1434,11 +1441,11 @@
       failure: (container.failure || (container.success ? null : container))
     };
 
-    options = options || { };
+    options = Object.clone(options);
     var onComplete = options.onComplete;
-    options.onComplete = (function(response, param) {
+    options.onComplete = (function(response, json) {
       this.updateContent(response.responseText);
-      if (Object.isFunction(onComplete)) onComplete(response, param);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
     }).bind(this);
 
     $super(url, options);
@@ -1460,10 +1467,6 @@
       }
       else receiver.update(responseText);
     }
-
-    if (this.success()) {
-      if (this.onComplete) this.onComplete.bind(this).defer();
-    }
   }
 });
 
@@ -1566,6 +1569,7 @@
     return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
   };
   Object.extend(this.Element, element || { });
+  if (element) this.Element.prototype = element.prototype;
 }).call(window);
 
 Element.cache = { };
@@ -1582,12 +1586,14 @@
   },
 
   hide: function(element) {
-    $(element).style.display = 'none';
+    element = $(element);
+    element.style.display = 'none';
     return element;
   },
 
   show: function(element) {
-    $(element).style.display = '';
+    element = $(element);
+    element.style.display = '';
     return element;
   },
 
@@ -1628,24 +1634,28 @@
         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
           insertions = {bottom:insertions};
 
-    var content, t, range;
+    var content, insert, tagName, childNodes;
 
-    for (position in insertions) {
+    for (var position in insertions) {
       content  = insertions[position];
       position = position.toLowerCase();
-      t = Element._insertionTranslations[position];
+      insert = Element._insertionTranslations[position];
 
       if (content && content.toElement) content = content.toElement();
       if (Object.isElement(content)) {
-        t.insert(element, content);
+        insert(element, content);
         continue;
       }
 
       content = Object.toHTML(content);
 
-      range = element.ownerDocument.createRange();
-      t.initializeRange(element, range);
-      t.insert(element, range.createContextualFragment(content.stripScripts()));
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
 
       content.evalScripts.bind(content).defer();
     }
@@ -1690,7 +1700,7 @@
   },
 
   descendants: function(element) {
-    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+    return $(element).select("*");
   },
 
   firstDescendant: function(element) {
@@ -1729,32 +1739,31 @@
     element = $(element);
     if (arguments.length == 1) return $(element.parentNode);
     var ancestors = element.ancestors();
-    return expression ? Selector.findElement(ancestors, expression, index) :
-      ancestors[index || 0];
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
   },
 
   down: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return element.firstDescendant();
-    var descendants = element.descendants();
-    return expression ? Selector.findElement(descendants, expression, index) :
-      descendants[index || 0];
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      Element.select(element, expression)[index || 0];
   },
 
   previous: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
     var previousSiblings = element.previousSiblings();
-    return expression ? Selector.findElement(previousSiblings, expression, index) :
-      previousSiblings[index || 0];
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
   },
 
   next: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
     var nextSiblings = element.nextSiblings();
-    return expression ? Selector.findElement(nextSiblings, expression, index) :
-      nextSiblings[index || 0];
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
   },
 
   select: function() {
@@ -1795,10 +1804,11 @@
     var attributes = { }, t = Element._attributeTranslations.write;
 
     if (typeof name == 'object') attributes = name;
-    else attributes[name] = value === undefined ? true : value;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
 
     for (var attr in attributes) {
-      var name = t.names[attr] || attr, value = attributes[attr];
+      name = t.names[attr] || attr;
+      value = attributes[attr];
       if (t.values[attr]) name = t.values[attr](element, value);
       if (value === false || value === null)
         element.removeAttribute(name);
@@ -1871,18 +1881,12 @@
     if (element.compareDocumentPosition)
       return (element.compareDocumentPosition(ancestor) & 8) === 8;
 
-    if (element.sourceIndex && !Prototype.Browser.Opera) {
-      var e = element.sourceIndex, a = ancestor.sourceIndex,
-       nextAncestor = ancestor.nextSibling;
-      if (!nextAncestor) {
-        do { ancestor = ancestor.parentNode; }
-        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
-      }
-      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
-    }
+    if (ancestor.contains)
+      return ancestor.contains(element) && ancestor !== element;
 
     while (element = element.parentNode)
       if (element == ancestor) return true;
+
     return false;
   },
 
@@ -1897,7 +1901,7 @@
     element = $(element);
     style = style == 'float' ? 'cssFloat' : style.camelize();
     var value = element.style[style];
-    if (!value) {
+    if (!value || value == 'auto') {
       var css = document.defaultView.getComputedStyle(element, null);
       value = css ? css[style] : null;
     }
@@ -1921,7 +1925,7 @@
       if (property == 'opacity') element.setOpacity(styles[property]);
       else
         elementStyle[(property == 'float' || property == 'cssFloat') ?
-          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
             property] = styles[property];
 
     return element;
@@ -1936,7 +1940,7 @@
 
   getDimensions: function(element) {
     element = $(element);
-    var display = $(element).getStyle('display');
+    var display = element.getStyle('display');
     if (display != 'none' && display != null) // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
 
@@ -1965,7 +1969,7 @@
       element.style.position = 'relative';
       // Opera returns the offset relative to the positioning context, when an
       // element is position relative but top and left have not been defined
-      if (window.opera) {
+      if (Prototype.Browser.Opera) {
         element.style.top = 0;
         element.style.left = 0;
       }
@@ -2020,9 +2024,9 @@
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        if (element.tagName == 'BODY') break;
+        if (element.tagName.toUpperCase() == 'BODY') break;
         var p = Element.getStyle(element, 'position');
-        if (p == 'relative' || p == 'absolute') break;
+        if (p !== 'static') break;
       }
     } while (element);
     return Element._returnOffset(valueL, valueT);
@@ -2030,7 +2034,7 @@
 
   absolutize: function(element) {
     element = $(element);
-    if (element.getStyle('position') == 'absolute') return;
+    if (element.getStyle('position') == 'absolute') return element;
     // Position.prepare(); // To be done manually by Scripty when it needs it.
 
     var offsets = element.positionedOffset();
@@ -2054,7 +2058,7 @@
 
   relativize: function(element) {
     element = $(element);
-    if (element.getStyle('position') == 'relative') return;
+    if (element.getStyle('position') == 'relative') return element;
     // Position.prepare(); // To be done manually by Scripty when it needs it.
 
     element.style.position = 'relative';
@@ -2105,7 +2109,7 @@
 
     element = forElement;
     do {
-      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
         valueT -= element.scrollTop  || 0;
         valueL -= element.scrollLeft || 0;
       }
@@ -2171,72 +2175,80 @@
   }
 };
 
-
-if (!document.createRange || Prototype.Browser.Opera) {
-  Element.Methods.insert = function(element, insertions) {
-    element = $(element);
-
-    if (Object.isString(insertions) || Object.isNumber(insertions) ||
-        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
-          insertions = { bottom: insertions };
-
-    var t = Element._insertionTranslations, content, position, pos, tagName;
-
-    for (position in insertions) {
-      content  = insertions[position];
-      position = position.toLowerCase();
-      pos      = t[position];
-
-      if (content && content.toElement) content = content.toElement();
-      if (Object.isElement(content)) {
-        pos.insert(element, content);
-        continue;
-      }
-
-      content = Object.toHTML(content);
-      tagName = ((position == 'before' || position == 'after')
-        ? element.parentNode : element).tagName.toUpperCase();
-
-      if (t.tags[tagName]) {
-        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
-        if (position == 'top' || position == 'after') fragments.reverse();
-        fragments.each(pos.insert.curry(element));
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
       }
-      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
-
-      content.evalScripts.bind(content).defer();
     }
+  );
 
-    return element;
-  };
-}
-
-if (Prototype.Browser.Opera) {
-  Element.Methods._getStyle = Element.Methods.getStyle;
-  Element.Methods.getStyle = function(element, style) {
-    switch(style) {
-      case 'left':
-      case 'top':
-      case 'right':
-      case 'bottom':
-        if (Element._getStyle(element, 'position') == 'static') return null;
-      default: return Element._getStyle(element, style);
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
     }
-  };
-  Element.Methods._readAttribute = Element.Methods.readAttribute;
-  Element.Methods.readAttribute = function(element, attribute) {
-    if (attribute == 'title') return element.title;
-    return Element._readAttribute(element, attribute);
-  };
+  );
 }
 
 else if (Prototype.Browser.IE) {
-  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      // IE throws an error if element is not in document
+      try { element.offsetParent }
+      catch(e) { return $(document.body) }
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
     Element.Methods[method] = Element.Methods[method].wrap(
       function(proceed, element) {
         element = $(element);
+        try { element.offsetParent }
+        catch(e) { return Element._returnOffset(0,0) }
         var position = element.getStyle('position');
-        if (position != 'static') return proceed(element);
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+          offsetParent.setStyle({ zoom: 1 });
         element.setStyle({ position: 'relative' });
         var value = proceed(element);
         element.setStyle({ position: position });
@@ -2245,6 +2257,14 @@
     );
   });
 
+  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+    function(proceed, element) {
+      try { element.offsetParent }
+      catch(e) { return Element._returnOffset(0,0) }
+      return proceed(element);
+    }
+  );
+
   Element.Methods.getStyle = function(element, style) {
     element = $(element);
     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
@@ -2301,7 +2321,7 @@
           return node ? node.value : "";
         },
         _getEv: function(element, attribute) {
-          var attribute = element.getAttribute(attribute);
+          attribute = element.getAttribute(attribute);
           return attribute ? attribute.toString().slice(23, -2) : null;
         },
         _flag: function(element, attribute) {
@@ -2318,7 +2338,10 @@
   };
 
   Element._attributeTranslations.write = {
-    names: Object.clone(Element._attributeTranslations.read.names),
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
     values: {
       checked: function(element, value) {
         element.checked = !!value;
@@ -2333,7 +2356,7 @@
   Element._attributeTranslations.has = {};
 
   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
-      'encType maxLength readOnly longDesc').each(function(attr) {
+      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
   });
@@ -2386,7 +2409,7 @@
       (value < 0.00001) ? 0 : value;
 
     if (value == 1)
-      if(element.tagName == 'IMG' && element.width) {
+      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
         element.width++; element.width--;
       } else try {
         var n = document.createTextNode(' ');
@@ -2398,7 +2421,7 @@
   };
 
   // Safari returns margins on body which is incorrect if the child is absolutely
-  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
   // KHTML/WebKit only.
   Element.Methods.cumulativeOffset = function(element) {
     var valueT = 0, valueL = 0;
@@ -2438,7 +2461,7 @@
   };
 }
 
-if (document.createElement('div').outerHTML) {
+if ('outerHTML' in document.createElement('div')) {
   Element.Methods.replace = function(element, content) {
     element = $(element);
 
@@ -2476,45 +2499,25 @@
 
 Element._getContentFromAnonymousElement = function(tagName, html) {
   var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
-  div.innerHTML = t[0] + html + t[1];
-  t[2].times(function() { div = div.firstChild });
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
   return $A(div.childNodes);
 };
 
 Element._insertionTranslations = {
-  before: {
-    adjacency: 'beforeBegin',
-    insert: function(element, node) {
-      element.parentNode.insertBefore(node, element);
-    },
-    initializeRange: function(element, range) {
-      range.setStartBefore(element);
-    }
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
   },
-  top: {
-    adjacency: 'afterBegin',
-    insert: function(element, node) {
-      element.insertBefore(node, element.firstChild);
-    },
-    initializeRange: function(element, range) {
-      range.selectNodeContents(element);
-      range.collapse(true);
-    }
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
   },
-  bottom: {
-    adjacency: 'beforeEnd',
-    insert: function(element, node) {
-      element.appendChild(node);
-    }
+  bottom: function(element, node) {
+    element.appendChild(node);
   },
-  after: {
-    adjacency: 'afterEnd',
-    insert: function(element, node) {
-      element.parentNode.insertBefore(node, element.nextSibling);
-    },
-    initializeRange: function(element, range) {
-      range.setStartAfter(element);
-    }
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
   },
   tags: {
     TABLE:  ['<table>',                '</table>',                   1],
@@ -2526,7 +2529,6 @@
 };
 
 (function() {
-  this.bottom.initializeRange = this.top.initializeRange;
   Object.extend(this.tags, {
     THEAD: this.tags.TBODY,
     TFOOT: this.tags.TBODY,
@@ -2538,7 +2540,7 @@
   hasAttribute: function(element, attribute) {
     attribute = Element._attributeTranslations.has[attribute] || attribute;
     var node = $(element).getAttributeNode(attribute);
-    return node && node.specified;
+    return !!(node && node.specified);
   }
 };
 
@@ -2547,9 +2549,9 @@
 Object.extend(Element, Element.Methods);
 
 if (!Prototype.BrowserFeatures.ElementExtensions &&
-    document.createElement('div').__proto__) {
+    document.createElement('div')['__proto__']) {
   window.HTMLElement = { };
-  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
   Prototype.BrowserFeatures.ElementExtensions = true;
 }
 
@@ -2564,7 +2566,7 @@
         element.nodeType != 1 || element == window) return element;
 
     var methods = Object.clone(Methods),
-      tagName = element.tagName, property, value;
+      tagName = element.tagName.toUpperCase(), property, value;
 
     // extend methods for specific tags
     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
@@ -2660,7 +2662,7 @@
     if (window[klass]) return window[klass];
 
     window[klass] = { };
-    window[klass].prototype = document.createElement(tagName).__proto__;
+    window[klass].prototype = document.createElement(tagName)['__proto__'];
     return window[klass];
   }
 
@@ -2686,11 +2688,18 @@
 
 document.viewport = {
   getDimensions: function() {
-    var dimensions = { };
+    var dimensions = { }, B = Prototype.Browser;
     $w('width height').each(function(d) {
       var D = d.capitalize();
-      dimensions[d] = self['inner' + D] ||
-       (document.documentElement['client' + D] || document.body['client' + D]);
+      if (B.WebKit && !document.evaluate) {
+        // Safari <3.0 needs self.innerWidth/Height
+        dimensions[d] = self['inner' + D];
+      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+        // Opera <9.5 needs document.body.clientWidth/Height
+        dimensions[d] = document.body['client' + D]
+      } else {
+        dimensions[d] = document.documentElement['client' + D];
+      }
     });
     return dimensions;
   },
@@ -2709,21 +2718,61 @@
       window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
   }
 };
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
  * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
  * license.  Please see http://www.yui-ext.com/ for more information. */
 
 var Selector = Class.create({
   initialize: function(expression) {
     this.expression = expression.strip();
-    this.compileMatcher();
+
+    if (this.shouldUseSelectorsAPI()) {
+      this.mode = 'selectorsAPI';
+    } else if (this.shouldUseXPath()) {
+      this.mode = 'xpath';
+      this.compileXPathMatcher();
+    } else {
+      this.mode = "normal";
+      this.compileMatcher();
+    }
+
   },
 
-  compileMatcher: function() {
-    // Selectors with namespaced attributes can't use the XPath version
-    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
-      return this.compileXPathMatcher();
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(e))
+      return false;
+
+    return true;
+  },
+
+  shouldUseSelectorsAPI: function() {
+    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+    if (!Selector._div) Selector._div = new Element('div');
+
+    // Make sure the browser treats the selector as valid. Test on an
+    // isolated element to minimize cost of this check.
+    try {
+      Selector._div.querySelector(this.expression);
+    } catch(e) {
+      return false;
+    }
 
+    return true;
+  },
+
+  compileMatcher: function() {
     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
         c = Selector.criteria, le, p, m;
 
@@ -2741,7 +2790,7 @@
         p = ps[i];
         if (m = e.match(p)) {
           this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
-    	      new Template(c[i]).evaluate(m));
+            new Template(c[i]).evaluate(m));
           e = e.replace(m[0], '');
           break;
         }
@@ -2780,8 +2829,27 @@
 
   findElements: function(root) {
     root = root || document;
-    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
-    return this.matcher(root);
+    var e = this.expression, results;
+
+    switch (this.mode) {
+      case 'selectorsAPI':
+        // querySelectorAll queries document-wide, then filters to descendants
+        // of the context element. That's not what we want.
+        // Add an explicit context to the selector if necessary.
+        if (root !== document) {
+          var oldId = root.id, id = $(root).identify();
+          e = "#" + id + " " + e;
+        }
+
+        results = $A(root.querySelectorAll(e)).map(Element.extend);
+        root.id = oldId;
+
+        return results;
+      case 'xpath':
+        return document._getElementsByXPath(this.xpath, root);
+      default:
+       return this.matcher(root);
+    }
   },
 
   match: function(element) {
@@ -2844,8 +2912,12 @@
     },
     className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
     id:           "[@id='#{1}']",
-    attrPresence: "[@#{1}]",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
     attr: function(m) {
+      m[1] = m[1].toLowerCase();
       m[3] = m[5] || m[6];
       return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
     },
@@ -2868,13 +2940,13 @@
       'first-child': '[not(preceding-sibling::*)]',
       'last-child':  '[not(following-sibling::*)]',
       'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
-      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
       'checked':     "[@checked]",
-      'disabled':    "[@disabled]",
-      'enabled':     "[not(@disabled)]",
+      'disabled':    "[(@disabled) and (@type!='hidden')]",
+      'enabled':     "[not(@disabled) and (@type!='hidden')]",
       'not': function(m) {
         var e = m[6], p = Selector.patterns,
-            x = Selector.xpath, le, m, v;
+            x = Selector.xpath, le, v;
 
         var exclusion = [];
         while (e && le != e && (/\S/).test(e)) {
@@ -2931,13 +3003,13 @@
   },
 
   criteria: {
-    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
-    className:    'n = h.className(n, r, "#{1}", c); c = false;',
-    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
-    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
     attr: function(m) {
       m[3] = (m[5] || m[6]);
-      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
     },
     pseudo: function(m) {
       if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
@@ -2961,8 +3033,9 @@
     tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
     id:           /^#([\w\-\*]+)(\b|$)/,
     className:    /^\.([\w\-\*]+)(\b|$)/,
-    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
-    attrPresence: /^\[([\w]+)\]/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
   },
 
@@ -2986,7 +3059,7 @@
 
     attr: function(element, matches) {
       var nodeValue = Element.readAttribute(element, matches[1]);
-      return Selector.operators[matches[2]](nodeValue, matches[3]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
     }
   },
 
@@ -3001,14 +3074,15 @@
 
     // marks an array of nodes for counting
     mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
       for (var i = 0, node; node = nodes[i]; i++)
-        node._counted = true;
+        node._countedByPrototype = _true;
       return nodes;
     },
 
     unmark: function(nodes) {
       for (var i = 0, node; node = nodes[i]; i++)
-        node._counted = undefined;
+        node._countedByPrototype = undefined;
       return nodes;
     },
 
@@ -3016,15 +3090,15 @@
     // "ofType" flag indicates whether we're indexing for nth-of-type
     // rather than nth-child
     index: function(parentNode, reverse, ofType) {
-      parentNode._counted = true;
+      parentNode._countedByPrototype = Prototype.emptyFunction;
       if (reverse) {
         for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
           var node = nodes[i];
-          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
         }
       } else {
         for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
-          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
       }
     },
 
@@ -3033,8 +3107,8 @@
       if (nodes.length == 0) return nodes;
       var results = [], n;
       for (var i = 0, l = nodes.length; i < l; i++)
-        if (!(n = nodes[i])._counted) {
-          n._counted = true;
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
           results.push(Element.extend(n));
         }
       return Selector.handlers.unmark(results);
@@ -3051,7 +3125,7 @@
     child: function(nodes) {
       var h = Selector.handlers;
       for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+        for (var j = 0, child; child = node.childNodes[j]; j++)
           if (child.nodeType == 1 && child.tagName != '!') results.push(child);
       }
       return results;
@@ -3074,7 +3148,7 @@
 
     nextElementSibling: function(node) {
       while (node = node.nextSibling)
-	      if (node.nodeType == 1) return node;
+        if (node.nodeType == 1) return node;
       return null;
     },
 
@@ -3086,7 +3160,7 @@
 
     // TOKEN FUNCTIONS
     tagName: function(nodes, root, tagName, combinator) {
-      tagName = tagName.toUpperCase();
+      var uTagName = tagName.toUpperCase();
       var results = [], h = Selector.handlers;
       if (nodes) {
         if (combinator) {
@@ -3099,7 +3173,7 @@
           if (tagName == "*") return nodes;
         }
         for (var i = 0, node; node = nodes[i]; i++)
-          if (node.tagName.toUpperCase() == tagName) results.push(node);
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
         return results;
       } else return root.getElementsByTagName(tagName);
     },
@@ -3146,16 +3220,18 @@
       return results;
     },
 
-    attrPresence: function(nodes, root, attr) {
+    attrPresence: function(nodes, root, attr, combinator) {
       if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
       var results = [];
       for (var i = 0, node; node = nodes[i]; i++)
         if (Element.hasAttribute(node, attr)) results.push(node);
       return results;
     },
 
-    attr: function(nodes, root, attr, value, operator) {
+    attr: function(nodes, root, attr, value, operator, combinator) {
       if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
       var handler = Selector.operators[operator], results = [];
       for (var i = 0, node; node = nodes[i]; i++) {
         var nodeValue = Element.readAttribute(node, attr);
@@ -3234,7 +3310,7 @@
       var h = Selector.handlers, results = [], indexed = [], m;
       h.mark(nodes);
       for (var i = 0, node; node = nodes[i]; i++) {
-        if (!node.parentNode._counted) {
+        if (!node.parentNode._countedByPrototype) {
           h.index(node.parentNode, reverse, ofType);
           indexed.push(node.parentNode);
         }
@@ -3261,7 +3337,7 @@
     'empty': function(nodes, value, root) {
       for (var i = 0, results = [], node; node = nodes[i]; i++) {
         // IE treats comments as element nodes
-        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        if (node.tagName == '!' || node.firstChild) continue;
         results.push(node);
       }
       return results;
@@ -3272,14 +3348,15 @@
       var exclusions = new Selector(selector).findElements(root);
       h.mark(exclusions);
       for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node._counted) results.push(node);
+        if (!node._countedByPrototype) results.push(node);
       h.unmark(exclusions);
       return results;
     },
 
     'enabled': function(nodes, value, root) {
       for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node.disabled) results.push(node);
+        if (!node.disabled && (!node.type || node.type !== 'hidden'))
+          results.push(node);
       return results;
     },
 
@@ -3299,18 +3376,29 @@
   operators: {
     '=':  function(nv, v) { return nv == v; },
     '!=': function(nv, v) { return nv != v; },
-    '^=': function(nv, v) { return nv.startsWith(v); },
+    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
     '$=': function(nv, v) { return nv.endsWith(v); },
     '*=': function(nv, v) { return nv.include(v); },
     '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
-    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+     '-').include('-' + (v || "").toUpperCase() + '-'); }
+  },
+
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
   },
 
   matchElements: function(elements, expression) {
-    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    var matches = $$(expression), h = Selector.handlers;
     h.mark(matches);
     for (var i = 0, results = [], element; element = elements[i]; i++)
-      if (element._counted) results.push(element);
+      if (element._countedByPrototype) results.push(element);
     h.unmark(matches);
     return results;
   },
@@ -3323,10 +3411,7 @@
   },
 
   findChildElements: function(element, expressions) {
-    var exprs = expressions.join(','), expressions = [];
-    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
-      expressions.push(m[1].strip());
-    });
+    expressions = Selector.split(expressions.join(','));
     var results = [], h = Selector.handlers;
     for (var i = 0, l = expressions.length, selector; i < l; i++) {
       selector = new Selector(expressions[i].strip());
@@ -3336,6 +3421,25 @@
   }
 });
 
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
 function $$() {
   return Selector.findChildElements(document, $A(arguments));
 }
@@ -3347,13 +3451,13 @@
 
   serializeElements: function(elements, options) {
     if (typeof options != 'object') options = { hash: !!options };
-    else if (options.hash === undefined) options.hash = true;
+    else if (Object.isUndefined(options.hash)) options.hash = true;
     var key, value, submitted = false, submit = options.submit;
 
     var data = elements.inject({ }, function(result, element) {
       if (!element.disabled && element.name) {
         key = element.name; value = $(element).getValue();
-        if (value != null && (element.type != 'submit' || (!submitted &&
+        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
             submit !== false && (!submit || key == submit) && (submitted = true)))) {
           if (key in result) {
             // a key is already present; construct an array of values
@@ -3514,7 +3618,6 @@
 
   disable: function(element) {
     element = $(element);
-    element.blur();
     element.disabled = true;
     return element;
   },
@@ -3545,31 +3648,31 @@
   },
 
   inputSelector: function(element, value) {
-    if (value === undefined) return element.checked ? element.value : null;
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
     else element.checked = !!value;
   },
 
   textarea: function(element, value) {
-    if (value === undefined) return element.value;
+    if (Object.isUndefined(value)) return element.value;
     else element.value = value;
   },
 
-  select: function(element, index) {
-    if (index === undefined)
+  select: function(element, value) {
+    if (Object.isUndefined(value))
       return this[element.type == 'select-one' ?
         'selectOne' : 'selectMany'](element);
     else {
-      var opt, value, single = !Object.isArray(index);
+      var opt, currentValue, single = !Object.isArray(value);
       for (var i = 0, length = element.length; i < length; i++) {
         opt = element.options[i];
-        value = this.optionValue(opt);
+        currentValue = this.optionValue(opt);
         if (single) {
-          if (value == index) {
+          if (currentValue == value) {
             opt.selected = true;
             return;
           }
         }
-        else opt.selected = index.include(value);
+        else opt.selected = value.include(currentValue);
       }
     }
   },
@@ -3740,21 +3843,42 @@
     isRightClick:  function(event) { return isButton(event, 2) },
 
     element: function(event) {
-      var node = Event.extend(event).target;
-      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+      event = Event.extend(event);
+
+      var node          = event.target,
+          type          = event.type,
+          currentTarget = event.currentTarget;
+
+      if (currentTarget && currentTarget.tagName) {
+        // Firefox screws up the "click" event when moving between radio buttons
+        // via arrow keys. It also screws up the "load" and "error" events on images,
+        // reporting the document as the target instead of the original image.
+        if (type === 'load' || type === 'error' ||
+          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+            && currentTarget.type === 'radio'))
+              node = currentTarget;
+      }
+      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+      return Element.extend(node);
     },
 
     findElement: function(event, expression) {
       var element = Event.element(event);
-      return element.match(expression) ? element : element.up(expression);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
     },
 
     pointer: function(event) {
+      var docElement = document.documentElement,
+      body = document.body || { scrollLeft: 0, scrollTop: 0 };
       return {
         x: event.pageX || (event.clientX +
-          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+          (docElement.scrollLeft || body.scrollLeft) -
+          (docElement.clientLeft || 0)),
         y: event.pageY || (event.clientY +
-          (document.documentElement.scrollTop || document.body.scrollTop))
+          (docElement.scrollTop || body.scrollTop) -
+          (docElement.clientTop || 0))
       };
     },
 
@@ -3799,7 +3923,7 @@
     };
 
   } else {
-    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
     Object.extend(Event.prototype, methods);
     return Prototype.K;
   }
@@ -3809,9 +3933,9 @@
   var cache = Event.cache;
 
   function getEventID(element) {
-    if (element._eventID) return element._eventID;
+    if (element._prototypeEventID) return element._prototypeEventID[0];
     arguments.callee.id = arguments.callee.id || 1;
-    return element._eventID = ++arguments.callee.id;
+    return element._prototypeEventID = [++arguments.callee.id];
   }
 
   function getDOMEventName(eventName) {
@@ -3839,7 +3963,7 @@
           return false;
 
       Event.extend(event);
-      handler.call(element, event)
+      handler.call(element, event);
     };
 
     wrapper.handler = handler;
@@ -3864,10 +3988,20 @@
         cache[id][eventName] = null;
   }
 
+
+  // Internet Explorer needs to remove event handlers on page unload
+  // in order to avoid memory leaks.
   if (window.attachEvent) {
     window.attachEvent("onunload", destroyCache);
   }
 
+  // Safari has a dummy event handler on page unload so that it won't
+  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+  // object when page is returned to via the back button using its bfcache.
+  if (Prototype.Browser.WebKit) {
+    window.addEventListener('unload', Prototype.emptyFunction, false);
+  }
+
   return {
     observe: function(element, eventName, handler) {
       element = $(element);
@@ -3921,11 +4055,12 @@
       if (element == document && document.createEvent && !element.dispatchEvent)
         element = document.documentElement;
 
+      var event;
       if (document.createEvent) {
-        var event = document.createEvent("HTMLEvents");
+        event = document.createEvent("HTMLEvents");
         event.initEvent("dataavailable", true, true);
       } else {
-        var event = document.createEventObject();
+        event = document.createEventObject();
         event.eventType = "ondataavailable";
       }
 
@@ -3938,7 +4073,7 @@
         element.fireEvent(event.eventType, event);
       }
 
-      return event;
+      return Event.extend(event);
     }
   };
 })());
@@ -3954,20 +4089,21 @@
 Object.extend(document, {
   fire:          Element.Methods.fire.methodize(),
   observe:       Element.Methods.observe.methodize(),
-  stopObserving: Element.Methods.stopObserving.methodize()
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
 });
 
 (function() {
   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
      Matthias Miller, Dean Edwards and John Resig. */
 
-  var timer, fired = false;
+  var timer;
 
   function fireContentLoadedEvent() {
-    if (fired) return;
+    if (document.loaded) return;
     if (timer) window.clearInterval(timer);
     document.fire("dom:loaded");
-    fired = true;
+    document.loaded = true;
   }
 
   if (document.addEventListener) {
@@ -4181,4 +4317,4 @@
 
 /*--------------------------------------------------------------------------*/
 
-Element.addMethods();
+Element.addMethods();
\ No newline at end of file

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/controls.js
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/controls.js	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/controls.js	Wed Feb  4 10:23:40 2009
@@ -1,24 +1,30 @@
-// script.aculo.us controls.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+// LOCAL BEST PRACTICAL MODIFICATIONS
+// lines 218 and 224, removing scrollIntoView
+// Leaving these in causes autocomplete lists to flip out and jump
+// the screen around if the length of the returned list is anywhere 
+// near the size of the viewport
+//
+// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
 
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
 // Contributors:
 //  Richard Livsey
 //  Rahul Bhargava
 //  Rob Wills
-// 
+//
 // script.aculo.us is freely distributable under the terms of an MIT-style license.
 // For details, see the script.aculo.us web site: http://script.aculo.us/
 
-// Autocompleter.Base handles all the autocompletion functionality 
+// Autocompleter.Base handles all the autocompletion functionality
 // that's independent of the data source for autocompletion. This
 // includes drawing the autocompletion menu, observing keyboard
 // and mouse events, and similar.
 //
-// Specific autocompleters need to provide, at the very least, 
+// Specific autocompleters need to provide, at the very least,
 // a getUpdatedChoices function that will be invoked every time
-// the text inside the monitored textbox changes. This method 
+// the text inside the monitored textbox changes. This method
 // should get the text for which to provide autocompletion by
 // invoking this.getToken(), NOT by directly accessing
 // this.element.value. This is to allow incremental tokenized
@@ -32,23 +38,23 @@
 // will incrementally autocomplete with a comma as the token.
 // Additionally, ',' in the above example can be replaced with
 // a token array, e.g. { tokens: [',', '\n'] } which
-// enables autocompletion on multiple tokens. This is most 
-// useful when one of the tokens is \n (a newline), as it 
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
 // allows smart autocompletion after linebreaks.
 
 if(typeof Effect == 'undefined')
   throw("controls.js requires including script.aculo.us' effects.js library");
 
-var Autocompleter = { }
+var Autocompleter = { };
 Autocompleter.Base = Class.create({
   baseInitialize: function(element, update, options) {
-    element          = $(element)
-    this.element     = element; 
-    this.update      = $(update);  
-    this.hasFocus    = false; 
-    this.changed     = false; 
-    this.active      = false; 
-    this.index       = 0;     
+    element          = $(element);
+    this.element     = element;
+    this.update      = $(update);
+    this.hasFocus    = false;
+    this.changed     = false;
+    this.active      = false;
+    this.index       = 0;
     this.entryCount  = 0;
     this.oldElementValue = this.element.value;
 
@@ -61,42 +67,42 @@
     this.options.tokens       = this.options.tokens || [];
     this.options.frequency    = this.options.frequency || 0.4;
     this.options.minChars     = this.options.minChars || 1;
-    this.options.onShow       = this.options.onShow || 
-      function(element, update){ 
+    this.options.onShow       = this.options.onShow ||
+      function(element, update){
         if(!update.style.position || update.style.position=='absolute') {
           update.style.position = 'absolute';
           Position.clone(element, update, {
-            setHeight: false, 
+            setHeight: false,
             offsetTop: element.offsetHeight
           });
         }
         Effect.Appear(update,{duration:0.15});
       };
-    this.options.onHide = this.options.onHide || 
+    this.options.onHide = this.options.onHide ||
       function(element, update){ new Effect.Fade(update,{duration:0.15}) };
 
-    if(typeof(this.options.tokens) == 'string') 
+    if(typeof(this.options.tokens) == 'string')
       this.options.tokens = new Array(this.options.tokens);
     // Force carriage returns as token delimiters anyway
     if (!this.options.tokens.include('\n'))
       this.options.tokens.push('\n');
 
     this.observer = null;
-    
+
     this.element.setAttribute('autocomplete','off');
 
     Element.hide(this.update);
 
     Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
-    Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
   },
 
   show: function() {
     if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
-    if(!this.iefix && 
+    if(!this.iefix &&
       (Prototype.Browser.IE) &&
       (Element.getStyle(this.update, 'position')=='absolute')) {
-      new Insertion.After(this.update, 
+      new Insertion.After(this.update,
        '<iframe id="' + this.update.id + '_iefix" '+
        'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
        'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
@@ -104,7 +110,7 @@
     }
     if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
   },
-  
+
   fixIEOverlapping: function() {
     Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
     this.iefix.style.zIndex = 1;
@@ -144,23 +150,23 @@
        case Event.KEY_UP:
          this.markPrevious();
          this.render();
-         if(Prototype.Browser.WebKit) Event.stop(event);
+         Event.stop(event);
          return;
        case Event.KEY_DOWN:
          this.markNext();
          this.render();
-         if(Prototype.Browser.WebKit) Event.stop(event);
+         Event.stop(event);
          return;
       }
-     else 
-       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+     else
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
          (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
 
     this.changed = true;
     this.hasFocus = true;
 
     if(this.observer) clearTimeout(this.observer);
-      this.observer = 
+      this.observer =
         setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
   },
 
@@ -172,35 +178,35 @@
 
   onHover: function(event) {
     var element = Event.findElement(event, 'LI');
-    if(this.index != element.autocompleteIndex) 
+    if(this.index != element.autocompleteIndex)
     {
         this.index = element.autocompleteIndex;
         this.render();
     }
     Event.stop(event);
   },
-  
+
   onClick: function(event) {
     var element = Event.findElement(event, 'LI');
     this.index = element.autocompleteIndex;
     this.selectEntry();
     this.hide();
   },
-  
+
   onBlur: function(event) {
     // needed to make click events working
     setTimeout(this.hide.bind(this), 250);
     this.hasFocus = false;
-    this.active = false;     
-  }, 
-  
+    this.active = false;
+  },
+
   render: function() {
     if(this.entryCount > 0) {
       for (var i = 0; i < this.entryCount; i++)
-        this.index==i ? 
-          Element.addClassName(this.getEntry(i),"selected") : 
+        this.index==i ?
+          Element.addClassName(this.getEntry(i),"selected") :
           Element.removeClassName(this.getEntry(i),"selected");
-      if(this.hasFocus) { 
+      if(this.hasFocus) {
         this.show();
         this.active = true;
       }
@@ -209,27 +215,27 @@
       this.hide();
     }
   },
-  
+
   markPrevious: function() {
-    if(this.index > 0) this.index--
+    if(this.index > 0) this.index--;
       else this.index = this.entryCount-1;
-    this.getEntry(this.index).scrollIntoView(true);
+    //this.getEntry(this.index).scrollIntoView(true);
   },
-  
+
   markNext: function() {
-    if(this.index < this.entryCount-1) this.index++
+    if(this.index < this.entryCount-1) this.index++;
       else this.index = 0;
-    this.getEntry(this.index).scrollIntoView(false);
+    //this.getEntry(this.index).scrollIntoView(false);
   },
-  
+
   getEntry: function(index) {
     return this.update.firstChild.childNodes[index];
   },
-  
+
   getCurrentEntry: function() {
     return this.getEntry(this.index);
   },
-  
+
   selectEntry: function() {
     this.active = false;
     this.updateElement(this.getCurrentEntry());
@@ -246,7 +252,7 @@
       if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
     } else
       value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-    
+
     var bounds = this.getTokenBounds();
     if (bounds[0] != -1) {
       var newValue = this.element.value.substr(0, bounds[0]);
@@ -259,7 +265,7 @@
     }
     this.oldElementValue = this.element.value;
     this.element.focus();
-    
+
     if (this.options.afterUpdateElement)
       this.options.afterUpdateElement(this.element, selectedElement);
   },
@@ -271,20 +277,20 @@
       Element.cleanWhitespace(this.update.down());
 
       if(this.update.firstChild && this.update.down().childNodes) {
-        this.entryCount = 
+        this.entryCount =
           this.update.down().childNodes.length;
         for (var i = 0; i < this.entryCount; i++) {
           var entry = this.getEntry(i);
           entry.autocompleteIndex = i;
           this.addObservers(entry);
         }
-      } else { 
+      } else {
         this.entryCount = 0;
       }
 
       this.stopIndicator();
       this.index = 0;
-      
+
       if(this.entryCount==1 && this.options.autoSelect) {
         this.selectEntry();
         this.hide();
@@ -300,7 +306,7 @@
   },
 
   onObserverEvent: function() {
-    this.changed = false;   
+    this.changed = false;
     this.tokenBounds = null;
     if(this.getToken().length>=this.options.minChars) {
       this.getUpdatedChoices();
@@ -353,16 +359,16 @@
 
   getUpdatedChoices: function() {
     this.startIndicator();
-    
-    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+
+    var entry = encodeURIComponent(this.options.paramName) + '=' +
       encodeURIComponent(this.getToken());
 
     this.options.parameters = this.options.callback ?
       this.options.callback(this.element, entry) : entry;
 
-    if(this.options.defaultParams) 
+    if(this.options.defaultParams)
       this.options.parameters += '&' + this.options.defaultParams;
-    
+
     new Ajax.Request(this.url, this.options);
   },
 
@@ -384,7 +390,7 @@
 // - choices - How many autocompletion choices to offer
 //
 // - partialSearch - If false, the autocompleter will match entered
-//                    text only at the beginning of strings in the 
+//                    text only at the beginning of strings in the
 //                    autocomplete array. Defaults to true, which will
 //                    match text at the beginning of any *word* in the
 //                    strings in the autocomplete array. If you want to
@@ -401,7 +407,7 @@
 // - ignoreCase - Whether to ignore case when autocompleting.
 //                 Defaults to true.
 //
-// It's possible to pass in a custom function as the 'selector' 
+// It's possible to pass in a custom function as the 'selector'
 // option, if you prefer to write your own autocompletion logic.
 // In that case, the other options above will not apply unless
 // you support them.
@@ -429,20 +435,20 @@
         var entry     = instance.getToken();
         var count     = 0;
 
-        for (var i = 0; i < instance.options.array.length &&  
-          ret.length < instance.options.choices ; i++) { 
+        for (var i = 0; i < instance.options.array.length &&
+          ret.length < instance.options.choices ; i++) {
 
           var elem = instance.options.array[i];
-          var foundPos = instance.options.ignoreCase ? 
-            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+          var foundPos = instance.options.ignoreCase ?
+            elem.toLowerCase().indexOf(entry.toLowerCase()) :
             elem.indexOf(entry);
 
           while (foundPos != -1) {
-            if (foundPos == 0 && elem.length != entry.length) { 
-              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+            if (foundPos == 0 && elem.length != entry.length) {
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
                 elem.substr(entry.length) + "</li>");
               break;
-            } else if (entry.length >= instance.options.partialChars && 
+            } else if (entry.length >= instance.options.partialChars &&
               instance.options.partialSearch && foundPos != -1) {
               if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
@@ -452,14 +458,14 @@
               }
             }
 
-            foundPos = instance.options.ignoreCase ? 
-              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+            foundPos = instance.options.ignoreCase ?
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
               elem.indexOf(entry, foundPos + 1);
 
           }
         }
         if (partial.length)
-          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
         return "<ul>" + ret.join('') + "</ul>";
       }
     }, options || { });
@@ -476,7 +482,7 @@
   setTimeout(function() {
     Field.activate(field);
   }, 1);
-}
+};
 
 Ajax.InPlaceEditor = Class.create({
   initialize: function(element, url, options) {
@@ -606,7 +612,7 @@
     this.triggerCallback('onEnterHover');
   },
   getText: function() {
-    return this.element.innerHTML;
+    return this.element.innerHTML.unescapeHTML();
   },
   handleAJAXFailure: function(transport) {
     this.triggerCallback('onFailure', transport);
@@ -782,7 +788,7 @@
       onSuccess: function(transport) {
         var js = transport.responseText.strip();
         if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
-          throw 'Server returned an invalid collection representation.';
+          throw('Server returned an invalid collection representation.');
         this._collection = eval(js);
         this.checkForExternalText();
       }.bind(this),
@@ -939,7 +945,7 @@
   loadingCollectionText: 'Loading options...'
 };
 
-// Delayed observer, like Form.Element.Observer, 
+// Delayed observer, like Form.Element.Observer,
 // but waits for delay after last key input
 // Ideal for live-search fields
 
@@ -949,7 +955,7 @@
     this.element   = $(element);
     this.callback  = callback;
     this.timer     = null;
-    this.lastValue = $F(this.element); 
+    this.lastValue = $F(this.element);
     Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
   },
   delayedListener: function(event) {

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/effects.js
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/effects.js	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/effects.js	Wed Feb  4 10:23:40 2009
@@ -1,50 +1,50 @@
-// script.aculo.us effects.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
 
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
 // Contributors:
 //  Justin Palmer (http://encytemedia.com/)
 //  Mark Pilgrim (http://diveintomark.org/)
 //  Martin Bialasinki
-// 
+//
 // script.aculo.us is freely distributable under the terms of an MIT-style license.
-// For details, see the script.aculo.us web site: http://script.aculo.us/ 
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
-// converts rgb() and #xxx to #xxxxxx format,  
-// returns self (or first argument) if not convertable  
-String.prototype.parseColor = function() {  
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
   var color = '#';
-  if (this.slice(0,4) == 'rgb(') {  
-    var cols = this.slice(4,this.length-1).split(',');  
-    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
-  } else {  
-    if (this.slice(0,1) == '#') {  
-      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
-      if (this.length==7) color = this.toLowerCase();  
-    }  
-  }  
-  return (color.length==7 ? color : (arguments[0] || this));  
+  if (this.slice(0,4) == 'rgb(') {
+    var cols = this.slice(4,this.length-1).split(',');
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+  } else {
+    if (this.slice(0,1) == '#') {
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+      if (this.length==7) color = this.toLowerCase();
+    }
+  }
+  return (color.length==7 ? color : (arguments[0] || this));
 };
 
 /*--------------------------------------------------------------------------*/
 
-Element.collectTextNodes = function(element) {  
+Element.collectTextNodes = function(element) {
   return $A($(element).childNodes).collect( function(node) {
-    return (node.nodeType==3 ? node.nodeValue : 
+    return (node.nodeType==3 ? node.nodeValue :
       (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
   }).flatten().join('');
 };
 
-Element.collectTextNodesIgnoreClass = function(element, className) {  
+Element.collectTextNodesIgnoreClass = function(element, className) {
   return $A($(element).childNodes).collect( function(node) {
-    return (node.nodeType==3 ? node.nodeValue : 
-      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+    return (node.nodeType==3 ? node.nodeValue :
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
         Element.collectTextNodesIgnoreClass(node, className) : ''));
   }).flatten().join('');
 };
 
 Element.setContentZoom = function(element, percent) {
-  element = $(element);  
-  element.setStyle({fontSize: (percent/100) + 'em'});   
+  element = $(element);
+  element.setStyle({fontSize: (percent/100) + 'em'});
   if (Prototype.Browser.WebKit) window.scrollBy(0,0);
   return element;
 };
@@ -72,28 +72,23 @@
   Transitions: {
     linear: Prototype.K,
     sinoidal: function(pos) {
-      return (-Math.cos(pos*Math.PI)/2) + 0.5;
+      return (-Math.cos(pos*Math.PI)/2) + .5;
     },
     reverse: function(pos) {
       return 1-pos;
     },
     flicker: function(pos) {
-      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
       return pos > 1 ? 1 : pos;
     },
     wobble: function(pos) {
-      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
     },
-    pulse: function(pos, pulses) { 
-      pulses = pulses || 5; 
-      return (
-        ((pos % (1/pulses)) * pulses).round() == 0 ? 
-              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
-          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
-        );
+    pulse: function(pos, pulses) {
+      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
     },
-    spring: function(pos) { 
-      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
+    spring: function(pos) {
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
     },
     none: function(pos) {
       return 0;
@@ -114,14 +109,14 @@
   tagifyText: function(element) {
     var tagifyStyle = 'position:relative';
     if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
-    
+
     element = $(element);
     $A(element.childNodes).each( function(child) {
       if (child.nodeType==3) {
         child.nodeValue.toArray().each( function(character) {
           element.insertBefore(
             new Element('span', {style: tagifyStyle}).update(
-              character == ' ' ? String.fromCharCode(160) : character), 
+              character == ' ' ? String.fromCharCode(160) : character),
               child);
         });
         Element.remove(child);
@@ -130,13 +125,13 @@
   },
   multiple: function(element, effect) {
     var elements;
-    if (((typeof element == 'object') || 
-        Object.isFunction(element)) && 
+    if (((typeof element == 'object') ||
+        Object.isFunction(element)) &&
        (element.length))
       elements = element;
     else
       elements = $(element).childNodes;
-      
+
     var options = Object.extend({
       speed: 0.1,
       delay: 0.0
@@ -158,7 +153,7 @@
     var options = Object.extend({
       queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
     }, arguments[2] || { });
-    Effect[element.visible() ? 
+    Effect[element.visible() ?
       Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
   }
 };
@@ -170,20 +165,20 @@
 Effect.ScopedQueue = Class.create(Enumerable, {
   initialize: function() {
     this.effects  = [];
-    this.interval = null;    
+    this.interval = null;
   },
   _each: function(iterator) {
     this.effects._each(iterator);
   },
   add: function(effect) {
     var timestamp = new Date().getTime();
-    
-    var position = Object.isString(effect.options.queue) ? 
+
+    var position = Object.isString(effect.options.queue) ?
       effect.options.queue : effect.options.queue.position;
-    
+
     switch(position) {
       case 'front':
-        // move unstarted effects after this effect  
+        // move unstarted effects after this effect
         this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
             e.startOn  += effect.finishOn;
             e.finishOn += effect.finishOn;
@@ -197,13 +192,13 @@
         timestamp = this.effects.pluck('finishOn').max() || timestamp;
         break;
     }
-    
+
     effect.startOn  += timestamp;
     effect.finishOn += timestamp;
 
     if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
       this.effects.push(effect);
-    
+
     if (!this.interval)
       this.interval = setInterval(this.loop.bind(this), 15);
   },
@@ -216,7 +211,7 @@
   },
   loop: function() {
     var timePos = new Date().getTime();
-    for(var i=0, len=this.effects.length;i<len;i++) 
+    for(var i=0, len=this.effects.length;i<len;i++)
       this.effects[i] && this.effects[i].loop(timePos);
   }
 });
@@ -225,7 +220,7 @@
   instances: $H(),
   get: function(queueName) {
     if (!Object.isString(queueName)) return queueName;
-    
+
     return this.instances.get(queueName) ||
       this.instances.set(queueName, new Effect.ScopedQueue());
   }
@@ -250,23 +245,35 @@
     this.fromToDelta  = this.options.to-this.options.from;
     this.totalTime    = this.finishOn-this.startOn;
     this.totalFrames  = this.options.fps*this.options.duration;
-    
-    eval('this.render = function(pos){ '+
-      'if (this.state=="idle"){this.state="running";'+
-      codeForEvent(this.options,'beforeSetup')+
-      (this.setup ? 'this.setup();':'')+ 
-      codeForEvent(this.options,'afterSetup')+
-      '};if (this.state=="running"){'+
-      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
-      'this.position=pos;'+
-      codeForEvent(this.options,'beforeUpdate')+
-      (this.update ? 'this.update(pos);':'')+
-      codeForEvent(this.options,'afterUpdate')+
-      '}}');
-    
+
+    this.render = (function() {
+      function dispatch(effect, eventName) {
+        if (effect.options[eventName + 'Internal'])
+          effect.options[eventName + 'Internal'](effect);
+        if (effect.options[eventName])
+          effect.options[eventName](effect);
+      }
+
+      return function(pos) {
+        if (this.state === "idle") {
+          this.state = "running";
+          dispatch(this, 'beforeSetup');
+          if (this.setup) this.setup();
+          dispatch(this, 'afterSetup');
+        }
+        if (this.state === "running") {
+          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
+          this.position = pos;
+          dispatch(this, 'beforeUpdate');
+          if (this.update) this.update(pos);
+          dispatch(this, 'afterUpdate');
+        }
+      };
+    })();
+
     this.event('beforeStart');
     if (!this.options.sync)
-      Effect.Queues.get(Object.isString(this.options.queue) ? 
+      Effect.Queues.get(Object.isString(this.options.queue) ?
         'global' : this.options.queue.scope).add(this);
   },
   loop: function(timePos) {
@@ -275,9 +282,9 @@
         this.render(1.0);
         this.cancel();
         this.event('beforeFinish');
-        if (this.finish) this.finish(); 
+        if (this.finish) this.finish();
         this.event('afterFinish');
-        return;  
+        return;
       }
       var pos   = (timePos - this.startOn) / this.totalTime,
           frame = (pos * this.totalFrames).round();
@@ -289,7 +296,7 @@
   },
   cancel: function() {
     if (!this.options.sync)
-      Effect.Queues.get(Object.isString(this.options.queue) ? 
+      Effect.Queues.get(Object.isString(this.options.queue) ?
         'global' : this.options.queue.scope).remove(this);
     this.state = 'finished';
   },
@@ -327,10 +334,10 @@
 Effect.Tween = Class.create(Effect.Base, {
   initialize: function(object, from, to) {
     object = Object.isString(object) ? $(object) : object;
-    var args = $A(arguments), method = args.last(), 
+    var args = $A(arguments), method = args.last(),
       options = args.length == 5 ? args[3] : null;
     this.method = Object.isFunction(method) ? method.bind(object) :
-      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      Object.isFunction(object[method]) ? object[method].bind(object) :
       function(value) { object[method] = value };
     this.start(Object.extend({ from: from, to: to }, options || { }));
   },
@@ -394,7 +401,7 @@
 
 // for backwards compatibility
 Effect.MoveBy = function(element, toTop, toLeft) {
-  return new Effect.Move(element, 
+  return new Effect.Move(element,
     Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
 };
 
@@ -416,15 +423,15 @@
   setup: function() {
     this.restoreAfterFinish = this.options.restoreAfterFinish || false;
     this.elementPositioning = this.element.getStyle('position');
-    
+
     this.originalStyle = { };
     ['top','left','width','height','fontSize'].each( function(k) {
       this.originalStyle[k] = this.element.style[k];
     }.bind(this));
-      
+
     this.originalTop  = this.element.offsetTop;
     this.originalLeft = this.element.offsetLeft;
-    
+
     var fontSize = this.element.getStyle('font-size') || '100%';
     ['em','px','%','pt'].each( function(fontSizeType) {
       if (fontSize.indexOf(fontSizeType)>0) {
@@ -432,9 +439,9 @@
         this.fontSizeType = fontSizeType;
       }
     }.bind(this));
-    
+
     this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
-    
+
     this.dims = null;
     if (this.options.scaleMode=='box')
       this.dims = [this.element.offsetHeight, this.element.offsetWidth];
@@ -509,17 +516,16 @@
 
 Effect.ScrollTo = function(element) {
   var options = arguments[1] || { },
-    scrollOffsets = document.viewport.getScrollOffsets(),
-    elementOffsets = $(element).cumulativeOffset(),
-    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  
+  scrollOffsets = document.viewport.getScrollOffsets(),
+  elementOffsets = $(element).cumulativeOffset();
 
   if (options.offset) elementOffsets[1] += options.offset;
 
   return new Effect.Tween(null,
     scrollOffsets.top,
-    elementOffsets[1] > max ? max : elementOffsets[1],
+    elementOffsets[1],
     options,
-    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+    function(p){ scrollTo(scrollOffsets.left, p.round()); }
   );
 };
 
@@ -531,9 +537,9 @@
   var options = Object.extend({
     from: element.getOpacity() || 1.0,
     to:   0.0,
-    afterFinishInternal: function(effect) { 
+    afterFinishInternal: function(effect) {
       if (effect.options.to!=0) return;
-      effect.element.hide().setStyle({opacity: oldOpacity}); 
+      effect.element.hide().setStyle({opacity: oldOpacity});
     }
   }, arguments[1] || { });
   return new Effect.Opacity(element,options);
@@ -549,15 +555,15 @@
     effect.element.forceRerendering();
   },
   beforeSetup: function(effect) {
-    effect.element.setOpacity(effect.options.from).show(); 
+    effect.element.setOpacity(effect.options.from).show();
   }}, arguments[1] || { });
   return new Effect.Opacity(element,options);
 };
 
 Effect.Puff = function(element) {
   element = $(element);
-  var oldStyle = { 
-    opacity: element.getInlineOpacity(), 
+  var oldStyle = {
+    opacity: element.getInlineOpacity(),
     position: element.getStyle('position'),
     top:  element.style.top,
     left: element.style.left,
@@ -565,12 +571,12 @@
     height: element.style.height
   };
   return new Effect.Parallel(
-   [ new Effect.Scale(element, 200, 
-      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
-     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
-     Object.extend({ duration: 1.0, 
+   [ new Effect.Scale(element, 200,
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+     Object.extend({ duration: 1.0,
       beforeSetupInternal: function(effect) {
-        Position.absolutize(effect.effects[0].element)
+        Position.absolutize(effect.effects[0].element);
       },
       afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().setStyle(oldStyle); }
@@ -582,12 +588,12 @@
   element = $(element);
   element.makeClipping();
   return new Effect.Scale(element, 0,
-    Object.extend({ scaleContent: false, 
-      scaleX: false, 
+    Object.extend({ scaleContent: false,
+      scaleX: false,
       restoreAfterFinish: true,
       afterFinishInternal: function(effect) {
         effect.element.hide().undoClipping();
-      } 
+      }
     }, arguments[1] || { })
   );
 };
@@ -595,15 +601,15 @@
 Effect.BlindDown = function(element) {
   element = $(element);
   var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, Object.extend({ 
-    scaleContent: false, 
+  return new Effect.Scale(element, 100, Object.extend({
+    scaleContent: false,
     scaleX: false,
     scaleFrom: 0,
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
     restoreAfterFinish: true,
     afterSetup: function(effect) {
-      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
-    },  
+      effect.element.makeClipping().setStyle({height: '0px'}).show();
+    },
     afterFinishInternal: function(effect) {
       effect.element.undoClipping();
     }
@@ -618,16 +624,16 @@
     from: 0,
     transition: Effect.Transitions.flicker,
     afterFinishInternal: function(effect) {
-      new Effect.Scale(effect.element, 1, { 
+      new Effect.Scale(effect.element, 1, {
         duration: 0.3, scaleFromCenter: true,
         scaleX: false, scaleContent: false, restoreAfterFinish: true,
-        beforeSetup: function(effect) { 
+        beforeSetup: function(effect) {
           effect.element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
           effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
         }
-      })
+      });
     }
   }, arguments[1] || { }));
 };
@@ -639,16 +645,16 @@
     left: element.getStyle('left'),
     opacity: element.getInlineOpacity() };
   return new Effect.Parallel(
-    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
       new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
     Object.extend(
       { duration: 0.5,
         beforeSetup: function(effect) {
-          effect.effects[0].element.makePositioned(); 
+          effect.effects[0].element.makePositioned();
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
-        } 
+        }
       }, arguments[1] || { }));
 };
 
@@ -676,7 +682,7 @@
     new Effect.Move(effect.element,
       { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
         effect.element.undoPositioned().setStyle(oldStyle);
-  }}) }}) }}) }}) }}) }});
+  }}); }}); }}); }}); }}); }});
 };
 
 Effect.SlideDown = function(element) {
@@ -684,9 +690,9 @@
   // SlideDown need to have the content of the element wrapped in a container element with fixed height!
   var oldInnerBottom = element.down().getStyle('bottom');
   var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, Object.extend({ 
-    scaleContent: false, 
-    scaleX: false, 
+  return new Effect.Scale(element, 100, Object.extend({
+    scaleContent: false,
+    scaleX: false,
     scaleFrom: window.opera ? 0 : 1,
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
     restoreAfterFinish: true,
@@ -694,11 +700,11 @@
       effect.element.makePositioned();
       effect.element.down().makePositioned();
       if (window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+      effect.element.makeClipping().setStyle({height: '0px'}).show();
     },
     afterUpdateInternal: function(effect) {
       effect.element.down().setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
     },
     afterFinishInternal: function(effect) {
       effect.element.undoClipping().undoPositioned();
@@ -712,8 +718,8 @@
   var oldInnerBottom = element.down().getStyle('bottom');
   var elementDimensions = element.getDimensions();
   return new Effect.Scale(element, window.opera ? 0 : 1,
-   Object.extend({ scaleContent: false, 
-    scaleX: false, 
+   Object.extend({ scaleContent: false,
+    scaleX: false,
     scaleMode: 'box',
     scaleFrom: 100,
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
@@ -723,7 +729,7 @@
       effect.element.down().makePositioned();
       if (window.opera) effect.element.setStyle({top: ''});
       effect.element.makeClipping().show();
-    },  
+    },
     afterUpdateInternal: function(effect) {
       effect.element.down().setStyle({bottom:
         (effect.dims[0] - effect.element.clientHeight) + 'px' });
@@ -736,15 +742,15 @@
   );
 };
 
-// Bug in opera makes the TD containing this element expand for a instance after finish 
+// Bug in opera makes the TD containing this element expand for a instance after finish
 Effect.Squish = function(element) {
-  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+  return new Effect.Scale(element, window.opera ? 1 : 0, {
     restoreAfterFinish: true,
     beforeSetup: function(effect) {
-      effect.element.makeClipping(); 
-    },  
+      effect.element.makeClipping();
+    },
     afterFinishInternal: function(effect) {
-      effect.element.hide().undoClipping(); 
+      effect.element.hide().undoClipping();
     }
   });
 };
@@ -764,13 +770,13 @@
     width: element.style.width,
     opacity: element.getInlineOpacity() };
 
-  var dims = element.getDimensions();    
+  var dims = element.getDimensions();
   var initialMoveX, initialMoveY;
   var moveX, moveY;
-  
+
   switch (options.direction) {
     case 'top-left':
-      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      initialMoveX = initialMoveY = moveX = moveY = 0;
       break;
     case 'top-right':
       initialMoveX = dims.width;
@@ -795,11 +801,11 @@
       moveY = -dims.height / 2;
       break;
   }
-  
+
   return new Effect.Move(element, {
     x: initialMoveX,
     y: initialMoveY,
-    duration: 0.01, 
+    duration: 0.01,
     beforeSetup: function(effect) {
       effect.element.hide().makeClipping().makePositioned();
     },
@@ -808,17 +814,17 @@
         [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
           new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
           new Effect.Scale(effect.element, 100, {
-            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
             sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
         ], Object.extend({
              beforeSetup: function(effect) {
-               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+               effect.effects[0].element.setStyle({height: '0px'}).show();
              },
              afterFinishInternal: function(effect) {
-               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
              }
            }, options)
-      )
+      );
     }
   });
 };
@@ -840,7 +846,7 @@
 
   var dims = element.getDimensions();
   var moveX, moveY;
-  
+
   switch (options.direction) {
     case 'top-left':
       moveX = moveY = 0;
@@ -857,19 +863,19 @@
       moveX = dims.width;
       moveY = dims.height;
       break;
-    case 'center':  
+    case 'center':
       moveX = dims.width / 2;
       moveY = dims.height / 2;
       break;
   }
-  
+
   return new Effect.Parallel(
     [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
       new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
       new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
-    ], Object.extend({            
+    ], Object.extend({
          beforeStartInternal: function(effect) {
-           effect.effects[0].element.makePositioned().makeClipping(); 
+           effect.effects[0].element.makePositioned().makeClipping();
          },
          afterFinishInternal: function(effect) {
            effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
@@ -879,12 +885,14 @@
 
 Effect.Pulsate = function(element) {
   element = $(element);
-  var options    = arguments[1] || { };
-  var oldOpacity = element.getInlineOpacity();
-  var transition = options.transition || Effect.Transitions.sinoidal;
-  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
-  reverser.bind(transition);
-  return new Effect.Opacity(element, 
+  var options    = arguments[1] || { },
+    oldOpacity = element.getInlineOpacity(),
+    transition = options.transition || Effect.Transitions.linear,
+    reverser   = function(pos){
+      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
+    };
+
+  return new Effect.Opacity(element,
     Object.extend(Object.extend({  duration: 2.0, from: 0,
       afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
     }, options), {transition: reverser}));
@@ -898,12 +906,12 @@
     width: element.style.width,
     height: element.style.height };
   element.makeClipping();
-  return new Effect.Scale(element, 5, Object.extend({   
+  return new Effect.Scale(element, 5, Object.extend({
     scaleContent: false,
     scaleX: false,
     afterFinishInternal: function(effect) {
-    new Effect.Scale(element, 1, { 
-      scaleContent: false, 
+    new Effect.Scale(element, 1, {
+      scaleContent: false,
       scaleY: false,
       afterFinishInternal: function(effect) {
         effect.element.hide().undoClipping().setStyle(oldStyle);
@@ -918,7 +926,7 @@
     var options = Object.extend({
       style: { }
     }, arguments[1] || { });
-    
+
     if (!Object.isString(options.style)) this.style = $H(options.style);
     else {
       if (options.style.include(':'))
@@ -936,18 +944,18 @@
           effect.transforms.each(function(transform) {
             effect.element.style[transform.style] = '';
           });
-        }
+        };
       }
     }
     this.start(options);
   },
-  
+
   setup: function(){
     function parseColor(color){
       if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
       color = color.parseColor();
       return $R(0,2).map(function(i){
-        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+        return parseInt( color.slice(i*2+1,i*2+3), 16 );
       });
     }
     this.transforms = this.style.map(function(pair){
@@ -967,9 +975,9 @@
       }
 
       var originalValue = this.element.getStyle(property);
-      return { 
-        style: property.camelize(), 
-        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+      return {
+        style: property.camelize(),
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
         targetValue: unit=='color' ? parseColor(value) : value,
         unit: unit
       };
@@ -980,13 +988,13 @@
           transform.unit != 'color' &&
           (isNaN(transform.originalValue) || isNaN(transform.targetValue))
         )
-      )
+      );
     });
   },
   update: function(position) {
     var style = { }, transform, i = this.transforms.length;
     while(i--)
-      style[(transform = this.transforms[i]).style] = 
+      style[(transform = this.transforms[i]).style] =
         transform.unit=='color' ? '#'+
           (Math.round(transform.originalValue[0]+
             (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
@@ -995,7 +1003,7 @@
           (Math.round(transform.originalValue[2]+
             (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
         (transform.originalValue +
-          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
             (transform.unit === null ? '' : transform.unit);
     this.element.setStyle(style, true);
   }
@@ -1032,7 +1040,7 @@
 });
 
 Element.CSS_PROPERTIES = $w(
-  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
   'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
   'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
   'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
@@ -1041,7 +1049,7 @@
   'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
   'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
   'right textIndent top width wordSpacing zIndex');
-  
+
 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
 
 String.__parseStyleElement = document.createElement('div');
@@ -1053,11 +1061,11 @@
     String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
     style = String.__parseStyleElement.childNodes[0].style;
   }
-  
+
   Element.CSS_PROPERTIES.each(function(property){
-    if (style[property]) styleRules.set(property, style[property]); 
+    if (style[property]) styleRules.set(property, style[property]);
   });
-  
+
   if (Prototype.Browser.IE && this.include('opacity'))
     styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
 
@@ -1076,14 +1084,14 @@
   Element.getStyles = function(element) {
     element = $(element);
     var css = element.currentStyle, styles;
-    styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
-      hash.set(property, css[property]);
-      return hash;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+      results[property] = css[property];
+      return results;
     });
-    if (!styles.opacity) styles.set('opacity', element.getOpacity());
+    if (!styles.opacity) styles.opacity = element.getOpacity();
     return styles;
   };
-};
+}
 
 Effect.Methods = {
   morph: function(element, style) {
@@ -1092,7 +1100,7 @@
     return element;
   },
   visualEffect: function(element, effect, options) {
-    element = $(element)
+    element = $(element);
     var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
     new Effect[klass](element, options);
     return element;
@@ -1106,17 +1114,17 @@
 
 $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
   'pulsate shake puff squish switchOff dropOut').each(
-  function(effect) { 
+  function(effect) {
     Effect.Methods[effect] = function(element, options){
       element = $(element);
       Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
       return element;
-    }
+    };
   }
 );
 
-$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
   function(f) { Effect.Methods[f] = Element[f]; }
 );
 
-Element.addMethods(Effect.Methods);
+Element.addMethods(Effect.Methods);
\ No newline at end of file

Modified: rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/scriptaculous.js
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/scriptaculous.js	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/NoAuth/js/scriptaculous/scriptaculous.js	Wed Feb  4 10:23:40 2009
@@ -1,7 +1,7 @@
-// script.aculo.us scriptaculous.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+// script.aculo.us scriptaculous.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
 
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// 
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // "Software"), to deal in the Software without restriction, including
@@ -9,7 +9,7 @@
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
 //
@@ -24,35 +24,37 @@
 // For details, see the script.aculo.us web site: http://script.aculo.us/
 
 var Scriptaculous = {
-  Version: '1.8.0',
+  Version: '1.8.2',
   require: function(libraryName) {
     // inserting via DOM fails in Safari 2.0, so brute force approach
     document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
   },
-  REQUIRED_PROTOTYPE: '1.6.0',
+  REQUIRED_PROTOTYPE: '1.6.0.3',
   load: function() {
-    function convertVersionString(versionString){
-      var r = versionString.split('.');
-      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+    function convertVersionString(versionString) {
+      var v = versionString.replace(/_.*|\./g, '');
+      v = parseInt(v + '0'.times(4-v.length));
+      return versionString.indexOf('_') > -1 ? v-1 : v;
     }
- 
-    if((typeof Prototype=='undefined') || 
-       (typeof Element == 'undefined') || 
+
+    if((typeof Prototype=='undefined') ||
+       (typeof Element == 'undefined') ||
        (typeof Element.Methods=='undefined') ||
-       (convertVersionString(Prototype.Version) < 
+       (convertVersionString(Prototype.Version) <
         convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
        throw("script.aculo.us requires the Prototype JavaScript framework >= " +
         Scriptaculous.REQUIRED_PROTOTYPE);
-    
-    $A(document.getElementsByTagName("script")).findAll( function(s) {
-      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
-    }).each( function(s) {
-      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
-      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+
+    var js = /scriptaculous\.js(\?.*)?$/;
+    $$('head script[src]').findAll(function(s) {
+      return s.src.match(js);
+    }).each(function(s) {
+      var path = s.src.replace(js, ''),
+      includes = s.src.match(/\?.*load=([a-z,]*)/);
       (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
        function(include) { Scriptaculous.require(path+include+'.js') });
     });
   }
-}
+};
 
-Scriptaculous.load();
+Scriptaculous.load();
\ No newline at end of file

Modified: rt/3.8/branches/html-css-cleanup/share/html/Search/Bulk.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Search/Bulk.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Search/Bulk.html	Wed Feb  4 10:23:40 2009
@@ -146,7 +146,20 @@
     &><em><% $CF->FriendlyType %></em></td>
 </td></tr>
 % } # end if while
- <tr><td align="right"><&|/l&>Attach</&>:</td><td><input name="UpdateAttachment" type="file" /></td></tr>
+% if (exists $session{'Attachments'}) {
+<tr><td><&|/l&>Attached file</&>:</td>
+<td>
+<&|/l&>Check box to delete</&><br />
+% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+<input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
+% } # end of foreach
+</td>
+</tr>
+% } # end of if
+
+ <tr><td align="right"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" />
+    <input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" />
+    <input type="hidden" class="hidden" name="UpdateAttach" value="1" /></td></tr>
  <tr><td class="labeltop"><&|/l&>Message</&>:</td><td>
  <& /Elements/MessageBox, Name=>"UpdateContent"&>
  </td></tr>
@@ -219,6 +232,35 @@
 
 my (@results);
 
+# {{{ deal with deleting uploaded attachments
+foreach my $key (keys %ARGS) {
+    if ($key =~ m/^DeleteAttach-(.+)$/) {
+        delete $session{'Attachments'}{$1};
+    }
+    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
+}
+# }}}
+
+# {{{ store the uploaded attachment in session
+if ($ARGS{'Attach'}) {            # attachment?
+    my $attachment = MakeMIMEEntity(
+        AttachmentFieldName => 'Attach'
+    );
+
+    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
+    $session{'Attachments'} = {
+        %{$session{'Attachments'} || {}},
+        $file_path => $attachment,
+    };
+}
+# }}}
+
+# delete temporary storage entry to make WebUI clean
+unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
+    delete $session{'Attachments'};
+}
+# }}}
+
 $Page ||= 1;
 
 $Format ||= RT->Config->Get('DefaultSearchResultFormat');
@@ -273,108 +315,115 @@
 my $cf_add_keys;
 @$cf_add_keys = grep { /^Bulk-Add-CustomField/ } keys %ARGS;
 
+unless ( $ARGS{'AddMoreAttach'} ) {
+    # Add session attachments if any to be processed by ProcessUpdateMessage
+    $ARGS{'UpdateAttachments'} = $session{'Attachments'} if ( $session{'Attachments'} );
+
+    while ( my $Ticket = $Tickets->Next ) {
+        next unless ( $ARGS{ "UpdateTicket" . $Ticket->Id } );
+
+        #Update the links
+        $ARGS{'id'} = $Ticket->id;
+        $queues{ $Ticket->QueueObj->Id }++;
+
+        my @updateresults, ProcessUpdateMessage(
+                TicketObj => $Ticket,
+                ARGSRef   => \%ARGS,
+            );
+
+        #Update the basics.
+        my @basicresults =
+          ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
+        my @dateresults =
+          ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+        #Update the watchers
+        my @watchresults =
+          ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+        foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
+            $ARGS{ $Ticket->id . "-" . $type } = $ARGS{"Ticket-$type"};
+            $ARGS{ $type . "-" . $Ticket->id } = $ARGS{"$type-Ticket"};
+        }
+        @linkresults =
+          ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS );
+        foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
+            delete $ARGS{ $type . "-" . $Ticket->id };
+            delete $ARGS{ $Ticket->id . "-" . $type };
+        }
 
-while ( my $Ticket = $Tickets->Next ) {
-    next unless ( $ARGS{ "UpdateTicket" . $Ticket->Id } );
-
-    #Update the links
-    $ARGS{'id'} = $Ticket->id;
-    $queues{ $Ticket->QueueObj->Id }++;
-
-    my @updateresults, ProcessUpdateMessage(
-            TicketObj => $Ticket,
-            ARGSRef   => \%ARGS,
-        );
-
-    #Update the basics.
-    my @basicresults =
-      ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
-    my @dateresults =
-      ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS );
-
-    #Update the watchers
-    my @watchresults =
-      ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS );
-
-    foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
-        $ARGS{ $Ticket->id . "-" . $type } = $ARGS{"Ticket-$type"};
-        $ARGS{ $type . "-" . $Ticket->id } = $ARGS{"$type-Ticket"};
-    }
-    @linkresults =
-      ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS );
-    foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
-        delete $ARGS{ $type . "-" . $Ticket->id };
-        delete $ARGS{ $Ticket->id . "-" . $type };
-    }
-
-    my @cfresults;
-
-    foreach my $list ( $cf_add_keys, $cf_del_keys ) {
-        next unless $list->[0];
+        my @cfresults;
 
+        foreach my $list ( $cf_add_keys, $cf_del_keys ) {
+            next unless $list->[0];
 
-        my $op;
-        if ( $list->[0] =~ /Add/ ) {
-            $op = 'add';
 
-        }
-        elsif ( $list->[0] =~ /Del/ ) {
-            $op = 'del';
-        }
-        else {
-            $RT::Logger->crit(
-                "Got an op that was neither add nor delete. can never happen"
-                  . $list->[0] );
-            last;
-        }
+            my $op;
+            if ( $list->[0] =~ /Add/ ) {
+                $op = 'add';
 
-        foreach my $key (@$list) {
-            my ( $cfid, $cf );
-            next if $key =~ /CustomField-(\d+)-Category$/;
-            if ( $key =~ /CustomField-(\d+)-/ ) {
-                $cfid = $1;
-                $cf   = RT::CustomField->new( $session{'CurrentUser'} );
-                $cf->Load($cfid);
             }
-            else {next}
-            my @values =
-              ref( $ARGS{$key} ) eq 'ARRAY'
-              ? @{ $ARGS{$key} }
-              : ( $ARGS{$key} );
-            map { s/(\r\n|\r)/\n/g; } @values;    # fix the newlines
-                 # now break the multiline values into multivalues
-            @values = map { split( /\n/, $_ ) } @values
-              unless ( $cf->SingleValue );
-
-            my $current_values = $Ticket->CustomFieldValues($cfid);
-            foreach my $value (@values) {
-                if ( $op eq 'del' && $current_values->HasEntry($value) ) {
-                    my ( $id, $msg ) = $Ticket->DeleteCustomFieldValue(
-                        Field => $cfid,
-                        Value => $value
-                    );
-                    push @cfresults, $msg;
-                }
+            elsif ( $list->[0] =~ /Del/ ) {
+                $op = 'del';
+            }
+            else {
+                $RT::Logger->crit(
+                    "Got an op that was neither add nor delete. can never happen"
+                      . $list->[0] );
+                last;
+            }
 
-                elsif ( $op eq 'add' && !$current_values->HasEntry($value) ) {
-                    my ( $id, $msg ) = $Ticket->AddCustomFieldValue(
-                        Field => $cfid,
-                        Value => $value
-                    );
-                    push @cfresults, $msg;
+            foreach my $key (@$list) {
+                my ( $cfid, $cf );
+                next if $key =~ /CustomField-(\d+)-Category$/;
+                if ( $key =~ /CustomField-(\d+)-/ ) {
+                    $cfid = $1;
+                    $cf   = RT::CustomField->new( $session{'CurrentUser'} );
+                    $cf->Load($cfid);
+                }
+                else {next}
+                my @values =
+                  ref( $ARGS{$key} ) eq 'ARRAY'
+                  ? @{ $ARGS{$key} }
+                  : ( $ARGS{$key} );
+                map { s/(\r\n|\r)/\n/g; } @values;    # fix the newlines
+                     # now break the multiline values into multivalues
+                @values = map { split( /\n/, $_ ) } @values
+                  unless ( $cf->SingleValue );
+
+                my $current_values = $Ticket->CustomFieldValues($cfid);
+                foreach my $value (@values) {
+                    if ( $op eq 'del' && $current_values->HasEntry($value) ) {
+                        my ( $id, $msg ) = $Ticket->DeleteCustomFieldValue(
+                            Field => $cfid,
+                            Value => $value
+                        );
+                        push @cfresults, $msg;
+                    }
+
+                    elsif ( $op eq 'add' && !$current_values->HasEntry($value) ) {
+                        my ( $id, $msg ) = $Ticket->AddCustomFieldValue(
+                            Field => $cfid,
+                            Value => $value
+                        );
+                        push @cfresults, $msg;
+                    }
                 }
             }
         }
-    }
-    my @tempresults = (
-        @watchresults,  @basicresults, @dateresults,
-        @updateresults, @linkresults,  @cfresults
-    );
+        my @tempresults = (
+            @watchresults,  @basicresults, @dateresults,
+            @updateresults, @linkresults,  @cfresults
+        );
 
-    @tempresults =
-      map { loc( "Ticket [_1]: [_2]", $Ticket->Id, $_ ) } @tempresults;
+        @tempresults =
+          map { loc( "Ticket [_1]: [_2]", $Ticket->Id, $_ ) } @tempresults;
+
+        @results = ( @results, @tempresults );
+    }
 
-    @results = ( @results, @tempresults );
+    # Cleanup WebUI
+    delete $session{'Attachments'};
 }
 
 my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );

Modified: rt/3.8/branches/html-css-cleanup/share/html/SelfService/Elements/Tabs
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/SelfService/Elements/Tabs	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/SelfService/Elements/Tabs	Wed Feb  4 10:23:40 2009
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-% $m->callback( %ARGS, tabs => $tabs );
+% $m->callback( %ARGS, tabs => $tabs, actions => $actions );
 <& /Elements/PageLayout,
     current_toptab => $current_toptab,
     current_tab => $current_tab,

Modified: rt/3.8/branches/html-css-cleanup/share/html/Ticket/Elements/ShowTransactionAttachments
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Ticket/Elements/ShowTransactionAttachments	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Ticket/Elements/ShowTransactionAttachments	Wed Feb  4 10:23:40 2009
@@ -142,7 +142,15 @@
 
     # if it's an image, show it as an image
     elsif ( RT->Config->Get('ShowTransactionImages') and  $message->ContentType =~ /^image\//i ) {
-        $m->out('<img src="'
+        my $filename =  $message->Filename || loc('(untitled)');
+        $m->out('<img'
+              . ' alt="'
+              . $filename
+              . '"' 
+              . ' title="'
+              . $filename
+              . '"' 
+              . ' src="'
               . $AttachPath . '/'
               . $Transaction->Id . '/'
               . $message->Id

Modified: rt/3.8/branches/html-css-cleanup/share/html/Ticket/ModifyAll.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/Ticket/ModifyAll.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/Ticket/ModifyAll.html	Wed Feb  4 10:23:40 2009
@@ -114,9 +114,22 @@
 </td></tr>
 %    } # end if while
 % } # end of if
+% if (exists $session{'Attachments'}) {
+<tr><td><&|/l&>Attached file</&>:</td>
+<td>
+<&|/l&>Check box to delete</&><br />
+% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+<input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
+% } # end of foreach
+</td>
+</tr>
+% } # end of if
+
   <tr>
     <td class="label"><&|/l&>Attach</&>:</td>
-    <td class="entry"><input name="UpdateAttachment" type="file" /></td>
+    <td class="entry"><input name="Attach" type="file" />
+    <input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" />
+    <input type="hidden" class="hidden" name="UpdateAttach" value="1" /></td>
   </tr>
   <tr>
     <td class="labeltop"><&|/l&>Content</&>:</td>
@@ -156,11 +169,40 @@
 $CanComment = 1 if ( $Ticket->CurrentUserHasRight('CommentOnTicket') or
                      $Ticket->CurrentUserHasRight('ModifyTicket') );
 
+# {{{ deal with deleting uploaded attachments
+foreach my $key (keys %ARGS) {
+    if ($key =~ m/^DeleteAttach-(.+)$/) {
+        delete $session{'Attachments'}{$1};
+    }
+    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
+}
+# }}}
+
+# {{{ store the uploaded attachment in session
+if ($ARGS{'Attach'}) {            # attachment?
+    my $attachment = MakeMIMEEntity(
+        AttachmentFieldName => 'Attach'
+    );
+
+    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
+    $session{'Attachments'} = {
+        %{$session{'Attachments'} || {}},
+        $file_path => $attachment,
+    };
+}
+# }}}
+
+# delete temporary storage entry to make WebUI clean
+unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
+    delete $session{'Attachments'};
+}
+# }}}
+
 
 $m->callback( TicketObj => $Ticket, ARGSRef => \%ARGS );
 my @results;
 
-unless ($OnlySearchForPeople or $OnlySearchForGroup ) {
+unless ($OnlySearchForPeople or $OnlySearchForGroup or $ARGS{'AddMoreAttach'} ) {
     # There might be two owners. 
     if ( ref ($ARGS{'Owner'} )) {
         my @owners =@{$ARGS{'Owner'}};
@@ -174,17 +216,13 @@
     push @results, ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS);
     push @results, ProcessObjectCustomFieldUpdates( Object => $Ticket, ARGSRef => \%ARGS);
     push @results, ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS);
-
-    if ($ARGS{'UpdateAttachment'}) {
-        my $attachment = MakeMIMEEntity(
-            AttachmentFieldName => 'UpdateAttachment'
-        );
-        my $file_path = Encode::decode_utf8("$ARGS{'UpdateAttachment'}");
-        $ARGS{'UpdateAttachments'}->{ $file_path } = $attachment;
-        delete $ARGS{'UpdateAttachment'};
-    }
-
+    
+    # Add session attachments if any to be processed by ProcessUpdateMessage
+    $ARGS{'UpdateAttachments'} = $session{'Attachments'} if ( $session{'Attachments'} );
     push @results, ProcessUpdateMessage( TicketObj => $Ticket, ARGSRef=>\%ARGS );
+    # Cleanup WebUI
+    delete $session{'Attachments'};
+
     push @results, ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
     push @results, ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS);
 }

Modified: rt/3.8/branches/html-css-cleanup/share/html/User/Elements/DelegateRights
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/User/Elements/DelegateRights	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/User/Elements/DelegateRights	Wed Feb  4 10:23:40 2009
@@ -57,7 +57,7 @@
 % }
 <table width="100%" border="0" cellspacing="0" cellpadding="3">
 <tr>
-        <th width="15%"><&|/l&>Personal groups:</&></th>
+        <th width="15%"><&|/l&>Personal Groups</&>:</th>
 % while (my $pg = $personalgroups->Next) {
 <th><%$pg->Name%></th>
 % }

Modified: rt/3.8/branches/html-css-cleanup/share/html/User/Groups/index.html
==============================================================================
--- rt/3.8/branches/html-css-cleanup/share/html/User/Groups/index.html	(original)
+++ rt/3.8/branches/html-css-cleanup/share/html/User/Groups/index.html	Wed Feb  4 10:23:40 2009
@@ -50,7 +50,7 @@
     current_subtab => 'User/Groups/index.html', 
     Title => $title &>
 
-<&|/l&>Personal groups</&>:<br />
+<% $title %>:<br />
 <ul>
 %while ( my $Group = $Groups->Next) {
 <li><a href="Modify.html?id=<%$Group->id%>"><%$Group->Name || loc('(empty)')%></a><br />
@@ -60,7 +60,7 @@
 <%INIT>
 my $Groups = RT::Groups->new($session{'CurrentUser'});
 $Groups->LimitToPersonalGroupsFor($session{'CurrentUser'}->PrincipalId());
-my $title = loc('Personal groups');
+my $title = loc('Personal Groups');
 
 </%INIT>
 <%ARGS>

Modified: rt/3.8/branches/html-css-cleanup/t/api/date.t
==============================================================================
--- rt/3.8/branches/html-css-cleanup/t/api/date.t	(original)
+++ rt/3.8/branches/html-css-cleanup/t/api/date.t	Wed Feb  4 10:23:40 2009
@@ -2,7 +2,17 @@
 
 use Test::MockTime qw(set_fixed_time restore_time);
 
-use Test::More tests => 167;
+use Test::More;
+my $tests = 167;
+my $localized_datetime_tests = eval { require DateTime; 1; } && eval { require DateTime::Locale; 1; };
+
+if ( $localized_datetime_tests ) {
+    # Include RT::Date::LocalizedDateTime tests
+    $tests += 7;
+}
+
+plan tests => $tests;
+
 
 use warnings; use strict;
 use RT::Test;
@@ -100,6 +110,9 @@
     is($date->Get(Format =>'RFC2822'),
        'Thu, 1 Jan 1970 00:00:00 +0000',
        "RFC2822 format with defaults");
+    is($date->Get(Format =>'LocalizedDateTime'),
+       'Thu, Jan 1, 1970 12:00:00 AM',
+       "LocalizedDateTime format with defaults") if ( $localized_datetime_tests );
 
     is($date->ISO(Time => 0),
        '1970-01-01',
@@ -110,6 +123,9 @@
     is($date->RFC2822(Time => 0),
        'Thu, 1 Jan 1970',
        "RFC2822 format without time part");
+    is($date->LocalizedDateTime(Time => 0),
+       'Thu, Jan 1, 1970',
+       "LocalizedDateTime format without time part") if ( $localized_datetime_tests );
 
     is($date->ISO(Date => 0),
        '00:00:00',
@@ -120,6 +136,9 @@
     is($date->RFC2822(Date => 0),
        '00:00:00 +0000',
        "RFC2822 format without date part");
+    is($date->LocalizedDateTime(Date => 0),
+       '12:00:00 AM',
+       "LocalizedDateTime format without date part") if ( $localized_datetime_tests );
 
     is($date->ISO(Date => 0, Seconds => 0),
        '00:00',
@@ -138,6 +157,19 @@
        '00:00:00 +0000',
        "RFC2822 format without 'day of week' and date parts(corner case test)");
 
+    is($date->LocalizedDateTime(AbbrDay => 0),
+       'Thursday, Jan 1, 1970 12:00:00 AM',
+       "LocalizedDateTime format without abbreviation of day") if ( $localized_datetime_tests );
+    is($date->LocalizedDateTime(AbbrMonth => 0),
+       'Thu, January 1, 1970 12:00:00 AM',
+       "LocalizedDateTime format without abbreviation of month") if ( $localized_datetime_tests );
+    is($date->LocalizedDateTime(DateFormat => 'short_date_format'),
+       '1/1/70 12:00:00 AM',
+       "LocalizedDateTime format with non default DateFormat") if ( $localized_datetime_tests );
+    is($date->LocalizedDateTime(TimeFormat => 'short_time_format'),
+       'Thu, Jan 1, 1970 12:00 AM',
+       "LocalizedDateTime format with non default TimeFormat") if ( $localized_datetime_tests );
+
     is($date->Date,
        '1970-01-01',
        "the default format for the 'Date' method is ISO");

Modified: rt/3.8/branches/html-css-cleanup/t/mail/gnupg-realmail.t
==============================================================================
--- rt/3.8/branches/html-css-cleanup/t/mail/gnupg-realmail.t	(original)
+++ rt/3.8/branches/html-css-cleanup/t/mail/gnupg-realmail.t	Wed Feb  4 10:23:40 2009
@@ -10,7 +10,7 @@
 plan skip_all => 'gpg executable is required.'
     unless RT::Test->find_executable('gpg');
 
-plan tests => 176;
+plan tests => 183;
 
 use Digest::MD5 qw(md5_hex);
 
@@ -64,6 +64,14 @@
     }
 }
 
+$eid = 18;
+{
+    my ($usage, $format, $attachment) = ('signed', 'inline', 'plain');
+    ++$eid;
+    diag "Email $eid: $usage, $attachment email with $format format" if $ENV{TEST_VERBOSE};
+    eval { email_ok($eid, $usage, $format, $attachment) };
+}
+
 sub email_ok {
     my ($eid, $usage, $format, $attachment) = @_;
     diag "email_ok $eid: $usage, $format, $attachment" if $ENV{'TEST_VERBOSE'};

Modified: rt/3.8/branches/html-css-cleanup/t/mail/gnupg-reverification.t
==============================================================================
--- rt/3.8/branches/html-css-cleanup/t/mail/gnupg-reverification.t	(original)
+++ rt/3.8/branches/html-css-cleanup/t/mail/gnupg-reverification.t	Wed Feb  4 10:23:40 2009
@@ -10,7 +10,7 @@
 plan skip_all => 'gpg executable is required.'
     unless RT::Test->find_executable('gpg');
 
-plan tests => 111;
+plan tests => 120;
 
 use File::Temp qw(tempdir);
 my $homedir = tempdir( CLEANUP => 1 );


More information about the Rt-commit mailing list