[Jifty-commit] jifty branch, js-refactor, created. cd3b37566f1d0cb7ddb57a9ecbbe3c0d4b931306

Jifty commits jifty-commit at lists.jifty.org
Thu Dec 10 14:09:43 EST 2009


The branch, js-refactor has been created
        at  cd3b37566f1d0cb7ddb57a9ecbbe3c0d4b931306 (commit)

- Log -----------------------------------------------------------------
commit cbc0cd20a84a3005c50a2e3d8b1ec822a4e6f862
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jun 6 22:05:02 2007 +0000

    Branch for refactoring and slimming of our JavaScript
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3368 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

commit 10cceccda18077ff033adce8a4a9ad50b5368898
Merge: cbc0cd2 719cfed
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jun 6 22:22:33 2007 +0000

    Merge from /jifty/trunk:3369
    
    r21744 at zot (orig r3369):  trs | 2007-06-06 18:20:52 -0400
     r21743 at zot:  tom | 2007-06-06 18:20:23 -0400
     add_javascript method to simplify adding JS libs and updated doc
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3370 e84bef0a-9b06-0410-84ba-c4c9edb13aeb


commit 0014805a4f1580a44e932e509c52acb86d084d8e
Merge: 10ccecc fee8f7a
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 7 00:15:39 2007 +0000

    Merge from /jifty/trunk:3372
    
    r21748 at zot (orig r3371):  trs | 2007-06-06 18:30:03 -0400
     r21747 at zot:  tom | 2007-06-06 18:29:47 -0400
     Clarifying comments about the plugins lists
    
    r21750 at zot (orig r3372):  trs | 2007-06-06 20:15:02 -0400
     r21749 at zot:  tom | 2007-06-06 20:14:44 -0400
     Update dep to include version of Module::Pluggable that is patched so that compile errors are caught
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3373 e84bef0a-9b06-0410-84ba-c4c9edb13aeb


commit e0c3a85fe65486bd9f469fb3d47043dd03fe0658
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 7 01:12:27 2007 +0000

    * Use Jifty::Util->share_root for finding plugin share roots so that if Jifty isn't installed it still DTRT
    * Only calculate static roots once and report on plugins adding roots (like the mason handler does)
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3374 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Plugin.pm b/lib/Jifty/Plugin.pm
index 4a9c040..9a277c3 100644
--- a/lib/Jifty/Plugin.pm
+++ b/lib/Jifty/Plugin.pm
@@ -98,8 +98,7 @@ sub _calculate_share {
         eval { $self->{share} = module_dir($class) };
     }
     unless ( $self->{share} ) {
-        local $@; # We're just avoiding File::ShareDir's failure behaviour of dying
-        eval { $self->{share} = module_dir('Jifty') };
+        $self->{share} = Jifty::Util->share_root;
         if ( $self->{'share'} ) {
             my $class_to_path = $class;
             $class_to_path =~ s|::|/|g;
diff --git a/lib/Jifty/View/Static/Handler.pm b/lib/Jifty/View/Static/Handler.pm
index 40c98b9..946fb01 100644
--- a/lib/Jifty/View/Static/Handler.pm
+++ b/lib/Jifty/View/Static/Handler.pm
@@ -50,7 +50,20 @@ Create a new static file handler. Likely, only the C<Jifty::Handler> needs to do
 =cut
 sub new {
     my $class = shift;
-    my $self = {};
+    
+    my @roots = (Jifty->config->framework('Web')->{StaticRoot});
+    for my $plugin ( Jifty->plugins ) {
+        my $root = $plugin->static_root;
+        if ( -d $root and -r $root ) {
+            push @roots, $root;
+            Jifty->log->debug( "Plugin @{[ref($plugin)]} static root added: (@{[$root ||'']})");
+        }
+    }
+    push @roots, (Jifty->config->framework('Web')->{DefaultStaticRoot});
+
+    my $self = {
+        roots => \@roots
+    };
     bless $self, $class;
 }
 
@@ -138,14 +151,11 @@ sub template_exists {
 sub file_path {
     my $self    = shift;
     my $file    = shift;
-    my @options = (Jifty->config->framework('Web')->{StaticRoot});
-    push @options, grep { -d $_ && -r $_ } map {$_->static_root} Jifty->plugins;
-    push @options, (Jifty->config->framework('Web')->{DefaultStaticRoot});
 
     # Chomp a leading "/static" - should this be configurable?
     $file =~ s/^\/*?static//; 
 
-    foreach my $path (@options) {
+    foreach my $path ( @{$self->{'roots'}} ) {
         my $abspath = Jifty::Util->absolute_path( File::Spec->catdir($path,$file ));
         # If the user is trying to request something outside our static root, 
         # decline the request

commit 77985def43bb1b41c4fd1387873e2cb72a87d31c
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 7 01:12:49 2007 +0000

    Pull out css_browser_selector.js into a plugin (and update it)
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3375 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index 8a0b731..4a16b9c 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -300,18 +300,18 @@ sub initial_config {
     my $self = shift;
     my $guess = $self->guess(@_);
     $guess->{'framework'}->{'ConfigFileVersion'} = 2;
-
+    
     # These are the plugins which new apps will get by default
-            $guess->{'framework'}->{'Plugins'} = [
-              { LetMe               => {}, },
-                { SkeletonApp            => {}, },
-                { REST               => {}, },
-                { Halo               => {}, },
-                { ErrorTemplates     => {}, },
-                { OnlineDocs         => {}, },
-                { CompressedCSSandJS => {}, },
-                { AdminUI            => {}, }
-                ];
+    $guess->{'framework'}->{'Plugins'} = [
+        { LetMe                 => {}, },
+        { SkeletonApp           => {}, },
+        { REST                  => {}, },
+        { Halo                  => {}, },
+        { ErrorTemplates        => {}, },
+        { OnlineDocs            => {}, },
+        { CompressedCSSandJS    => {}, },
+        { AdminUI               => {}, },
+    ];
     return $guess;
 }
 
@@ -320,7 +320,7 @@ sub initial_config {
 =head2 update_config  $CONFIG
 
 Takes an application's configuration as a hashref.  Right now, it just sets up
-plugins that match an older jifty version's defaults
+plugins that match an older Jifty versions' defaults.
 
 =cut
 
@@ -328,17 +328,20 @@ sub update_config {
     my $self = shift;
     my $config = shift;
     if ( $config->{'framework'}->{'ConfigFileVersion'} <2) {
-            # These are the plugins which old apps expect because their
-            # features used to be in the core.
-            unshift (@{$config->{'framework'}->{'Plugins'}}, 
-                { SkeletonApp            => {}, },
-                { REST               => {}, },
-                { Halo               => {}, },
-                { ErrorTemplates     => {}, },
-                { OnlineDocs         => {}, },
-                { CompressedCSSandJS => {}, },
-                { AdminUI            => {}, }
-            );
+        # These are the plugins which old apps expect because their
+        # features used to be in the core.
+        unshift (@{$config->{'framework'}->{'Plugins'}}, 
+            { SkeletonApp           => {}, },
+            { REST                  => {}, },
+            { Halo                  => {}, },
+            { ErrorTemplates        => {}, },
+            { OnlineDocs            => {}, },
+            { CompressedCSSandJS    => {}, },
+            { AdminUI               => {}, },
+
+            # JS libs which are now turning into plugins
+            { CSSBrowserSelectorJS  => {}, }
+        );
     }
 
     return $config;
diff --git a/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm b/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm
new file mode 100644
index 0000000..6eb4a63
--- /dev/null
+++ b/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::CSSBrowserSelectorJS;
+use base qw/Jifty::Plugin Class::Accessor/;
+
+=head1 NAME
+
+Jifty::Plugin::CSSBrowserSelectorJS
+
+=head1 DESCRIPTION
+
+This plugin provides JavaScript which gives you means to write CSS for specific
+browsers.  It is enabled by default (unless you use C<ConfigFileVersion: 2>).
+
+See L<http://rafael.adm.br/css_browser_selector/> for more details.
+
+=cut
+
+sub init { Jifty->web->add_javascript( 'css_browser_selector.js' ) }
+
+1;
diff --git a/lib/Jifty/Web.pm b/lib/Jifty/Web.pm
index 7e836b8..5558d19 100644
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@ -68,7 +68,6 @@ __PACKAGE__->javascript_libs([qw(
     yui/menu.js
     app.js
     app_behaviour.js
-    css_browser_selector.js
 )]);
 
 =head1 METHODS
diff --git a/share/plugins/Jifty/Plugin/CSSBrowserSelectorJS/web/static/js/css_browser_selector.js b/share/plugins/Jifty/Plugin/CSSBrowserSelectorJS/web/static/js/css_browser_selector.js
new file mode 100644
index 0000000..ca1a90d
--- /dev/null
+++ b/share/plugins/Jifty/Plugin/CSSBrowserSelectorJS/web/static/js/css_browser_selector.js
@@ -0,0 +1,15 @@
+// CSS Browser Selector   v0.2.5
+// Documentation:         http://rafael.adm.br/css_browser_selector
+// License:               http://creativecommons.org/licenses/by/2.5/
+// Author:                Rafael Lima (http://rafael.adm.br)
+// Contributors:          http://rafael.adm.br/css_browser_selector#contributors
+var css_browser_selector = function() {
+	var 
+		ua=navigator.userAgent.toLowerCase(),
+		is=function(t){ return ua.indexOf(t) != -1; },
+		h=document.getElementsByTagName('html')[0],
+		b=(!(/opera|webtv/i.test(ua))&&/msie (\d)/.test(ua))?('ie ie'+RegExp.$1):is('gecko/')? 'gecko':is('opera/9')?'opera opera9':/opera (\d)/.test(ua)?'opera opera'+RegExp.$1:is('konqueror')?'konqueror':is('applewebkit/')?'webkit safari':is('mozilla/')?'gecko':'',
+		os=(is('x11')||is('linux'))?' linux':is('mac')?' mac':is('win')?' win':'';
+	var c=b+os+' js';
+	h.className += h.className?' '+c:c;
+}();
\ No newline at end of file
diff --git a/share/web/static/js/css_browser_selector.js b/share/web/static/js/css_browser_selector.js
deleted file mode 100644
index 52cde04..0000000
--- a/share/web/static/js/css_browser_selector.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// CSS Browser Selector   v0.4
-// Documentation:         http://rafael.adm.br/css_browser_selector
-// License:               http://creativecommons.org/licenses/by/2.5/
-// Author:                Rafael Lima (http://rafael.adm.br)
-// Contributors:          http://rafael.adm.br/css_browser_selector#contributors
-function css_browser_selector() {
-	var ua = navigator.userAgent.toLowerCase(); 
-	var h = document.getElementsByTagName('html')[0];
-	if(ua.indexOf('msie') != -1 && !(ua.indexOf('opera') != -1) && (ua.indexOf('webtv') == -1) ) h.className='ie';
-	else if(ua.indexOf('gecko/') != -1) h.className='gecko';
-	else if(ua.indexOf('opera') != -1) h.className='opera';
-	else if(ua.indexOf('konqueror') != -1) h.className='konqueror';
-	else if(ua.indexOf('applewebkit/') != - 1) h.className='safari';
-}
-css_browser_selector();
\ No newline at end of file

commit db27df9a9114359e47304df37c5db48d1c3804c4
Merge: 77985de a06f80e
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 7 08:08:10 2007 +0000

    Merge from /jifty/trunk:3383
    
    r21775 at zot (orig r3376):  clkao | 2007-06-06 21:57:13 -0400
    correct en.po charset.
    r21776 at zot (orig r3377):  clkao | 2007-06-06 22:24:03 -0400
    In SPA mode, allow Action::Redirect to happen within webservice and have
    the client js accepts it.
    
    r21777 at zot (orig r3378):  jesse | 2007-06-06 23:13:31 -0400
     r58024 at pinglin:  jesse | 2007-06-05 19:23:04 -0400
     * Removing a spurious 'warn'
    
    r21778 at zot (orig r3379):  jesse | 2007-06-06 23:14:49 -0400
     r58045 at pinglin:  jesse | 2007-06-06 23:02:18 -0400
     * Added a 'feedback' plugin
    
    
    r21779 at zot (orig r3380):  jesse | 2007-06-06 23:14:58 -0400
     r58046 at pinglin:  jesse | 2007-06-06 23:10:17 -0400
     * docs!
    
    r21782 at zot (orig r3381):  bartb | 2007-06-07 00:30:49 -0400
    typo fix
    r21784 at zot (orig r3382):  trs | 2007-06-07 00:48:54 -0400
     r21781 at zot:  tom | 2007-06-07 00:45:50 -0400
     Only squelch "can't locate" errors relating to the class we're trying to require.  If it's something else, it's likely a module use'd by the module we're requiring.
    
    r21788 at zot (orig r3383):  trs | 2007-06-07 01:38:08 -0400
     r21787 at zot:  tom | 2007-06-07 01:37:58 -0400
     Back that back out for now -- breaks Doxory
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3384 e84bef0a-9b06-0410-84ba-c4c9edb13aeb


commit b0aa974dc78da0e0180dfd5b1d07a28a730f96f9
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 8 03:35:23 2007 +0000

    formatDate.js -> FormatDateJS
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3394 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index 4a16b9c..bdd0a31 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -341,6 +341,7 @@ sub update_config {
 
             # JS libs which are now turning into plugins
             { CSSBrowserSelectorJS  => {}, }
+            { FormatDateJS          => {}, }
         );
     }
 
diff --git a/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm b/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm
index 6eb4a63..340c731 100644
--- a/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm
+++ b/lib/Jifty/Plugin/CSSBrowserSelectorJS.pm
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 
 package Jifty::Plugin::CSSBrowserSelectorJS;
-use base qw/Jifty::Plugin Class::Accessor/;
+use base qw/Jifty::Plugin/;
 
 =head1 NAME
 
@@ -11,7 +11,7 @@ Jifty::Plugin::CSSBrowserSelectorJS
 =head1 DESCRIPTION
 
 This plugin provides JavaScript which gives you means to write CSS for specific
-browsers.  It is enabled by default (unless you use C<ConfigFileVersion: 2>).
+browsers.
 
 See L<http://rafael.adm.br/css_browser_selector/> for more details.
 
diff --git a/lib/Jifty/Plugin/FormatDateJS.pm b/lib/Jifty/Plugin/FormatDateJS.pm
new file mode 100644
index 0000000..2c876fa
--- /dev/null
+++ b/lib/Jifty/Plugin/FormatDateJS.pm
@@ -0,0 +1,20 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::FormatDateJS;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::FormatDateJS
+
+=head1 DESCRIPTION
+
+This plugin provides JavaScript which adds a formatDate function to JavaScript
+Dates.
+
+=cut
+
+sub init { Jifty->web->add_javascript( 'formatDate.js' ) }
+
+1;
diff --git a/share/web/static/js/formatDate.js b/share/plugins/Jifty/Plugin/FormatDateJS/web/static/js/formatDate.js
similarity index 100%
rename from share/web/static/js/formatDate.js
rename to share/plugins/Jifty/Plugin/FormatDateJS/web/static/js/formatDate.js

commit f3dca3b2020c9809609055a6ecfb678d502e6485
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 8 04:26:15 2007 +0000

    Extract JSAN (DOM.Events -> YAHOO.util.Event)
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3395 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index bdd0a31..80b614a 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -342,6 +342,7 @@ sub update_config {
             # JS libs which are now turning into plugins
             { CSSBrowserSelectorJS  => {}, }
             { FormatDateJS          => {}, }
+            { JSAN                  => {}, }
         );
     }
 
diff --git a/lib/Jifty/Plugin/JSAN.pm b/lib/Jifty/Plugin/JSAN.pm
new file mode 100644
index 0000000..9bfcdf7
--- /dev/null
+++ b/lib/Jifty/Plugin/JSAN.pm
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::JSAN;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::JSAN
+
+=head1 DESCRIPTION
+
+This plugin provides the base of the JSAN JavaScript library.
+
+See L<http://openjsan.org> for more information.
+
+=cut
+
+sub init {
+    Jifty->web->add_javascript(qw(
+        jsan/JSAN.js
+        setup_jsan.js
+        jsan/Upgrade/Array/push.js
+        jsan/DOM/Events.js
+    ));
+}
+
+1;
diff --git a/lib/Jifty/Web.pm b/lib/Jifty/Web.pm
index 5558d19..0112523 100644
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@ -30,11 +30,6 @@ __PACKAGE__->mk_classdata($_)
            cached_javascript cached_javascript_digest cached_javascript_time javascript_libs);
 
 __PACKAGE__->javascript_libs([qw(
-    jsan/JSAN.js
-    jsan/Push.js
-    setup_jsan.js
-    jsan/Upgrade/Array/push.js
-    jsan/DOM/Events.js
     json.js
     prototype.js
     cssquery/cssQuery.js
@@ -45,7 +40,6 @@ __PACKAGE__->javascript_libs([qw(
     scriptaculous/builder.js
     scriptaculous/effects.js
     scriptaculous/controls.js
-    formatDate.js
     jifty.js
     jifty_utils.js
     jifty_subs.js
diff --git a/lib/Jifty/Web/Form/Field.pm b/lib/Jifty/Web/Form/Field.pm
index ef06e70..788ebab 100644
--- a/lib/Jifty/Web/Form/Field.pm
+++ b/lib/Jifty/Web/Form/Field.pm
@@ -635,7 +635,7 @@ sub focus_javascript {
     return undef;
     if($self->focus) {
         return qq{document.getElementById("@{[$self->element_id]}").focus()};
-        return qq{DOM.Events.addListener( window, "load", function(){document.getElementById("@{[$self->element_id]}").focus()})};
+        return qq{YAHOO.util.Event.addListener( window, "load", function(){document.getElementById("@{[$self->element_id]}").focus()})};
     }
 }
 
diff --git a/share/web/static/js/jsan/DOM/Events.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/DOM/Events.js
similarity index 100%
rename from share/web/static/js/jsan/DOM/Events.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/DOM/Events.js
diff --git a/share/web/static/js/jsan/JSAN.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/JSAN.js
similarity index 100%
rename from share/web/static/js/jsan/JSAN.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/JSAN.js
diff --git a/share/web/static/js/jsan/Push.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Push.js
similarity index 100%
rename from share/web/static/js/jsan/Push.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Push.js
diff --git a/share/web/static/js/jsan/Upgrade.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade.js
similarity index 100%
rename from share/web/static/js/jsan/Upgrade.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade.js
diff --git a/share/web/static/js/jsan/Upgrade/Array/push.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade/Array/push.js
similarity index 100%
rename from share/web/static/js/jsan/Upgrade/Array/push.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade/Array/push.js
diff --git a/share/web/static/js/jsan/Upgrade/Function/apply.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade/Function/apply.js
similarity index 100%
rename from share/web/static/js/jsan/Upgrade/Function/apply.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/jsan/Upgrade/Function/apply.js
diff --git a/share/web/static/js/behaviour.js b/share/web/static/js/behaviour.js
index 3fac85e..75084ee 100644
--- a/share/web/static/js/behaviour.js
+++ b/share/web/static/js/behaviour.js
@@ -1,6 +1,5 @@
 /*
-   Modified to fix some bugs, use a different css query engine, and to
-   to use JSAN classes.
+   Modified to fix some bugs and use a different css query engine.
    
    Based on Behaviour v1.1 by Ben Nolan, June 2005, which was based
    largely on the work of Simon Willison.
@@ -27,9 +26,6 @@
 
 */   
 
-JSAN.use("DOM.Events");
-JSAN.use("Upgrade.Array.push");
-
 var Behaviour = {
     list: [],
     
@@ -56,4 +52,4 @@ var Behaviour = {
     }
 }    
 
-DOM.Events.addListener( window, "load", function() { Behaviour.apply() } );
+YAHOO.util.Event.addListener( window, "load", function() { Behaviour.apply() } );
diff --git a/share/web/static/js/bps_util.js b/share/web/static/js/bps_util.js
index d59f1ee..5a7a29f 100644
--- a/share/web/static/js/bps_util.js
+++ b/share/web/static/js/bps_util.js
@@ -16,8 +16,6 @@ function createCalendarLink(id) {
     return Jifty.Calendar.registerDateWidget( id );
 }
 
-JSAN.use("DOM.Events");
-
 function buttonToLink(e) {
     var link = document.createElement("a");
     link.setAttribute("href","#");
@@ -31,7 +29,7 @@ function buttonToLink(e) {
        and the form submitted normally (without any Ajax-ness)
     */
     if ( !onclick ) {
-        DOM.Events.addListener( link, "click", function(ev) {
+        YAHOO.util.Event.addListener( link, "click", function(ev) {
             var a = ev.target;
             var hidden = document.createElement("input");
             hidden.setAttribute("type", "hidden");
diff --git a/share/web/static/js/calendar.js b/share/web/static/js/calendar.js
index 89a67b4..810b163 100644
--- a/share/web/static/js/calendar.js
+++ b/share/web/static/js/calendar.js
@@ -1,5 +1,3 @@
-JSAN.use("DOM.Events");
-
 if ( typeof Jifty == "undefined" ) Jifty = { };
 
 Jifty.Calendar = {
@@ -8,8 +6,8 @@ Jifty.Calendar = {
         
         if ( !input ) return false;
 
-        DOM.Events.addListener( input, "focus", Jifty.Calendar.toggleCalendar );
-        DOM.Events.addListener( input, "blur", Jifty.Calendar.doBlur );
+        YAHOO.util.Event.addListener( input, "focus", Jifty.Calendar.toggleCalendar );
+        YAHOO.util.Event.addListener( input, "blur", Jifty.Calendar.doBlur );
         return true;
     },
 
@@ -136,5 +134,5 @@ Jifty.Calendar = {
     }
 };
 
-/*DOM.Events.addListener( window, "click", Jifty.Calendar.hideOpenCalendar );*/
+/*YAHOO.util.Event.addListener( window, "click", Jifty.Calendar.hideOpenCalendar );*/
 
diff --git a/share/web/static/js/context_menu.js b/share/web/static/js/context_menu.js
index 512b3d8..87b767c 100644
--- a/share/web/static/js/context_menu.js
+++ b/share/web/static/js/context_menu.js
@@ -1,5 +1,3 @@
-JSAN.use("DOM.Events");
-
 if (typeof Jifty == "undefined") Jifty = { };
 
 function prepExpandButton(e) {
@@ -107,6 +105,6 @@ Jifty.ContextMenu = {
     }
 };
 
-DOM.Events.addListener( document, "click", Jifty.ContextMenu.hideOpenMenu );
+YAHOO.util.Event.addListener( document, "click", Jifty.ContextMenu.hideOpenMenu );
 Behaviour.register( Jifty.ContextMenu.behaviourRules );
 
diff --git a/share/web/static/js/jifty.js b/share/web/static/js/jifty.js
index 6e1bb97..87fbb8c 100644
--- a/share/web/static/js/jifty.js
+++ b/share/web/static/js/jifty.js
@@ -427,14 +427,12 @@ Object.extend(Form.Element, {
              && !event.metaKey && !event.altKey && !event.ctrlKey )
         {
             if ( Form.Element.clickDefaultButton( event.target ) )
-                event.preventDefault();
+                YAHOO.util.Event.preventDefault( event );
         }
     }
 
 });
 
-JSAN.use("DOM.Events");
-
 
 // Form elements should focus if the CSS says so.
 Behaviour.register( { ".focus": function(e) {
@@ -449,7 +447,7 @@ Behaviour.register( { ".focus": function(e) {
 // Form elements should AJAX validate if the CSS says so
 Behaviour.register({
     'input.ajaxvalidation, textarea.ajaxvalidation, input.ajaxcanonicalization, textarea.ajaxcanonicalization': function(elt) {
-        DOM.Events.addListener(elt, "blur", function () {
+        YAHOO.util.Event.addListener(elt, "blur", function () {
                 Form.Element.validate(elt);
             });
     },
@@ -471,7 +469,7 @@ Behaviour.register({
             && !Element.hasClassName( e, "ajaxautocompletes" ) )
         {
             /* Do not use keydown as the event, it will not work as expected in Safari */
-            DOM.Events.addListener( e, "keypress", Form.Element.handleEnter );
+            YAHOO.util.Event.addListener( e, "keypress", Form.Element.handleEnter );
             Element.addClassName( e, "jifty_enter_handler_attached" );
         }
     },
diff --git a/share/web/static/js/key_bindings.js b/share/web/static/js/key_bindings.js
index 114414a..60788e5 100644
--- a/share/web/static/js/key_bindings.js
+++ b/share/web/static/js/key_bindings.js
@@ -1,8 +1,6 @@
 // Copyright 2004-2006, Best Practical Solutions, LLC
 // This Library is licensed to you under the same terms as Perl 5.x
 
-JSAN.use("DOM.Events");
-
 if ( typeof Jifty == "undefined" ) Jifty = {};
 
 Jifty.KeyBindings = {
@@ -13,7 +11,7 @@ Jifty.KeyBindings = {
         if ( Jifty.KeyBindings.listener )
             return;
         
-        Jifty.KeyBindings.listener = DOM.Events.addListener(
+        Jifty.KeyBindings.listener = YAHOO.util.Event.addListener(
                                         document,
                                         "keydown",
                                         Jifty.KeyBindings.doClick
@@ -21,7 +19,7 @@ Jifty.KeyBindings = {
     },
 
     deactivate: function() {
-        DOM.Events.removeListener(Jifty.KeyBindings.listener);
+        YAHOO.util.Event.removeListener(Jifty.KeyBindings.listener);
     },
 
     doClick: function(e) {
@@ -38,7 +36,7 @@ Jifty.KeyBindings = {
             var binding = Jifty.KeyBindings.get(code);
             
             if (binding) {
-                e.preventDefault();
+                YAHOO.util.Event.preventDefault( e );
                 
                 if (binding["action"] == "goto") {
                     document.location = (binding["data"]);

commit cd403ea594d45a4b02fae13c2fe322383c842d09
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 8 05:29:12 2007 +0000

    Extracted Rico
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3396 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index 80b614a..ba7d2ca 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -340,9 +340,10 @@ sub update_config {
             { AdminUI               => {}, },
 
             # JS libs which are now turning into plugins
-            { CSSBrowserSelectorJS  => {}, }
-            { FormatDateJS          => {}, }
-            { JSAN                  => {}, }
+            { CSSBrowserSelectorJS  => {}, },
+            { FormatDateJS          => {}, },
+            { JSAN                  => {}, },
+            { Rico                  => {}, },
         );
     }
 
diff --git a/lib/Jifty/Plugin/Rico.pm b/lib/Jifty/Plugin/Rico.pm
new file mode 100644
index 0000000..3a99948
--- /dev/null
+++ b/lib/Jifty/Plugin/Rico.pm
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Rico;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::Rico
+
+=head1 DESCRIPTION
+
+This plugin provides a modified (and outdated!) version of the Rico JavaScript
+library.
+
+See L<http://openrico.org> for more information.
+
+=cut
+
+sub init { Jifty->web->add_javascript( 'rico.js' ) }
+
+1;
diff --git a/lib/Jifty/Web.pm b/lib/Jifty/Web.pm
index 0112523..71e86fa 100644
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@ -30,6 +30,14 @@ __PACKAGE__->mk_classdata($_)
            cached_javascript cached_javascript_digest cached_javascript_time javascript_libs);
 
 __PACKAGE__->javascript_libs([qw(
+    yui/yahoo.js
+    yui/dom.js
+    yui/event.js
+    yui/calendar.js
+    yui/element-beta.js
+    yui/tabview.js
+    yui/container.js
+    yui/menu.js
     json.js
     prototype.js
     cssquery/cssQuery.js
@@ -51,15 +59,6 @@ __PACKAGE__->javascript_libs([qw(
     key_bindings.js
     context_menu.js
     bps_util.js
-    rico.js
-    yui/yahoo.js
-    yui/dom.js
-    yui/event.js
-    yui/calendar.js
-    yui/element-beta.js
-    yui/tabview.js
-    yui/container.js
-    yui/menu.js
     app.js
     app_behaviour.js
 )]);
diff --git a/share/web/static/js/rico.js b/share/plugins/Jifty/Plugin/Rico/web/static/js/rico.js
similarity index 100%
rename from share/web/static/js/rico.js
rename to share/plugins/Jifty/Plugin/Rico/web/static/js/rico.js

commit 1e70654a3708ac209d359752cfb1313d2c0e8d6f
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 8 07:43:22 2007 +0000

    * Update prototype.js from 1.4.0 to 1.5.1 -- backwards compat _seems_ to be okay
    * Switch from using cssQuery to $$() from Prototype for a ~200ms speed increase (as seen in HM)
    * Extract cssQuery
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3400 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index ba7d2ca..45f3b91 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -344,6 +344,7 @@ sub update_config {
             { FormatDateJS          => {}, },
             { JSAN                  => {}, },
             { Rico                  => {}, },
+            { cssQuery              => {}, },
         );
     }
 
diff --git a/lib/Jifty/Plugin/cssQuery.pm b/lib/Jifty/Plugin/cssQuery.pm
new file mode 100644
index 0000000..1faa1d0
--- /dev/null
+++ b/lib/Jifty/Plugin/cssQuery.pm
@@ -0,0 +1,29 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::cssQuery;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::cssQuery
+
+=head1 DESCRIPTION
+
+This plugin provides a slightly modified version of the cssQuery library
+by Dean Edwards.
+
+See L<http://http://dean.edwards.name/my/cssQuery/> for more information.
+
+=cut
+
+sub init {
+    Jifty->web->add_javascript(qw(
+        cssquery/cssQuery.js
+        cssquery/cssQuery-level2.js
+        cssquery/cssQuery-level3.js
+        cssquery/cssQuery-standard.js
+    ));
+}
+
+1;
diff --git a/lib/Jifty/Web.pm b/lib/Jifty/Web.pm
index 71e86fa..20fcd87 100644
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@ -40,10 +40,6 @@ __PACKAGE__->javascript_libs([qw(
     yui/menu.js
     json.js
     prototype.js
-    cssquery/cssQuery.js
-    cssquery/cssQuery-level2.js
-    cssquery/cssQuery-level3.js
-    cssquery/cssQuery-standard.js
     behaviour.js
     scriptaculous/builder.js
     scriptaculous/effects.js
diff --git a/share/web/static/js/cssquery/cssQuery-level2.js b/share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-level2.js
similarity index 100%
rename from share/web/static/js/cssquery/cssQuery-level2.js
rename to share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-level2.js
diff --git a/share/web/static/js/cssquery/cssQuery-level3.js b/share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-level3.js
similarity index 100%
rename from share/web/static/js/cssquery/cssQuery-level3.js
rename to share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-level3.js
diff --git a/share/web/static/js/cssquery/cssQuery-standard.js b/share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-standard.js
similarity index 100%
rename from share/web/static/js/cssquery/cssQuery-standard.js
rename to share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery-standard.js
diff --git a/share/web/static/js/cssquery/cssQuery.js b/share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery.js
similarity index 100%
rename from share/web/static/js/cssquery/cssQuery.js
rename to share/plugins/Jifty/Plugin/cssQuery/web/static/js/cssquery/cssQuery.js
diff --git a/share/web/static/js/behaviour.js b/share/web/static/js/behaviour.js
index 75084ee..43fa0b5 100644
--- a/share/web/static/js/behaviour.js
+++ b/share/web/static/js/behaviour.js
@@ -34,19 +34,24 @@ var Behaviour = {
     },
     
     apply: function() {
-	var root = arguments[0];
-	if(root) root = $(root);
+        var root = arguments[0];
+        if (root) root = $(root);
 
         for (var h = 0; sheet = Behaviour.list[h]; h++) {
             for (var selector in sheet) {
-		var start = new Date();
-                var elements = cssQuery(selector, root);
+                var start = new Date();
+                if ( root )
+                    root.getElementsBySelector( selector ).each( sheet[selector] );
+                else
+                    $$( selector ).each( sheet[selector] );
+                    
+                /*var elements = cssQuery(selector, root);
 
                 if ( !elements ) continue;
 
                 for (var i = 0; element = elements[i]; i++) {
                     sheet[selector](element);
-		}
+                }*/
             }
         }
     }
diff --git a/share/web/static/js/jifty.js b/share/web/static/js/jifty.js
index 87fbb8c..e04ca6b 100644
--- a/share/web/static/js/jifty.js
+++ b/share/web/static/js/jifty.js
@@ -595,7 +595,7 @@ function prepare_element_for_update(f) {
         // Find where we are going to go
         var element = $('region-' + f['region']);
         if (f['element']) {
-            var possible = cssQuery(f['element']);
+            var possible = $$(f['element']);
             if (possible.length == 0)
                 element = null;
             else
diff --git a/share/web/static/js/prototype.js b/share/web/static/js/prototype.js
index ce7148c..5d2100f 100644
--- a/share/web/static/js/prototype.js
+++ b/share/web/static/js/prototype.js
@@ -1,21 +1,34 @@
-/*  Prototype JavaScript framework, version 1.4.0
- *  (c) 2005 Sam Stephenson <sam at conio.net>
- *
- *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
- *  against the source tree, available from the Prototype darcs repository.
+/*  Prototype JavaScript framework, version 1.5.1
+ *  (c) 2005-2007 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
- *
- *  For details, see the Prototype web site: http://prototype.conio.net/
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
  *
 /*--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.4.0',
-  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+  Version: '1.5.1',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      (document.createElement('div').__proto__ !==
+       document.createElement('form').__proto__)
+  },
+
+  ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
+  JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
 
-  emptyFunction: function() {},
-  K: function(x) {return x}
+  emptyFunction: function() { },
+  K: function(x) { return x }
 }
 
 var Class = {
@@ -29,22 +42,62 @@ var Class = {
 var Abstract = new Object();
 
 Object.extend = function(destination, source) {
-  for (property in source) {
+  for (var property in source) {
     destination[property] = source[property];
   }
   return destination;
 }
 
-Object.inspect = function(object) {
-  try {
-    if (object == undefined) return 'undefined';
-    if (object == null) return 'null';
-    return object.inspect ? object.inspect() : object.toString();
-  } catch (e) {
-    if (e instanceof RangeError) return '...';
-    throw e;
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch(type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (object.ownerDocument === document) return;
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+    return '{' + results.join(', ') + '}';
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
   }
-}
+});
 
 Function.prototype.bind = function() {
   var __method = this, args = $A(arguments), object = args.shift();
@@ -54,17 +107,15 @@ Function.prototype.bind = function() {
 }
 
 Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
+  var __method = this, args = $A(arguments), object = args.shift();
   return function(event) {
-    return __method.call(object, event || window.event);
+    return __method.apply(object, [event || window.event].concat(args));
   }
 }
 
 Object.extend(Number.prototype, {
   toColorPart: function() {
-    var digits = this.toString(16);
-    if (this < 16) return '0' + digits;
-    return digits;
+    return this.toPaddedString(2, 16);
   },
 
   succ: function() {
@@ -74,14 +125,32 @@ Object.extend(Number.prototype, {
   times: function(iterator) {
     $R(0, this, true).each(iterator);
     return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
   }
 });
 
+Date.prototype.toJSON = function() {
+  return '"' + this.getFullYear() + '-' +
+    (this.getMonth() + 1).toPaddedString(2) + '-' +
+    this.getDate().toPaddedString(2) + 'T' +
+    this.getHours().toPaddedString(2) + ':' +
+    this.getMinutes().toPaddedString(2) + ':' +
+    this.getSeconds().toPaddedString(2) + '"';
+};
+
 var Try = {
   these: function() {
     var returnValue;
 
-    for (var i = 0; i < arguments.length; i++) {
+    for (var i = 0, length = arguments.length; i < length; i++) {
       var lambda = arguments[i];
       try {
         returnValue = lambda();
@@ -106,40 +175,83 @@ PeriodicalExecuter.prototype = {
   },
 
   registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
   },
 
   onTimerEvent: function() {
     if (!this.currentlyExecuting) {
       try {
         this.currentlyExecuting = true;
-        this.callback();
+        this.callback(this);
       } finally {
         this.currentlyExecuting = false;
       }
     }
   }
 }
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
 
-/*--------------------------------------------------------------------------*/
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
 
-function $() {
-  var elements = new Array();
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
 
-  for (var i = 0; i < arguments.length; i++) {
-    var element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
 
-    if (arguments.length == 1)
-      return element;
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
 
-    elements.push(element);
-  }
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
 
-  return elements;
-}
-Object.extend(String.prototype, {
   stripTags: function() {
     return this.replace(/<\/?[^>]+>/gi, '');
   },
@@ -157,28 +269,40 @@ Object.extend(String.prototype, {
   },
 
   evalScripts: function() {
-    return this.extractScripts().map(eval);
+    return this.extractScripts().map(function(script) { return eval(script) });
   },
 
   escapeHTML: function() {
-    var div = document.createElement('div');
-    var text = document.createTextNode(this);
-    div.appendChild(text);
-    return div.innerHTML;
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
   },
 
   unescapeHTML: function() {
     var div = document.createElement('div');
     div.innerHTML = this.stripTags();
-    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
   },
 
-  toQueryParams: function() {
-    var pairs = this.match(/^\??(.*)$/)[1].split('&');
-    return pairs.inject({}, function(params, pairString) {
-      var pair = pairString.split('=');
-      params[pair[0]] = pair[1];
-      return params;
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (hash[key].constructor != Array) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
     });
   },
 
@@ -186,46 +310,152 @@ Object.extend(String.prototype, {
     return this.split('');
   },
 
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    var result = '';
+    for (var i = 0; i < count; i++) result += this;
+    return result;
+  },
+
   camelize: function() {
-    var oStringList = this.split('-');
-    if (oStringList.length == 1) return oStringList[0];
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
 
-    var camelizedString = this.indexOf('-') == 0
-      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
-      : oStringList[0];
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
 
-    for (var i = 1, len = oStringList.length; i < len; i++) {
-      var s = oStringList[i];
-      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
-    }
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 
-    return camelizedString;
+    return camelized;
   },
 
-  inspect: function() {
-    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
+        return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
   }
 });
 
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
 String.prototype.parseQuery = String.prototype.toQueryParams;
 
-var $break    = new Object();
-var $continue = new Object();
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + String.interpret(object[match[3]]);
+    });
+  }
+}
+
+var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
 
 var Enumerable = {
   each: function(iterator) {
     var index = 0;
     try {
       this._each(function(value) {
-        try {
-          iterator(value, index++);
-        } catch (e) {
-          if (e != $continue) throw e;
-        }
+        iterator(value, index++);
       });
     } catch (e) {
       if (e != $break) throw e;
     }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
   },
 
   all: function(iterator) {
@@ -238,7 +468,7 @@ var Enumerable = {
   },
 
   any: function(iterator) {
-    var result = true;
+    var result = false;
     this.each(function(value, index) {
       if (result = !!(iterator || Prototype.K)(value, index))
         throw $break;
@@ -249,12 +479,12 @@ var Enumerable = {
   collect: function(iterator) {
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push((iterator || Prototype.K)(value, index));
     });
     return results;
   },
 
-  detect: function (iterator) {
+  detect: function(iterator) {
     var result;
     this.each(function(value, index) {
       if (iterator(value, index)) {
@@ -295,6 +525,14 @@ var Enumerable = {
     return found;
   },
 
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
   inject: function(memo, iterator) {
     this.each(function(value, index) {
       memo = iterator(memo, value, index);
@@ -304,7 +542,7 @@ var Enumerable = {
 
   invoke: function(method) {
     var args = $A(arguments).slice(1);
-    return this.collect(function(value) {
+    return this.map(function(value) {
       return value[method].apply(value, args);
     });
   },
@@ -313,7 +551,7 @@ var Enumerable = {
     var result;
     this.each(function(value, index) {
       value = (iterator || Prototype.K)(value, index);
-      if (value >= (result || value))
+      if (result == undefined || value >= result)
         result = value;
     });
     return result;
@@ -323,7 +561,7 @@ var Enumerable = {
     var result;
     this.each(function(value, index) {
       value = (iterator || Prototype.K)(value, index);
-      if (value <= (result || value))
+      if (result == undefined || value < result)
         result = value;
     });
     return result;
@@ -356,7 +594,7 @@ var Enumerable = {
   },
 
   sortBy: function(iterator) {
-    return this.collect(function(value, index) {
+    return this.map(function(value, index) {
       return {value: value, criteria: iterator(value, index)};
     }).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
@@ -365,7 +603,7 @@ var Enumerable = {
   },
 
   toArray: function() {
-    return this.collect(Prototype.K);
+    return this.map();
   },
 
   zip: function() {
@@ -375,11 +613,14 @@ var Enumerable = {
 
     var collections = [this].concat(args).map($A);
     return this.map(function(value, index) {
-      iterator(value = collections.pluck(index));
-      return value;
+      return iterator(collections.pluck(index));
     });
   },
 
+  size: function() {
+    return this.toArray().length;
+  },
+
   inspect: function() {
     return '#<Enumerable:' + this.toArray().inspect() + '>';
   }
@@ -398,19 +639,35 @@ var $A = Array.from = function(iterable) {
     return iterable.toArray();
   } else {
     var results = [];
-    for (var i = 0; i < iterable.length; i++)
+    for (var i = 0, length = iterable.length; i < length; i++)
       results.push(iterable[i]);
     return results;
   }
 }
 
+if (Prototype.Browser.WebKit) {
+  $A = Array.from = function(iterable) {
+    if (!iterable) return [];
+    if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+      iterable.toArray) {
+      return iterable.toArray();
+    } else {
+      var results = [];
+      for (var i = 0, length = iterable.length; i < length; i++)
+        results.push(iterable[i]);
+      return results;
+    }
+  }
+}
+
 Object.extend(Array.prototype, Enumerable);
 
-Array.prototype._reverse = Array.prototype.reverse;
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
 
 Object.extend(Array.prototype, {
   _each: function(iterator) {
-    for (var i = 0; i < this.length; i++)
+    for (var i = 0, length = this.length; i < length; i++)
       iterator(this[i]);
   },
 
@@ -429,13 +686,13 @@ Object.extend(Array.prototype, {
 
   compact: function() {
     return this.select(function(value) {
-      return value != undefined || value != null;
+      return value != null;
     });
   },
 
   flatten: function() {
     return this.inject([], function(array, value) {
-      return array.concat(value.constructor == Array ?
+      return array.concat(value && value.constructor == Array ?
         value.flatten() : [value]);
     });
   },
@@ -448,7 +705,7 @@ Object.extend(Array.prototype, {
   },
 
   indexOf: function(object) {
-    for (var i = 0; i < this.length; i++)
+    for (var i = 0, length = this.length; i < length; i++)
       if (this[i] == object) return i;
     return -1;
   },
@@ -457,23 +714,110 @@ Object.extend(Array.prototype, {
     return (inline !== false ? this : this.toArray())._reverse();
   },
 
-  shift: function() {
-    var result = this[0];
-    for (var i = 0; i < this.length - 1; i++)
-      this[i] = this[i + 1];
-    this.length--;
-    return result;
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
   },
 
   inspect: function() {
     return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (arguments[i].constructor == Array) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(object) {
+  if (object instanceof Hash) this.merge(object);
+  else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+    parts.add = arguments.callee.addPair;
+
+    this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+      var value = pair.value;
+
+      if (value && typeof value == 'object') {
+        if (value.constructor == Array) value.each(function(value) {
+          parts.add(pair.key, value);
+        });
+        return;
+      }
+      parts.add(pair.key, value);
+    });
+
+    return parts.join('&');
+  },
+
+  toJSON: function(object) {
+    var results = [];
+    this.prototype._each.call(object, function(pair) {
+      var value = Object.toJSON(pair.value);
+      if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
+    });
+    return '{' + results.join(', ') + '}';
   }
 });
-var Hash = {
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+  key = encodeURIComponent(key);
+  if (value === undefined) this.push(key);
+  else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
   _each: function(iterator) {
-    for (key in this) {
+    for (var key in this) {
       var value = this[key];
-      if (typeof value == 'function') continue;
+      if (value && value == Hash.prototype[key]) continue;
 
       var pair = [key, value];
       pair.key = key;
@@ -491,31 +835,66 @@ var Hash = {
   },
 
   merge: function(hash) {
-    return $H(hash).inject($H(this), function(mergedHash, pair) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
       mergedHash[pair.key] = pair.value;
       return mergedHash;
     });
   },
 
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
   toQueryString: function() {
-    return this.map(function(pair) {
-      return pair.map(encodeURIComponent).join('=');
-    }).join('&');
+    return Hash.toQueryString(this);
   },
 
   inspect: function() {
     return '#<Hash:{' + this.map(function(pair) {
       return pair.map(Object.inspect).join(': ');
     }).join(', ') + '}>';
+  },
+
+  toJSON: function() {
+    return Hash.toJSON(this);
   }
-}
+});
 
 function $H(object) {
-  var hash = Object.extend({}, object || {});
-  Object.extend(hash, Enumerable);
-  Object.extend(hash, Hash);
-  return hash;
-}
+  if (object instanceof Hash) return object;
+  return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+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;
+}()) Hash.prototype._each = function(iterator) {
+  var cache = [];
+  for (var key in this) {
+    var value = this[key];
+    if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+    cache.push(key);
+    var pair = [key, value];
+    pair.key = key;
+    pair.value = value;
+    iterator(pair);
+  }
+};
 ObjectRange = Class.create();
 Object.extend(ObjectRange.prototype, Enumerable);
 Object.extend(ObjectRange.prototype, {
@@ -527,10 +906,10 @@ Object.extend(ObjectRange.prototype, {
 
   _each: function(iterator) {
     var value = this.start;
-    do {
+    while (this.include(value)) {
       iterator(value);
       value = value.succ();
-    } while (this.include(value));
+    }
   },
 
   include: function(value) {
@@ -549,9 +928,9 @@ var $R = function(start, end, exclusive) {
 var Ajax = {
   getTransport: function() {
     return Try.these(
+      function() {return new XMLHttpRequest()},
       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
-      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
-      function() {return new XMLHttpRequest()}
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
     ) || false;
   },
 
@@ -565,18 +944,18 @@ Ajax.Responders = {
     this.responders._each(iterator);
   },
 
-  register: function(responderToAdd) {
-    if (!this.include(responderToAdd))
-      this.responders.push(responderToAdd);
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
   },
 
-  unregister: function(responderToRemove) {
-    this.responders = this.responders.without(responderToRemove);
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
   },
 
   dispatch: function(callback, request, transport, json) {
     this.each(function(responder) {
-      if (responder[callback] && typeof responder[callback] == 'function') {
+      if (typeof responder[callback] == 'function') {
         try {
           responder[callback].apply(responder, [request, transport, json]);
         } catch (e) {}
@@ -591,7 +970,6 @@ Ajax.Responders.register({
   onCreate: function() {
     Ajax.activeRequestCount++;
   },
-
   onComplete: function() {
     Ajax.activeRequestCount--;
   }
@@ -603,19 +981,15 @@ Ajax.Base.prototype = {
     this.options = {
       method:       'post',
       asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
       parameters:   ''
     }
     Object.extend(this.options, options || {});
-  },
 
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0
-        || (this.transport.status >= 200 && this.transport.status < 300);
-  },
-
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
   }
 }
 
@@ -624,6 +998,8 @@ Ajax.Request.Events =
   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 
 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
   initialize: function(url, options) {
     this.transport = Ajax.getTransport();
     this.setOptions(options);
@@ -631,116 +1007,150 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
   },
 
   request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length > 0) parameters += '&_=';
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
 
-    try {
-      this.url = url;
-      if (this.options.method == 'get' && parameters.length > 0)
-        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Hash.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
 
+    try {
+      if (this.options.onCreate) this.options.onCreate(this.transport);
       Ajax.Responders.dispatch('onCreate', this, this.transport);
 
-      this.transport.open(this.options.method, this.url,
+      this.transport.open(this.method.toUpperCase(), this.url,
         this.options.asynchronous);
 
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
 
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
       this.setRequestHeaders();
 
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
 
-    } catch (e) {
-      this.dispatchException(e);
-    }
-  },
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
 
-  setRequestHeaders: function() {
-    var requestHeaders =
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version];
-
-    if (this.options.method == 'post') {
-      var hasContentType = 0;
-      if (this.options.requestHeaders)
-          for (var i = 0; i < this.options.requestHeaders.length; i += 2) 
-              if (this.options.requestHeaders[i] == 'Content-Type')
-                  hasContentType = 1;
-      if (hasContentType == 0)
-          requestHeaders.push('Content-Type', 'application/x-www-form-urlencoded');
-
-      /* Force "Connection: close" for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651.
-       */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
     }
-
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
-    for (var i = 0; i < requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+    catch (e) {
+      this.dispatchException(e);
+    }
   },
 
   onStateChange: function() {
     var readyState = this.transport.readyState;
-    if (readyState != 1)
+    if (readyState > 1 && !((readyState == 4) && this._complete))
       this.respondToReadyState(this.transport.readyState);
   },
 
-  header: function(name) {
-    try {
-      return this.transport.getResponseHeader(name);
-    } catch (e) {}
-  },
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
 
-  evalJSON: function() {
-    try {
-      return eval(this.header('X-JSON'));
-    } catch (e) {}
-  },
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
 
-  evalResponse: function() {
-    try {
-      return eval(this.transport.responseText);
-    } catch (e) {
-      this.dispatchException(e);
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
     }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
   },
 
   respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
+    var state = Ajax.Request.Events[readyState];
     var transport = this.transport, json = this.evalJSON();
 
-    if (event == 'Complete') {
+    if (state == 'Complete') {
       try {
+        this._complete = true;
         (this.options['on' + this.transport.status]
-         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
          || Prototype.emptyFunction)(transport, json);
       } catch (e) {
         this.dispatchException(e);
       }
 
-      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
-        this.evalResponse();
+      var contentType = this.getHeader('Content-type');
+      if (contentType && contentType.strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
     }
 
     try {
-      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
-      Ajax.Responders.dispatch('on' + event, this, transport, json);
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
     } catch (e) {
       this.dispatchException(e);
     }
 
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
       this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? json.evalJSON() : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
   },
 
   dispatchException: function(exception) {
@@ -753,41 +1163,37 @@ Ajax.Updater = Class.create();
 
 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
   initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $(container.success) : $(container),
-      failure: container.failure ? $(container.failure) :
-        (container.success ? null : $(container))
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
     }
 
     this.transport = Ajax.getTransport();
     this.setOptions(options);
 
     var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function(transport, object) {
+    this.options.onComplete = (function(transport, param) {
       this.updateContent();
-      onComplete(transport, object);
+      onComplete(transport, param);
     }).bind(this);
 
     this.request(url);
   },
 
   updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
     var response = this.transport.responseText;
 
-    if (!this.options.evalScripts)
-      response = response.stripScripts();
+    if (!this.options.evalScripts) response = response.stripScripts();
 
-    if (receiver) {
-      if (this.options.insertion) {
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
         new this.options.insertion(receiver, response);
-      } else {
-        Element.update(receiver, response);
-      }
+      else
+        receiver.update(response);
     }
 
-    if (this.responseIsSuccess()) {
+    if (this.success()) {
       if (this.onComplete)
         setTimeout(this.onComplete.bind(this), 10);
     }
@@ -816,7 +1222,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
   },
 
   stop: function() {
-    this.updater.onComplete = undefined;
+    this.updater.options.onComplete = undefined;
     clearTimeout(this.timer);
     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   },
@@ -836,60 +1242,252 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   }
 });
-document.getElementsByClassName = function(className, parentElement) {
-  var children = ($(parentElement) || document.body).getElementsByTagName('*');
-  return $A(children).inject([], function(elements, child) {
-    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
-      elements.push(child);
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
     return elements;
-  });
+  }
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
 }
 
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+
+  document.getElementsByClassName = function(className, parentElement) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  var elements = [], child;
+  for (var i = 0, length = children.length; i < length; i++) {
+    child = children[i];
+    if (Element.hasClassName(child, className))
+      elements.push(Element.extend(child));
+  }
+  return elements;
+};
+
 /*--------------------------------------------------------------------------*/
 
-if (!window.Element) {
-  var Element = new Object();
-}
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+  var F = Prototype.BrowserFeatures;
+  if (!element || !element.tagName || element.nodeType == 3 ||
+   element._extended || F.SpecificElementExtensions || element == window)
+    return element;
+
+  var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+   T = Element.Methods.ByTag;
+
+  // extend methods for all tags (Safari doesn't need this)
+  if (!F.ElementExtensions) {
+    Object.extend(methods, Element.Methods),
+    Object.extend(methods, Element.Methods.Simulated);
+  }
 
-Object.extend(Element, {
+  // extend methods for specific tags
+  if (T[tagName]) Object.extend(methods, T[tagName]);
+
+  for (var property in methods) {
+    var value = methods[property];
+    if (typeof value == 'function' && !(property in element))
+      element[property] = cache.findOrStore(value);
+  }
+
+  element._extended = Prototype.emptyFunction;
+  return element;
+};
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+};
+
+Element.Methods = {
   visible: function(element) {
     return $(element).style.display != 'none';
   },
 
-  toggle: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      Element[Element.visible(element) ? 'hide' : 'show'](element);
-    }
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
   },
 
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
-    }
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
   },
 
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
-    }
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
   },
 
   remove: function(element) {
     element = $(element);
     element.parentNode.removeChild(element);
+    return element;
   },
 
   update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
     $(element).innerHTML = html.stripScripts();
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
 
-  getHeight: function(element) {
+  replace: function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  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];
+  },
+
+  previous: function(element, expression, index) {
     element = $(element);
-    return element.offsetHeight;
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  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];
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      if (!element.attributes) return null;
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      return attribute ? attribute.nodeValue : null;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
   },
 
   classNames: function(element) {
@@ -898,67 +1496,114 @@ Object.extend(Element, {
 
   hasClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).include(className);
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
   },
 
   addClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).add(className);
+    Element.classNames(element).add(className);
+    return element;
   },
 
   removeClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).remove(className);
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
   },
 
   // removes whitespace-only text node children
   cleanWhitespace: function(element) {
     element = $(element);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
-        Element.remove(node);
+        element.removeChild(node);
+      node = nextNode;
     }
+    return element;
   },
 
   empty: function(element) {
-    return $(element).innerHTML.match(/^\s*$/);
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
   },
 
   scrollTo: function(element) {
     element = $(element);
-    var x = element.x ? element.x : element.offsetLeft,
-        y = element.y ? element.y : element.offsetTop;
-    window.scrollTo(x, y);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
   },
 
   getStyle: function(element, style) {
     element = $(element);
-    var value = element.style[style.camelize()];
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
     if (!value) {
-      if (document.defaultView && document.defaultView.getComputedStyle) {
-        var css = document.defaultView.getComputedStyle(element, null);
-        value = css ? css.getPropertyValue(style) : null;
-      } else if (element.currentStyle) {
-        value = element.currentStyle[style.camelize()];
-      }
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
     }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
 
-    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
-      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
 
-    return value == 'auto' ? null : value;
+  setStyle: function(element, styles, camelized) {
+    element = $(element);
+    var elementStyle = element.style;
+
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property])
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (camelized ? property : property.camelize())] = styles[property];
+
+    return element;
   },
 
-  setStyle: function(element, style) {
+  setOpacity: function(element, value) {
     element = $(element);
-    for (name in style)
-      element.style[name.camelize()] = style[name];
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
   },
 
   getDimensions: function(element) {
     element = $(element);
-    if (Element.getStyle(element, 'display') != 'none')
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
 
     // All *Width and *Height properties give 0 on elements with display none,
@@ -966,12 +1611,13 @@ Object.extend(Element, {
     var els = element.style;
     var originalVisibility = els.visibility;
     var originalPosition = els.position;
+    var originalDisplay = els.display;
     els.visibility = 'hidden';
     els.position = 'absolute';
-    els.display = '';
+    els.display = 'block';
     var originalWidth = element.clientWidth;
     var originalHeight = element.clientHeight;
-    els.display = 'none';
+    els.display = originalDisplay;
     els.position = originalPosition;
     els.visibility = originalVisibility;
     return {width: originalWidth, height: originalHeight};
@@ -990,6 +1636,7 @@ Object.extend(Element, {
         element.style.left = 0;
       }
     }
+    return element;
   },
 
   undoPositioned: function(element) {
@@ -1002,26 +1649,270 @@ Object.extend(Element, {
         element.style.bottom =
         element.style.right = '';
     }
+    return element;
   },
 
   makeClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element._overflow = element.style.overflow;
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
       element.style.overflow = 'hidden';
+    return element;
   },
 
   undoClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element.style.overflow = element._overflow;
-    element._overflow = undefined;
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
   }
+};
+
+Object.extend(Element.Methods, {
+  childOf: Element.Methods.descendantOf,
+  childElements: Element.Methods.immediateDescendants
 });
 
-var Toggle = new Object();
-Toggle.display = Element.toggle;
+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);
+    }
+  };
+}
+else if (Prototype.Browser.IE) {
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  // IE is missing .innerHTML support for TABLE-related elements
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      depth.times(function() { div = div.firstChild });
+      $A(div.childNodes).each(function(node) { element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() { html.evalScripts() }, 10);
+    return element;
+  }
+}
+else if (Prototype.Browser.Gecko) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+Element._attributeTranslations = {
+  names: {
+    colspan:   "colSpan",
+    rowspan:   "rowSpan",
+    valign:    "vAlign",
+    datetime:  "dateTime",
+    accesskey: "accessKey",
+    tabindex:  "tabIndex",
+    enctype:   "encType",
+    maxlength: "maxLength",
+    readonly:  "readOnly",
+    longdesc:  "longDesc"
+  },
+  values: {
+    _getAttr: function(element, attribute) {
+      return element.getAttribute(attribute, 2);
+    },
+    _flag: function(element, attribute) {
+      return $(element).hasAttribute(attribute) ? attribute : null;
+    },
+    style: function(element) {
+      return element.style.cssText.toLowerCase();
+    },
+    title: function(element) {
+      var node = element.getAttributeNode('title');
+      return node.specified ? node.nodeValue : null;
+    }
+  }
+};
+
+(function() {
+  Object.extend(this, {
+    href: this._getAttr,
+    src:  this._getAttr,
+    type: this._getAttr,
+    disabled: this._flag,
+    checked:  this._flag,
+    readonly: this._flag,
+    multiple: this._flag
+  });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations, node;
+    attribute = t.names[attribute] || attribute;
+    node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+  window.HTMLElement = {};
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || {});
+  else {
+    if (tagName.constructor == Array) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = {};
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = {};
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (typeof klass == "undefined") continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+};
+
+var Toggle = { display: Element.toggle };
 
 /*--------------------------------------------------------------------------*/
 
@@ -1038,7 +1929,8 @@ Abstract.Insertion.prototype = {
       try {
         this.element.insertAdjacentHTML(this.adjacency, this.content);
       } catch (e) {
-        if (this.element.tagName.toLowerCase() == 'tbody') {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
           this.insertContent(this.contentFromAnonymousTable());
         } else {
           throw e;
@@ -1137,220 +2029,814 @@ Element.ClassNames.prototype = {
 
   add: function(classNameToAdd) {
     if (this.include(classNameToAdd)) return;
-    this.set(this.toArray().concat(classNameToAdd).join(' '));
+    this.set($A(this).concat(classNameToAdd).join(' '));
   },
 
   remove: function(classNameToRemove) {
     if (!this.include(classNameToRemove)) return;
-    this.set(this.select(function(className) {
-      return className != classNameToRemove;
-    }).join(' '));
+    this.set($A(this).without(classNameToRemove).join(' '));
   },
 
   toString: function() {
-    return this.toArray().join(' ');
+    return $A(this).join(' ');
   }
-}
+};
 
 Object.extend(Element.ClassNames.prototype, Enumerable);
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $(arguments[i]).value = '';
+/* 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();
+
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
   },
 
-  focus: function(element) {
-    $(element).focus();
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e]; return;
+    }
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+    	      new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le,  m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
   },
 
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
-    return true;
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
   },
 
-  select: function(element) {
-    $(element).select();
+  match: function(element) {
+    return this.findElements(document).include(element);
   },
 
-  activate: function(element) {
-    element = $(element);
-    element.focus();
-    if (element.select)
-      element.select();
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
   }
-}
+};
 
-/*--------------------------------------------------------------------------*/
+Object.extend(Selector, {
+  _cache: {},
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (typeof h === 'function') return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      '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', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
 
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
+  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;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo:       function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    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]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) 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++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      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;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    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++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
 
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+	      if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!nodes && root == document) return targetNode ? [targetNode] : [];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      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) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
     }
+  },
 
-    return queryComponents.join('&');
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    '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;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      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);
+      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);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
   },
 
-  getElements: function(form) {
-    form = $(form);
-    var elements = new Array();
+  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.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() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
 
-    for (tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
     }
-    return elements;
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != null) {
+         	if (key in result) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
   },
 
   getInputs: function(form, typeName, name) {
     form = $(form);
     var inputs = form.getElementsByTagName('input');
 
-    if (!typeName && !name)
-      return inputs;
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
 
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
       var input = inputs[i];
-      if ((typeName && input.type != typeName) ||
-          (name && input.name != name))
+      if ((typeName && input.type != typeName) || (name && input.name != name))
         continue;
-      matchingInputs.push(input);
+      matchingInputs.push(Element.extend(input));
     }
 
     return matchingInputs;
   },
 
   disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.blur();
-      element.disabled = 'true';
-    }
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
   },
 
   enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.disabled = '';
-    }
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
   },
 
   findFirstElement: function(form) {
-    return Form.getElements(form).find(function(element) {
+    return $(form).getElements().find(function(element) {
       return element.type != 'hidden' && !element.disabled &&
         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     });
   },
 
   focusFirstElement: function(form) {
-    Field.activate(Form.findFirstElement(form));
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
   },
 
-  reset: function(form) {
-    $(form).reset();
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || {});
+
+    var params = options.parameters;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (typeof params == 'string') params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(form.readAttribute('action'), options);
   }
 }
 
+/*--------------------------------------------------------------------------*/
+
 Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+}
+
+Form.Element.Methods = {
   serialize: function(element) {
     element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
     var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    return Form.Element.Serializers[method](element);
+  },
 
-    if (parameter) {
-      var key = encodeURIComponent(parameter[0]);
-      if (key.length == 0) return;
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
 
-      if (parameter[1].constructor != Array)
-        parameter[1] = [parameter[1]];
+  present: function(element) {
+    return $(element).value != '';
+  },
 
-      return parameter[1].map(function(value) {
-        return key + '=' + encodeURIComponent(value);
-      }).join('&');
-    }
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+        !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) {}
+    return element;
   },
 
-  getValue: function(element) {
+  disable: function(element) {
     element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
 
-    if (parameter)
-      return parameter[1];
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
   }
 }
 
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
 Form.Element.Serializers = {
   input: function(element) {
     switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
       case 'checkbox':
       case 'radio':
         return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
     }
-    return false;
   },
 
   inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
+    return element.checked ? element.value : null;
   },
 
   textarea: function(element) {
-    return [element.name, element.value];
+    return element.value;
   },
 
   select: function(element) {
-    return Form.Element.Serializers[element.type == 'select-one' ?
+    return this[element.type == 'select-one' ?
       'selectOne' : 'selectMany'](element);
   },
 
   selectOne: function(element) {
-    var value = '', opt, index = element.selectedIndex;
-    if (index >= 0) {
-      opt = element.options[index];
-      value = opt.value;
-      if (!value && !('value' in opt))
-        value = opt.text;
-    }
-    return [element.name, value];
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
   },
 
   selectMany: function(element) {
-    var value = new Array();
-    for (var i = 0; i < element.length; i++) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
       var opt = element.options[i];
-      if (opt.selected) {
-        var optValue = opt.value;
-        if (!optValue && !('value' in opt))
-          optValue = opt.text;
-        value.push(optValue);
-      }
+      if (opt.selected) values.push(this.optionValue(opt));
     }
-    return [element.name, value];
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
   }
 }
 
 /*--------------------------------------------------------------------------*/
 
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
 Abstract.TimedObserver = function() {}
 Abstract.TimedObserver.prototype = {
   initialize: function(element, frequency, callback) {
@@ -1368,7 +2854,9 @@ Abstract.TimedObserver.prototype = {
 
   onTimerEvent: function() {
     var value = this.getValue();
-    if (this.lastValue != value) {
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
       this.callback(this.element, value);
       this.lastValue = value;
     }
@@ -1413,9 +2901,7 @@ Abstract.EventObserver.prototype = {
   },
 
   registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
   },
 
   registerCallback: function(element) {
@@ -1425,11 +2911,7 @@ Abstract.EventObserver.prototype = {
         case 'radio':
           Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
+        default:
           Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
@@ -1464,9 +2946,13 @@ Object.extend(Event, {
   KEY_RIGHT:    39,
   KEY_DOWN:     40,
   KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
 
   element: function(event) {
-    return event.target || event.srcElement;
+    return $(event.target || event.srcElement);
   },
 
   isLeftClick: function(event) {
@@ -1519,7 +3005,7 @@ Object.extend(Event, {
 
   unloadCache: function() {
     if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
       Event.stopObserving.apply(this, Event.observers[i]);
       Event.observers[i][0] = null;
     }
@@ -1527,36 +3013,37 @@ Object.extend(Event, {
   },
 
   observe: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.attachEvent))
+      (Prototype.Browser.WebKit || element.attachEvent))
       name = 'keydown';
 
-    this._observeAndCache(element, name, observer, useCapture);
+    Event._observeAndCache(element, name, observer, useCapture);
   },
 
   stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.detachEvent))
+        (Prototype.Browser.WebKit || element.attachEvent))
       name = 'keydown';
 
     if (element.removeEventListener) {
       element.removeEventListener(name, observer, useCapture);
     } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
     }
   }
 });
 
 /* prevent memory leaks in IE */
-Event.observe(window, 'unload', Event.unloadCache, false);
+if (Prototype.Browser.IE)
+  Event.observe(window, 'unload', Event.unloadCache, false);
 var Position = {
   // set to true if needed, warning: firefox performance problems
   // NOT neeeded for page scrolling, only if draggable contained in
@@ -1603,7 +3090,8 @@ var Position = {
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        p = Element.getStyle(element, 'position');
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
         if (p == 'relative' || p == 'absolute') break;
       }
     } while (element);
@@ -1659,17 +3147,6 @@ var Position = {
         element.offsetWidth;
   },
 
-  clone: function(source, target) {
-    source = $(source);
-    target = $(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
-  },
-
   page: function(forElement) {
     var valueT = 0, valueL = 0;
 
@@ -1679,15 +3156,17 @@ var Position = {
       valueL += element.offsetLeft || 0;
 
       // Safari fix
-      if (element.offsetParent==document.body)
+      if (element.offsetParent == document.body)
         if (Element.getStyle(element,'position')=='absolute') break;
 
     } while (element = element.offsetParent);
 
     element = forElement;
     do {
-      valueT -= element.scrollTop  || 0;
-      valueL -= element.scrollLeft || 0;
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
     } while (element = element.parentNode);
 
     return [valueL, valueT];
@@ -1748,10 +3227,10 @@ var Position = {
     element._originalHeight = element.style.height;
 
     element.style.position = 'absolute';
-    element.style.top    = top + 'px';;
-    element.style.left   = left + 'px';;
-    element.style.width  = width + 'px';;
-    element.style.height = height + 'px';;
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
   },
 
   relativize: function(element) {
@@ -1773,7 +3252,7 @@ var Position = {
 // Safari returns margins on body which is incorrect if the child is absolutely
 // positioned.  For performance reasons, redefine Position.cumulativeOffset for
 // KHTML/WebKit only.
-if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+if (Prototype.Browser.WebKit) {
   Position.cumulativeOffset = function(element) {
     var valueT = 0, valueL = 0;
     do {
@@ -1788,3 +3267,5 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
     return [valueL, valueT];
   }
 }
+
+Element.addMethods();
\ No newline at end of file

commit ff6fe051e59f4bb221829ee84ae2a70718f45aac
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jul 12 17:06:49 2007 +0000

    Whoops, forgot this
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3680 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --git a/share/web/static/js/setup_jsan.js b/share/plugins/Jifty/Plugin/JSAN/web/static/js/setup_jsan.js
similarity index 100%
rename from share/web/static/js/setup_jsan.js
rename to share/plugins/Jifty/Plugin/JSAN/web/static/js/setup_jsan.js

commit a7e752f5029ad6e616a4a5289223a3d3f4c1567e
Merge: ff6fe05 efafa7e
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Jul 31 21:23:52 2007 +0000

    Merge from /jifty/trunk:3742
    
    r21838 at zot (orig r3385):  yves | 2007-06-07 09:27:32 -0400
    * debian deps
    
    r21842 at zot (orig r3389):  sartak | 2007-06-07 18:07:12 -0400
     r20399 at caladan:  sartak | 2007-06-07 16:23:12 -0400
     Make sure there's always a stash during testing since we don't always go through the web
    
    r21844 at zot (orig r3391):  sartak | 2007-06-07 18:14:44 -0400
     r20405 at caladan:  sartak | 2007-06-07 18:07:59 -0400
     Pushed the wrong change, oops :)
    
    r21869 at zot (orig r3405):  clkao | 2007-06-08 16:46:17 -0400
    Merge from fragcont branch to include minimum support of continuation in
    webservices requests to make SinglePage work.
    
     r18076 at ubuntu (orig r3386):  clkao | 2007-06-07 15:59:34 -0400
     branch for supporting fragment calls with continuation.
     r18077 at ubuntu (orig r3387):  clkao | 2007-06-07 16:05:17 -0400
     first cut of continuation serialization & calling with webservices.
     r18078 at ubuntu (orig r3388):  clkao | 2007-06-07 17:35:03 -0400
     Have call continuation work on webservices, but not return properly yet.
     r18080 at ubuntu (orig r3390):  clkao | 2007-06-07 18:11:09 -0400
     keeping __page's state won't get us anywhere...
     r18093 at ubuntu (orig r3392):  clkao | 2007-06-07 19:19:35 -0400
     ditto for other implicit __page fragment to ignore region state.
     r18094 at ubuntu (orig r3393):  clkao | 2007-06-07 19:20:11 -0400
     fallback value being array makes the crud action on server side sad.
     r18110 at ubuntu (orig r3397):  clkao | 2007-06-08 02:43:23 -0400
     Support redirect to external url in webservices.
     r18111 at ubuntu (orig r3398):  clkao | 2007-06-08 03:01:27 -0400
     Prevent infinite loop when unable to render mason_internal_error.
    
     r18112 at ubuntu (orig r3399):  clkao | 2007-06-08 03:15:31 -0400
     Handle J:C internal redirect, which is used by error raised by dispatcher.
     r18118 at ubuntu (orig r3401):  clkao | 2007-06-08 13:00:58 -0400
     * If we are doing external redirect, supress fragment replacement.
     * fix refresh_self in submit so on failed action response we get
       __page refreshed that is like page reload.
    
     r18119 at ubuntu (orig r3402):  clkao | 2007-06-08 13:13:54 -0400
     On internal redirect, reset nav and page_nav.
     r18120 at ubuntu (orig r3403):  clkao | 2007-06-08 15:42:44 -0400
     There's no point removing url in clickable and put in _orig_url anymore,
     which also happens to break redirect($clickable).
    
     r18125 at ubuntu (orig r3404):  clkao | 2007-06-08 16:42:32 -0400
     misc cleanups.
    
    r21871 at zot (orig r3407):  jesse | 2007-06-08 17:03:11 -0400
     r58173 at pinglin:  jesse | 2007-06-08 16:58:00 -0400
     * Back out a Croak back to a die. (We're using it for exception handling, not to-user reporting)
    
    r21872 at zot (orig r3408):  jesse | 2007-06-08 17:03:17 -0400
     r58174 at pinglin:  jesse | 2007-06-08 17:01:58 -0400
      * Refactor handle_request to extract out methods
      * Only warn about denied actions on validate if they're also actions we want to run
    
    
    r21873 at zot (orig r3409):  clkao | 2007-06-08 17:15:38 -0400
    More comment about the JAFF handling special case from update().
    
    r21874 at zot (orig r3410):  clkao | 2007-06-08 17:21:23 -0400
    misc webservices_redirect cleanup.
    
    r21875 at zot (orig r3411):  sartak | 2007-06-08 17:47:39 -0400
     r20470 at caladan:  sartak | 2007-06-08 17:46:44 -0400
     Add a little pod coverage
    
    r21876 at zot (orig r3412):  jesse | 2007-06-08 17:52:00 -0400
     r58183 at pinglin:  jesse | 2007-06-08 17:51:41 -0400
     * We'd gotten the $actions logic  slightly wrong during refactoring. The new code lets you submit everything/nothing/something, rather than just something or nothing ;)
    
    r21877 at zot (orig r3413):  jesse | 2007-06-08 19:16:51 -0400
     r58188 at pinglin:  jesse | 2007-06-08 19:16:30 -0400
     * switch url from an attribute to a child node for ajax redirects. (The old behaviour didn't work in safari)
    
    r21878 at zot (orig r3414):  clkao | 2007-06-08 22:32:57 -0400
    only do spa magic if onclick hook is hash - so onclick='someJavascript();' would work
    r21907 at zot (orig r3415):  clkao | 2007-06-09 10:44:09 -0400
    Allow spa to be disabled by page class.
    r21908 at zot (orig r3416):  clkao | 2007-06-09 10:51:59 -0400
    Fix onclick initialization in spa.
    r21909 at zot (orig r3417):  jesse | 2007-06-09 17:23:55 -0400
     r58190 at pinglin:  jesse | 2007-06-09 16:08:50 -0400
     * The cookbook was wrong about the auth recipe
    
    r21910 at zot (orig r3418):  jesse | 2007-06-09 17:24:05 -0400
     r58191 at pinglin:  jesse | 2007-06-09 16:08:59 -0400
     * Quiet a dispatcher warning
    
    r21911 at zot (orig r3419):  jesse | 2007-06-09 17:24:11 -0400
     r58192 at pinglin:  jesse | 2007-06-09 17:23:24 -0400
     meta.yml updates
    
    r21912 at zot (orig r3420):  jesse | 2007-06-09 17:24:22 -0400
     r58193 at pinglin:  jesse | 2007-06-09 17:23:33 -0400
      * Don't trip over classes we can't require
    
    r21913 at zot (orig r3421):  dpavlin | 2007-06-09 19:06:29 -0400
    bump version to 0.02, remove requirement to modify submit form button (and
    in process break next_page after it, because it turns normal form submit
    into Ajax form submit which doesn't handle redirects very well -- because it
    usually redirects to page which isn't valid XML)
    r21914 at zot (orig r3422):  dpavlin | 2007-06-09 19:08:58 -0400
    include verison string in module code
    r21915 at zot (orig r3423):  bartb | 2007-06-09 23:16:04 -0400
    typo fix
    r21916 at zot (orig r3424):  clkao | 2007-06-10 02:04:16 -0400
    For post fragment update script evaluation, use YUI OnAvailable
    rather than hardcoded setTimeout.
    
    r21917 at zot (orig r3425):  clkao | 2007-06-10 02:05:48 -0400
    when rendering the keybindings div, reset Jifty.KeyBindings.
    Ideally we might want to keep the state with the div so it resets
    itself when replaced.
    
    r22019 at zot (orig r3426):  clkao | 2007-06-10 18:47:53 -0400
    Make multiple use base lines into one for readability.
    
    r22032 at zot (orig r3427):  clkao | 2007-06-10 19:15:50 -0400
    Move the javascript concatenating logic from Jifty::Web to the plugin.
    
    r22033 at zot (orig r3428):  clkao | 2007-06-10 19:40:25 -0400
    CompressedCSSandJS: support optional javascript minifier.
    
    r22037 at zot (orig r3432):  clkao | 2007-06-10 20:49:10 -0400
    minor cleanups for ccjs.
    r22038 at zot (orig r3433):  sky | 2007-06-10 21:24:04 -0400
    remove trailing whitespace
    r22039 at zot (orig r3434):  clkao | 2007-06-10 21:29:18 -0400
    Unbreak nojs form submitting when SPA is enabled.
    r22040 at zot (orig r3435):  sky | 2007-06-10 21:33:24 -0400
    trailing whitespace....
    r22041 at zot (orig r3436):  clkao | 2007-06-10 21:46:34 -0400
    add IPC::Run3 to dependency.
    r22042 at zot (orig r3437):  sky | 2007-06-11 00:12:24 -0400
    work around annoying Module::Pluggable bug
    r22043 at zot (orig r3438):  jesse | 2007-06-11 02:37:40 -0400
     r58206 at pinglin:  jesse | 2007-06-11 02:29:31 -0400
     * Got the tabview plugin working with non-CRUD code.
     * got the tabview plugin preloading the first tab, even if it's marked as a region rather than a static tab
    
    r22044 at zot (orig r3439):  jesse | 2007-06-11 02:37:50 -0400
     r58207 at pinglin:  jesse | 2007-06-11 02:31:00 -0400
     * Refactor the CRUD view to make it easier to customize the output
    
    r22045 at zot (orig r3440):  clkao | 2007-06-11 03:32:36 -0400
    Fix javascript emission when CompressedCSSandJS plugin is not loaded noticed
    by dpavlin++.
    
    r22046 at zot (orig r3441):  clkao | 2007-06-11 05:05:43 -0400
    Example::Todo is too hard to type.  Yada!
    
     r18180 at ubuntu:  clkao | 2007-06-11 04:42:19 -0400
     yada!
     r18181 at ubuntu:  clkao | 2007-06-11 04:44:38 -0400
     yada!
     r18182 at ubuntu:  clkao | 2007-06-11 04:46:17 -0400
     yada!
     r18183 at ubuntu:  clkao | 2007-06-11 05:03:12 -0400
     yada!
    
    r22047 at zot (orig r3442):  clkao | 2007-06-11 05:07:42 -0400
    Plugin view class should respect app page.
    r22048 at zot (orig r3443):  clkao | 2007-06-11 05:36:08 -0400
    Helper method for mounting crud view.
    r22065 at zot (orig r3448):  clkao | 2007-06-12 08:28:28 -0400
    Do not call update() when ctrl-clicked
    r22072 at zot (orig r3453):  clkao | 2007-06-12 11:28:57 -0400
    Always give the dispatcher unescaped path like fastcgi does,
    from standalone, webservice, and region entrance.
    
    r22074 at zot (orig r3455):  clkao | 2007-06-12 12:34:29 -0400
    Fix the template subclassing tests.
    
    r22076 at zot (orig r3456):  clkao | 2007-06-12 15:14:08 -0400
    YourApp::View->use_mason_wrapper now lets your write page {} in TD but uses
    your mason wrapper.
    
    r22084 at zot (orig r3461):  clkao | 2007-06-12 15:54:44 -0400
    Make use_mason_wrapper actually work.
    
    r22086 at zot (orig r3462):  trs | 2007-06-12 16:23:20 -0400
    
    r22087 at zot (orig r3463):  trs | 2007-06-12 16:23:37 -0400
     r22078 at zot:  tom | 2007-06-12 15:30:55 -0400
     Extraneous warning
    
    r22088 at zot (orig r3464):  trs | 2007-06-12 16:23:47 -0400
     r22079 at zot:  tom | 2007-06-12 16:22:25 -0400
     Be more in line with original code and apply Behaviour immediately, not in the callback
    
    r22107 at zot (orig r3466):  jesse | 2007-06-12 18:05:18 -0400
    
    r22108 at zot (orig r3467):  jesse | 2007-06-12 18:05:48 -0400
    
    r22109 at zot (orig r3468):  jesse | 2007-06-12 18:06:30 -0400
     r58252 at pinglin:  jesse | 2007-06-12 17:54:09 -0400
     * try not to interfere when a user clicks on a link with a modifier key pressed
    
    r22110 at zot (orig r3469):  clkao | 2007-06-12 18:56:44 -0400
    Fix jifty->web->out from without td used with mason wrapper.
    r22111 at zot (orig r3470):  jesse | 2007-06-12 19:15:10 -0400
     r58268 at pinglin:  jesse | 2007-06-12 19:15:01 -0400
      * _redirect expects a local path, not a fully formed URL. (This only actually broke internal redirects)
    
    r22112 at zot (orig r3471):  clkao | 2007-06-12 20:11:27 -0400
    New Template::Declare page syntax for additional code block run
    before wrapper, and have the returned value passed to wrapper:
    
    template foo =>
      page { title => 'this is title', other => 'foo' }
      content {
        h1 { 'blah }
      };
    
    r22143 at zot (orig r3474):  jesse | 2007-06-14 02:15:34 -0400
     r58297 at pinglin:  jesse | 2007-06-14 02:14:20 -0400
     * Jifty->web->form->next_page got dropped when you submit only a specific action with Jifty->web->form->submit. This commit autoadds next_page if you submit only a subset of actions.
    
    r22150 at zot (orig r3481):  sterling | 2007-06-14 10:46:41 -0400
    
    r22151 at zot (orig r3482):  sterling | 2007-06-14 10:49:08 -0400
     r7618 at dynpc145:  andrew | 2007-06-14 09:42:56 -0500
     Cleaning up and improving some of the view documentation.
    
    r22152 at zot (orig r3483):  yves | 2007-06-14 11:16:25 -0400
    * debian dependancies
    * T::D 0.21 needed to pass tests
    
    r22193 at zot (orig r3492):  trs | 2007-06-14 17:42:02 -0400
    
    r22194 at zot (orig r3493):  trs | 2007-06-14 17:42:15 -0400
     r22078 at zot:  tom | 2007-06-12 15:30:55 -0400
     Extraneous warning
    
    r22195 at zot (orig r3494):  trs | 2007-06-14 17:42:18 -0400
     r22079 at zot:  tom | 2007-06-12 16:22:25 -0400
     Be more in line with original code and apply Behaviour immediately, not in the callback
    
    r22196 at zot (orig r3495):  trs | 2007-06-14 17:42:31 -0400
     r22184 at zot:  tom | 2007-06-14 17:41:23 -0400
     Also export display_columns so it's not required to override it
    
    r22217 at zot (orig r3496):  jesse | 2007-06-14 20:18:13 -0400
     r58374 at pinglin:  jesse | 2007-06-14 20:17:33 -0400
      * Full doc for the existing CRUD templates
    
    r22222 at zot (orig r3501):  jesse | 2007-06-15 00:17:09 -0400
     r58389 at pinglin:  jesse | 2007-06-15 00:16:54 -0400
     * CRUD plugin cleanup and refactoring
    
    r24653 at zot (orig r3507):  jesse | 2007-06-15 12:07:40 -0400
    * Extracting talks from the jifty dist
    r24686 at zot (orig r3514):  trs | 2007-06-17 16:53:50 -0400
     r24679 at zot:  tom | 2007-06-16 15:20:42 -0400
     HTTPS and HTTP adjectives for dispatcher rules
    
    r24687 at zot (orig r3515):  trs | 2007-06-17 16:53:55 -0400
     r24680 at zot:  tom | 2007-06-16 16:41:16 -0400
     Allow scheme to be specified for Jifty->web->url.  This functionality was taken out during the move from "heuristics" to URI.pm, but for a non-apparent reason.
    
    r24688 at zot (orig r3516):  trs | 2007-06-17 16:54:01 -0400
     r24683 at zot:  tom | 2007-06-17 16:49:49 -0400
     Now with more POD!  (and passing POD tests)
    
    r24689 at zot (orig r3517):  trs | 2007-06-17 16:54:09 -0400
     r24684 at zot:  tom | 2007-06-17 16:53:22 -0400
     Fix typos
    
    r24699 at zot (orig r3527):  bartb | 2007-06-18 05:25:24 -0400
    typo fix
    r24700 at zot (orig r3528):  audreyt | 2007-06-18 11:03:37 -0400
    * Web::Form::Link - When escape_label was set, the tooltip property was
      (very sillily!) first escaped, then discarded away, displaying the
      unescaped text instead.  It now escapes properly.
    * Also make tooltips with value '0' display properly.
    r24705 at zot (orig r3529):  trs | 2007-06-18 16:40:38 -0400
     r24704 at zot:  tom | 2007-06-18 16:40:03 -0400
     Allow app changeable cookie names
    
    r24764 at zot (orig r3532):  clkao | 2007-06-19 18:56:51 -0400
    debug plugin for logging dispatched rules and current user.
    r24765 at zot (orig r3533):  clkao | 2007-06-19 19:05:52 -0400
    info rather than debug.
    r24767 at zot (orig r3534):  trs | 2007-06-19 22:57:00 -0400
     r24761 at zot:  tom | 2007-06-19 22:56:13 -0400
     * Use Jifty::Util->share_root for finding plugin share roots so that if Jifty isn't installed it still DTRT
     * Only calculate static roots once and report on plugins adding roots (like the mason handler does)
    
    r24789 at zot (orig r3542):  trs | 2007-06-21 00:06:09 -0400
     r24784 at zot:  tom | 2007-06-21 00:03:42 -0400
     Convert to using the form of declaring titles that works when using the mason wrapper with TD
    
    r24795 at zot (orig r3543):  trs | 2007-06-21 02:47:05 -0400
     r24794 at zot:  tom | 2007-06-21 02:46:02 -0400
     Restore and update the OOM patch to the YUI calendar
    
    r24818 at zot (orig r3544):  ruz | 2007-06-21 11:03:23 -0400
    * protect ourself from circular references between User and CurrentUser
    
    we do it in user_object, so solution is generic should work in any case, but
    only if people use the method to set user_object and user model uses _current_user
    key to store reference to the CurrentUser
    
    r24819 at zot (orig r3545):  gugod | 2007-06-22 01:23:43 -0400
    Add a "next" parameter so one can specify the next page to go after
    openid login.
    
    r24820 at zot (orig r3546):  ruz | 2007-06-22 09:33:34 -0400
    * add {_resurect_current_user} field into user module
    r24821 at zot (orig r3547):  bartb | 2007-06-22 18:19:29 -0400
    added see also section to Jifty::Upgrade
    r24822 at zot (orig r3548):  bartb | 2007-06-23 18:26:00 -0400
    typo
    r24823 at zot (orig r3549):  alexmv | 2007-06-24 17:19:47 -0400
     r19898 at zoq-fot-pik:  chmrr | 2007-06-24 17:17:51 -0400
      * Mapper edge case failure (when no 'name' was given)
    
    r24824 at zot (orig r3550):  audreyt | 2007-06-25 13:48:22 -0400
    * No matter what _resurect_current_user is intended for, it ought
      to be spelled _resurrect_current_user instead. :-)
    r24844 at zot (orig r3570):  sterling | 2007-06-26 12:38:44 -0400
     r7829 at riddle:  andrew | 2007-06-26 11:37:40 -0500
     Added a many-to-many relationship recipe to the cookbook.
    
    r24845 at zot (orig r3571):  alexmv | 2007-06-26 16:11:04 -0400
     r19955 at zoq-fot-pik:  chmrr | 2007-06-26 16:10:33 -0400
      * Recent YAML::Syck's get confused by trying to be too smart with
        $test_config, which is a filehandle which *also* stringifies to the
        path.  Force stringification to get consistent (working) results.
    
    r24920 at zot (orig r3580):  sterling | 2007-06-27 21:43:41 -0400
     r7874 at dynpc145:  andrew | 2007-06-27 20:42:34 -0500
     Fleshing out and cleaning up the documentation for Jifty::Plugin::User.
    
    r24921 at zot (orig r3581):  sterling | 2007-06-27 21:43:53 -0400
     r7875 at dynpc145:  andrew | 2007-06-27 20:43:00 -0500
     Fleshing out and cleaning up the documentation for Jifty::Plugin::Authentication::Password.
    
    r24924 at zot (orig r3584):  nelhage | 2007-06-29 19:25:18 -0400
    word-wrapping some POD
    r24925 at zot (orig r3585):  alexmv | 2007-06-29 19:48:14 -0400
     r19971 at zoq-fot-pik:  chmrr | 2007-06-29 19:46:25 -0400
      * Move pulling action defaults from action in request into the
        action's new, instead of a "private" argument.
      * Remove an extra place where we default the moniker to
        _generate_moniker
      * A bunch of POD (re)wrapping.
    
    r24926 at zot (orig r3586):  alexmv | 2007-06-29 19:56:06 -0400
     r19973 at zoq-fot-pik:  chmrr | 2007-06-29 19:54:26 -0400
      * The true return value of the module needs to not be part of the POD
      * Fix a POD nitpick
    
    r24950 at zot (orig r3588):  sterling | 2007-07-01 20:25:25 -0400
     r7901 at dynpc145:  andrew | 2007-07-01 19:21:40 -0500
     Altered the ClassLoader to create Collections for models deeper under the model namespace,like "App::Model::Foo::Bar". These is consistent with the handling for actions and other components already in place.
    
    r24951 at zot (orig r3589):  sterling | 2007-07-01 20:25:30 -0400
     r7902 at dynpc145:  andrew | 2007-07-01 19:23:07 -0500
     Fixed the formatting of the comment blocks in the POD.
    
    r24952 at zot (orig r3590):  sterling | 2007-07-01 20:25:37 -0400
     r7903 at dynpc145:  andrew | 2007-07-01 19:24:12 -0500
     Added an additional see also to Jifty::Manual::AccessControl to the User and Authentication::Password plugins.
    
    r24953 at zot (orig r3591):  sterling | 2007-07-01 20:35:12 -0400
     r7908 at dynpc145:  andrew | 2007-07-01 19:34:08 -0500
     Adding documentation to Jifty::Plugin::Debug to fix POD coverage failures.
    
    r24954 at zot (orig r3592):  sterling | 2007-07-01 20:35:18 -0400
     r7909 at dynpc145:  andrew | 2007-07-01 19:34:48 -0500
     Changing the Jifty console requirement back to default => 0.
    
    r24979 at zot (orig r3603):  trs | 2007-07-03 03:22:37 -0400
     r24976 at zot:  tom | 2007-07-03 02:49:39 -0400
     Basic Facebook auth plugin
    
    r24980 at zot (orig r3604):  trs | 2007-07-03 03:22:43 -0400
     r24977 at zot:  tom | 2007-07-03 03:04:34 -0400
     More pod, forceable login
    
    r24981 at zot (orig r3605):  trs | 2007-07-03 03:22:46 -0400
     r24978 at zot:  tom | 2007-07-03 03:22:01 -0400
     Provide a way to link existing users with a Facebook account
    
    r25010 at zot (orig r3610):  dpavlin | 2007-07-04 17:23:45 -0400
    check if current_user->can('user_object') when tring to figure out timezone
    r25011 at zot (orig r3611):  sterling | 2007-07-06 12:36:35 -0400
     r7940 at dynpc145:  andrew | 2007-07-06 11:34:54 -0500
     Added NAME sections and short descriptions for the CPAN module summary.
    
    r25038 at zot (orig r3612):  sterling | 2007-07-06 17:57:05 -0400
     r7942 at dynpc145:  andrew | 2007-07-06 16:56:27 -0500
     Added a recipe for using "result_of" to fetch information about actions for use in appends.
    
    r25041 at zot (orig r3613):  trs | 2007-07-06 19:05:11 -0400
     r25037 at zot:  tom | 2007-07-06 19:04:08 -0400
     - Fix our test mech to not choke on non-default cookie names
     - Clean up page headers
     - Remove unecessary var
    
    r25053 at zot (orig r3614):  sterling | 2007-07-07 15:29:31 -0400
     r7946 at dynpc145:  andrew | 2007-07-07 14:12:17 -0500
     Added support for application-specific plugins (i.e., App::Plugin::XXX) and plugins named using the fully-qualified Jifty::Plugin::XXX name.
    
    r25058 at zot (orig r3619):  jesse | 2007-07-09 05:02:03 -0400
     r60264 at 102:  jesse | 2007-07-08 21:52:09 -0700
      * Template::Declare behaviour change for "show 'foo'" to now show foo at the current template depth.
    
    r25059 at zot (orig r3620):  jesse | 2007-07-09 05:02:16 -0400
     r60267 at 102:  jesse | 2007-07-08 22:03:24 -0700
     * Relative to absolute pathing fix
    
    r25060 at zot (orig r3621):  jesse | 2007-07-09 05:02:29 -0400
     r60271 at 102:  jesse | 2007-07-08 22:23:13 -0700
     * minor refactoring
    
    r25071 at zot (orig r3632):  clkao | 2007-07-09 11:16:35 -0400
    Fix typo.
    r25075 at zot (orig r3634):  clkao | 2007-07-09 18:15:55 -0400
    support external javascripts.
    r25078 at zot (orig r3637):  clkao | 2007-07-09 19:37:52 -0400
    First cut of GoogleMap plugin.  You can now declare a column
    as geolocation, and you'll be able to display and edit it
    with a google map widget.
    
    r25079 at zot (orig r3638):  clkao | 2007-07-09 19:51:51 -0400
    Oops, debug info not killed before last commit.
    r25080 at zot (orig r3639):  clkao | 2007-07-09 19:54:55 -0400
    Fix typo.
    r25086 at zot (orig r3642):  clkao | 2007-07-09 23:46:50 -0400
    address search.  only works for accurate result for now.
    r25087 at zot (orig r3643):  clkao | 2007-07-10 00:15:50 -0400
    Minor fine tune.
    r25089 at zot (orig r3645):  clkao | 2007-07-10 08:59:25 -0400
    move most js from googlemap::widget into google_map.js
    r25091 at zot (orig r3646):  clkao | 2007-07-10 09:17:04 -0400
    Support readonly for googlemap widget.
    r25093 at zot (orig r3647):  clkao | 2007-07-10 10:26:55 -0400
    Address search!
    r25096 at zot (orig r3649):  clkao | 2007-07-10 17:26:09 -0400
    in REST record_to_data, skip container columns.
    r25119 at zot (orig r3650):  clkao | 2007-07-10 17:44:12 -0400
    type can be empty for container columns.
    r25120 at zot (orig r3651):  clkao | 2007-07-10 17:44:51 -0400
    Minor cleanup.
    
    r25121 at zot (orig r3652):  clkao | 2007-07-10 18:09:06 -0400
    Revert the logic change.  We still want to include container fields in list.
    r25123 at zot (orig r3653):  trs | 2007-07-10 21:34:21 -0400
    
    r25124 at zot (orig r3654):  trs | 2007-07-10 21:34:34 -0400
     r25118 at zot:  tom | 2007-07-10 21:33:18 -0400
     - Plugin CSS and JS (which should be put in share/plugin/Jifty/Plugin/Foo/web/static/{css,js}) is now compressed by CCJS
     - CCJS can compress more than just main.css by calling Jifty->web->add_css("file.css")
    
    r25126 at zot (orig r3655):  jesse | 2007-07-10 21:41:40 -0400
     r60363 at pinglin:  jesse | 2007-07-10 18:40:21 -0700
      * Doc updates
    
    r25128 at zot (orig r3656):  trs | 2007-07-10 22:03:26 -0400
     r25125 at zot:  tom | 2007-07-10 22:02:35 -0400
     Move GoogleMap plugin CSS and JS to proper spot now
    
    r25131 at zot (orig r3657):  trs | 2007-07-10 22:04:51 -0400
     r25130 at zot:  tom | 2007-07-10 22:04:40 -0400
     Supposed to be deleted
    
    r25160 at zot (orig r3658):  clkao | 2007-07-10 23:27:51 -0400
    Asset model mixin for adding created_by for default permission.
    
    r25161 at zot (orig r3659):  clkao | 2007-07-11 00:18:52 -0400
    googlemap widget  alignment fix.
    r25162 at zot (orig r3660):  clkao | 2007-07-11 10:07:46 -0400
    add_external_javascript for googlemap
    r25163 at zot (orig r3661):  clkao | 2007-07-11 10:12:06 -0400
    add_external_javascript takes array
    r25164 at zot (orig r3662):  clkao | 2007-07-11 10:30:45 -0400
    tabview plugin: allow render_tab in top level without current_region.
    r25165 at zot (orig r3663):  clkao | 2007-07-11 12:51:45 -0400
    * Make geocoder search usable even not in edit mode.
    * Add Cancel button in edit mode.
    
    r25166 at zot (orig r3664):  clkao | 2007-07-11 13:26:18 -0400
    Don't bother showing edit link if current user can't
    r25167 at zot (orig r3665):  jesse | 2007-07-11 14:02:56 -0400
     r60378 at mobile131:  jesse | 2007-07-10 21:59:41 -0700
     rename checkpoint
    
    r25168 at zot (orig r3666):  jesse | 2007-07-11 14:03:08 -0400
     r60379 at mobile131:  jesse | 2007-07-10 22:00:20 -0700
      Asset renamed to ActorMetadata
    
    
    r25169 at zot (orig r3667):  jesse | 2007-07-11 14:03:16 -0400
     r60382 at mobile131:  jesse | 2007-07-10 23:07:23 -0700
     * Incredibly naive "image column" userpic plugin.
         - should mark the column as not to be preloaded in regular operation
         - should not hardocde mimetype
         - should have a pretty ajax picker
         - should limit the image's size
    
    
    r25170 at zot (orig r3668):  clkao | 2007-07-11 15:57:22 -0400
    i18n for feedback plugin.
    r25171 at zot (orig r3669):  clkao | 2007-07-11 16:07:13 -0400
    Double precision
    r25177 at zot (orig r3673):  trs | 2007-07-11 20:04:13 -0400
     r25176 at zot:  tom | 2007-07-11 20:03:57 -0400
     - Minor refactoring for easy wording change
     - Provide the D in CRUD
    
    r25179 at zot (orig r3674):  clkao | 2007-07-11 20:58:19 -0400
    Fix IE issues with search div.
    r25184 at zot (orig r3678):  trs | 2007-07-12 00:47:47 -0400
     r25178 at zot:  tom | 2007-07-12 00:45:24 -0400
     Add some classes
    
    r25186 at zot (orig r3679):  trs | 2007-07-12 01:01:48 -0400
     r25185 at zot:  tom | 2007-07-12 00:50:05 -0400
     Extract out per_page
    
    r25188 at zot (orig r3681):  clkao | 2007-07-12 16:50:34 -0400
    * Make enter key submit in the address search popup.
    * Fix the close link in ambigious address popup.
    
    r25191 at zot (orig r3683):  ternus | 2007-07-13 01:12:32 -0400
    
    HTML branch merge
    
    r25194 at zot (orig r3685):  trs | 2007-07-13 03:49:08 -0400
    merge down again
    r25195 at zot (orig r3686):  falcone | 2007-07-13 11:40:56 -0400
    
    r25196 at zot (orig r3687):  falcone | 2007-07-13 11:42:09 -0400
     r22635 at ketch:  falcone | 2007-07-13 11:34:09 -0400
     * add missing Test::Log4perl dep and update the Jifty::DBI requirement
    
    r25198 at zot (orig r3688):  bartb | 2007-07-13 16:45:39 -0400
    typo fixes
    r25199 at zot (orig r3689):  jesse | 2007-07-14 00:02:15 -0400
     r60637 at pinglin:  jesse | 2007-07-13 22:06:16 -0400
     * Updated tabview plugin to work when it's not already inside a region
    
    
    r25200 at zot (orig r3690):  jesse | 2007-07-14 00:02:23 -0400
     r60638 at pinglin:  jesse | 2007-07-13 22:07:04 -0400
     * Fixed CRUD view to no longer require you to specify a base_path for the plugin's view. It was redundant and we could intuit it.
    
    r25208 at zot (orig r3695):  trs | 2007-07-16 19:06:58 -0400
     r25207 at zot:  tom | 2007-07-16 19:06:19 -0400
     Need parens
    
    r25294 at zot (orig r3696):  sterling | 2007-07-16 22:28:58 -0400
     r8092 at dynpc145:  andrew | 2007-07-16 21:28:04 -0500
     Plugins may now have regular models. These are deployed after the schema is setup after adding the plugin to installation. Plugins can bootstrap and upgrade their schema with their own version numbers just like applications (though with slightly different details).
    
    r25295 at zot (orig r3697):  sterling | 2007-07-16 23:21:16 -0400
     r8098 at dynpc145:  andrew | 2007-07-16 22:20:31 -0500
     Cleaned up the class loader a bit to make all the auto-generated methods use the same name, remove an unnecessary elsif, and add a few more comments.
    
    r25547 at zot (orig r3699):  trs | 2007-07-18 20:39:45 -0400
     r25546 at zot:  tom | 2007-07-18 20:38:59 -0400
     Don't mess with the HTML by default.  This should likely become configurable in the future.
    
    r25575 at zot (orig r3700):  sterling | 2007-07-19 09:36:47 -0400
     r8113 at dynpc145:  andrew | 2007-07-18 21:01:53 -0500
     Fixing API qualification to make it possible to access actions associated with plugins via ->new_class.
    
    r25583 at zot (orig r3702):  trs | 2007-07-19 15:23:09 -0400
     r25574 at zot:  tom | 2007-07-19 15:22:29 -0400
     Make sure we get UTF-8
    
    r25586 at zot (orig r3703):  trs | 2007-07-19 16:10:55 -0400
     r25585 at zot:  tom | 2007-07-19 16:10:15 -0400
     Now make sure it's actually UTF-8
    
    r25609 at zot (orig r3705):  clkao | 2007-07-20 10:00:23 -0400
    Push milestone 1 of trimclient to trunk.
    
    r25621 at zot (orig r3706):  jesse | 2007-07-22 16:21:40 -0400
    
    r25622 at zot (orig r3707):  jesse | 2007-07-22 16:21:58 -0400
     r61112 at 106:  jesse | 2007-07-22 10:20:34 -0700
     * The warnings come from the server code after the fork. Test::Log4Perl isn't going to catch them.
    
    r25625 at zot (orig r3708):  jesse | 2007-07-22 16:22:02 -0400
     r61115 at 106:  jesse | 2007-07-22 12:48:51 -0700
     * added some pod to help make pod tests pass
    
    r25639 at zot (orig r3715):  clkao | 2007-07-24 06:09:47 -0400
    Fix a fragment update regression caused by the trimclient merge.
    
    r25702 at zot (orig r3723):  sterling | 2007-07-27 15:53:01 -0400
    
    r25703 at zot (orig r3724):  sterling | 2007-07-27 15:54:54 -0400
     r8183 at riddle:  andrew | 2007-07-27 12:49:07 -0700
     Adding a plugin for rendering charts of data.
    
    r25704 at zot (orig r3725):  sterling | 2007-07-27 15:56:25 -0400
     r8184 at riddle:  andrew | 2007-07-27 12:49:42 -0700
     Removing a directory that should not have committed previously.
    
    r25705 at zot (orig r3726):  sterling | 2007-07-27 15:58:53 -0400
    
    r25708 at zot (orig r3727):  sterling | 2007-07-27 16:12:43 -0400
     r8192 at riddle:  andrew | 2007-07-27 13:11:49 -0700
     Reverting mistaken revision r3723
    
    r25710 at zot (orig r3728):  sterling | 2007-07-27 16:17:32 -0400
     r8194 at riddle:  andrew | 2007-07-27 13:16:59 -0700
     Reverting mistaken revision r3723, take 2
    
    r25714 at zot (orig r3730):  jesse | 2007-07-28 20:31:47 -0400
     r64900 at pinglin:  jesse | 2007-07-28 18:18:31 -0500
     * Moniker bulletproofing. Suggested by Mikko Lapasti
    
    r25716 at zot (orig r3731):  sterling | 2007-07-29 16:15:59 -0400
     r8199 at dynpc145:  andrew | 2007-07-29 15:13:38 -0500
     Added documentation to the experimental Chart plugin.
    
    r25718 at zot (orig r3732):  sterling | 2007-07-29 16:16:07 -0400
     r8200 at dynpc145:  andrew | 2007-07-29 15:15:21 -0500
     Added the Chart::Base recommendation for the Chart plugin.
    
    r25722 at zot (orig r3734):  jesse | 2007-07-29 17:53:45 -0400
     r64915 at pinglin:  jesse | 2007-07-29 17:53:35 -0400
     * Resolve import conflicts now that T::D and J::V::D::Helpers have a thingy with the same name
    
    r25724 at zot (orig r3735):  sterling | 2007-07-29 22:16:24 -0400
     r8203 at dynpc145:  andrew | 2007-07-29 21:13:01 -0500
     Adding a test suite for Jifty::Plugin::Chart, but it is having weird troubles loading Chart::* because that seems to disconnect the server output or something.
    
    r25726 at zot (orig r3736):  sterling | 2007-07-29 22:16:30 -0400
     r8204 at dynpc145:  andrew | 2007-07-29 21:13:35 -0500
     Adding Image::Info dependency used during testing of Jifty::Plugin::Chart
    
    r25728 at zot (orig r3737):  sterling | 2007-07-29 22:16:37 -0400
     r8205 at dynpc145:  andrew | 2007-07-29 21:15:43 -0500
     Regarding Jifty::Plugin::Chart: Added better comments. Fixed some error handling. Switched to using scalar_png(). Switched to using ->require rather than an eval to load Chart classes. Eliminated the need for IO::String. Moved some processing out of View and into Dispatcher.
    
    r25730 at zot (orig r3738):  sartak | 2007-07-30 16:57:21 -0400
     r29652 at caladan:  sartak | 2007-07-30 16:56:47 -0400
     Add a load_by_kv to Jifty::Web::Session
    
    r25732 at zot (orig r3739):  sartak | 2007-07-30 16:58:18 -0400
     r29654 at caladan:  sartak | 2007-07-30 16:58:03 -0400
     add myself to AUTHORS :)
    
    r25737 at zot (orig r3742):  jesse | 2007-07-30 20:38:33 -0400
     r64932 at pinglin:  jesse | 2007-07-30 20:37:16 -0400
     * First cut of a UUID column plugin, with a basic test in the user model
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3744 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --cc lib/Jifty/Web.pm
index 20fcd87,08108f4..5a1e177
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@@ -26,24 -26,31 +26,29 @@@ __PACKAGE__->mk_accessors
  );
  
  __PACKAGE__->mk_classdata($_)
-     for qw(cached_css        cached_css_digest        cached_css_time
-            cached_javascript cached_javascript_digest cached_javascript_time javascript_libs);
+     for qw(cached_css cached_css_digest cached_css_time
+            css_files  javascript_libs   external_javascript_libs);
+ 
+ __PACKAGE__->css_files([qw( main.css )]);
+ 
+ __PACKAGE__->external_javascript_libs([]);
  
  __PACKAGE__->javascript_libs([qw(
 -    jsan/JSAN.js
 -    jsan/Push.js
 -    setup_jsan.js
 -    jsan/Upgrade/Array/push.js
 -    jsan/DOM/Events.js
 +    yui/yahoo.js
 +    yui/dom.js
 +    yui/event.js
 +    yui/calendar.js
 +    yui/element-beta.js
 +    yui/tabview.js
 +    yui/container.js
 +    yui/menu.js
      json.js
      prototype.js
 -    cssquery/cssQuery.js
 -    cssquery/cssQuery-level2.js
 -    cssquery/cssQuery-level3.js
 -    cssquery/cssQuery-standard.js
      behaviour.js
      scriptaculous/builder.js
      scriptaculous/effects.js
      scriptaculous/controls.js
 -    formatDate.js
+     template_declare.js
      jifty.js
      jifty_utils.js
      jifty_subs.js
@@@ -55,10 -62,22 +60,12 @@@
      key_bindings.js
      context_menu.js
      bps_util.js
 -    rico.js
 -    yui/yahoo.js
 -    yui/dom.js
 -    yui/event.js
 -    yui/calendar.js
 -    yui/element-beta.js
 -    yui/tabview.js
 -    yui/container.js
 -    yui/menu.js
      app.js
      app_behaviour.js
 -    css_browser_selector.js
  )]);
  
+ use Jifty::DBI::Class::Trigger;
+ 
  =head1 METHODS
  
  =head3 new
diff --cc share/web/static/js/key_bindings.js
index 60788e5,413b179..d8d2f7b
--- a/share/web/static/js/key_bindings.js
+++ b/share/web/static/js/key_bindings.js
@@@ -19,9 -21,13 +19,13 @@@ Jifty.KeyBindings = 
      },
  
      deactivate: function() {
 -        DOM.Events.removeListener(Jifty.KeyBindings.listener);
 +        YAHOO.util.Event.removeListener(Jifty.KeyBindings.listener);
      },
  
+     reset: function() {
+         Jifty.KeyBindings.bindings = new Array();
+     },
+ 
      doClick: function(e) {
          if (e.target.nodeType == 3) // defeat Safari bug
              e.target = e.target.parentNode;

commit 4ad1b11aa39a6d0b4d79150779b7490cdd82c9f6
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Jul 31 21:28:53 2007 +0000

    Fix merge bug
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@3745 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

commit cd3b37566f1d0cb7ddb57a9ecbbe3c0d4b931306
Merge: 4ad1b11 d1b555f
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Thu Nov 29 17:25:16 2007 +0000

    Mergedown from trunk
    
     r65010 at pinglin (orig r3747):  sterling | 2007-07-31 22:32:37 -0400
    
     r65011 at pinglin (orig r3748):  sterling | 2007-07-31 22:32:48 -0400
      r8268 at dynpc145:  andrew | 2007-07-31 21:28:14 -0500
      Added a hack to chart.t (forcing an early load of GD) to avoid the segfault that was causing it to fail. Removed the TODO block from the test.
    
     r65012 at pinglin (orig r3749):  sterling | 2007-07-31 22:33:01 -0400
      r8269 at dynpc145:  andrew | 2007-07-31 21:29:42 -0500
      Fixed the way arguments are passed to the render() method in Jifty::Plugin::Chart::Web.
    
     r65013 at pinglin (orig r3750):  sterling | 2007-07-31 22:33:09 -0400
      r8270 at dynpc145:  andrew | 2007-07-31 21:31:01 -0500
      Moved the chart/* dispatch to chart/chart/* to make room for alternate charting mechanisms.
    
     r65014 at pinglin (orig r3751):  sterling | 2007-07-31 22:33:17 -0400
      r8271 at dynpc145:  andrew | 2007-07-31 21:31:21 -0500
      Added a renderer for GD::Graph
    
     r65015 at pinglin (orig r3752):  sterling | 2007-07-31 22:33:32 -0400
      r8272 at dynpc145:  andrew | 2007-07-31 21:31:41 -0500
      Updated the module recommendations for the Chart plugin.
    
     r65016 at pinglin (orig r3753):  sterling | 2007-07-31 22:47:45 -0400
      r8289 at dynpc145:  andrew | 2007-07-31 21:42:52 -0500
      Updated POD and removed an unnecessary extra subroutine call.
    
     r65017 at pinglin (orig r3754):  sterling | 2007-07-31 22:47:56 -0400
      r8290 at dynpc145:  andrew | 2007-07-31 21:44:59 -0500
      Fixed POD coverage issue.
    
     r65018 at pinglin (orig r3755):  sterling | 2007-07-31 22:48:02 -0400
      r8291 at dynpc145:  andrew | 2007-07-31 21:47:05 -0500
      Fixed an eensy POD bug.
    
     r65019 at pinglin (orig r3756):  sterling | 2007-07-31 23:03:26 -0400
      r8296 at dynpc145:  andrew | 2007-07-31 22:03:08 -0500
      Made the chart plugin test smarter and added one for the GD::Graph renderer.
    
     r65020 at pinglin (orig r3757):  trs | 2007-07-31 23:41:50 -0400
      r25774 at zot:  tom | 2007-07-31 23:40:12 -0400
      Basic PlotKit renderer for Chart plugin
    
     r65021 at pinglin (orig r3758):  trs | 2007-08-01 02:49:40 -0400
      r25776 at zot:  tom | 2007-08-01 02:49:29 -0400
      - Uncomment neccessary require
      - Make sure to handle undefined stuff
    
     r65038 at pinglin (orig r3760):  jesse | 2007-08-01 15:15:14 -0400
    
     r65039 at pinglin (orig r3761):  jesse | 2007-08-01 15:15:32 -0400
      r65037 at pinglin:  jesse | 2007-08-01 15:11:00 -0400
       * clean up load_by_kv
    
     r65074 at pinglin (orig r3762):  sartak | 2007-08-01 15:31:06 -0400
      r29755 at caladan:  sartak | 2007-08-01 15:30:21 -0400
      Add failing tests for load_by_kv with chr(0)
    
     r65075 at pinglin (orig r3763):  trs | 2007-08-01 22:33:42 -0400
      r25802 at zot:  tom | 2007-08-01 22:33:03 -0400
      *Very* custom packed PlotKit (from svn) that no longer depends on MochiKit exporting functions into the global namespace
    
      Still need to solve the issue of why MochiKit blows up when included in our honkin' JS file...
    
     r65076 at pinglin (orig r3764):  trs | 2007-08-02 02:28:00 -0400
      r25809 at zot:  tom | 2007-08-02 02:25:34 -0400
      Don't mess with the data structure if it's already what plotkit expects
    
     r65077 at pinglin (orig r3765):  sterling | 2007-08-02 09:27:53 -0400
      r8303 at dynpc145:  andrew | 2007-08-02 08:26:56 -0500
      Add explicit hide/show for the error, warning, and canonicalization note divs. This solves some long time ugliness on IE. However, it is not perfect as I am still getting some rendering issues on IE. Does not seem to break anything on FF.
    
     r65173 at pinglin (orig r3767):  sterling | 2007-08-03 12:10:43 -0400
      r8325 at riddle:  andrew | 2007-08-03 11:09:49 -0500
      Making the IMG-based chart renderers capable of handling CSS styling with some added behaviour.
    
     r65174 at pinglin (orig r3768):  sterling | 2007-08-03 12:40:37 -0400
      r8328 at riddle:  andrew | 2007-08-03 11:40:04 -0500
      Added a renderer parameter to the chart() method and add per-renderer initialization.
    
     r65187 at pinglin (orig r3781):  sterling | 2007-08-03 17:31:50 -0400
      r8333 at riddle:  andrew | 2007-08-03 16:30:37 -0500
      Fix failing dependency test because Chart uses GD to fix testing.
    
     r65188 at pinglin (orig r3782):  sterling | 2007-08-03 17:32:01 -0400
      r8334 at riddle:  andrew | 2007-08-03 16:31:13 -0500
      Updated the behaviour script used by IMG chart renderers to make it more URI aware.
    
     r65193 at pinglin (orig r3787):  sterling | 2007-08-04 18:30:52 -0400
      r8351 at dynpc145:  andrew | 2007-08-04 16:13:23 -0500
      Standardizing the chart types across the three current renderers.
    
     r65194 at pinglin (orig r3788):  sterling | 2007-08-04 18:31:00 -0400
      r8370 at dynpc145:  andrew | 2007-08-04 17:29:36 -0500
       r8356 at dynpc145:  andrew | 2007-08-04 16:33:13 -0500
       Added better error handling on renderer require.
    
    
     r65195 at pinglin (orig r3789):  sterling | 2007-08-04 18:31:06 -0400
      r8371 at dynpc145:  andrew | 2007-08-04 17:30:09 -0500
       r8357 at dynpc145:  andrew | 2007-08-04 16:33:37 -0500
       Fixed typos in the new type handling code and fixed error handling.
    
    
     r65196 at pinglin (orig r3790):  sterling | 2007-08-04 18:31:18 -0400
      r8372 at dynpc145:  andrew | 2007-08-04 17:30:09 -0500
       r8358 at dynpc145:  andrew | 2007-08-04 16:46:12 -0500
       Fixing my previous brain damage and additional typos.
    
    
     r65197 at pinglin (orig r3791):  sterling | 2007-08-04 18:31:26 -0400
      r8373 at dynpc145:  andrew | 2007-08-04 17:30:10 -0500
       r8359 at dynpc145:  andrew | 2007-08-04 16:53:49 -0500
       Improved the way the DIV tag is generated for PlotKit.
    
    
     r65198 at pinglin (orig r3792):  sterling | 2007-08-04 18:31:35 -0400
      r8374 at dynpc145:  andrew | 2007-08-04 17:30:10 -0500
       r8360 at dynpc145:  andrew | 2007-08-04 16:59:22 -0500
       Removed some redundant code from the PlotKit renderer and added support for options to the GD::Graph and Chart renderers.
    
    
     r65200 at pinglin (orig r3794):  sterling | 2007-08-05 17:24:28 -0400
      r8386 at dynpc145:  andrew | 2007-08-05 16:23:50 -0500
      Adding the SimpleBars renderer as a decent, dead-simple HTML-based renderer for HorizontalBars and a prototype for using tables for client-side chart configuration.
    
     r65201 at pinglin (orig r3795):  sterling | 2007-08-05 19:07:44 -0400
      r8392 at dynpc145:  andrew | 2007-08-05 18:06:59 -0500
       r8388 at dynpc145:  andrew | 2007-08-05 17:19:52 -0500
       Make sure we do not attempt to render 0 pixel values no matter what.
    
    
     r65202 at pinglin (orig r3796):  trs | 2007-08-05 20:34:44 -0400
      r25915 at zot:  tom | 2007-08-05 20:34:08 -0400
      Render onAvailable instead of on window load so that we work in regions
    
     r65203 at pinglin (orig r3797):  trs | 2007-08-05 21:35:49 -0400
      r25919 at zot:  tom | 2007-08-05 21:35:39 -0400
      Use PlotKit.Base.map explicitly
    
     r65840 at pinglin (orig r3820):  trs | 2007-08-06 17:18:23 -0400
      r25943 at zot:  tom | 2007-08-06 17:17:51 -0400
      Without the conditional we'll hide errors as soon as we display them
    
     r65841 at pinglin (orig r3821):  trs | 2007-08-06 19:38:53 -0400
      r25961 at zot:  tom | 2007-08-06 19:38:14 -0400
      Fix view CRUD template's method of getting the record
    
     r65842 at pinglin (orig r3822):  trs | 2007-08-08 00:54:59 -0400
      r25997 at zot:  tom | 2007-08-08 00:54:25 -0400
      Bunch of updates to the chart plugin
    
          - Refactored dispatcher
          - Added XML SWF renderer
          - Renderers are now passed the configuration hash when init'd
    
    
     r65843 at pinglin (orig r3823):  trs | 2007-08-08 01:01:40 -0400
      r25999 at zot:  tom | 2007-08-08 01:01:30 -0400
      - Treat the width and height appropriately
      - Add the XML::Simple dep
    
     r65844 at pinglin (orig r3824):  trs | 2007-08-08 01:12:55 -0400
      r26001 at zot:  tom | 2007-08-08 01:12:44 -0400
      Don't specify an align attribute
    
     r65845 at pinglin (orig r3825):  trs | 2007-08-08 01:46:05 -0400
      r26005 at zot:  tom | 2007-08-08 01:45:55 -0400
      Whoops.  Forgot to check in the actual XML SWF library.  This is version 4.6.
    
     r65857 at pinglin (orig r3837):  agentz | 2007-08-10 05:09:35 -0400
      r1254 at agentz-office:  agentz | 2007-08-10 17:08:14 +0800
      Makefile.PL - do NOT skip any tests under t/ unintentionally ;)
    
     r65858 at pinglin (orig r3838):  clkao | 2007-08-10 05:27:34 -0400
     Correct a crud component path.
     r65859 at pinglin (orig r3839):  clkao | 2007-08-10 05:42:40 -0400
     In action argument creation from model, do not assume refers_to
     always want a select based on id which we might not be referring to.
     Allow user to override render_as for refers_to columns.
    
     r65862 at pinglin (orig r3842):  sterling | 2007-08-10 09:57:59 -0400
      r8486 at riddle:  andrew | 2007-08-10 08:57:04 -0500
      Removing extra comma from JavaScript list.
    
     r65863 at pinglin (orig r3843):  sterling | 2007-08-10 10:19:16 -0400
      r8491 at riddle:  andrew | 2007-08-10 09:18:27 -0500
      Chart plugin configuration updates:
       * Deprecating the renderer option.
       * Adding the DefaultRenderer option to replace renderer.
       * Adding the PreloadRenderers option to allow additional renderers to be preloaded.
       * Updated the documentation.
       * Made sure that the configuration is always passed to the renderer constructor, even if they are loaded late.
    
     r65864 at pinglin (orig r3844):  sartak | 2007-08-10 12:20:53 -0400
      r30163 at caladan:  sartak | 2007-08-10 12:20:01 -0400
      todo-ify failing tests in t/13-sessions.t
    
     r65865 at pinglin (orig r3845):  sterling | 2007-08-10 12:26:54 -0400
      r8499 at riddle:  andrew | 2007-08-10 11:26:05 -0500
      Fixing the chart plugin tests to match up with changes that have happened to the API since they were written.
    
     r65866 at pinglin (orig r3846):  sterling | 2007-08-10 16:50:53 -0400
      r8506 at riddle:  andrew | 2007-08-10 15:49:46 -0500
      Fix to a JavaScript bug performing the rerendering of IMG graphs.
    
     r65868 at pinglin (orig r3848):  clkao | 2007-08-11 03:48:56 -0400
     Follow up to r3839, do not change the logic if refers_to is not jifty::record
     r65869 at pinglin (orig r3849):  agentz | 2007-08-11 07:54:48 -0400
      r1257 at agentz-office:  agentz | 2007-08-10 19:23:18 +0800
      fixed Jifty::View::Declare::Helpers since we now install tag subs directly to the target package instead of using @EXPORT. We now makes use of T::D::Tags's @TagSubs struct
    
     r65870 at pinglin (orig r3850):  agentz | 2007-08-11 07:57:41 -0400
      r1268 at agentz-office:  agentz | 2007-08-11 19:54:25 +0800
      made jifty trunk work with earlier versions of TD
    
     r65871 at pinglin (orig r3851):  agentz | 2007-08-11 08:01:07 -0400
      r1272 at agentz-office:  agentz | 2007-08-11 20:00:22 +0800
      'svk push' sometimes gives me trouble...and i dunno why...
    
     r65883 at pinglin (orig r3858):  sterling | 2007-08-12 16:23:46 -0400
      r8534 at riddle:  andrew | 2007-08-12 15:13:21 -0500
      Improving some of the Pod, code comments, and minor perl tidying.
    
     r65884 at pinglin (orig r3859):  sterling | 2007-08-12 16:24:45 -0400
    
     r65885 at pinglin (orig r3860):  sterling | 2007-08-12 16:31:45 -0400
      r8541 at riddle:  andrew | 2007-08-12 15:31:19 -0500
      Fixing error that occurred during previous push.
    
     r65886 at pinglin (orig r3861):  sterling | 2007-08-12 18:13:31 -0400
      r8577 at riddle:  andrew | 2007-08-12 17:12:34 -0500
      Fixing POD issues in CRUD, adding some code comments, and minor perl tidying.
    
     r65887 at pinglin (orig r3862):  sterling | 2007-08-12 18:32:08 -0400
      r8579 at riddle:  andrew | 2007-08-12 17:31:33 -0500
      Additional fix to POD coverage for the CRUDView attribute.
    
     r65888 at pinglin (orig r3863):  sterling | 2007-08-12 19:01:50 -0400
      r8581 at riddle:  andrew | 2007-08-12 18:01:14 -0500
      Added more POD to fix coverage issues and added a description to the client_cacheable and client_cache_content methods of PageRegion.
    
     r65902 at pinglin (orig r3864):  sterling | 2007-08-12 21:17:57 -0400
      r8583 at riddle:  andrew | 2007-08-12 20:17:16 -0500
      Improved code comments and minor perl tidy.
    
     r65910 at pinglin (orig r3872):  agentz | 2007-08-13 02:18:39 -0400
      r1287 at agentz-office:  agentz | 2007-08-13 14:18:06 +0800
      trivial edits
    
     r65911 at pinglin (orig r3873):  agentz | 2007-08-13 02:34:11 -0400
      r1297 at agentz-office:  agentz | 2007-08-13 14:33:35 +0800
      Makefile.PL - Jifty now depends on TD 0.26
    
     r66123 at pinglin (orig r3877):  sterling | 2007-08-13 21:54:57 -0400
      r8534 at dynpc145:  andrew | 2007-08-12 15:13:21 -0500
      Improving some of the Pod, code comments, and minor perl tidying.
    
     r66124 at pinglin (orig r3878):  sterling | 2007-08-13 21:56:46 -0400
    
     r66125 at pinglin (orig r3879):  sterling | 2007-08-13 21:56:52 -0400
      r8541 at dynpc145:  andrew | 2007-08-12 15:31:19 -0500
      Fixing error that occurred during previous push.
    
     r66126 at pinglin (orig r3880):  sterling | 2007-08-13 21:58:40 -0400
      r8577 at dynpc145:  andrew | 2007-08-12 17:12:34 -0500
      Fixing POD issues in CRUD, adding some code comments, and minor perl tidying.
    
     r66127 at pinglin (orig r3881):  sterling | 2007-08-13 21:58:53 -0400
      r8579 at dynpc145:  andrew | 2007-08-12 17:31:33 -0500
      Additional fix to POD coverage for the CRUDView attribute.
    
     r66128 at pinglin (orig r3882):  sterling | 2007-08-13 21:59:04 -0400
      r8581 at dynpc145:  andrew | 2007-08-12 18:01:14 -0500
      Added more POD to fix coverage issues and added a description to the client_cacheable and client_cache_content methods of PageRegion.
    
     r66129 at pinglin (orig r3883):  sterling | 2007-08-13 21:59:16 -0400
      r8583 at dynpc145:  andrew | 2007-08-12 20:17:16 -0500
      Improved code comments and minor perl tidy.
    
     r66130 at pinglin (orig r3884):  sterling | 2007-08-13 21:59:32 -0400
      r8619 at dynpc145:  andrew | 2007-08-13 19:51:58 -0500
      Cleaning up the Pod for Jifty::Bootstrap
    
     r66131 at pinglin (orig r3885):  sterling | 2007-08-13 21:59:57 -0400
      r8620 at dynpc145:  andrew | 2007-08-13 20:51:09 -0500
      Major Pod improvement to the class loader, many more helpful code comments, and some tidying for the class loader. Phew.
    
     r66132 at pinglin (orig r3886):  sterling | 2007-08-13 22:16:58 -0400
      r8630 at dynpc145:  andrew | 2007-08-13 21:16:20 -0500
      Updating Pod and adding code comments to Jifty::Client.
    
     r66133 at pinglin (orig r3887):  efunneko | 2007-08-13 22:38:51 -0400
     Added myself to AUTHORS
     r66136 at pinglin (orig r3890):  sterling | 2007-08-13 22:46:42 -0400
      r8635 at dynpc145:  andrew | 2007-08-13 21:46:18 -0500
      Cleaning up the Pod and adding a few code comments to Jifty::Collection.
    
     r66140 at pinglin (orig r3894):  sterling | 2007-08-13 23:49:27 -0400
      r8641 at dynpc145:  andrew | 2007-08-13 22:49:12 -0500
      Updating Pod and source comments for Jifty::Config. Performed some Perl tidying and added a new section describing why Jifty uses three levels of configuration files (may need additional editting).
    
     r66145 at pinglin (orig r3899):  sterling | 2007-08-14 08:48:29 -0400
      r8648 at dynpc145:  andrew | 2007-08-14 07:48:11 -0500
      Fixing POD test failure.
    
     r66146 at pinglin (orig r3900):  efunneko | 2007-08-14 13:16:43 -0400
     Added support for more javascript triggers for most form elements:  onclick onchange ondblclick onmousedown onmouseup onmouseover onmousemove onmouseout onfocus onblur onkeypress onkeydown onkeyup onselect
     r66149 at pinglin (orig r3903):  efunneko | 2007-08-14 20:16:51 -0400
     Removed some duplicated code
     r66152 at pinglin (orig r3906):  trs | 2007-08-15 23:02:54 -0400
      r26160 at zot:  tom | 2007-08-15 23:02:16 -0400
      Give the browser (particularly Safari) some more "settle time"
    
     r66159 at pinglin (orig r3913):  sterling | 2007-08-16 23:08:51 -0400
      r8678 at dynpc145:  andrew | 2007-08-14 19:26:11 -0500
      Adding tangent and return to the glossary.
    
     r66160 at pinglin (orig r3914):  sterling | 2007-08-16 23:09:08 -0400
      r8695 at dynpc145:  andrew | 2007-08-16 22:07:42 -0500
      Fixes to use_mason_wrapper():
       * Added a test to make sure use_mason_wrapper() works.
       * Added $jifty_internal_request to note whether a Mason request is internal or not.
       * Altered the autohandler to use $jifty_internal_request when blocking access to /_elements/
       * Fixed error handling in the autohandler to redirect to /__jifty/error/requested_private_component rather than /errors/requested_private_component
    
     r66161 at pinglin (orig r3915):  sterling | 2007-08-19 09:39:34 -0400
      r8812 at riddle:  andrew | 2007-08-19 08:38:43 -0500
      Added a new helper, new_record_action(), that wraps new_action() with additional help creating Create, Update, Delete, and Search actions for models.
    
     r66162 at pinglin (orig r3916):  sterling | 2007-08-20 13:09:31 -0400
      r8814 at riddle:  andrew | 2007-08-20 12:08:34 -0500
      Adding a plugin for using the jQuery Javascript library with Jifty.
    
     r66165 at pinglin (orig r3919):  sterling | 2007-08-20 16:55:02 -0400
      r8818 at riddle:  andrew | 2007-08-20 15:29:01 -0500
      Adding documentation for Action and Static attributes to resolve POD coverage test failures.
    
     r66166 at pinglin (orig r3920):  sterling | 2007-08-20 16:55:08 -0400
      r8819 at riddle:  andrew | 2007-08-20 15:38:38 -0500
      Cleaning up documentation to fix pod coverage test failures.
    
     r66167 at pinglin (orig r3921):  sterling | 2007-08-20 16:55:14 -0400
      r8820 at riddle:  andrew | 2007-08-20 15:54:10 -0500
      Fixing pod coverage problems with the jQuery plugin.
    
     r66168 at pinglin (orig r3922):  sterling | 2007-08-20 16:57:37 -0400
      r8824 at riddle:  andrew | 2007-08-20 15:57:13 -0500
      Undoing previous accidental patch to Jifty::Continuation because I have not finished proofing it.
    
     r66171 at pinglin (orig r3924):  jesse | 2007-08-20 19:17:48 -0400
      r65916 at 000-176-581:  jesse | 2007-08-13 16:49:12 -0400
      * POD fixes
    
     r66172 at pinglin (orig r3925):  jesse | 2007-08-20 19:18:11 -0400
      r65917 at 000-176-581:  jesse | 2007-08-13 17:08:59 -0400
      * The image column isn't at all baked. it was a bad fork of userpic
    
     r66197 at pinglin (orig r3926):  yves | 2007-08-21 10:49:01 -0400
     debian packaging
    
     r66519 at pinglin (orig r3929):  clkao | 2007-08-21 22:03:01 -0400
     require Class::Trigger 0.12 for abortable triggers used by css/js compression plugin.
     r66520 at pinglin (orig r3930):  clkao | 2007-08-21 22:12:04 -0400
     GD is not mandatory requirement.  Skip chart test if not found.
     r66521 at pinglin (orig r3931):  clkao | 2007-08-21 22:13:42 -0400
     var/mason shouldn't be here
     r66522 at pinglin (orig r3932):  clkao | 2007-08-21 22:17:35 -0400
     ditto for chart.t
     r66523 at pinglin (orig r3933):  efunneko | 2007-08-22 10:42:15 -0400
     Fixed a problem where some disabled elements weren't re-enabled after a submission when the disabled elements were outside of the fragment being refreshed.
     r66524 at pinglin (orig r3934):  audreyt | 2007-08-22 11:03:57 -0400
     * Jifty::View::Declare::CRUD - I18N.
     r66525 at pinglin (orig r3935):  audreyt | 2007-08-22 11:06:44 -0400
     * Jifty::View::Declare - Work around Perl 5.9.5 bug by avoid punning
       the constant name BaseClass with the subclass name ::BaseClass.
     r66526 at pinglin (orig r3936):  clkao | 2007-08-22 11:22:40 -0400
     properly skip client side td test if new B is not found.
     r66527 at pinglin (orig r3937):  audreyt | 2007-08-22 11:38:30 -0400
     * Jifty::Plugin::SkeletonApp::Dispatcher -
       Do not override the 'Home' menu item if the app had set it already.
     r66528 at pinglin (orig r3938):  sartak | 2007-08-22 11:56:46 -0400
      r30488 at caladan:  sartak | 2007-08-22 11:55:55 -0400
      Add friendly_date method to Jifty::DateTime which special-cases yesterday/today/tomorrow
    
     r66530 at pinglin (orig r3940):  clkao | 2007-08-23 04:40:04 -0400
     Jifty.Subs needs outs_raw.
     r66531 at pinglin (orig r3941):  clkao | 2007-08-23 10:31:08 -0400
     respect initial PATH in env under fastcgi.
     r66532 at pinglin (orig r3942):  clkao | 2007-08-23 10:32:51 -0400
     bad mason dir.
     r66533 at pinglin (orig r3943):  clkao | 2007-08-23 10:40:57 -0400
     don't use pager to determine items as list_items might be called
     directly and no pager has been set on the collection.
    
     r66534 at pinglin (orig r3944):  clkao | 2007-08-23 10:42:20 -0400
     update tabview plugin to use new relative path syntax for td show().
     r66538 at pinglin (orig r3945):  jesse | 2007-08-23 12:47:40 -0400
      r66536 at pinglin:  jesse | 2007-08-23 12:19:20 -0400
      * Minor pod additions. removing unneeded wrappers
    
     r66541 at pinglin (orig r3946):  jesse | 2007-08-23 14:02:19 -0400
      r66539 at pinglin:  jesse | 2007-08-23 12:58:28 -0400
      * WWW::Mechanize versions before 1.30 had broken gzip behaviour which broke tests
    
     r66542 at pinglin (orig r3947):  jesse | 2007-08-23 14:02:33 -0400
      r66540 at pinglin:  jesse | 2007-08-23 13:56:06 -0400
      * Remove cargo-culting changing of $0 from Jifty::SubTest. It doesn't affect test output but does break things
    
     r66544 at pinglin (orig r3948):  sunnavy | 2007-08-23 22:37:49 -0400
     more translations in zh_cn.po
     r66545 at pinglin (orig r3949):  sunnavy | 2007-08-23 23:44:24 -0400
     changed View.pm in HelloKitty in accordance with current Jifty libs
     r66546 at pinglin (orig r3950):  jesse | 2007-08-23 23:49:09 -0400
      r66543 at pinglin:  jesse | 2007-08-23 23:48:47 -0400
      * New changelog generation tool
    
     r66557 at pinglin (orig r3954):  jesse | 2007-08-24 00:19:23 -0400
      r66553 at pinglin:  jesse | 2007-08-24 00:17:00 -0400
       *releng
    
     r66558 at pinglin (orig r3955):  jesse | 2007-08-24 00:19:47 -0400
      r66555 at pinglin:  jesse | 2007-08-24 00:17:26 -0400
       *signature
    
     r66578 at pinglin (orig r3958):  ishigaki | 2007-08-24 01:28:00 -0400
     t/TestApp-Plugin-JQuery/etc/config.yml: colons in the database name is not allowed on win32
     r66579 at pinglin (orig r3959):  agentz | 2007-08-24 01:31:15 -0400
      r1340 at agentz-office:  agentz | 2007-08-24 13:29:53 +0800
      updated View/Declare/Helpers.pm to use @Template::Declare::Tags::TAG_SUB_LIST instead of TagSubs
    
     r66594 at pinglin (orig r3974):  clkao | 2007-08-24 17:47:08 -0400
     In td's page { foo => 'bar', title => 'blah' } content {} style,
     pass the metadata to the content code as the second argument.
    
     r66595 at pinglin (orig r3975):  clkao | 2007-08-24 17:48:21 -0400
     Make spa support "normal" form_submit of a form with submit_to
     that has no action.
    
     r66598 at pinglin (orig r3978):  clkao | 2007-08-25 05:12:30 -0400
     Don't deal with empty submit_to.
     r66599 at pinglin (orig r3979):  clkao | 2007-08-25 06:07:47 -0400
     actually we want to use sp_submit whenever we have submit_to and
     no onclick.
     r66600 at pinglin (orig r3980):  clkao | 2007-08-25 06:10:10 -0400
     pesky mason files.
     r66601 at pinglin (orig r3981):  clkao | 2007-08-25 17:27:57 -0400
     minor cleanups to the tabview plugin.
     r66602 at pinglin (orig r3982):  clkao | 2007-08-26 06:21:03 -0400
     Allow a more flexible way to use tabview, such using custom labels.
    
     r66603 at pinglin (orig r3983):  clkao | 2007-08-26 06:35:36 -0400
     tabview: make name and path optional.
     r66604 at pinglin (orig r3984):  sterling | 2007-08-26 17:28:29 -0400
      r8648 at riddle:  andrew | 2007-08-14 07:48:11 -0500
      Fixing POD test failure.
    
     r66605 at pinglin (orig r3985):  sterling | 2007-08-26 17:29:11 -0400
      r8695 at riddle:  andrew | 2007-08-16 22:07:42 -0500
      Fixes to use_mason_wrapper():
       * Added a test to make sure use_mason_wrapper() works.
       * Added $jifty_internal_request to note whether a Mason request is internal or not.
       * Altered the autohandler to use $jifty_internal_request when blocking access to /_elements/
       * Fixed error handling in the autohandler to redirect to /__jifty/error/requested_private_component rather than /errors/requested_private_component
    
     r66606 at pinglin (orig r3986):  sterling | 2007-08-26 17:29:17 -0400
      r8812 at riddle:  andrew | 2007-08-19 08:38:43 -0500
      Added a new helper, new_record_action(), that wraps new_action() with additional help creating Create, Update, Delete, and Search actions for models.
    
     r66607 at pinglin (orig r3987):  sterling | 2007-08-26 17:31:29 -0400
      r8814 at riddle:  andrew | 2007-08-20 12:08:34 -0500
      Adding a plugin for using the jQuery Javascript library with Jifty.
    
     r66608 at pinglin (orig r3988):  sterling | 2007-08-26 17:31:36 -0400
      r8818 at riddle:  andrew | 2007-08-20 15:29:01 -0500
      Adding documentation for Action and Static attributes to resolve POD coverage test failures.
    
     r66609 at pinglin (orig r3989):  sterling | 2007-08-26 17:33:35 -0400
      r8819 at riddle:  andrew | 2007-08-20 15:38:38 -0500
      Cleaning up documentation to fix pod coverage test failures.
    
     r66610 at pinglin (orig r3990):  sterling | 2007-08-26 17:33:44 -0400
      r8820 at riddle:  andrew | 2007-08-20 15:54:10 -0500
      Fixing pod coverage problems with the jQuery plugin.
    
     r66611 at pinglin (orig r3991):  sterling | 2007-08-26 17:34:04 -0400
      r8824 at riddle:  andrew | 2007-08-20 15:57:13 -0500
      Undoing previous accidental patch to Jifty::Continuation because I have not finished proofing it.
    
     r66612 at pinglin (orig r3992):  sterling | 2007-08-26 17:34:24 -0400
      r8977 at riddle:  andrew | 2007-08-26 16:28:08 -0500
      Removing Test::Log4perl. It isn't used anywhere anymore.
    
     r66613 at pinglin (orig r3993):  sterling | 2007-08-26 17:39:45 -0400
      r8988 at riddle:  andrew | 2007-08-26 16:39:26 -0500
      Reverting the addition of these files which were added by a push error.
    
     r66950 at pinglin (orig r4017):  sterling | 2007-08-30 22:26:52 -0400
      r11089 at riddle:  andrew | 2007-08-30 21:26:36 -0500
      Making Jifty::Web::Menu more extensible.
    
     r66951 at pinglin (orig r4018):  sartak | 2007-08-31 14:51:04 -0400
      r40422 at onn:  sartak | 2007-08-31 14:50:15 -0400
      Add (grr, passing) tests for setting columns in the test app
    
     r66952 at pinglin (orig r4019):  audreyt | 2007-08-31 17:21:56 -0400
     * Jifty::Notification - Minor doc fix - The html_body method was
       misspelled as html-body.
     r66953 at pinglin (orig r4020):  sartak | 2007-08-31 17:30:44 -0400
      r40432 at onn:  sartak | 2007-08-31 17:30:24 -0400
      In Jifty::DateTime->new, set the timezone to floating if it looks like just a date
    
     r66954 at pinglin (orig r4021):  audreyt | 2007-08-31 17:36:37 -0400
     * SendFeedback.pm: Take away the phrase "hiveminder" from the documentation.
     r66960 at pinglin (orig r4027):  clkao | 2007-09-01 15:12:49 -0400
     spa: don't translate javascript: links.
    
     r66961 at pinglin (orig r4028):  clkao | 2007-09-01 15:15:52 -0400
     tabview: fix the logic for defered tabs.
    
     r66962 at pinglin (orig r4029):  clkao | 2007-09-02 16:11:58 -0400
     Name the first Jifty->new during test 'pre_init', and tell plugins
     not to register triggers just yet to avoid duplicated triggers.
    
     r66963 at pinglin (orig r4030):  clkao | 2007-09-02 16:35:45 -0400
     Change how plugin is initialized so _pre_init is actually
     noted.
    
     r66964 at pinglin (orig r4031):  clkao | 2007-09-02 18:32:52 -0400
     First cut of Selenium testing support in Jifty.
    
     r66965 at pinglin (orig r4032):  ruz | 2007-09-02 19:47:58 -0400
     * don't gen a collection class if there is no model
     r66968 at pinglin (orig r4035):  sterling | 2007-09-02 23:18:41 -0400
      r11133 at dynpc145:  andrew | 2007-09-02 22:17:41 -0500
      Additional selector to make sure that autocomplete hides .hidden_value on .inline forms.
    
     r66971 at pinglin (orig r4038):  clkao | 2007-09-03 16:30:09 -0400
     my computer is slow. be more patient about selenium rc startup.
     r66972 at pinglin (orig r4039):  ruz | 2007-09-03 16:55:55 -0400
     * collection generator may be called when we're loading models
       and not all models have been loaded to the moment, so we have
       to try to require the model before testing if it esists.
     r66973 at pinglin (orig r4040):  ishigaki | 2007-09-04 07:33:00 -0400
     Jifty::DBI now  uses Class::Trigger instead of Jifty::DBi::Class::Trigger (since rev3968)
     r66974 at pinglin (orig r4041):  ishigaki | 2007-09-04 07:48:01 -0400
     added Test::Log4perl dep again; it is surely used at t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t
     r66976 at pinglin (orig r4043):  falcone | 2007-09-04 15:13:10 -0400
      r24400 at ketch:  falcone | 2007-09-04 15:10:44 -0400
      * revert changes to ClassLoader.  Collection classes are failing to be generated
        in some TD views
    
     r66978 at pinglin (orig r4045):  sterling | 2007-09-04 18:36:53 -0400
      r11186 at riddle:  andrew | 2007-09-04 17:35:47 -0500
      Added requirement for URI 1.31, which adds uri_escape_utf8().
    
     r67019 at pinglin (orig r4048):  jesse | 2007-09-06 14:51:56 -0400
      r66786 at pinglin:  jesse | 2007-08-30 20:05:58 -0400
       * we want to compute UUIDs every time. really. it's not useful to always have the same uuid
    
     r67034 at pinglin (orig r4052):  clkao | 2007-09-07 06:03:33 -0400
     default not to preserve state when spa is replacing __page
     r67035 at pinglin (orig r4053):  clkao | 2007-09-07 06:21:33 -0400
     Do not do clever & broken input_name for buttons when preserve_state is false.
     r67040 at pinglin (orig r4058):  sartak | 2007-09-07 11:40:03 -0400
      r42421 at onn:  sartak | 2007-09-07 11:39:31 -0400
      Revert clkao's fixes for now because they break apps :/
    
     r67041 at pinglin (orig r4059):  jesse | 2007-09-07 16:08:37 -0400
      r67030 at pinglin:  jesse | 2007-09-07 16:07:47 -0400
      * Clean up Jifty::Web initialization a bit.
    
     r67070 at pinglin (orig r4060):  clkao | 2007-09-07 18:19:30 -0400
     actormetadata: provide a current_user_is_owner method.
    
     r67071 at pinglin (orig r4061):  clkao | 2007-09-07 18:23:57 -0400
     webservices_rediret: don't redirect if there's any failed action.
    
     r67072 at pinglin (orig r4062):  clkao | 2007-09-08 09:42:57 -0400
     first cut of tests for singlepage app plugin.
     r67084 at pinglin (orig r4063):  sterling | 2007-09-10 11:47:38 -0400
      r11815 at riddle:  andrew | 2007-09-10 10:46:00 -0500
      Improving POD and minor tidying.
    
     r67090 at pinglin (orig r4069):  sartak | 2007-09-10 14:59:26 -0400
      r42468 at onn:  sartak | 2007-09-10 14:58:49 -0400
      friendly_date should be relative to the user, not floating
    
     r67096 at pinglin (orig r4070):  jesse | 2007-09-10 22:04:29 -0400
      r67092 at pinglin:  jesse | 2007-09-10 18:26:34 -0400
      * Some databases have schema creation single-threadedness. If they block, wait and retry for up to a minute
    
     r67097 at pinglin (orig r4071):  jesse | 2007-09-10 22:04:38 -0400
      r67093 at pinglin:  jesse | 2007-09-10 18:26:51 -0400
      * pod fixes
    
     r67098 at pinglin (orig r4072):  jesse | 2007-09-10 22:04:44 -0400
      r67094 at pinglin:  jesse | 2007-09-10 18:27:22 -0400
      * When testing, don't just use a single global test database. Instead, use one per testfile. This allows some measure of parallelization
    
     r67099 at pinglin (orig r4073):  jesse | 2007-09-10 22:04:54 -0400
      r67095 at pinglin:  jesse | 2007-09-10 22:01:20 -0400
       * tabview shouldn't force absolute paths to relative ones
    
     r67273 at pinglin (orig r4075):  sterling | 2007-09-11 12:50:51 -0400
      r11935 at dynpc145:  andrew | 2007-09-11 11:50:08 -0500
       * Ripping out new_record_action() from Jifty::Web.
       * Re-implementing it as as_create_action(), as_update_action(), as_delete_action, and as_search_action() in Jifty::Record and Jifty::Collection.
    
     r67274 at pinglin (orig r4076):  sterling | 2007-09-11 13:05:17 -0400
      r11937 at dynpc145:  andrew | 2007-09-11 12:04:53 -0500
      Making the as_*_action() methods accept a paramhash for additional new_action() parameters.
    
     r67275 at pinglin (orig r4077):  sartak | 2007-09-11 13:33:46 -0400
      r42507 at onn:  sartak | 2007-09-11 12:19:53 -0400
      Use ->clone on DateTime objects to avoid possible DST issues, etc
    
     r67276 at pinglin (orig r4078):  sterling | 2007-09-11 13:38:43 -0400
      r11939 at dynpc145:  andrew | 2007-09-11 12:13:59 -0500
      Clearing out old new_record_action() tests.
    
     r67277 at pinglin (orig r4079):  sterling | 2007-09-11 13:38:49 -0400
      r11940 at dynpc145:  andrew | 2007-09-11 12:14:37 -0500
      Clearing out old new_record_action() tests.
    
     r67278 at pinglin (orig r4080):  sterling | 2007-09-11 13:38:57 -0400
      r11941 at dynpc145:  andrew | 2007-09-11 12:38:20 -0500
       * Simplifying the CRUD code for generating the record_class and making it more flexible.
       * Adding a stub for CRUD testing.
    
     r67279 at pinglin (orig r4081):  sterling | 2007-09-11 13:56:15 -0400
      r11947 at dynpc145:  andrew | 2007-09-11 12:55:30 -0500
      POD clean-up, some code commenting, and minor tidying.
    
     r67281 at pinglin (orig r4083):  sterling | 2007-09-11 16:04:38 -0400
      r11951 at dynpc145:  andrew | 2007-09-11 15:04:01 -0500
      Clean up the Pod, added a Synopsis, added a Why? section, added code comments, and some code tidying.
    
     r67282 at pinglin (orig r4084):  sterling | 2007-09-11 17:38:58 -0400
      r11956 at dynpc145:  andrew | 2007-09-11 16:38:08 -0500
      Adding another CRUD setup example to the synopsis.
    
     r67283 at pinglin (orig r4085):  sterling | 2007-09-11 18:16:14 -0400
      r11959 at dynpc145:  andrew | 2007-09-11 17:15:52 -0500
      Don't override object_type if the CRUD view package already defines object_type.
    
     r67294 at pinglin (orig r4096):  sterling | 2007-09-11 22:51:52 -0400
      r12003 at dynpc145:  andrew | 2007-09-11 21:44:01 -0500
      Fixing a bug that prevents any but the first plugin being checked for schema updates.
    
     r67295 at pinglin (orig r4097):  sterling | 2007-09-11 22:52:01 -0400
      r12004 at dynpc145:  andrew | 2007-09-11 21:46:41 -0500
      Plugin upgrade incorrectly assumes a DB version of 0.0.1 when none is found.
    
     r67296 at pinglin (orig r4098):  sterling | 2007-09-11 22:54:25 -0400
      r12007 at dynpc145:  andrew | 2007-09-11 21:54:00 -0500
      Bring the default back, but at 0.0.0 to prevent uninitialized complaints.
    
     r67300 at pinglin (orig r4102):  sterling | 2007-09-12 16:43:13 -0400
      r12040 at riddle:  andrew | 2007-09-12 15:41:46 -0500
      Added missing jifty-result-popup div to the page detritus section of Jifty::View::Declare::Page.
    
     r67302 at pinglin (orig r4104):  sterling | 2007-09-12 23:23:28 -0400
      r12048 at dynpc145:  andrew | 2007-09-12 21:21:59 -0500
      Fixing a typo in the plugin DB version metadata key affecting upgrades.
    
     r67303 at pinglin (orig r4105):  sterling | 2007-09-12 23:23:35 -0400
      r12049 at dynpc145:  andrew | 2007-09-12 22:19:16 -0500
      Improving schema setup for plugins that are turned on after the application is initially deployed.
    
     r67304 at pinglin (orig r4106):  sterling | 2007-09-13 11:33:54 -0400
      r12057 at dynpc145:  andrew | 2007-09-13 10:22:35 -0500
      Removing the display style from the jifty-result-popup, which is breaking it.
    
     r67306 at pinglin (orig r4108):  sterling | 2007-09-13 12:04:19 -0400
      r12066 at dynpc145:  andrew | 2007-09-13 11:03:51 -0500
      Clean up the masonXXXXXXXXXX temp directory after each test run.
    
     r67308 at pinglin (orig r4110):  clkao | 2007-09-13 16:24:14 -0400
     Handle region redirect with continuation return (for spa).
    
     r67309 at pinglin (orig r4111):  clkao | 2007-09-13 16:33:15 -0400
     Remove debug info.
     r67310 at pinglin (orig r4112):  clkao | 2007-09-13 18:40:15 -0400
     Patch by Alex to make sp_submit_form to be happier with J:V vars.
     r67311 at pinglin (orig r4113):  sterling | 2007-09-13 23:19:05 -0400
      r12105 at riddle:  andrew | 2007-09-13 22:14:55 -0500
      Make find_plugins() sensitive to wantarray.
    
     r67318 at pinglin (orig r4114):  sartak | 2007-09-14 17:35:37 -0400
      r42507 at onn:  sartak | 2007-09-11 12:19:53 -0400
      Use ->clone on DateTime objects to avoid possible DST issues, etc
    
     r67319 at pinglin (orig r4115):  sartak | 2007-09-14 17:36:16 -0400
      r42673 at onn:  sartak | 2007-09-14 17:34:32 -0400
      The REST dispatcher should warn $@ if an error occurs
    
     r67320 at pinglin (orig r4116):  sartak | 2007-09-15 03:00:08 -0400
      r42711 at onn:  sartak | 2007-09-15 02:59:55 -0400
      Fix mismerge..
    
     r67321 at pinglin (orig r4117):  clkao | 2007-09-15 09:51:12 -0400
     When replacing a region, use the technique described in
    
     http://blog.stevenlevithan.com/archives/faster-than-innerhtml
    
     to improve performance.
    
     r67322 at pinglin (orig r4118):  clkao | 2007-09-16 13:57:14 -0400
     Revert r4117 as the element readiness state is not compatible with what it was.
     r67325 at pinglin (orig r4119):  jesse | 2007-09-16 21:08:09 -0400
      r67324 at pinglin:  jesse | 2007-09-16 21:05:58 -0400
      Added an app_page_footer page call to jifty view declare page
    
     r67327 at pinglin (orig r4120):  sunnavy | 2007-09-17 09:05:10 -0400
     a tiny change and typo fix
     r67330 at pinglin (orig r4122):  sartak | 2007-09-17 12:44:14 -0400
      r42743 at onn:  sartak | 2007-09-17 12:44:04 -0400
      Add a 'now' method to Jifty::DateTime which sets the timezone to the current user's timezone.
      DateTime::now passes time_zone => UTC to Jifty::DateTime::now, which caused it to skip over the currentuser/timezone magic.
    
     r67331 at pinglin (orig r4123):  sartak | 2007-09-17 12:56:33 -0400
      r42750 at onn:  sartak | 2007-09-17 12:56:10 -0400
      Add some more tests for Jifty::DateTime in the TestApp
    
     r67332 at pinglin (orig r4124):  sartak | 2007-09-17 14:27:25 -0400
      r42755 at onn:  sartak | 2007-09-17 14:26:59 -0400
      Add support for "title" attribute on elements, which shows tooltips
    
     r67333 at pinglin (orig r4125):  jesse | 2007-09-17 18:06:07 -0400
      r67329 at 000-251-384:  jesse | 2007-09-17 18:05:43 -0400
      * The cache was actually a huge performance problem
    
     r67336 at pinglin (orig r4126):  jesse | 2007-09-17 18:15:33 -0400
      r67334 at 000-251-384:  jesse | 2007-09-17 18:15:06 -0400
      * Don't do weird things with loading model subclasses
    
     r67337 at pinglin (orig r4127):  jesse | 2007-09-17 18:15:41 -0400
      r67335 at 000-251-384:  jesse | 2007-09-17 18:15:15 -0400
      * Class::Accessor::Fast cleanups
    
     r67339 at pinglin (orig r4128):  jesse | 2007-09-17 22:23:05 -0400
      r67338 at pinglin:  jesse | 2007-09-17 22:19:33 -0400
      * Use much less UNIVERSAL::require (to shave a second or two off start times)
    
     r67411 at pinglin (orig r4130):  sartak | 2007-09-18 16:07:02 -0400
      r42743 at onn:  sartak | 2007-09-17 12:44:04 -0400
      Add a 'now' method to Jifty::DateTime which sets the timezone to the current user's timezone.
      DateTime::now passes time_zone => UTC to Jifty::DateTime::now, which caused it to skip over the currentuser/timezone magic.
    
     r67412 at pinglin (orig r4131):  sartak | 2007-09-18 16:07:09 -0400
      r42750 at onn:  sartak | 2007-09-17 12:56:10 -0400
      Add some more tests for Jifty::DateTime in the TestApp
    
     r67413 at pinglin (orig r4132):  sartak | 2007-09-18 16:07:13 -0400
      r42755 at onn:  sartak | 2007-09-17 14:26:59 -0400
      Add support for "title" attribute on elements, which shows tooltips
    
     r67414 at pinglin (orig r4133):  sartak | 2007-09-18 16:07:18 -0400
      r42785 at onn:  sartak | 2007-09-18 16:06:26 -0400
      Add new LeakDetector plugin. It kinda sorta works :)
    
     r67415 at pinglin (orig r4134):  sartak | 2007-09-18 16:27:33 -0400
      r42796 at onn:  sartak | 2007-09-18 16:27:18 -0400
      Some cleanups in LeakDetector
    
     r67416 at pinglin (orig r4135):  sartak | 2007-09-18 16:35:55 -0400
      r42798 at onn:  sartak | 2007-09-18 16:35:47 -0400
      More cleanup, hide zero-leak requests by default
    
     r67417 at pinglin (orig r4136):  sartak | 2007-09-18 16:46:01 -0400
      r42800 at onn:  sartak | 2007-09-18 16:45:51 -0400
      Include the list of object types leaked in the request report
    
     r67431 at pinglin (orig r4137):  sartak | 2007-09-19 13:34:21 -0400
      r42802 at onn:  sartak | 2007-09-18 17:01:23 -0400
      Move files from LeakDetector to LeakTracker (because that's the term Cat uses)
    
     r67432 at pinglin (orig r4138):  sartak | 2007-09-19 13:34:24 -0400
      r42803 at onn:  sartak | 2007-09-18 17:02:47 -0400
      Fix package names, add copyright notices
    
     r67433 at pinglin (orig r4139):  sartak | 2007-09-19 13:34:29 -0400
      r42805 at onn:  sartak | 2007-09-19 10:51:27 -0400
      Move memleak deps into its own Makefile.PL section
    
     r67434 at pinglin (orig r4140):  sartak | 2007-09-19 13:34:36 -0400
      r42812 at onn:  sartak | 2007-09-19 11:02:34 -0400
      POD for LeakTracker's subs
    
     r67435 at pinglin (orig r4141):  jesse | 2007-09-19 22:43:18 -0400
      r67430 at pinglin:  jesse | 2007-09-19 22:41:42 -0400
      * Switch to a single call to Module::Pluggable to help improve startup performance.
    
     r67450 at pinglin (orig r4143):  jesse | 2007-09-20 12:00:20 -0400
      r67448 at pinglin:  jesse | 2007-09-20 11:59:56 -0400
      * Some parts of that last commit to use only one Module::Pluggable weren't as hot as they appeared to be;
    
     r67564 at pinglin (orig r4144):  clkao | 2007-09-24 03:54:07 -0400
     allow onsubmit in form to include custom javascript.
    
     r67569 at pinglin (orig r4149):  clkao | 2007-09-24 19:29:17 -0400
     In Jifty::Model::Session:
     * Turn session_id, data_key, key_type into case_sensitive,
       so we don't do useless tolower on loading sessions.
    
     * index session_id by default.
    
     r67570 at pinglin (orig r4150):  clkao | 2007-09-24 20:09:37 -0400
     Update zh-tw po to include translations for
     authentication::password plugin.
    
     r67571 at pinglin (orig r4151):  jesse | 2007-09-25 00:14:36 -0400
      r67563 at pinglin:  jesse | 2007-09-25 00:13:14 -0400
      * Updated the JSON and YAML transports for web service requests to allow complex data structures in actions
    
     r67624 at pinglin (orig r4152):  sunnavy | 2007-09-26 08:52:27 -0400
     doc fixes
     r67625 at pinglin (orig r4153):  sartak | 2007-09-26 14:57:39 -0400
      r42802 at onn:  sartak | 2007-09-18 17:01:23 -0400
      Move files from LeakDetector to LeakTracker (because that's the term Cat uses)
    
     r67626 at pinglin (orig r4154):  sartak | 2007-09-26 14:57:48 -0400
      r42803 at onn:  sartak | 2007-09-18 17:02:47 -0400
      Fix package names, add copyright notices
    
     r67627 at pinglin (orig r4155):  sartak | 2007-09-26 14:57:53 -0400
      r42805 at onn:  sartak | 2007-09-19 10:51:27 -0400
      Move memleak deps into its own Makefile.PL section
    
     r67628 at pinglin (orig r4156):  sartak | 2007-09-26 14:57:57 -0400
      r42812 at onn:  sartak | 2007-09-19 11:02:34 -0400
      POD for LeakTracker's subs
    
     r67629 at pinglin (orig r4157):  sartak | 2007-09-26 14:58:31 -0400
      r43101 at onn:  sartak | 2007-09-26 14:47:29 -0400
      Initial implementation of Jifty::Test::WWW::Declare, with basic tests in TestApp
    
     r67630 at pinglin (orig r4158):  sartak | 2007-09-26 14:58:38 -0400
      r43102 at onn:  sartak | 2007-09-26 14:57:22 -0400
      Override T:W:D's get with ours which prepends the server's URL
      Start documentation
    
     r67632 at pinglin (orig r4160):  clkao | 2007-09-27 14:50:29 -0400
     New as_string method for Jifty::Web::Form::Link.
    
     r67633 at pinglin (orig r4161):  sunnavy | 2007-09-28 10:28:15 -0400
     doc fixes
     r67634 at pinglin (orig r4162):  jesse | 2007-09-28 14:44:05 -0400
      r67623 at pinglin:  jesse | 2007-09-28 14:43:47 -0400
      * When loading a web request from a data structure, don't totally throw away the path it was requested at.
    
     r67905 at pinglin (orig r4165):  jesse | 2007-10-01 15:52:42 -0400
      r27276 at hualien:  jesse | 2007-10-01 15:50:22 -0400
      * Fixes to several of my fixes for Jifty::Request::load_from_data_structure
    
     r67908 at pinglin (orig r4168):  alexmv | 2007-10-01 17:45:26 -0400
      r22186 at zoq-fot-pik:  chmrr | 2007-10-01 17:41:16 -0400
       * Force a clone which doesn't have as many bugs
    
     r67910 at pinglin (orig r4170):  jesse | 2007-10-02 00:55:17 -0400
      r27295 at hualien:  jesse | 2007-10-02 00:50:39 -0400
      * Rather than helpfully failing to load model classes that have compiletime errors, actually die with the error that caused the class not to load.
    
     r67940 at pinglin (orig r4200):  jesse | 2007-10-03 13:07:01 -0400
      r27368 at hualien:  jesse | 2007-10-03 13:06:49 -0400
      * Allow users to name their apps in lowercase (as pragmas ?!)
          - Reported by SteveH++
    
    
     r67941 at pinglin (orig r4201):  sterling | 2007-10-03 14:43:26 -0400
      r12827 at riddle:  andrew | 2007-10-03 13:41:53 -0500
      Use blessed() instead of ref() to keep an if statement from tripping on ->isa().
    
     r67944 at pinglin (orig r4204):  alexmv | 2007-10-03 22:55:38 -0400
      r23400 at zoq-fot-pik:  chmrr | 2007-10-03 22:53:18 -0400
       * Recursively transform content structures
       * Set a flag when we change the method, in case apps care
    
     r67946 at pinglin (orig r4206):  sterling | 2007-10-03 23:59:11 -0400
      r12837 at riddle:  andrew | 2007-10-03 22:57:39 -0500
      Added a new plugin that provides an autocomplete widget specially for record references. Helpful in cases when a select is just be too big to contemplate.
    
     r67947 at pinglin (orig r4207):  sterling | 2007-10-04 00:01:59 -0400
      r12840 at riddle:  andrew | 2007-10-03 23:01:09 -0500
      Adding missing JavaScript file and cleaning up a method left-over from testing.
    
     r67948 at pinglin (orig r4208):  clkao | 2007-10-04 03:25:39 -0400
     Fix a bug that after you access a static css file, it breaks the
     compressed css by not actually squishing main.css, hence requests
     static css files under __jifty/css.
    
     r67952 at pinglin (orig r4212):  sterling | 2007-10-04 11:33:24 -0400
      r6447 at viper:  andrew | 2007-10-04 10:33:05 -0500
      Fixing the data fetcher on the AutoReference box so that it sends the text being typed rather than the current value in the hidden field.
    
     r67988 at pinglin (orig r4215):  audreyt | 2007-10-09 14:17:34 -0400
     * Fix Jifty::Server::Prefork's document so it mentions
       Net::Server::PreFork, instead of Net::Server::Prefork.
       (Yes this is very confusing.)
     r67989 at pinglin (orig r4216):  sartak | 2007-10-09 17:42:26 -0400
    
     r67990 at pinglin (orig r4217):  sartak | 2007-10-09 17:42:30 -0400
      r43559 at onn:  sartak | 2007-10-09 17:21:52 -0400
      Add Jifty::Util->is_app_root to determine whether a path looks enough like the app root
    
     r67991 at pinglin (orig r4218):  sartak | 2007-10-09 17:42:35 -0400
      r43560 at onn:  sartak | 2007-10-09 17:42:13 -0400
      Add the ability for apps to have very fine-grained testing (such as overriding config for a subdir of t/, or even a particular test file).
      Still needs tests and I suppose some more doc :)
    
     r67992 at pinglin (orig r4219):  sartak | 2007-10-09 18:13:19 -0400
      r43565 at onn:  sartak | 2007-10-09 18:13:07 -0400
      Revert my weird revert/checkin..
    
     r67993 at pinglin (orig r4220):  sartak | 2007-10-09 19:40:34 -0400
      r43567 at onn:  sartak | 2007-10-09 19:40:28 -0400
      Test config loading is now complete code-wise :)
      And it has tests now too! Just need to doc it in manual or Jifty::Config
    
     r67994 at pinglin (orig r4221):  sartak | 2007-10-09 19:56:14 -0400
      r43569 at onn:  sartak | 2007-10-09 19:56:09 -0400
      Fix the problem of tests having both config and a correctly-relative $0
      Jifty::SubTest now makes available the old Cwd (which as 03-nosubtest demonstrates doesn't break apps that don't use Jifty::SubTest)
      Also a bit of doc in Jifty::Config
      The Feature is Done. :)
    
     r68047 at pinglin (orig r4222):  sunnavy | 2007-10-10 03:41:43 -0400
     don't warn if database doesn't exist when dropping database
     r68049 at pinglin (orig r4224):  sunnavy | 2007-10-10 10:58:47 -0400
     warnings should be to STDERR
     r68050 at pinglin (orig r4225):  audreyt | 2007-10-10 12:24:11 -0400
     * The JIFTY_SITE_CONFIG environment variable was silently ignored.
     r68051 at pinglin (orig r4226):  sunnavy | 2007-10-10 14:13:24 -0400
     no need to print STDERR, we can warn in $SIG{__WARN__}
     r68052 at pinglin (orig r4227):  alexmv | 2007-10-10 22:44:04 -0400
      r23483 at zoq-fot-pik:  chmrr | 2007-10-10 22:41:42 -0400
       * Mailboxes need to have unique names for parallel testing
    
     r68053 at pinglin (orig r4228):  alexmv | 2007-10-10 22:44:21 -0400
      r23484 at zoq-fot-pik:  chmrr | 2007-10-10 22:42:01 -0400
       * Just a perltidy
    
     r68054 at pinglin (orig r4229):  alexmv | 2007-10-10 22:44:40 -0400
      r23485 at zoq-fot-pik:  chmrr | 2007-10-10 22:43:00 -0400
       * Pg wants to connect to template1, but CREATE DATABASE foo TEMPLATE template0
    
     r68132 at pinglin (orig r4231):  falcone | 2007-10-11 15:10:39 -0400
      r25357 at ketch:  falcone | 2007-10-11 15:05:07 -0400
      * add a bug tag
    
     r68133 at pinglin (orig r4232):  falcone | 2007-10-11 15:10:58 -0400
      r25358 at ketch:  falcone | 2007-10-11 15:06:00 -0400
      * make format_entry return text rather than printing
      * use the returned message to generate the changelog rather than printing to STDOUT
    
     r68134 at pinglin (orig r4233):  falcone | 2007-10-11 15:27:26 -0400
      r25361 at ketch:  falcone | 2007-10-11 15:26:34 -0400
      * usage documentation
    
     r68135 at pinglin (orig r4234):  falcone | 2007-10-11 16:07:09 -0400
      r25363 at ketch:  falcone | 2007-10-11 16:05:54 -0400
      * talk about how to do iterative editing
    
     r68136 at pinglin (orig r4235):  falcone | 2007-10-11 16:07:49 -0400
      r25364 at ketch:  falcone | 2007-10-11 16:06:15 -0400
      * add an exclude command for logs you don't need to ship
    
     r68137 at pinglin (orig r4236):  falcone | 2007-10-11 16:08:02 -0400
      r25365 at ketch:  falcone | 2007-10-11 16:06:34 -0400
      * fix so we don't try to print to a bogus filehandle
    
     r68138 at pinglin (orig r4237):  falcone | 2007-10-11 16:18:32 -0400
      r25369 at ketch:  falcone | 2007-10-11 16:18:00 -0400
      * use existing t-discard tag
      * don't autoskip things, just group them together and you can
        delete them
    
     r68139 at pinglin (orig r4238):  alexmv | 2007-10-11 23:39:44 -0400
      r23516 at zoq-fot-pik:  chmrr | 2007-10-11 23:39:14 -0400
       * We depend on the bugfix that File::Spec->rel2abs("/foo","/foo") eq "."
    
     r68140 at pinglin (orig r4239):  falcone | 2007-10-12 10:15:54 -0400
      r25389 at ketch:  falcone | 2007-10-12 10:15:06 -0400
      * remove doubled loop over log messages
      * explicitly loop over uncategorized first, then offer
        the user to review all the changes again
      * reformatting
    
     r68141 at pinglin (orig r4240):  clkao | 2007-10-15 07:53:15 -0400
     Add cdn option to CompressedCSSandJS plugin.
     r68143 at pinglin (orig r4241):  jesse | 2007-10-15 08:32:46 -0400
      r68130 at 70:  jesse | 2007-10-15 13:28:14 +0100
      * Updating the Jifty web menu component to respect classes added by users
    
     r68144 at pinglin (orig r4242):  jesse | 2007-10-15 08:33:06 -0400
      r68131 at 70:  jesse | 2007-10-15 13:29:26 +0100
      * Cache gzipped output from the compressedcssandjs plugin
    
     r68159 at pinglin (orig r4243):  clkao | 2007-10-16 07:07:17 -0400
     Don't require recipients in Jifty::Notificaiton be objects.
     r68161 at pinglin (orig r4245):  sartak | 2007-10-16 17:31:35 -0400
      r43759 at onn:  sartak | 2007-10-16 17:31:18 -0400
      Make sure Jifty::Filter::DateTime doesn't disturb Floating datetimes
      Otherwise, dates would get set to the current_user's timezone, throwing a wrench in the works
    
     r68163 at pinglin (orig r4246):  jesse | 2007-10-17 10:31:10 -0400
      r68158 at pinglin:  jesse | 2007-10-17 15:29:38 +0100
      * Added the ability to craft a Jifty button with a "submit" link which submits hardcoded arguments.
    
     r68167 at pinglin (orig r4247):  jesse | 2007-10-17 11:46:16 -0400
      r68164 at pinglin:  jesse | 2007-10-17 16:34:30 +0100
      * Fixing long-broken support for the "JIFTY_FASTTEST" env variable
    
     r68168 at pinglin (orig r4248):  jesse | 2007-10-17 11:46:27 -0400
      r68165 at pinglin:  jesse | 2007-10-17 16:35:39 +0100
      added a bit more semantic markup to the crud view
    
     r68169 at pinglin (orig r4249):  jesse | 2007-10-17 11:46:35 -0400
      r68166 at pinglin:  jesse | 2007-10-17 16:45:22 +0100
    
      * working toward onclick => { submit => { action => $action,
                                      arguments => { foo => 'value', bar => 'other value'} }}
    
      Jifty's js still needs help
    
     r68170 at pinglin (orig r4250):  clkao | 2007-10-17 13:04:25 -0400
     Make action_arguments effective from js update()
     r68171 at pinglin (orig r4251):  alexmv | 2007-10-17 13:14:10 -0400
      r23709 at zoq-fot-pik:  chmrr | 2007-10-17 13:13:45 -0400
       * Untabify
    
     r68172 at pinglin (orig r4252):  sartak | 2007-10-17 15:43:22 -0400
      r43816 at onn:  sartak | 2007-10-17 15:43:11 -0400
      Start adding an OAuth plugin
    
     r68173 at pinglin (orig r4253):  sartak | 2007-10-17 18:08:30 -0400
      r43829 at onn:  sartak | 2007-10-17 18:08:12 -0400
      Some more fleshing out of the OAuth plugin
    
     r68326 at pinglin (orig r4254):  sartak | 2007-10-18 15:36:32 -0400
      r43835 at onn:  sartak | 2007-10-18 15:36:22 -0400
      Many improvements, including some pages
    
     r68327 at pinglin (orig r4255):  sartak | 2007-10-18 16:57:03 -0400
      r43843 at onn:  sartak | 2007-10-18 16:56:56 -0400
      Include the http method (GET, POST, etc) in the debug message
    
     r68328 at pinglin (orig r4256):  sartak | 2007-10-18 17:13:32 -0400
      r43845 at onn:  sartak | 2007-10-18 17:13:26 -0400
      Turns out we can't (so easily) have the URLs be configurable. Not too much of a problem though
      Other fixes
    
     r68329 at pinglin (orig r4257):  sartak | 2007-10-18 19:20:24 -0400
      r43847 at onn:  sartak | 2007-10-18 19:20:16 -0400
      Start writing tests
      Change timestamp to (ugh) time_stamp, because timestamp is a reserved word in a few SQL apps
    
     r68330 at pinglin (orig r4258):  sartak | 2007-10-18 21:52:03 -0400
      r43849 at onn:  sartak | 2007-10-18 21:51:56 -0400
      Expand the tests, now they actually test something real!
      So right now the OAuth plugin is rejecting a few kinds of bad requests (e.g. unknown consumer) while accepting a good request. Now to make more requests of each type :)
    
     r68331 at pinglin (orig r4259):  sartak | 2007-10-18 23:34:56 -0400
      r43851 at onn:  sartak | 2007-10-18 23:34:48 -0400
      The first few test scripts are now complete, and uncovered many bugs. Yay.
    
     r68332 at pinglin (orig r4260):  sartak | 2007-10-19 00:30:18 -0400
      r43853 at onn:  sartak | 2007-10-19 00:30:11 -0400
      Now we send responses to token requests (and test them)
      What's left:
          Authorizing request tokens is coded, but it's slightly flawed somehow
          Getting access tokens is coded, but not tested
          No code yet for the actual accessing of resources
    
     r68333 at pinglin (orig r4261):  sartak | 2007-10-19 11:50:33 -0400
      r43871 at onn:  sartak | 2007-10-19 11:50:24 -0400
      Require Net::OAuth 0.04 because 0.03 needs my patch
      Don't run tests if Net::OAuth is uninstalled
    
     r68334 at pinglin (orig r4262):  clkao | 2007-10-19 12:21:59 -0400
     * Delay i18n handle init until we have session.
     * SkeletonApp dispatcher rule to set language in session.
    
     r68335 at pinglin (orig r4263):  audreyt | 2007-10-19 13:48:09 -0400
     * Jifty::I18N - Implement the L10N.AllowedLang key to limit
       the set of languages available to the user.
       Use case: ja.po is incomplete and we wish to hide it from users.
       Requested by: clkao
     r68336 at pinglin (orig r4264):  sartak | 2007-10-19 16:22:19 -0400
      r43888 at onn:  sartak | 2007-10-19 16:22:10 -0400
      Refactor timestamp and nonce out of tokens and into consumers
      Thanks to hannesty for discussing how it works
    
     r68338 at pinglin (orig r4266):  sartak | 2007-10-19 17:18:43 -0400
      r43894 at onn:  sartak | 2007-10-19 17:18:30 -0400
      Users can now authorize/deny request tokens
    
     r68339 at pinglin (orig r4267):  sartak | 2007-10-19 18:19:55 -0400
      r43905 at onn:  sartak | 2007-10-19 18:19:45 -0400
      Doc
    
     r68340 at pinglin (orig r4268):  sartak | 2007-10-19 23:26:41 -0400
      r43907 at onn:  sartak | 2007-10-19 23:26:32 -0400
      Another example app: ShrinkURL, which is basically (surprise!) tinyurl
    
     r68341 at pinglin (orig r4269):  sartak | 2007-10-19 23:37:59 -0400
      r43909 at onn:  sartak | 2007-10-19 23:37:52 -0400
      More comments
    
     r68344 at pinglin (orig r4272):  sartak | 2007-10-20 00:34:31 -0400
      r43915 at onn:  sartak | 2007-10-20 00:34:20 -0400
      A lot of POD for OAuth, and some tiny fixes elsewhere
    
     r68345 at pinglin (orig r4273):  sartak | 2007-10-20 00:44:16 -0400
      r43917 at onn:  sartak | 2007-10-20 00:44:07 -0400
      Somehow this file got duplicated. I might've done it. shrug
    
     r68346 at pinglin (orig r4274):  sartak | 2007-10-20 00:47:37 -0400
      r43919 at onn:  sartak | 2007-10-20 00:47:22 -0400
      Add Dispatcher, revert some changes that aren't ready (weird ClassLoader errors? shrug)
    
     r68347 at pinglin (orig r4275):  sartak | 2007-10-20 00:54:38 -0400
      r43921 at onn:  sartak | 2007-10-20 00:54:31 -0400
      Dispatcher and config fixes, start adding a new test file
    
     r68348 at pinglin (orig r4276):  clkao | 2007-10-20 04:19:40 -0400
     simplify ccjs plugin with static handler method call.
     r68349 at pinglin (orig r4277):  clkao | 2007-10-20 04:28:15 -0400
     oops, should look for js files under js.
     r68350 at pinglin (orig r4278):  clkao | 2007-10-20 05:01:36 -0400
     Jifty::I18N: provide available_languages method.
     r68351 at pinglin (orig r4279):  clkao | 2007-10-20 05:15:33 -0400
     bin/jifty po now takes two more options:
     * --dir for additional directories to look at so javascript
       files can be scanned.
    
     * --js for generating json dictionaries for messages appeared
       in javascript files declared with Jifty::Web.
    
     r68352 at pinglin (orig r4280):  clkao | 2007-10-20 09:56:48 -0400
     Provide get_current language method in Jifty::I18N.
     r68355 at pinglin (orig r4283):  clkao | 2007-10-21 12:48:36 -0400
     our version of prototype can't deal with synchronous request.
     r68356 at pinglin (orig r4284):  clkao | 2007-10-21 12:52:49 -0400
     Support serving json version of po and localization in javascript
     space.
    
     r68357 at pinglin (orig r4285):  audreyt | 2007-10-21 16:08:25 -0400
     * Jifty::Request::Mapper - Avoid uninitialized warnings when
       $args{destination} ends up undefined.
     r68358 at pinglin (orig r4286):  audreyt | 2007-10-21 16:25:26 -0400
     * Jifty::I18N::promote_encoding:
    
         Multi-part form data have no notion of charsets, so we return the string
         verbatim here, to avoid the "Unquoted / not allowed in Content-Type"
         warnings when the Base64-encoded MIME boundary string contains "/".
    
       Prompted by this Content-Type header found in real world:
         multipart/form-data; boundary=----WebKitFormBoundaryRqXyEnBQ/5VSsexe
    
       which triggered this error:
         -- 2007/10/22 04:19:30 WARN> Carp.pm:46 Carp::carp
         Unquoted / not allowed in Content-Type! at /usr/local/lib/perl5/site_perl/5.9.5/Jifty/I18N.pm line 226
         -- 2007/10/22 04:19:30 WARN> Carp.pm:46 Carp::carp
         Illegal Content-Type parameter /5VSsexe at /usr/local/lib/perl5/site_perl/5.9.5/Jifty/I18N.pm line 226
    
     r68359 at pinglin (orig r4287):  clkao | 2007-10-22 00:09:54 -0400
     zh_tw l10n for Authenication::Password plugin.
     r68360 at pinglin (orig r4288):  sunnavy | 2007-10-22 03:44:24 -0400
     typo fix
     r68361 at pinglin (orig r4289):  clkao | 2007-10-22 12:05:27 -0400
     rollback r4242 which cached the gzipped js/css even through
     develmode reload.
    
     r68363 at pinglin (orig r4290):  clkao | 2007-10-22 12:16:41 -0400
     12:09 < obra> Aaaaaaaaaaaaaaaaaaaaaaaaa
     r68366 at pinglin (orig r4291):  audreyt | 2007-10-22 13:03:43 -0400
     * Jifty::I18N - POD fixup.
     r68367 at pinglin (orig r4292):  jesse | 2007-10-22 13:07:27 -0400
      r68158 at pinglin:  jesse | 2007-10-17 10:29:38 -0400
      * Added the ability to craft a Jifty button with a "submit" link which submits hardcoded arguments.
    
     r68368 at pinglin (orig r4293):  jesse | 2007-10-22 13:07:46 -0400
      r68164 at pinglin:  jesse | 2007-10-17 11:34:30 -0400
      * Fixing long-broken support for the "JIFTY_FASTTEST" env variable
    
     r68369 at pinglin (orig r4294):  jesse | 2007-10-22 13:07:51 -0400
      r68165 at pinglin:  jesse | 2007-10-17 11:35:39 -0400
      added a bit more semantic markup to the crud view
    
     r68377 at pinglin (orig r4295):  jesse | 2007-10-22 13:10:34 -0400
      r68166 at pinglin:  jesse | 2007-10-17 11:45:22 -0400
    
      * working toward onclick => { submit => { action => $action,
                                      arguments => { foo => 'value', bar => 'other value'} }}
    
      Jifty's js still needs help
      r68365 at pinglin:  jesse | 2007-10-22 13:06:27 -0400
      * Fixes to not cache compressed css+js while in devel mode.
      * Refactoring js compression code into the plugin where it belongs.
    
     r68410 at pinglin (orig r4296):  clkao | 2007-10-23 04:04:22 -0400
     allow switching current language for Localization.
     r68411 at pinglin (orig r4297):  ishigaki | 2007-10-23 08:29:43 -0400
     Modules other than Net::OAuth::Request in Net-OAuth distro don't have version
     r68412 at pinglin (orig r4298):  sartak | 2007-10-23 12:20:03 -0400
      r44064 at onn:  sartak | 2007-10-23 12:19:36 -0400
      Ah ha, plugins need to be very careful about loading appclass models, because classloader isn't set up for them yet
    
     r68413 at pinglin (orig r4299):  ishigaki | 2007-10-23 12:42:30 -0400
     updated ja.po
     r68460 at pinglin (orig r4300):  clkao | 2007-10-24 03:08:22 -0400
     loc.js cleanups.
     r68461 at pinglin (orig r4301):  sterling | 2007-10-24 11:30:34 -0400
      r13634 at riddle:  andrew | 2007-10-24 10:29:05 -0500
      Adding File::Temp 0.15 requirement for cleanup().
    
     r68462 at pinglin (orig r4302):  sterling | 2007-10-24 11:30:42 -0400
      r13635 at riddle:  andrew | 2007-10-24 10:29:46 -0500
      Minor edit: fixing reference to Jifty::Request::Action to Jifty::Action
    
     r68463 at pinglin (orig r4303):  sterling | 2007-10-24 12:24:02 -0400
      r13638 at riddle:  andrew | 2007-10-24 11:23:50 -0500
      Undoing 4302. There is such a class. Thanks to chmrr.
    
     r68464 at pinglin (orig r4304):  alexmv | 2007-10-24 14:02:04 -0400
      r23967 at zoq-fot-pik:  chmrr | 2007-10-24 13:57:17 -0400
       * Menu class is undef by default; fall back to the empty string
    
     r68466 at pinglin (orig r4306):  sartak | 2007-10-24 14:07:57 -0400
      r44126 at onn:  sartak | 2007-10-24 14:07:37 -0400
      Bump JDBI dep to 0.45 (Filter::DateTime changes)
    
     r68467 at pinglin (orig r4307):  sunnavy | 2007-10-24 16:50:36 -0400
     added TestApp-Plugin-OnClick, initially for the update of prototype.js
     r68468 at pinglin (orig r4308):  sartak | 2007-10-24 17:25:05 -0400
      r44156 at onn:  sartak | 2007-10-24 17:24:51 -0400
      Add a hook to change a menu item's label
    
     r68471 at pinglin (orig r4311):  sartak | 2007-10-25 14:28:39 -0400
      r44178 at onn:  sartak | 2007-10-25 14:28:11 -0400
      Remove the unused drop-column logic from jifty schema --setup, since ->columns returns only active cols
    
     r68598 at pinglin (orig r4316):  sterling | 2007-10-26 15:39:43 -0400
      r13658 at riddle:  andrew | 2007-10-26 14:24:15 -0500
      Fix it so that AutoReference fields show the _brief_description rather than the id in read mode.
    
     r68599 at pinglin (orig r4317):  sterling | 2007-10-26 15:40:02 -0400
      r13659 at riddle:  andrew | 2007-10-26 14:38:32 -0500
      Expanded the previous fix to cover sticky values and the initial value in the input tag.
    
     r68657 at pinglin (orig r4319):  sterling | 2007-10-26 17:06:39 -0400
      r13664 at riddle:  andrew | 2007-10-26 16:06:06 -0500
      Modified the JS to ignore the [id:1] text at the end of the references.
    
     r68658 at pinglin (orig r4320):  sterling | 2007-10-27 02:04:44 -0400
      r13668 at dynpc145:  andrew | 2007-10-27 01:03:49 -0500
      Fixed AutoReference so that it is possible to set the field to empty if not mandatory.
    
     r68660 at pinglin (orig r4322):  ruz | 2007-10-28 02:24:12 -0400
     * if refers_to is not mandatory field then add 'no_value' option to select box
     r68661 at pinglin (orig r4323):  clkao | 2007-10-28 23:50:52 -0400
     Provide an after_include_javascript for plugins to do javascript initialisation.
     r68662 at pinglin (orig r4324):  clkao | 2007-10-29 00:13:52 -0400
     Jifty::Plugin::I18N:
     - provides SetLang action
     - provides loc.js and other facilities to l10n your javascript along with po.
    
     r68663 at pinglin (orig r4325):  sartak | 2007-10-29 00:15:30 -0400
      r44250 at onn:  sartak | 2007-10-29 00:15:09 -0400
      Skip OAuth tests if Crypt::OpenSSL::RSA is unavailable
      RSA should be optional, will fix later
    
     r69504 at pinglin (orig r4334):  sartak | 2007-10-30 13:08:52 -0400
      r44431 at onn:  sartak | 2007-10-30 13:08:19 -0400
      Get rid of spurious warning in Auth::Password plugin
    
     r69505 at pinglin (orig r4335):  alexmv | 2007-10-30 13:16:38 -0400
      r24200 at zoq-fot-pik:  chmrr | 2007-10-30 13:13:53 -0400
       * Fix test count
    
     r69506 at pinglin (orig r4336):  alexmv | 2007-10-30 18:22:53 -0400
      r24221 at zoq-fot-pik:  chmrr | 2007-10-30 18:21:04 -0400
       * Work around a "feature" in Hash::Merge < 0.10
    
     r69507 at pinglin (orig r4337):  sartak | 2007-10-30 18:34:34 -0400
      r44433 at onn:  sartak | 2007-10-30 14:17:57 -0400
      Wrote most of the authorization tests
    
     r69508 at pinglin (orig r4338):  sartak | 2007-10-30 18:34:53 -0400
    
     r69509 at pinglin (orig r4339):  sartak | 2007-10-30 18:35:14 -0400
      r44443 at onn:  sartak | 2007-10-30 18:34:06 -0400
      More tests, start implementing callbacks, but failing :)
    
     r69510 at pinglin (orig r4340):  agentz | 2007-10-31 04:46:06 -0400
      r2877 at agentz-office:  agentz | 2007-10-31 16:44:52 +0800
      updated my email address in AUTHORS to agentzh at agentzh.org
    
     r69511 at pinglin (orig r4341):  clkao | 2007-10-31 05:03:02 -0400
     * I18N::Action::SetLang: scalar::defer is trigger some oddness,
       rewrite available languages as normal runtime code, and cached.
    
     r69512 at pinglin (orig r4342):  sterling | 2007-10-31 12:59:29 -0400
      r13658 at riddle:  andrew | 2007-10-26 14:24:15 -0500
      Fix it so that AutoReference fields show the _brief_description rather than the id in read mode.
    
     r69513 at pinglin (orig r4343):  sterling | 2007-10-31 12:59:35 -0400
      r13659 at riddle:  andrew | 2007-10-26 14:38:32 -0500
      Expanded the previous fix to cover sticky values and the initial value in the input tag.
    
     r69514 at pinglin (orig r4344):  sterling | 2007-10-31 12:59:39 -0400
      r13664 at riddle:  andrew | 2007-10-26 16:06:06 -0500
      Modified the JS to ignore the [id:1] text at the end of the references.
    
     r69515 at pinglin (orig r4345):  sterling | 2007-10-31 12:59:44 -0400
      r13668 at riddle:  andrew | 2007-10-27 01:03:49 -0500
      Fixed AutoReference so that it is possible to set the field to empty if not mandatory.
    
     r69516 at pinglin (orig r4346):  sterling | 2007-10-31 13:00:05 -0400
      r13792 at riddle:  andrew | 2007-10-31 11:57:22 -0500
      Fixing POD coverage errors in the AutoReference plugin.
    
     r69517 at pinglin (orig r4347):  sartak | 2007-10-31 13:21:39 -0400
      r44449 at onn:  sartak | 2007-10-31 13:21:07 -0400
      Implement callback URLs and test them
    
     r69519 at pinglin (orig r4349):  sunnavy | 2007-10-31 15:20:42 -0400
     we need DateTime::Locale
     r69520 at pinglin (orig r4350):  sartak | 2007-10-31 17:15:18 -0400
      r44469 at onn:  sartak | 2007-10-31 17:14:59 -0400
      Most AccessToken tests done :) first implementation was right on
    
     r69521 at pinglin (orig r4351):  sartak | 2007-10-31 17:15:50 -0400
      r44473 at onn:  sartak | 2007-10-31 17:15:32 -0400
      Whoops, committed in a subdirectory
    
     r69522 at pinglin (orig r4352):  sartak | 2007-10-31 18:07:41 -0400
      r44475 at onn:  sartak | 2007-10-31 18:07:21 -0400
      Various refactors and cleanups
      Request tokens are now properly set to "used"
      Life of a request token lengthened on authorize
      Fixed duplicate timestamp/nonce check (and test)
    
     r69523 at pinglin (orig r4353):  audreyt | 2007-11-01 13:13:52 -0400
     * Jifty::I18N::promote_encoding - r4286 by yours truly broke
       auto-decoding for POST requests.
    
       This is a better fix to avoid the "Unquoted / not allowed in
       Content-Type" warning (which arugably is a Email::MIME::ContentType
       glitch), by scanning the Content-Type header for the substring
       "charset" before parsing it.
    
       This way, when "charset" is not found in that header,
       we still fallback to Encode::Guess, regardless of whether
       the request was of type "multipart/form-data".
     r69524 at pinglin (orig r4354):  alexmv | 2007-11-01 14:42:40 -0400
      r24221 at zoq-fot-pik:  chmrr | 2007-10-30 18:21:04 -0400
       * Work around a "feature" in Hash::Merge < 0.10
    
     r69525 at pinglin (orig r4355):  alexmv | 2007-11-01 14:43:37 -0400
    
     r69526 at pinglin (orig r4356):  alexmv | 2007-11-01 14:43:47 -0400
    
     r69527 at pinglin (orig r4357):  alexmv | 2007-11-01 14:43:51 -0400
      r24273 at zoq-fot-pik:  chmrr | 2007-11-01 14:42:20 -0400
       * Horrible Hash::Merge workaround, part 2
    
     r69631 at pinglin (orig r4359):  sartak | 2007-11-02 14:43:38 -0400
      r44548 at onn:  sartak | 2007-11-02 14:43:04 -0400
      Correctly skip OAuth tests in the absence of Net::OAuth
    
     r69632 at pinglin (orig r4360):  sartak | 2007-11-02 14:48:06 -0400
      r44550 at onn:  sartak | 2007-11-02 14:47:40 -0400
      Make Test::WWW::Declare an optional dep for now
    
     r69635 at pinglin (orig r4363):  sterling | 2007-11-02 16:16:18 -0400
      r13658 at riddle:  andrew | 2007-10-26 14:24:15 -0500
      Fix it so that AutoReference fields show the _brief_description rather than the id in read mode.
    
     r69636 at pinglin (orig r4364):  sterling | 2007-11-02 16:17:37 -0400
      r13659 at riddle:  andrew | 2007-10-26 14:38:32 -0500
      Expanded the previous fix to cover sticky values and the initial value in the input tag.
    
     r69637 at pinglin (orig r4365):  sterling | 2007-11-02 16:17:45 -0400
      r13664 at riddle:  andrew | 2007-10-26 16:06:06 -0500
      Modified the JS to ignore the [id:1] text at the end of the references.
    
     r69638 at pinglin (orig r4366):  sterling | 2007-11-02 16:17:52 -0400
      r13668 at riddle:  andrew | 2007-10-27 01:03:49 -0500
      Fixed AutoReference so that it is possible to set the field to empty if not mandatory.
    
     r69639 at pinglin (orig r4367):  sterling | 2007-11-02 16:18:46 -0400
      r13817 at riddle:  andrew | 2007-11-02 14:58:30 -0500
      Fixed a problem where AutoReference breaks in Search actions.
    
     r69642 at pinglin (orig r4370):  clkao | 2007-11-04 02:15:12 -0500
     Add an additional trigger in Clickable for SinglePage to filter
     out state variables for region-__page.
    
     r69643 at pinglin (orig r4371):  clkao | 2007-11-04 06:23:36 -0500
     I18N plugin:
     - log when user is somehow able to pass in disallowed lang.
     - inline the initial dictionary json.
    
     r71464 at pinglin (orig r4376):  sartak | 2007-11-05 20:09:00 -0500
      r44469 at onn:  sartak | 2007-10-31 17:14:59 -0400
      Most AccessToken tests done :) first implementation was right on
    
     r71465 at pinglin (orig r4377):  sartak | 2007-11-05 20:09:12 -0500
      r44473 at onn:  sartak | 2007-10-31 17:15:32 -0400
      Whoops, committed in a subdirectory
    
     r71466 at pinglin (orig r4378):  sartak | 2007-11-05 20:09:23 -0500
      r44475 at onn:  sartak | 2007-10-31 18:07:21 -0400
      Various refactors and cleanups
      Request tokens are now properly set to "used"
      Life of a request token lengthened on authorize
      Fixed duplicate timestamp/nonce check (and test)
    
     r71467 at pinglin (orig r4379):  sartak | 2007-11-05 20:09:50 -0500
      r44697 at onn:  sartak | 2007-11-05 20:08:14 -0500
      Add a Jifty::DateTime set_current_user_timezone method
    
     r71468 at pinglin (orig r4380):  sartak | 2007-11-05 20:25:16 -0500
      r44707 at onn:  sartak | 2007-11-05 20:24:45 -0500
      Add Jifty::DateTime::from_epoch which does our usual timezone magic, use set_current_user_timezone more, too
    
     r71469 at pinglin (orig r4381):  clkao | 2007-11-06 03:11:39 -0500
     Add a ForwardCompatible config in Database section.  The usecase
     is that when you switch your checkout to something with older
     version of app schema, you can declare it is compatiable with
     the future version that is currently in db.
    
     r71470 at pinglin (orig r4382):  clkao | 2007-11-06 03:29:55 -0500
     support SELENIUM_RC_TEST_AGAINST and SELENIUM_RC_BROWSER environment variable, and allow args to be passed into selenium rc invoker.
     r71471 at pinglin (orig r4383):  alexmv | 2007-11-06 13:41:25 -0500
      r24425 at zoq-fot-pik:  chmrr | 2007-11-06 13:37:43 -0500
       * Remove debugging statement
    
     r71472 at pinglin (orig r4384):  clkao | 2007-11-06 17:23:51 -0500
     I18n: fix json dictionary fallback.
     r71473 at pinglin (orig r4385):  clkao | 2007-11-06 17:28:02 -0500
     I18N: load json dict from absolute path.
     r71474 at pinglin (orig r4386):  sartak | 2007-11-06 21:45:50 -0500
      r44749 at onn:  sartak | 2007-11-06 21:45:14 -0500
      First cut of protected resource requests
    
     r71486 at pinglin (orig r4391):  jesse | 2007-11-07 12:47:02 -0500
      r71485 at pinglin:  jesse | 2007-11-07 12:46:14 -0500
      * only run pod coverage tests if you're a developer
    
     r71640 at pinglin (orig r4395):  sartak | 2007-11-09 13:31:35 -0500
      r44778 at onn:  sartak | 2007-11-07 14:41:01 -0500
      Refactor the tests
    
     r71641 at pinglin (orig r4396):  sartak | 2007-11-09 13:31:43 -0500
      r44779 at onn:  sartak | 2007-11-07 14:50:59 -0500
      Whoops, the consumer and user were using the same mech. But fixing that didn't break anything :)
    
     r71642 at pinglin (orig r4397):  sartak | 2007-11-09 13:31:49 -0500
      r44785 at onn:  sartak | 2007-11-07 15:36:47 -0500
      Begin adding tests for protected resource requests
    
     r71643 at pinglin (orig r4398):  sartak | 2007-11-09 13:31:54 -0500
      r44786 at onn:  sartak | 2007-11-07 15:37:39 -0500
      Various improvements of try_oauth so it gets further (but not all the way) in the correct codepath
    
     r71644 at pinglin (orig r4399):  sartak | 2007-11-09 13:32:06 -0500
      r44787 at onn:  sartak | 2007-11-07 15:49:58 -0500
      More fixes to get protected resource requests going
      We might need a WWW::Mechanize::OAuth subclass, so that each get/post will be properly wrapped as an OAuth request
    
     r71650 at pinglin (orig r4405):  sartak | 2007-11-09 20:56:11 -0500
      r44970 at onn:  sartak | 2007-11-09 20:47:18 -0500
      Add another changelog sorting program, this one I think works a bit better :)
    
     r71651 at pinglin (orig r4406):  sartak | 2007-11-09 20:56:24 -0500
      r44971 at onn:  sartak | 2007-11-09 20:47:42 -0500
      Sort change groups by header, typo fix
    
     r71652 at pinglin (orig r4407):  sartak | 2007-11-09 20:56:27 -0500
      r44972 at onn:  sartak | 2007-11-09 20:55:15 -0500
      Add 'performance' tag to sort-changelog
    
     r71653 at pinglin (orig r4408):  sartak | 2007-11-09 21:08:31 -0500
      r44981 at onn:  sartak | 2007-11-09 21:08:04 -0500
      Add a Jifty::Manual which lists each of the manuals with a short description
      Secret confession: I was tired of perldoc Jifty::Manual::<tab> failing :)
    
     r71654 at pinglin (orig r4409):  gugod | 2007-11-10 03:04:39 -0500
     When it's a ::Create action, $event_info won't have a record_id
     there, cause a fatal error when it's published latter.
    
     r71655 at pinglin (orig r4410):  sunnavy | 2007-11-10 05:04:05 -0500
     refactor Jifty::Test a bit
     r71659 at pinglin (orig r4414):  clkao | 2007-11-10 11:33:08 -0500
     older Email::LocalDelivery is buggy.
     r71660 at pinglin (orig r4415):  jesse | 2007-11-11 00:47:37 -0500
      r71589 at pinglin:  jesse | 2007-11-09 12:51:51 -0500
      * Get rid of another place where we had a custom accessor that meant 'arguments' - for great consistency
    
     r71661 at pinglin (orig r4416):  jesse | 2007-11-11 00:47:57 -0500
      r71590 at pinglin:  jesse | 2007-11-09 12:52:30 -0500
      * Upgrade a region error from a debug to a warning.
    
     r71662 at pinglin (orig r4417):  jesse | 2007-11-11 00:48:05 -0500
      r71639 at pinglin:  jesse | 2007-11-11 00:46:59 -0500
      * Fix compatibility with Test::WWW::Mechanize
    
     r71808 at pinglin (orig r4418):  c9s | 2007-11-12 00:58:25 -0500
     A command line completion script is added into contrib/
     r71811 at pinglin (orig r4421):  clkao | 2007-11-12 13:17:49 -0500
     In TD View handler, don't just return without printing
     headers if the response content is empty, as when you
     access /__jifty/empty.
    
     r71814 at pinglin (orig r4424):  chapman | 2007-11-13 11:53:51 -0500
     Fixed the feedback plugin so it works even if the current
     user doesn't have permission to read their own email.
    
     r71817 at pinglin (orig r4427):  alexmv | 2007-11-13 13:37:29 -0500
      r24696 at zoq-fot-pik:  chmrr | 2007-11-13 13:36:31 -0500
       * POD re-wrapping
       * perltidy
       * Use Jifty::DBI's new ->_new_record_args and _new_collection_args
       * Bump the JDBI dep accordingly
    
     r71819 at pinglin (orig r4429):  sunnavy | 2007-11-13 14:28:29 -0500
     require CSS::Squish 0.07, no need to call _resolve_file any more
     r71820 at pinglin (orig r4430):  alexmv | 2007-11-13 14:31:50 -0500
      r24705 at zoq-fot-pik:  chmrr | 2007-11-13 14:31:20 -0500
       * <embed> isn't a tag which gets a close on it
    
     r71821 at pinglin (orig r4431):  ruz | 2007-11-14 10:17:30 -0500
     * use object calls instead of instance calls
     * use better handling of roots so server relative paths
       work as expected unless you're bind to a location
     r71927 at pinglin (orig r4432):  sterling | 2007-11-14 21:32:17 -0500
      r14053 at dynpc145:  andrew | 2007-11-14 20:26:49 -0600
      Adding a description to Jifty::Everything docs.
    
     r71928 at pinglin (orig r4433):  sterling | 2007-11-14 21:32:27 -0500
      r14054 at dynpc145:  andrew | 2007-11-14 20:31:17 -0600
      Added a synopsis and license section to docs.
    
     r71936 at pinglin (orig r4435):  sunnavy | 2007-11-15 12:30:49 -0500
     merged prototype-1.6 to trunk
     r71938 at pinglin (orig r4437):  sunnavy | 2007-11-15 12:57:16 -0500
     not dep test for bin/sort-changelog
     r72011 at pinglin (orig r4439):  alexmv | 2007-11-15 15:22:53 -0500
      r24785 at zoq-fot-pik:  chmrr | 2007-11-15 15:20:31 -0500
       * Finalize triggers if possible (supported in forthcoming
         Class::Trigger release)
       * Override JDBI's default of having refers_to null return undef,
         instead of object with no id
       * RightsFrom 'foo_id' should work like RightsFrom 'foo'
    
     r72012 at pinglin (orig r4440):  sartak | 2007-11-15 15:42:44 -0500
      r44970 at onn:  sartak | 2007-11-09 20:47:18 -0500
      Add another changelog sorting program, this one I think works a bit better :)
    
     r72013 at pinglin (orig r4441):  sartak | 2007-11-15 15:42:51 -0500
      r44971 at onn:  sartak | 2007-11-09 20:47:42 -0500
      Sort change groups by header, typo fix
    
     r72014 at pinglin (orig r4442):  sartak | 2007-11-15 15:42:56 -0500
      r44972 at onn:  sartak | 2007-11-09 20:55:15 -0500
      Add 'performance' tag to sort-changelog
    
     r72015 at pinglin (orig r4443):  sartak | 2007-11-15 15:43:00 -0500
      r44981 at onn:  sartak | 2007-11-09 21:08:04 -0500
      Add a Jifty::Manual which lists each of the manuals with a short description
      Secret confession: I was tired of perldoc Jifty::Manual::<tab> failing :)
    
     r72016 at pinglin (orig r4444):  sartak | 2007-11-15 15:43:10 -0500
      r44996 at onn:  sartak | 2007-11-11 16:41:01 -0500
      Add changelogger dependencies
    
     r72017 at pinglin (orig r4445):  sartak | 2007-11-15 15:44:23 -0500
    
     r72021 at pinglin (orig r4449):  sartak | 2007-11-15 15:58:49 -0500
      r45207 at onn:  sartak | 2007-11-15 15:58:10 -0500
      Add TestMode: 1 to the default test config
    
     r72023 at pinglin (orig r4451):  sunnavy | 2007-11-16 12:35:57 -0500
     tiny typo fix
     r72024 at pinglin (orig r4452):  sartak | 2007-11-16 14:44:20 -0500
      r45262 at onn:  sartak | 2007-11-16 14:43:36 -0500
      Remove bin/*-changelog, as they've been moved into their own dist, App-Changelogger
    
     r72025 at pinglin (orig r4453):  sunnavy | 2007-11-16 14:54:06 -0500
     clean dependency test since changelogger has been moved out
     r72026 at pinglin (orig r4454):  sartak | 2007-11-16 15:23:18 -0500
      r45265 at onn:  sartak | 2007-11-16 14:56:01 -0500
      Remove the bin/*-changelog workaround in t/01-deps
    
     r72027 at pinglin (orig r4455):  sartak | 2007-11-16 15:23:33 -0500
      r45267 at onn:  sartak | 2007-11-16 15:21:43 -0500
      carp, don't warn, for a "title" Mason/TD warning
    
     r72030 at pinglin (orig r4458):  sartak | 2007-11-16 16:24:06 -0500
      r45274 at onn:  sartak | 2007-11-16 16:23:30 -0500
      Bump to 0.71116, update Changelog, SIGNATURE, MANIFEST, etc
    
     r72031 at pinglin (orig r4459):  sartak | 2007-11-16 18:39:04 -0500
      r45277 at onn:  sartak | 2007-11-16 18:38:57 -0500
      Revert r4435 because it breaks Safari 3.0.4
    
     r72032 at pinglin (orig r4460):  sterling | 2007-11-17 23:53:50 -0500
      r14120 at dynpc145:  andrew | 2007-11-17 22:53:30 -0600
      Adding SYNOPSIS, LICENSE, and code comments.
    
     r72033 at pinglin (orig r4461):  sterling | 2007-11-17 23:55:24 -0500
      r14122 at dynpc145:  andrew | 2007-11-17 22:55:17 -0600
      Stripping off the execute flag in SVN.
    
     r72122 at pinglin (orig r4462):  jesse | 2007-11-18 16:39:16 -0500
      r72119 at pinglin:  jesse | 2007-11-18 12:46:06 -0600
      * Added a new 'AutoUpgrade' option for Jifty and Application schemas,
      so you don't need to manually upgrade every time jifty or your app version bumps
    
     r72142 at pinglin (orig r4463):  yves | 2007-11-19 06:29:56 -0500
     update fr.po and debian control
    
     r72145 at pinglin (orig r4466):  yves | 2007-11-19 12:17:15 -0500
     first release for an experimental mixin ldap release
    
     r72146 at pinglin (orig r4467):  sartak | 2007-11-19 18:46:25 -0500
      r45310 at onn:  sartak | 2007-11-19 18:46:02 -0500
      Add better support for dates in the REST plugin, rewrite a somewhat hairy method
    
     r72162 at pinglin (orig r4470):  ishigaki | 2007-11-20 01:48:17 -0500
     added "rel" attribute to Jifty::Web::Form::Link/Element, to allow <a rel="nofollow"...> or <a rel="next"...>
     r72273 at pinglin (orig r4505):  sterling | 2007-11-20 14:38:15 -0500
      r14171 at dynpc145:  andrew | 2007-11-20 13:38:04 -0600
      Assume the page is "1" if no value for page is given to the pager in CRUD.
    
     r72276 at pinglin (orig r4508):  falcone | 2007-11-20 16:02:19 -0500
      r26763 at ketch:  falcone | 2007-11-20 16:00:31 -0500
      * mention Net::LDAP because of the new Plugin so we don't fail 01-dependencies
    
     r72327 at pinglin (orig r4536):  clkao | 2007-11-25 17:39:42 -0500
     r4534 belongs to trunk.
    
      In ClassLoader, if an action matches Create* that isn't about
      creating a model, don't just return but continue to see if there's
      any matching actions from plugins.
    
      This solves CreateOpenIDUser in particular.
    
     r72328 at pinglin (orig r4537):  jesse | 2007-11-26 00:48:08 -0500
      r72323 at pinglin:  jesse | 2007-11-26 00:39:46 -0500
      * A bit of refactoring to expose some more overridable templates in CRUD view
    
     r72336 at pinglin (orig r4538):  yves | 2007-11-26 11:41:32 -0500
     add Jasig CAS plugin with mixin model user
    
     r72338 at pinglin (orig r4540):  jesse | 2007-11-26 13:38:31 -0500
      r72334 at pinglin:  jesse | 2007-11-26 11:42:35 -0500
      * upgraded request logging from DEBUG to INFO
    
     r72339 at pinglin (orig r4541):  jesse | 2007-11-26 13:38:41 -0500
      r72335 at pinglin:  jesse | 2007-11-26 13:29:51 -0500
      * More trying to force caching of static content.
    
    
     r72374 at pinglin (orig r4543):  sunnavy | 2007-11-26 14:40:28 -0500
     merged prototpe-1.6 to trunk again, wish it's all right this time :)
     r72375 at pinglin (orig r4544):  sartak | 2007-11-26 15:14:15 -0500
      r45601 at onn:  sartak | 2007-11-26 15:13:53 -0500
      Remove some debugging statements
    
     r72381 at pinglin (orig r4545):  gugod | 2007-11-27 01:33:54 -0500
     Unbreak OpenID plugin with new Jifty::API implementation.
    
     Give fully-qualified action class name here because Jifty::API does
     not resolve action class names to those ones provided by plugins
     (those under Jifty::Plugin::*::Action namespace) This is maybe a
     bug, but need further discussion.
    
     r72382 at pinglin (orig r4546):  sunnavy | 2007-11-27 11:07:17 -0500
     make a looser check for Content-Type since firefox 3 will append charset stuff to it sometimes
     r72404 at pinglin (orig r4547):  sartak | 2007-11-27 20:40:29 -0500
      r45691 at onn:  sartak | 2007-11-27 20:40:06 -0500
      Add an 'as_user' method and have 'as_superuser' use it
    
     r72420 at pinglin (orig r4551):  gugod | 2007-11-28 05:45:03 -0500
     revert its previous revision for it's fixed earler in classloader.
    
     r72434 at pinglin (orig r4553):  sartak | 2007-11-28 16:40:25 -0500
      r45710 at onn:  sartak | 2007-11-28 16:40:03 -0500
      Don't send cookies for static files. We suspect that doing so was screwing up non-compliant proxies that were caching the cookie and causing.. "amusing" security problems
    
     r72441 at pinglin (orig r4560):  jesse | 2007-11-28 18:39:44 -0500
      r72432 at pinglin:  jesse | 2007-11-28 18:34:07 -0500
      * Somewhat more correct handling of follow_link_ok. (Better compat with WWW::Mechanize's syntax)
    
    
    
    
    git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/branches/js-refactor@4568 e84bef0a-9b06-0410-84ba-c4c9edb13aeb

diff --cc lib/Jifty/Config.pm
index b7ac68c,20643cc..dcaca23
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@@ -327,24 -365,20 +364,29 @@@ plugins that match an older Jifty versi
  sub update_config {
      my $self = shift;
      my $config = shift;
 -
 -    # This app configuration predates the plugin refactor
 -    if ( $config->{'framework'}->{'ConfigFileVersion'} < 2) {
 -
 +    if ( $config->{'framework'}->{'ConfigFileVersion'} <2) {
 +        # These are the plugins which old apps expect because their
 +        # features used to be in the core.
 +        unshift (@{$config->{'framework'}->{'Plugins'}}, 
 +            { SkeletonApp           => {}, },
 +            { REST                  => {}, },
 +            { Halo                  => {}, },
 +            { ErrorTemplates        => {}, },
 +            { OnlineDocs            => {}, },
 +            { CompressedCSSandJS    => {}, },
 +            { AdminUI               => {}, },
- 
++        )
++    }
++    if ( $config->{'framework'}->{'ConfigFileVersion'} <3) {
+         # These are the plugins which old apps expect because their
+         # features used to be in the core.
+         unshift (@{$config->{'framework'}->{'Plugins'}}, 
 -            { SkeletonApp            => {}, },
 -            { REST               => {}, },
 -            { Halo               => {}, },
 -            { ErrorTemplates     => {}, },
 -            { OnlineDocs         => {}, },
 -            { CompressedCSSandJS => {}, },
 -            { AdminUI            => {}, }
 +            # JS libs which are now turning into plugins
 +            { CSSBrowserSelectorJS  => {}, },
 +            { FormatDateJS          => {}, },
 +            { JSAN                  => {}, },
 +            { Rico                  => {}, },
 +            { cssQuery              => {}, },
          );
      }
  
diff --cc lib/Jifty/Web.pm
index 5a1e177,0fac2cd..d8d6cdd
--- a/lib/Jifty/Web.pm
+++ b/lib/Jifty/Web.pm
@@@ -48,7 -47,9 +46,8 @@@ __PACKAGE__->javascript_libs([qw
      scriptaculous/builder.js
      scriptaculous/effects.js
      scriptaculous/controls.js
 -    formatDate.js
      template_declare.js
+     loc.js
      jifty.js
      jifty_utils.js
      jifty_subs.js
@@@ -60,11 -61,21 +59,11 @@@
      key_bindings.js
      context_menu.js
      bps_util.js
 -    rico.js
 -    yui/yahoo.js
 -    yui/dom.js
 -    yui/event.js
 -    yui/calendar.js
 -    yui/element-beta.js
 -    yui/tabview.js
 -    yui/container.js
 -    yui/menu.js
      app.js
      app_behaviour.js
 -    css_browser_selector.js
  )]);
  
- use Jifty::DBI::Class::Trigger;
+ use Class::Trigger;
  
  =head1 METHODS
  
diff --cc share/web/static/js/prototype.js
index 5d2100f,aefd89a..71a34ed
--- a/share/web/static/js/prototype.js
+++ b/share/web/static/js/prototype.js
@@@ -99,41 -197,55 +197,64 @@@ Object.extend(Object, 
    }
  });
  
- Function.prototype.bind = function() {
-   var __method = this, args = $A(arguments), object = args.shift();
-   return function() {
-     return __method.apply(object, args.concat($A(arguments)));
-   }
- }
+ Object.extend(Function.prototype, {
+   argumentNames: function() {
+     var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+     return names.length == 1 && !names[0] ? [] : names;
+   },
  
- Function.prototype.bindAsEventListener = function(object) {
-   var __method = this, args = $A(arguments), object = args.shift();
-   return function(event) {
-     return __method.apply(object, [event || window.event].concat(args));
-   }
- }
+   bind: function() {
+     if (arguments.length < 2 && arguments[0] === undefined) return this;
+     var __method = this, args = $A(arguments), object = args.shift();
+     return function() {
+       return __method.apply(object, args.concat($A(arguments)));
+     }
+   },
  
- Object.extend(Number.prototype, {
-   toColorPart: function() {
-     return this.toPaddedString(2, 16);
+   bindAsEventListener: function() {
+     var __method = this, args = $A(arguments), object = args.shift();
+     return function(event) {
+       return __method.apply(object, [event || window.event].concat(args));
+     }
    },
  
-   succ: function() {
-     return this + 1;
+   curry: function() {
+     if (!arguments.length) return this;
+     var __method = this, args = $A(arguments);
+     return function() {
+       return __method.apply(this, args.concat($A(arguments)));
+     }
    },
  
-   times: function(iterator) {
-     $R(0, this, true).each(iterator);
-     return this;
+   delay: function() {
+     var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+     return window.setTimeout(function() {
+       return __method.apply(__method, args);
+     }, timeout);
+   },
+ 
+   wrap: function(wrapper) {
+     var __method = this;
+     return function() {
+       return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+     }
+   },
+ 
+   methodize: function() {
+     if (this._methodized) return this._methodized;
+     var __method = this;
+     return this._methodized = function() {
+       return __method.apply(null, [this].concat($A(arguments)));
+     };
 +  },
 +
 +  toPaddedString: function(length, radix) {
 +    var string = this.toString(radix || 10);
 +    return '0'.times(length - string.length) + string;
 +  },
 +
 +  toJSON: function() {
 +    return isFinite(this) ? this.toString() : 'null';
    }
  });
  
@@@ -194,20 -317,20 +326,33 @@@ var PeriodicalExecuter = Class.create(
        }
      }
    }
- }
+ });
+ Object.extend(String, {
+   interpret: function(value) {
+     return value == null ? '' : String(value);
+   },
+   specialChar: {
+     '\b': '\\b',
+     '\t': '\\t',
+     '\n': '\\n',
+     '\f': '\\f',
+     '\r': '\\r',
+     '\\': '\\\\'
+   }
+ });
 +Object.extend(String, {
 +  interpret: function(value) {
 +    return value == null ? '' : String(value);
 +  },
 +  specialChar: {
 +    '\b': '\\b',
 +    '\t': '\\t',
 +    '\n': '\\n',
 +    '\f': '\\f',
 +    '\r': '\\r',
 +    '\\': '\\\\'
 +  }
 +});
  
  Object.extend(String.prototype, {
    gsub: function(pattern, replacement) {
@@@ -476,10 -626,11 +648,11 @@@ var Enumerable = 
      return result;
    },
  
-   collect: function(iterator) {
+   collect: function(iterator, context) {
+     iterator = iterator ? iterator.bind(context) : Prototype.K;
      var results = [];
      this.each(function(value, index) {
 -      results.push(iterator(value, index));
 +      results.push((iterator || Prototype.K)(value, index));
      });
      return results;
    },
@@@ -806,98 -976,130 +998,134 @@@ Object.extend(Number.prototype, 
    }
  });
  
- Hash.toQueryString.addPair = function(key, value, prefix) {
-   key = encodeURIComponent(key);
-   if (value === undefined) this.push(key);
-   else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
- }
- 
- Object.extend(Hash.prototype, Enumerable);
- Object.extend(Hash.prototype, {
-   _each: function(iterator) {
-     for (var key in this) {
-       var value = this[key];
-       if (value && value == Hash.prototype[key]) continue;
+ $w('abs round ceil floor').each(function(method){
+   Number.prototype[method] = Math[method].methodize();
+ });
+ function $H(object) {
+   return new Hash(object);
+ };
  
-       var pair = [key, value];
-       pair.key = key;
-       pair.value = value;
-       iterator(pair);
+ 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);
+       }
      }
 +  },
 +
-   keys: function() {
-     return this.pluck('key');
-   },
++  toJSON: function() {
++    return Hash.toJSON(this);
+   }
  
-   values: function() {
-     return this.pluck('value');
-   },
+   function toQueryPair(key, value) {
+     if (Object.isUndefined(value)) return key;
+     return key + '=' + encodeURIComponent(String.interpret(value));
+   }
  
-   merge: function(hash) {
-     return $H(hash).inject(this, function(mergedHash, pair) {
-       mergedHash[pair.key] = pair.value;
-       return mergedHash;
-     });
-   },
+   return {
+     initialize: function(object) {
+       this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+     },
  
-   remove: function() {
-     var result;
-     for(var i = 0, length = arguments.length; i < length; i++) {
-       var value = this[arguments[i]];
-       if (value !== undefined){
-         if (result === undefined) result = value;
-         else {
-           if (result.constructor != Array) result = [result];
-           result.push(value)
-         }
-       }
-       delete this[arguments[i]];
-     }
-     return result;
-   },
+     _each: each,
  
-   toQueryString: function() {
-     return Hash.toQueryString(this);
-   },
+     set: function(key, value) {
+       return this._object[key] = value;
+     },
  
-   inspect: function() {
-     return '#<Hash:{' + this.map(function(pair) {
-       return pair.map(Object.inspect).join(': ');
-     }).join(', ') + '}>';
-   },
+     get: function(key) {
+       return this._object[key];
+     },
  
-   toJSON: function() {
-     return Hash.toJSON(this);
-   }
- });
+     unset: function(key) {
+       var value = this._object[key];
+       delete this._object[key];
+       return value;
+     },
  
- function $H(object) {
-   if (object instanceof Hash) return object;
-   return new Hash(object);
- };
+     toObject: function() {
+       return Object.clone(this._object);
+     },
+ 
+     keys: function() {
+       return this.pluck('key');
+     },
+ 
+     values: function() {
+       return this.pluck('value');
+     },
+ 
+     index: function(value) {
+       var match = this.detect(function(pair) {
+         return pair.value === value;
+       });
+       return match && match.key;
+     },
+ 
+     merge: function(object) {
+       return this.clone().update(object);
+     },
+ 
+     update: function(object) {
+       return new Hash(object).inject(this, function(result, pair) {
+         result.set(pair.key, pair.value);
+         return result;
+       });
+     },
+ 
+     toQueryString: function() {
+       return this.map(function(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);
+       }).join('&');
+     },
+ 
+     inspect: function() {
+       return '#<Hash:{' + this.map(function(pair) {
+         return pair.map(Object.inspect).join(': ');
+       }).join(', ') + '}>';
+     },
+ 
+     toJSON: function() {
+       return Object.toJSON(this.toObject());
+     },
  
- // Safari iterates over shadowed properties
- 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;
- }()) Hash.prototype._each = function(iterator) {
-   var cache = [];
-   for (var key in this) {
-     var value = this[key];
-     if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
-     cache.push(key);
-     var pair = [key, value];
-     pair.key = key;
-     pair.value = value;
-     iterator(pair);
+     clone: function() {
+       return new Hash(this);
+     }
    }
- };
- ObjectRange = Class.create();
- Object.extend(ObjectRange.prototype, Enumerable);
- Object.extend(ObjectRange.prototype, {
+ })());
+ 
+ Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+ Hash.from = $H;
+ var ObjectRange = Class.create(Enumerable, {
    initialize: function(start, end, exclusive) {
      this.start = start;
      this.end = end;
@@@ -1346,25 -1604,27 +1630,28 @@@ Element.Methods = 
      return element;
    },
  
-   update: function(element, html) {
-     html = typeof html == 'undefined' ? '' : html.toString();
-     $(element).innerHTML = html.stripScripts();
-     setTimeout(function() {html.evalScripts()}, 10);
+   update: function(element, content) {
+     element = $(element);
+     if (content && content.toElement) content = content.toElement();
+     if (Object.isElement(content)) return element.update().insert(content);
+     content = Object.toHTML(content);
+     element.innerHTML = content.stripScripts();
+     content.evalScripts.bind(content).defer();
+     return element;
 +    return element;
    },
  
-   replace: function(element, html) {
+   replace: function(element, content) {
      element = $(element);
-     html = typeof html == 'undefined' ? '' : html.toString();
-     if (element.outerHTML) {
-       element.outerHTML = html.stripScripts();
-     } else {
+     if (content && content.toElement) content = content.toElement();
+     else if (!Object.isElement(content)) {
+       content = Object.toHTML(content);
        var range = element.ownerDocument.createRange();
-       range.selectNodeContents(element);
-       element.parentNode.replaceChild(
-         range.createContextualFragment(html.stripScripts()), element);
+       range.selectNode(element);
+       content.evalScripts.bind(content).defer();
+       content = range.createContextualFragment(content.stripScripts());
      }
-     setTimeout(function() {html.evalScripts()}, 10);
+     element.parentNode.replaceChild(content, element);
      return element;
    },
  
@@@ -2592,69 -3325,75 +3352,283 @@@ Object.extend(Selector, 
    },
  
    findElement: function(elements, expression, index) {
+     if (Object.isNumber(expression)) {
+       index = expression; expression = false;
+     }
+     return Selector.matchElements(elements, expression || '*')[index || 0];
+   },
+ 
+   findChildElements: function(element, expressions) {
+     var exprs = expressions.join(','), expressions = [];
+     exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+       expressions.push(m[1].strip());
+     });
+     var results = [], h = Selector.handlers;
+     for (var i = 0, l = expressions.length, selector; i < l; i++) {
+       selector = new Selector(expressions[i].strip());
+       h.concat(results, selector.findElements(element));
+     }
+     return (l > 1) ? h.unique(results) : results;
+   }
+ });
+ 
+ function $$() {
+   return Selector.findChildElements(document, $A(arguments));
+ }
+ var Form = {
+   reset: function(form) {
+     $(form).reset();
+     return form;
+   },
+ 
+   serializeElements: function(elements, options) {
+     if (typeof options != 'object') options = { hash: !!options };
+     else if (options.hash === undefined) 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 &&
+             submit !== false && (!submit || key == submit) && (submitted = true)))) {
+           if (key in result) {
+             // a key is already present; construct an array of values
+             if (!Object.isArray(result[key])) result[key] = [result[key]];
+             result[key].push(value);
+           }
+           else result[key] = value;
+         }
+       }
+       return result;
+     });
+ 
+     return options.hash ? data : Object.toQueryString(data);
+   }
+ };
++  },
+ 
+ Form.Methods = {
+   serialize: function(form, options) {
+     return Form.serializeElements(Form.getElements(form), options);
+   },
+ 
+   getElements: function(form) {
+     return $A($(form).getElementsByTagName('*')).inject([],
+       function(elements, child) {
+         if (Form.Element.Serializers[child.tagName.toLowerCase()])
+           elements.push(Element.extend(child));
+         return elements;
+       }
+     );
+   },
+ 
++  pseudos: {
++    'first-child': function(nodes, value, root) {
++      for (var i = 0, results = [], node; node = nodes[i]; i++) {
++        if (Selector.handlers.previousElementSibling(node)) continue;
++          results.push(node);
++      }
++      return results;
++    },
++    'last-child': function(nodes, value, root) {
++      for (var i = 0, results = [], node; node = nodes[i]; i++) {
++        if (Selector.handlers.nextElementSibling(node)) continue;
++          results.push(node);
++      }
++      return results;
++    },
++    'only-child': function(nodes, value, root) {
++      var h = Selector.handlers;
++      for (var i = 0, results = [], node; node = nodes[i]; i++)
++        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
++          results.push(node);
++      return results;
++    },
++    'nth-child':        function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, formula, root);
++    },
++    'nth-last-child':   function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, formula, root, true);
++    },
++    'nth-of-type':      function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, formula, root, false, true);
++    },
++    'nth-last-of-type': function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, formula, root, true, true);
++    },
++    'first-of-type':    function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, "1", root, false, true);
++    },
++    'last-of-type':     function(nodes, formula, root) {
++      return Selector.pseudos.nth(nodes, "1", root, true, true);
++    },
++    'only-of-type':     function(nodes, formula, root) {
++      var p = Selector.pseudos;
++      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
++    },
++
++    // handles the an+b logic
++    getIndices: function(a, b, total) {
++      if (a == 0) return b > 0 ? [b] : [];
++      return $R(1, total).inject([], function(memo, i) {
++        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
++        return memo;
++      });
++    },
++
++    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
++    nth: function(nodes, formula, root, reverse, ofType) {
++      if (nodes.length == 0) return [];
++      if (formula == 'even') formula = '2n+0';
++      if (formula == 'odd')  formula = '2n+1';
++      var h = Selector.handlers, results = [], indexed = [], m;
++      h.mark(nodes);
++      for (var i = 0, node; node = nodes[i]; i++) {
++        if (!node.parentNode._counted) {
++          h.index(node.parentNode, reverse, ofType);
++          indexed.push(node.parentNode);
++        }
++      }
++      if (formula.match(/^\d+$/)) { // just a number
++        formula = Number(formula);
++        for (var i = 0, node; node = nodes[i]; i++)
++          if (node.nodeIndex == formula) results.push(node);
++      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
++        if (m[1] == "-") m[1] = -1;
++        var a = m[1] ? Number(m[1]) : 1;
++        var b = m[2] ? Number(m[2]) : 0;
++        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
++        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
++          for (var j = 0; j < l; j++)
++            if (node.nodeIndex == indices[j]) results.push(node);
++        }
++      }
++      h.unmark(nodes);
++      h.unmark(indexed);
++      return results;
++    },
++
++    '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;
++        results.push(node);
++      }
++      return results;
++    },
++
++    'not': function(nodes, selector, root) {
++      var h = Selector.handlers, selectorType, m;
++      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);
++      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);
++      return results;
++    },
++
++    'disabled': function(nodes, value, root) {
++      for (var i = 0, results = [], node; node = nodes[i]; i++)
++        if (node.disabled) results.push(node);
++      return results;
++    },
++
++    'checked': function(nodes, value, root) {
++      for (var i = 0, results = [], node; node = nodes[i]; i++)
++        if (node.checked) results.push(node);
++      return results;
++    }
++  },
++
++  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.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() + '-'); }
++  },
++
++  matchElements: function(elements, expression) {
++    var matches = new Selector(expression).findElements(), h = Selector.handlers;
++    h.mark(matches);
++    for (var i = 0, results = [], element; element = elements[i]; i++)
++      if (element._counted) results.push(element);
++    h.unmark(matches);
++    return results;
++  },
++
++  findElement: function(elements, expression, index) {
 +    if (typeof expression == 'number') {
 +      index = expression; expression = false;
 +    }
 +    return Selector.matchElements(elements, expression || '*')[index || 0];
 +  },
 +
 +  findChildElements: function(element, expressions) {
 +    var exprs = expressions.join(','), expressions = [];
 +    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
 +      expressions.push(m[1].strip());
 +    });
 +    var results = [], h = Selector.handlers;
 +    for (var i = 0, l = expressions.length, selector; i < l; i++) {
 +      selector = new Selector(expressions[i].strip());
 +      h.concat(results, selector.findElements(element));
 +    }
 +    return (l > 1) ? h.unique(results) : results;
 +  }
 +});
 +
 +function $$() {
 +  return Selector.findChildElements(document, $A(arguments));
 +}
 +var Form = {
 +  reset: function(form) {
 +    $(form).reset();
 +    return form;
 +  },
 +
 +  serializeElements: function(elements, getHash) {
 +    var data = elements.inject({}, function(result, element) {
 +      if (!element.disabled && element.name) {
 +        var key = element.name, value = $(element).getValue();
 +        if (value != null) {
 +         	if (key in result) {
 +            if (result[key].constructor != Array) result[key] = [result[key]];
 +            result[key].push(value);
 +          }
 +          else result[key] = value;
 +        }
 +      }
 +      return result;
 +    });
 +
 +    return getHash ? data : Hash.toQueryString(data);
 +  }
 +};
 +
 +Form.Methods = {
 +  serialize: function(form, getHash) {
 +    return Form.serializeElements(Form.getElements(form), getHash);
 +  },
 +
 +  getElements: function(form) {
 +    return $A($(form).getElementsByTagName('*')).inject([],
 +      function(elements, child) {
 +        if (Form.Element.Serializers[child.tagName.toLowerCase()])
 +          elements.push(Element.extend(child));
 +        return elements;
 +      }
 +    );
 +  },
 +
    getInputs: function(form, typeName, name) {
      form = $(form);
      var inputs = form.getElementsByTagName('input');
@@@ -2794,9 -3547,9 +3782,11 @@@ Form.Element.Serializers = 
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
-         return Form.Element.Serializers.inputSelector(element);
+         return Form.Element.Serializers.inputSelector(element, value);
+       default:
+         return Form.Element.Serializers.textarea(element, value);
 +      default:
 +        return Form.Element.Serializers.textarea(element);
      }
    },
  

-----------------------------------------------------------------------


More information about the Jifty-commit mailing list