[Jifty-commit] r3759 - in jifty/branches/virtual-models: . examples/Yada/etc examples/Yada/lib examples/Yada/lib/Yada examples/Yada/lib/Yada/View examples/Yada/share/web/static/js examples/Yada/share/web/static/js/Asynapse lib/Jifty lib/Jifty/Manual lib/Jifty/Plugin lib/Jifty/Plugin/ActorMetadata/Mixin/Model lib/Jifty/Plugin/Chart lib/Jifty/Plugin/Chart/Renderer lib/Jifty/Plugin/Chart/Renderer/GD lib/Jifty/Plugin/GoogleMap lib/Jifty/Plugin/REST lib/Jifty/Plugin/SkeletonApp lib/Jifty/Plugin/UUID lib/Jifty/View/Declare lib/Jifty/Web share/plugins/Jifty/Plugin/Chart share/plugins/Jifty/Plugin/Chart/web share/plugins/Jifty/Plugin/Chart/web/static share/plugins/Jifty/Plugin/Chart/web/static/js share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit share/web/static/js share/web/templates/__jifty/webservices t t/TestApp-Plugin-Chart t/TestApp-Plugin-Chart/bin t/TestApp-Plugin-Chart/doc t/TestApp-Plugin-Chart/etc t/TestApp-Plugin-Chart/lib t/TestApp-Plugin-Chart/lib/TestApp t/TestApp-Plugin-Chart/lib/TestApp/Plugin t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Action t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Model t/TestApp-Plugin-Chart/log t/TestApp-Plugin-Chart/share t/TestApp-Plugin-Chart/share/po t/TestApp-Plugin-Chart/share/web t/TestApp-Plugin-Chart/share/web/static t/TestApp-Plugin-Chart/share/web/templates t/TestApp-Plugin-Chart/t t/TestApp-Plugin-Chart/var t/TestApp-Plugin-Chart/var/mason t/TestApp-Plugin-Chart/var/mason/cache t/TestApp-Plugin-Chart/var/mason/obj t/TestApp/lib/TestApp/Model t/TestApp/t t/clientside

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Wed Aug 1 12:25:50 EDT 2007


Author: sterling
Date: Wed Aug  1 12:25:47 2007
New Revision: 3759

Added:
   jifty/branches/virtual-models/examples/Yada/lib/Yada.pm
   jifty/branches/virtual-models/examples/Yada/share/web/static/js/
   jifty/branches/virtual-models/examples/Yada/share/web/static/js/Asynapse/
   jifty/branches/virtual-models/examples/Yada/share/web/static/js/Asynapse/REST.js
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/Chart.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/GD/
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/GD/Graph.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/PlotKit.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/View.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Web.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/UUID/
   jifty/branches/virtual-models/lib/Jifty/Plugin/UUID.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/UUID/Widget.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Compile.pm
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/MochiKit.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/__package__.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Base.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Canvas.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/EasyPlot.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Layout.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit_Packed.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SVG.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetCanvas.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetSVG.js
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/dummy.svg
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/excanvas.js   (contents, props changed)
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/mochikit.noexport.js
   jifty/branches/virtual-models/share/web/static/js/template_declare.js
   jifty/branches/virtual-models/t/13-sessions.t
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/Makefile.PL
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/bin/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/bin/jifty   (contents, props changed)
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/doc/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/etc/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/etc/config.yml
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Action/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Model/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/log/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/share/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/share/po/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/share/web/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/share/web/static/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/share/web/templates/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/chart.t
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/gd_graph.t
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/var/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/var/mason/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/var/mason/cache/
   jifty/branches/virtual-models/t/TestApp-Plugin-Chart/var/mason/obj/
   jifty/branches/virtual-models/t/clientside/
   jifty/branches/virtual-models/t/clientside/td.t
Modified:
   jifty/branches/virtual-models/   (props changed)
   jifty/branches/virtual-models/AUTHORS
   jifty/branches/virtual-models/META.yml
   jifty/branches/virtual-models/Makefile.PL
   jifty/branches/virtual-models/examples/Yada/etc/config.yml
   jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm
   jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm
   jifty/branches/virtual-models/lib/Jifty/Action.pm
   jifty/branches/virtual-models/lib/Jifty/Manual/Glossary.pod
   jifty/branches/virtual-models/lib/Jifty/Notification.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/GoogleMap/Widget.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/REST/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/BaseClass.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm
   jifty/branches/virtual-models/lib/Jifty/Web.pm
   jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm
   jifty/branches/virtual-models/lib/Jifty/Web/Session.pm
   jifty/branches/virtual-models/share/web/static/js/jifty.js
   jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm
   jifty/branches/virtual-models/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t
   jifty/branches/virtual-models/t/TestApp/t/05-editactions-Record.t
   jifty/branches/virtual-models/t/TestApp/t/config-Record

Log:
 r8301 at riddle:  andrew | 2007-08-01 11:24:12 -0500
  r8183 at riddle:  andrew | 2007-07-27 14:49:07 -0500
  Adding a plugin for rendering charts of data.
  r8184 at riddle:  andrew | 2007-07-27 14:49:42 -0500
  Removing a directory that should not have committed previously.
  r8187 at riddle:  andrew | 2007-07-27 14:51:09 -0500
   r8164 at riddle (orig r3702):  trs | 2007-07-19 12:23:09 -0700
    r25574 at zot:  tom | 2007-07-19 15:22:29 -0400
    Make sure we get UTF-8
   
   r8165 at riddle (orig r3703):  trs | 2007-07-19 13:10:55 -0700
    r25585 at zot:  tom | 2007-07-19 16:10:15 -0400
    Now make sure it's actually UTF-8 
   
   r8167 at riddle (orig r3705):  clkao | 2007-07-20 07:00:23 -0700
   Push milestone 1 of trimclient to trunk.
   
   r8168 at riddle (orig r3706):  jesse | 2007-07-22 13:21:40 -0700
   
   r8169 at riddle (orig r3707):  jesse | 2007-07-22 13:21:58 -0700
    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.
   
   r8170 at riddle (orig r3708):  jesse | 2007-07-22 13:22:02 -0700
    r61115 at 106:  jesse | 2007-07-22 12:48:51 -0700
    * added some pod to help make pod tests pass
   
   r8177 at riddle (orig r3715):  clkao | 2007-07-24 03:09:47 -0700
   Fix a fragment update regression caused by the trimclient merge.
   
  
  r8192 at riddle:  andrew | 2007-07-27 15:11:49 -0500
  Reverting mistaken revision r3723
  r8194 at riddle:  andrew | 2007-07-27 15:16:59 -0500
  Reverting mistaken revision r3723, take 2
  r8198 at riddle:  andrew | 2007-07-29 13:47:15 -0500
   r8197 at dynpc145 (orig r3730):  jesse | 2007-07-28 19:31:47 -0500
    r64900 at pinglin:  jesse | 2007-07-28 18:18:31 -0500
    * Moniker bulletproofing. Suggested by Mikko Lapasti
   
  
  r8199 at riddle:  andrew | 2007-07-29 15:13:38 -0500
  Added documentation to the experimental Chart plugin.
  r8200 at riddle:  andrew | 2007-07-29 15:15:21 -0500
  Added the Chart::Base recommendation for the Chart plugin.
  r8203 at riddle:  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.
  r8204 at riddle:  andrew | 2007-07-29 21:13:35 -0500
  Adding Image::Info dependency used during testing of Jifty::Plugin::Chart
  r8205 at riddle:  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.
  r8208 at riddle:  andrew | 2007-07-29 21:16:14 -0500
   r8207 at dynpc145 (orig r3734):  jesse | 2007-07-29 16:53:45 -0500
    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
   
  
  r8268 at riddle:  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.
  r8269 at riddle:  andrew | 2007-07-31 21:29:42 -0500
  Fixed the way arguments are passed to the render() method in Jifty::Plugin::Chart::Web.
  r8270 at riddle:  andrew | 2007-07-31 21:31:01 -0500
  Moved the chart/* dispatch to chart/chart/* to make room for alternate charting mechanisms.
  r8271 at riddle:  andrew | 2007-07-31 21:31:21 -0500
  Added a renderer for GD::Graph
  r8272 at riddle:  andrew | 2007-07-31 21:31:41 -0500
  Updated the module recommendations for the Chart plugin.
  r8282 at riddle:  andrew | 2007-07-31 21:32:26 -0500
   r8273 at dynpc145 (orig r3738):  sartak | 2007-07-30 15:57:21 -0500
    r29652 at caladan:  sartak | 2007-07-30 16:56:47 -0400
    Add a load_by_kv to Jifty::Web::Session
   
   r8274 at dynpc145 (orig r3739):  sartak | 2007-07-30 15:58:18 -0500
    r29654 at caladan:  sartak | 2007-07-30 16:58:03 -0400
    add myself to AUTHORS :)
   
   r8277 at dynpc145 (orig r3742):  jesse | 2007-07-30 19:38:33 -0500
    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
   
  
  r8289 at riddle:  andrew | 2007-07-31 21:42:52 -0500
  Updated POD and removed an unnecessary extra subroutine call.
  r8290 at riddle:  andrew | 2007-07-31 21:44:59 -0500
  Fixed POD coverage issue.
  r8291 at riddle:  andrew | 2007-07-31 21:47:05 -0500
  Fixed an eensy POD bug.
  r8292 at riddle:  andrew | 2007-07-31 21:47:31 -0500
  
  r8296 at riddle:  andrew | 2007-07-31 22:03:08 -0500
  Made the chart plugin test smarter and added one for the GD::Graph renderer.
  r8300 at riddle:  andrew | 2007-08-01 11:23:03 -0500
   r8298 at riddle (orig r3757):  trs | 2007-07-31 22:41:50 -0500
    r25774 at zot:  tom | 2007-07-31 23:40:12 -0400
    Basic PlotKit renderer for Chart plugin
   
   r8299 at riddle (orig r3758):  trs | 2007-08-01 01:49:40 -0500
    r25776 at zot:  tom | 2007-08-01 02:49:29 -0400
    - Uncomment neccessary require
    - Make sure to handle undefined stuff
   
  
 


Modified: jifty/branches/virtual-models/AUTHORS
==============================================================================
--- jifty/branches/virtual-models/AUTHORS	(original)
+++ jifty/branches/virtual-models/AUTHORS	Wed Aug  1 12:25:47 2007
@@ -30,3 +30,4 @@
 Alexander Klink <alech at cpan.org>
 Andreas Koenig <andreas.koenig.7os6VVqR at franz.ak.mind.de>
 sunnavy <sunnavy at gmail.com>
+Shawn M Moore <sartak at gmail.com>

Modified: jifty/branches/virtual-models/META.yml
==============================================================================
--- jifty/branches/virtual-models/META.yml	(original)
+++ jifty/branches/virtual-models/META.yml	Wed Aug  1 12:25:47 2007
@@ -65,6 +65,7 @@
   Email::MIME::ContentType: 0
   Email::MIME::CreateHTML: 0
   Email::MIME::Creator: 0
+  Email::MIME::Modifier: 0
   Email::Send: 1.99_01
   Email::Simple: 0
   Email::Simple::Creator: 0
@@ -97,6 +98,7 @@
   Module::Refresh: 0
   Module::ScanDeps: 0
   Object::Declare: 0.13
+  PadWalker: 0
   Params::Validate: 0
   Pod::Simple: 0
   SQL::ReservedWords: 0

Modified: jifty/branches/virtual-models/Makefile.PL
==============================================================================
--- jifty/branches/virtual-models/Makefile.PL	(original)
+++ jifty/branches/virtual-models/Makefile.PL	Wed Aug  1 12:25:47 2007
@@ -26,6 +26,7 @@
 requires('Email::MIME::Creator');
 requires('Email::MIME::ContentType');
 requires('Email::MIME::CreateHTML');
+requires('Email::MIME::Modifier');
 requires('Email::Send' => '1.99_01'); # Email::Send::Jifty::Test
 requires('Email::Simple');
 requires('Email::Simple::Creator');
@@ -57,6 +58,7 @@
 requires('Module::Refresh');
 requires('Module::ScanDeps');
 requires('Object::Declare' => '0.13');
+requires('PadWalker');
 requires('Params::Validate');
 requires('Scalar::Defer' => '0.10');
 requires('Shell::Command');
@@ -146,7 +148,13 @@
     ],
     'Jifty console' => [
         -default => 0,
-        recommends('Devel::EvalContext')
+        recommends('Devel::EvalContext'),
+    ],
+    'Chart Plugin (none of these must be installed for Charts to work)' => [
+        -default => 0,
+        recommends('Chart::Base'),
+        recommends('GD::Graph'),
+        recommends('Image::Info'), # for testing
     ],
 );
 

Modified: jifty/branches/virtual-models/examples/Yada/etc/config.yml
==============================================================================
--- jifty/branches/virtual-models/examples/Yada/etc/config.yml	(original)
+++ jifty/branches/virtual-models/examples/Yada/etc/config.yml	Wed Aug  1 12:25:47 2007
@@ -2,6 +2,7 @@
 application:
   OpenIDSecret: sekrit13
 framework: 
+  ConfigFileVersion: 2
   AdminMode: 1
   SkipAccessControl: 1
   ApplicationClass: Yada
@@ -16,7 +17,7 @@
     RecordBaseClass: Jifty::DBI::Record::Cachable
     User: ''
     Version: 0.0.1
-  DevelMode: 0
+  DevelMode: 1
   L10N: 
     PoDir: share/po
   LogLevel: INFO
@@ -34,11 +35,13 @@
     - User: {}
     - Authentication::Password: {}
     - OpenID: {}
+    - SinglePage: {}
 
   PubSub: 
     Backend: Memcached
     Enable: ~
   TemplateClass: Yada::View
+  ClientTemplate: 1
   Web: 
     BaseURL: http://localhost
     DataDir: var/mason

Added: jifty/branches/virtual-models/examples/Yada/lib/Yada.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,6 @@
+package Yada;
+
+
+Jifty->web->add_javascript(qw( Asynapse/REST.js trimpath-template.js ) );
+
+1;

Modified: jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm
==============================================================================
--- jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm	Wed Aug  1 12:25:47 2007
@@ -1,18 +1,63 @@
 package Yada::View;
 use Jifty::View::Declare -base;
+use strict;
+
+use Jifty::View::Declare::CRUD;
+for (qw/todo/) {
+    Jifty::View::Declare::CRUD->mount_view($_);
+}
 
 template 'index.html' => page {
     my $self = shift;
     title { _('Yada!') };
 
+    render_region('test_region');
+
+    hyperlink(label => 'FAQ',
+	      onclick => [{region => 'test_region',
+			   replace_with => '_faq',
+			  }]);
+
     form {
+	set(item_path => '/todo/view_brief');
 	render_region(name => 'list', path => '/todo/list');
     }
 };
 
-use Jifty::View::Declare::CRUD;
-for (qw/todo/) {
-    Jifty::View::Declare::CRUD->mount_view($_);
-}
+template '_faq' => sub :Static {
+    hyperlink(label => 'close', onclick => [{replace_with => '/__jifty/empty'}]);
+
+    div {
+        attr { id => "faq" };
+        h2 { _('Using Yada') }
+        dl {
+            dt { 'Yada Yada Yada!'}
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+template 'signup' => page {
+    title is _('Sign up');
+    render_region(name => 'signup_widget', path => '_signup');
+};
+
+template '_signup' => sub :Action {
+    my $action = Jifty->web->new_action( class => 'Signup', moniker => 'signupnow');
+    my $next = undef;
+#    with ( call => $next ),
+    form {
+	render_param( $action => 'name' , focus => 1);
+	render_param( $action => $_ ) for ( grep {$_ ne 'name'} $action->argument_names );
+
+	form_return( label => _('Sign up'), submit => $action );
+    }
+
+};
+
 
 1;

Modified: jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm
==============================================================================
--- jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm	Wed Aug  1 12:25:47 2007
@@ -1,6 +1,21 @@
 package Yada::View::Todo;
 use strict;
-use Jifty::View::Declare -base;
 use base 'Jifty::View::Declare::CRUD';
+use Jifty::View::Declare -base;
+
+template 'view_brief' => sub {
+    my $self = shift;
+    my ( $object_type, $id ) = ( $self->object_type, get('id') );
+    my $record = $self->_get_record($id);
+
+    div { {class is "description" };
+	  outs($record->description);
+	  hyperlink(label => 'details',
+		    onclick => [{region => 'test_region',
+				 replace_with => $self->fragment_for('view'),
+				 args         => { id => $id },
+				}]);
+      };
+};
 
 1;

Added: jifty/branches/virtual-models/examples/Yada/share/web/static/js/Asynapse/REST.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/share/web/static/js/Asynapse/REST.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,285 @@
+if ( typeof Asynapse == 'undefined' ) {
+    Asynapse = {}
+}
+
+if ( typeof Asynapse.REST == 'undefined' ) {
+    Asynapse.REST = {}
+}
+
+Asynapse.REST.VERSION = "0.10"
+
+Asynapse.REST.Model = function(model) {
+    this._model = model
+    return this;
+}
+
+Asynapse.REST.Model.prototype = {
+    /* Corresponds Jifty's REST Pluing API */
+    show_item_field: function(column, key, field) {
+        var url = "/=/model/*/*/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+            .replace("*", field)
+
+        return this.eval_ajax_get(url);        
+    },
+    
+    show_item: function(column, key) {
+        var url = "/=/model/*/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_model_items: function(column) {
+        var url = "/=/model/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_model_columns: function() {
+        var url = "/=/model/*.js"
+            .replace("*", this._model)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_models: function() {
+        var url = "/=/model.js"
+
+        return this.eval_ajax_get(url);
+    },
+
+    create_item: function(item) {
+        var url ="/=/model/*.js"
+            .replace("*", this._model)
+
+        var req = new Ajax.Request(url, {
+            method: 'post',
+            asynchronous: false,
+            postBody: $H(item).toQueryString()
+        });
+        if ( req.responseIsSuccess() ) {
+            eval(req.transport.responseText);
+            return $H($_)
+        } else {
+            return null;
+        }
+    },
+    
+    replace_item: function(item) {
+        var url = "/=/action/update" + this._model + ".js"
+        new Ajax.Request(url, {
+            method: 'post',
+            contentType: 'application/x-www-form-urlencoded',
+            postBody: $H(item).toQueryString()            
+        });
+    },
+
+    delete_item: function(column, key) {
+        var url = "/=/model/*/*/*"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+        
+        new Ajax.Request(url, {
+            method: 'DELETE',
+            contentType: 'application/x-www-form-urlencoded'
+        });
+        return null;
+    },
+    
+    /* Internal Helpers */
+    eval_ajax_get: function(url) {
+        eval(this.ajax_get(url));
+        return $_ ? Object.extend({},$_) : null;
+    },
+    ajax_get: function(url) {
+        var req = new Ajax.Request(url, {
+            method: 'GET',
+            asynchronous: false
+        })
+        if ( req.responseIsSuccess() ) {
+            return req.transport.responseText;
+        }
+        else {
+            return "var $_ = null";
+        }
+    }
+}
+
+Asynapse.REST.Model.ActiveRecord = function(model) {
+    Object.extend(this, new Asynapse.REST.Model(model));
+    this._attributes = {}
+    return this;
+}
+
+Asynapse.REST.Model.ActiveRecord.prototype = {
+    new: function() {
+        return this;
+    },
+    
+    find: function(param) {
+        if ( typeof param == 'number' ) {
+            return this.show_item("id", param)
+        }
+    },
+
+    find_by_id: function(id) {
+        return this.show_item("id", id)
+    },
+
+    create: function(attributes) {
+        var r = this.create_item(attributes);
+
+        if (r.success) {
+            return this.show_item("id", Number(r.content.id) )
+        }
+        return null;
+    },
+
+    delete: function(id) {
+        this.delete_item("id", id)
+        return null
+    },
+
+    update: function(id, attributes) {
+        var obj = this.find(id)
+        obj = Object.extend(obj, attributes)
+        return this.replace_item( obj )
+    },
+
+    write_attribute: function(attr, value) {
+    }
+}
+
+/* Great Aliases */
+Asynapse.Model = Asynapse.REST.Model
+Asynapse.ActiveRecord = Asynapse.REST.Model.ActiveRecord
+AsynapseRecord = Asynapse.REST.Model.ActiveRecord
+
+/**
+=head1 NAME
+
+Asynapse.REST - Asynapse REST Client
+
+=head1 VERSION
+
+This document describes Asynapse.REST version 0.10
+
+=head1 SYNOPSIS
+
+    # Define Your own AsynapseRecord Class.
+    Person = new AsynapseRecord('person')
+
+    # Use it
+    var p = Person.find(1)
+
+=head1 DESCRIPTION
+
+Asynapse.REST is the namespace for being a general REST client in
+Asynapse framework. Under this namespace, so far we arrange
+C<Asynapse.REST.Model> for Asynapse Model Classes. It means to
+provide an abstration layer for data existing at given REST server(s).
+
+With many flavours of data abstration layer in the world, we choose
+to emulate ActiveRecord as our first target, which has a plain
+simple object semantics, and very compatible to javascript.
+The implementation of it called C<AsynapseRecord>.
+
+To use it, you must first create your own record classes, like this:
+
+    Person = new AsynapseRecord('person')
+
+After this, Person becomes your Person model class, and then you
+can do:
+
+    var p = Person.find(1)
+
+To find a person by its id. Besides C<find>, C<create>, C<update>,
+and C<delete> are also implemented.
+
+Here's more detail about how to use these interfaces. They are all
+"class methods".
+
+=over
+
+=item find( id )
+
+Retrieve a record from this model with given id.
+
+=item create( attr )
+
+Create a new with attributes specified in attr hash.
+
+=item update( id, attr )
+
+Update the record with primary key id with new sets of
+attributes specified in attr hash.
+
+=item delete( id )
+
+Remove the record with given id.
+
+=back
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+AsynapseRecord requires no configuration files or environment
+variables.  However, you need a Jifty instance with REST plugin
+(which is given by default now.)
+
+Since JavaScript cannot do XSS, it assumed the your Jifty instance's
+URL resides at C</>, and entry points of REST servces starts from
+C</=/>. It should be made possible to change this assumption in the
+future to match more presets in different frameworks.
+
+=head1 BUGS AND LIMITATIONS
+
+The asynapse project is hosted at L<http://code.google.com/p/asynapse/>.
+You may contact the authors or submit issues using the web interface.
+
+=head1 AUTHOR
+
+Kang-min Liu  C<< <gugod at gugod.org> >>
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Kang-min Liu C<< <gugod at gugod.org> >>. All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+*/ 
+

Modified: jifty/branches/virtual-models/lib/Jifty/Action.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Action.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Action.pm	Wed Aug  1 12:25:47 2007
@@ -119,6 +119,26 @@
         $self->_get_current_user();
     }
 
+
+    if ( $args{'moniker'} ) {
+        if ( $args{'moniker'} =~ /[\;]/ ) {
+            $args{'moniker'} =~ s/[\;]/_/g;
+            $self->log->warn(
+                "Moniker @{[$args{'moniker'}]} contains invalid characters. It should not contain any ';' characters. "
+                    . "It has been autocorrected, but you should correct your code"
+            );
+        }
+        if ( $args{'moniker'} =~ /^\d/ ) {
+            $args{'moniker'} = "fixup-" . $args{'moniker'};
+            $self->log->warn(
+                "Moniker @{[$args{'moniker'}]} contains invalid characters. It can not begin with a digit. "
+                    . "It has been autocorrected, but you should correct your code"
+            );
+
+        }
+    }
+
+
     $self->moniker($args{'moniker'} || $self->_generate_moniker);
     $self->order($args{'order'});
 

Modified: jifty/branches/virtual-models/lib/Jifty/Manual/Glossary.pod
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Manual/Glossary.pod	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Manual/Glossary.pod	Wed Aug  1 12:25:47 2007
@@ -102,13 +102,15 @@
 
 =item moniker
 
-Every instance of a L<Jifty::Action> has a B<moniker>.  A moniker is
-an arbitrary-length nonempty string containing no semicolons.
-Monikers serve as identifiers for actions, to associate arguments with
-actions and to access specific actions "by name".  Monikers need not
-be globally unique, but they must be unique within a single request.
+Every instance of a L<Jifty::Action> has a B<moniker>.  Monikers
+serve as identifiers for actions, to associate arguments with actions
+and to access specific actions "by name".  Monikers need not be
+globally unique, but they must be unique within a single request.
 Monikers have no semantic meaning. See L<Jifty::Action/monikers>
 
+A moniker is an arbitrary-length nonempty string containing no
+semicolons. It may not begin with a digit.
+
 =item parameter
 
 A B<parameter> is a named parameter to an L</action>.  Jifty generally renders

Modified: jifty/branches/virtual-models/lib/Jifty/Notification.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Notification.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Notification.pm	Wed Aug  1 12:25:47 2007
@@ -7,6 +7,7 @@
 use Email::Send            ();
 use Email::MIME::Creator;
 use Email::MIME::CreateHTML;
+use Email::MIME::Modifier;
 
 __PACKAGE__->mk_accessors(
     qw/body html_body preface footer subject from _recipients _to_list to/);
@@ -98,6 +99,9 @@
     return unless ($to);
     my $message = "";
     my $appname = Jifty->config->framework('ApplicationName');
+
+    my %attrs = ( charset => 'UTF-8' );
+
     if ($self->html_body) {
       $message = Email::MIME->create_html(
 					     header => [
@@ -105,12 +109,16 @@
 							To      => $to,
 							Subject => Encode::encode('MIME-Header', $self->subject || _("A notification from %1!",$appname )),
 						       ],
-					     attributes => { charset => 'UTF-8' },
-					     text_body => $self->full_body,
-					     body => $self->full_html,
+					     attributes => \%attrs,
+                         text_body_attributes => \%attrs,
+                         body_attributes => \%attrs,
+					     text_body => Encode::encode_utf8($self->full_body),
+					     body => Encode::encode_utf8($self->full_html),
                          embed => 0,
                          inline_css => 0
 					    );
+        # Since the containing messsage will still be us-ascii otherwise
+        $message->charset_set( $attrs{'charset'} );
     } else {
             $message = Email::MIME->create(
 					     header => [
@@ -118,7 +126,7 @@
 							To      => $to,
 							Subject => Encode::encode('MIME-Header', $self->subject || _("A notification from %1!",$appname )),
 						       ],
-					     attributes => { charset => 'UTF-8' },
+					     attributes => \%attrs,
 					     
 					     parts => $self->parts
 					    );

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata.pm	Wed Aug  1 12:25:47 2007
@@ -4,4 +4,30 @@
 package Jifty::Plugin::ActorMetadata;
 use base qw/Jifty::Plugin/;
 
+=head1 NAME
+
+Jifty::Plugin::ActorMetadata
+
+=head1 DESCRIPTION
+ 
+This plugin adds a model mixin which adds C<created_by>, C<created_on> and C<updated_on> columns to a model class.
+
+=head1 EXAMPLE 
+
+use strict;
+ use warnings;
+ 
+ package MeetMeow::Model::Cat;
+ use Jifty::DBI::Schema;
+ 
+ use MeetMeow::Record schema {
+ 
+         ...
+ 
+ };
+ use Jifty::Plugin::ActorMetadata::Mixin::Model::ActorMetadata; # created_by, created_on, updated_on
+ 
+
+=cut
+
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm	Wed Aug  1 12:25:47 2007
@@ -84,6 +84,12 @@
     $self->add_trigger(name => 'before_create', callback => \&before_create);
 }
 
+=head2 before_create
+
+Sets C<created_by>, C<created_on>, C<updated_on> based on the current user and time.
+
+=cut
+
 sub before_create {
     my $self = shift;
     my $args = shift;
@@ -94,7 +100,15 @@
     return 1;
 }
 
+=head2 current_user_can
+
+Rejects creation unless there's a current_user. 
+Rejects update or deletion unless the current_user is the creator.  (Jesse says: this feels like wrong logic for this mixin)
+
+=cut
+
 # XXX: Move this to an abortable trigger
+
 sub current_user_can {
     my $self = shift;
     my $action = shift;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,110 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart;
+use base qw/ Jifty::Plugin Class::Accessor::Fast /;
+
+use Jifty::Plugin::Chart::Web;
+
+__PACKAGE__->mk_accessors(qw/ renderer /);
+
+=head1 NAME
+
+Jifty::Plugin::Chart - A charting API for Jifty
+
+=head1 SYNOPSIS
+
+In your F<config.yml>:
+
+  Plugins:
+    - Chart: {}
+
+In your Mason templates:
+
+  <% Jifty->web->chart(
+      type   => 'Bar',
+      width  => 400,
+      height => 300,
+      data   => [
+          [ '2004', '2005', '2006', '2007' ], # labels
+          [ 14,     15,     17,     22     ], # first data set
+          [ 22,     25,     20,     21     ], # second data set
+      ],
+  ) %>
+
+=head1 DESCRIPTION
+
+B<CAUTION:> This plugin is experimental. The API I<will> change.
+
+This plugin provides a charting API that can be used by Jifty applications to build data visualizations without regard to the underlying rendering mechanism.
+
+As of this writing, the API is a barely veiled interface over L<Chart>. However, I intend to expand the interface to apply to something like Maani's XML/SWF Charts or Imprise Javascript charts or even something like OpenLaszlo (or something Open Source and Perl if I can find or build such a thing in time).
+
+=head1 INTERFACE
+
+By adding this method to the plugin configuration for your Jifty application, you will cause L<Jifty::Web> to inherit a new method, C<chart>, which is the cornerstone of this API.
+
+This method is described in L<Jifty::Plugin::Chart::Web> and an example is shown in the L</SYNOPSIS> above.
+
+=head1 CONFIGURATION
+
+The plugin takes a single configuration option called C<renderer>. This may be set to a chart renderer class, which is just an implementation of L<Jifty::Plugin::Chart::Renderer>. The default, L<Jifty::Plugin::Chart::Renderer::Chart>, uses L<Chart> to render charts as PNG files which are then included in your pages for you.
+
+Here is an example configuration for F<config.yml>:
+
+  Plugins:
+    - Chart:
+        renderer: Chart
+
+=head1 METHODS
+
+=head2 init
+
+Adds the L<Jifty::Plugin::Chart::Web/chart> method to L<Jifty::Web>.
+
+=cut
+
+sub init {
+    my $self = shift;
+    my %args = (
+        renderer => 'Chart',
+        @_,
+    );
+
+    if ( $args{renderer} !~ /::/ ) {
+        $args{renderer} = __PACKAGE__.'::Renderer::'.$args{renderer};
+    }
+
+    eval "use $args{renderer}";
+    warn $@ if $@;
+    $self->renderer( $args{renderer} );
+
+    if ( $self->renderer =~ 'PlotKit' ) {
+        # XXX TODO: Why does MochiKit need to be loaded before everything else?
+        Jifty->web->javascript_libs([
+            'MochiKit/MochiKit.js',
+            @{ Jifty->web->javascript_libs },
+            'PlotKit/PlotKit_Packed.js'
+        ]);
+    }
+
+    push @Jifty::Web::ISA, 'Jifty::Plugin::Chart::Web';
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin>, L<Jifty::Web>, L<Jifty::Plugin::Chart::Renderer>, L<Jifty::Plugin::Chart::Renderer::Chart>, L<Jifty::Plugin::Chart::View>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and redistributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Dispatcher.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,93 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Dispatcher;
+use Jifty::Dispatcher -base;
+
+use Jifty::YAML;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Dispatcher - Dispatcher for the chart API plugin
+
+=head1 RULES
+
+=head2 chart/chart/*
+
+Grabs the chart configuration stored in the key indicated in C<$1> and unpacks it using L<YAML>. It then passes it to the L<Jifty::Plugin::Chart::View/chart> template.
+
+=cut
+
+on 'chart/chart/*' => run {
+    # Create a session ID to lookup the chart configuration
+    my $session_id = 'chart_' . $1;
+
+    # Unpack the data and then clear it from the session
+    my $args = Jifty::YAML::Load( Jifty->web->session->get( $session_id ) );
+    Jifty->web->session->remove( $session_id );
+
+    # No data? Act like a 404
+    last_rule unless defined $args;
+
+    # Use the "type" to determine which class to use
+    my $class = 'Chart::' . $args->{type};
+
+    # Load that class or die if it does not exist
+    $class->require;
+
+    # Remember the class name for the view
+    $args->{class} = $class;
+
+    # Send them on to chart the chart
+    set 'args' => $args;
+    show 'chart/chart'
+};
+
+=head2 chart/gd_graph/*
+
+Grabs the chart configuration stored in the key indicated in C<$1> and unpacks it using L<YAML>. It then passes it to the L<Jifty::Plugin::Chart::View/chart> template.
+
+=cut
+
+on 'chart/gd_graph/*' => run {
+    # Create a session ID to lookup the chart configuration
+    my $session_id = 'chart_' . $1;
+
+    # Unpack the data and then clear it from the session
+    my $args = Jifty::YAML::Load( Jifty->web->session->get( $session_id ) );
+    Jifty->web->session->remove( $session_id );
+
+    # No data? Act like a 404
+    last_rule unless defined $args;
+
+    # Use the "type" to determine which class to use
+    my $class = 'GD::Graph::' . $args->{type};
+
+    # Load that class or die if it does not exist
+    $class->require;
+
+    # Remember the class name for the view
+    $args->{class} = $class;
+
+    # Send them on to chart the chart
+    set 'args' => $args;
+    show 'chart/gd_graph'
+};
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::View>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and redistributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,68 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer - Base class for chart rendering classes
+
+=head1 SYNOPSIS
+
+In your F<config.yml>:
+
+  Plugins:
+    - Chart:
+        renderer: MyApp::Renderer;
+
+In F<lib/MyApp/Renderer.pm>:
+
+  package MyApp::Renderer;
+  use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+  sub render {
+      my $self = shift;
+      my %args = (
+          type   => 'points',
+          width  => 400,
+          height => 300,
+          data   => [],
+          @_,
+      );
+
+      # Output your chart
+      Jifty->web->out( #{ Output your chart here... } );
+
+      # You could also return it as a string...
+      return;
+  }
+
+=head1 METHODS
+
+Your renderer implementation must subclass this package and implement the following methods:
+
+=head2 render
+
+  Jifty->web->out($renderer->render(%args));
+
+See L<Jifty::Plugin::Chart::Web> for the arguments. It must (at least) accept the arguments given to the L<Jifty::Plugin::Chart::Web/chart> method.
+
+The C<render> method may either return it's output or print it out using L<Jifty::Web::out>.
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::Web>, L<Jifty::Plugin::Chart::Renderer::Chart>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/Chart.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/Chart.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,60 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer::Chart;
+use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+use Jifty::YAML;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer::Chart - A chart renderer using PNG charts
+
+=head1 DESCRIPTION
+
+This is the default chart renderer used by the L<Jifty::Plugin::Chart> plugin. It works by rendering an IMG tag in the HTML output, which then points to a URL which, when dispatched, retrieves the stored configuration and renders the chart using the L<Chart> package.
+
+=head1 METHODS
+
+=head2 render
+
+Implemented the L<Jifty::Plugin::Chart::Renderer/render> method interface.
+
+=cut
+
+sub render {
+    my $self = shift;
+    my %args = @_;
+
+    # Make sure the type is ready to be used as a class name
+    $args{type} = ucfirst lc $args{type};
+
+    # Save the data for retrieval from the session later
+    my $chart_id   = Jifty->web->serial;
+    my $session_id = 'chart_' . $chart_id;
+    Jifty->web->session->set( $session_id => Jifty::YAML::Dump(\%args) );
+
+    # Output the <img> tag and include the chart's configuration key
+    Jifty->web->out(qq{<img src="/chart/chart/$chart_id" width="$args{width}" height="$args{height}"/>});
+
+    # Make sure we don't return anything that will get output
+    return;
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart>, L<Jifty::Plugin::Chart::Renderer>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/GD/Graph.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/GD/Graph.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer::GD::Graph;
+use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer::GD::Graph - A chart renderer using GD::Graph
+
+=head1 SYNOPSIS
+
+In F<config.yml>:
+
+  Plugins:
+    - Chart:
+        renderer: Jifty::Plugin::Chart::Renderer::GD::Graph
+
+=head1 DESCRIPTION
+
+This is a chart renderer that uses L<GD::Graph> to build charts.
+
+=head1 METHODS
+
+=head2 render
+
+Renders an IMG tag referring to the L<GD::Graph> image view.
+
+=cut
+
+sub render {
+    my $self = shift;
+    my %args = @_;
+
+    # Convert the type to lowercase
+    $args{type} = lc $args{type};
+
+    # Save the data for retrieval from the session later
+    my $chart_id   = Jifty->web->serial;
+    my $session_id = 'chart_' . $chart_id;
+    Jifty->web->session->set( $session_id => Jifty::YAML::Dump(\%args) );
+
+    # Output the <img> tag and include the chart's configuration key
+    Jifty->web->out(qq{<img src="/chart/gd_graph/$chart_id" width="$args{width}" height="$args{height}"/>});
+
+    # Make sure we don't return anything that will get output
+    return;
+}
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/PlotKit.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Renderer/PlotKit.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,114 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer::PlotKit;
+use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+use Jifty::YAML;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer::PlotKit - A chart renderer using PlotKit
+
+=head1 DESCRIPTION
+
+This is an alternate chart renderer used by the L<Jifty::Plugin::Chart> plugin. It works by rendering a <div> tag in the HTML output and some JavaScript in a <script> tag.
+
+=head1 METHODS
+
+=head2 render
+
+Implemented the L<Jifty::Plugin::Chart::Renderer/render> method interface.
+
+=cut
+
+sub render {
+    my $self = shift;
+    my %args = ( options => {}, @_ );
+
+    # Turn any subs into values returned
+    for my $key (keys %args) {
+        $args{$key} = $args{$key}->(\%args) if ref $args{$key} eq 'CODE';
+    }
+
+    my %types = (
+        Lines   => 'line',
+        Bars    => 'bar',
+        Pie     => 'pie',
+    );
+
+    # Make sure the type is ready to be used
+    $args{type} = $types{ ucfirst lc $args{type} } || undef;
+
+    if ( not defined $args{type} ) {
+        Jifty->log->warn("Unsupported chart type: $args{type}!");
+        return;
+    }
+
+    $self->_transform_data( \%args );
+
+    # Save the data for retrieval from the session later
+    my $chart_id   = 'chart_' . Jifty->web->serial;
+
+    # Output the <canvas> tag and include the chart's JS
+    Jifty->web->out(<<"    END_OF_HTML");
+<div id="$chart_id" height="$args{height}" width="$args{width}"></div>
+
+<script type="text/javascript">
+var plot = function() {
+    var plotter = PlotKit.EasyPlot(
+        "$args{type}",
+        @{[Jifty::JSON::objToJson( $args{options} )]},
+        \$("$chart_id"),
+        @{[Jifty::JSON::objToJson( $args{data} )]}
+    );
+};
+YAHOO.util.Event.addListener( window, "load", plot );
+</script>
+    END_OF_HTML
+
+    # Make sure we don't return anything that will get output
+    return;
+}
+
+sub _transform_data {
+    my $self = shift;
+    my $args = shift;
+
+    my @data;
+    my $labels = shift @{ $args->{data} };
+
+    for ( my $i = 0; $i < @$labels; $i++ ) {
+        push @{$args->{options}{xTicks}}, { v => $i, label => $labels->[$i] }
+            if defined $labels->[$i];
+    }
+    
+    for my $dataset ( @{ $args->{data} } ) {
+        my @ds;
+        for ( my $i = 0; $i < @$dataset; $i++ ) {
+            # PlotKit can't deal with undefined values
+            push @ds, [ $i, defined $dataset->[$i] ? $dataset->[$i] : '0' ];
+        }
+        push @data, \@ds;
+    }
+
+    $args->{data} = \@data;
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart>, L<Jifty::Plugin::Chart::Renderer>
+
+=head1 AUTHOR
+
+Thomas Sibley
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Best Practical Solutions, LLC
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/View.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,88 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::View;
+use Jifty::View::Declare -base;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::View - Views for the renderers built into the Chart plugin
+
+=head1 TEMPLATES
+
+=head2 chart/chart
+
+This shows a chart using L<Chart>. It expects to find the arguments in the C<args> parameter, which is setup for it in L<Jifty::Plugin::Chart::Dispatcher>.
+
+This will output a PNG file unless there is an error building the chart.
+
+=cut
+
+template 'chart/chart' => sub {
+    # Load the arguments
+    my $args = get 'args';
+
+    # Set the output type to the PNG file type
+    Jifty->handler->apache->content_type('image/png');
+
+    # Render the chart and output the PNG file generated
+    eval {
+        my $chart = $args->{class}->new( $args->{width}, $args->{height} );
+        # XXX scalar_png() is undocumented!!! Might bad to rely upon.
+        outs_raw($chart->scalar_png($args->{data}));
+    };
+
+    # Should have thrown an error if bad stuff happened, handle that
+    if ($@) {
+        Jifty->log->error("Failed to render chart: $@");
+        die $@;
+    }
+};
+
+=head2 chart/gd_graph
+
+This shows a chart using L<GD::Graph>. It expects to find the arguments in the C<args> parameter, which is setup for it in L<Jifty::Plugin::Chart::Dispatcher>.
+
+This will output a PNG file unless there is an error building the chart.
+
+=cut
+
+template 'chart/gd_graph' => sub {
+    # Load the arguments
+    my $args = get 'args';
+
+    # Set the output type to the PNG file type
+    Jifty->handler->apache->content_type('image/png');
+
+    # Render the chart and output the PNG file generated
+    eval {
+        my $graph = $args->{class}->new( $args->{width}, $args->{height} );
+        my $gd    = $graph->plot($args->{data})
+            or die $graph->error;
+        outs_raw($gd->png);
+    };
+
+    # Should have thrown an error if bad stuff happened, handle that
+    if ($@) {
+        Jifty->log->error("Failed to render chart: $@");
+        die $@;
+    }
+};
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::Dispatcher>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Web.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Chart/Web.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,129 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Web;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Web - Base class to add to Jifty::Web's ISA
+
+=head1 DESCRIPTION
+
+When the L<Jifty::Plugin::Chart> is loaded, this class is added as a base class for L<Jifty::Web> to add the L</chart> method to that class.
+
+=head1 METHODS
+
+=head2 chart
+
+  Jifty->web->out(Jifty->web->chart(%args));
+
+The arguments passed in C<%args> may include:
+
+=over
+
+=item type
+
+This will be one of the following scalar values indicating the kind of chart:
+
+=over
+
+=item Points
+
+This is the default value. A scatter plot with each dataset represented using differnet dot styles.
+
+=item Lines
+
+A line plot with each dataset presented as separate line.
+
+=item Bars
+
+A bar chart with each dataset set side-by-side.
+
+=item StackedBars
+
+A bar chart with each dataset stacked on top of each other.
+
+=item Pie
+
+A pie chart with a single dataset representing the values for different pieces of the pie.
+
+=item HorizontalBars
+
+A bar chart turned sideways.
+
+=back
+
+=item width
+
+The width, in pixels, the chart should take on the page. Defaults to 400.
+
+=item height
+
+The height, in pixels, the chart should take on the page. Defaults to 300.
+
+=item data
+
+An array of arrays containing the data. The first array in the parent array is a list of labels. Each following array is the set of data points matching each label in the first array.
+
+Defaults to no data (i.e., it must be given if anything useful is to happen).
+
+=back
+
+Here's an example:
+
+  <% Jifty->web->chart(
+      type   => 'Pie',
+      width  => 400,
+      height => 300,
+      data   => sub {
+          [
+              [ 2004, 2005, 2006, 2007 ],
+              [ 26, 37, 12, 42 ]
+          ];
+      },
+  ) %>
+
+Be sure to output anything returned by the method (unless it returns undef).
+
+=cut
+
+sub chart {
+    my $self = shift;
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Chart');
+
+    # TODO It might be a good idea to make this config.yml-able
+    # Setup the defaults
+    my %args = (
+        type   => 'points',
+        width  => 400,
+        height => 300,
+        data   => [],
+        @_,
+    );
+
+    # Turn any subs into values returned
+    for my $key (keys %args) {
+        $args{$key} = $args{$key}->(\%args) if ref $args{$key} eq 'CODE';
+    }
+
+    # Call the rendering plugin's render method
+    return $plugin->renderer->render(%args);
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart>, L<Jifty::Plugin::Chart::Renderer>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/GoogleMap/Widget.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/GoogleMap/Widget.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/GoogleMap/Widget.pm	Wed Aug  1 12:25:47 2007
@@ -14,6 +14,13 @@
 
 =cut
 
+=head2 accessors
+
+Returns this class's Clas::Accessor based accessors
+
+=cut
+
+
 sub accessors { shift->SUPER::accessors() };
 
 =head2 render_widget

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/REST/Dispatcher.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/REST/Dispatcher.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/REST/Dispatcher.pm	Wed Aug  1 12:25:47 2007
@@ -215,6 +215,11 @@
     elsif ($accept =~ /j(?:ava)?s|ecmascript/i) {
         $apache->header_out('Content-Type' => 'application/javascript; charset=UTF-8');
         $apache->send_http_header;
+	# XXX: temporary hack to fix _() that aren't respected by json dumper
+	for (values %{$_[0]}) {
+	    $_->{label} = "$_->{label}" if exists $_->{label} && defined ref $_->{label};
+	    $_->{hints} = "$_->{hints}" if exists $_->{hints} && defined ref $_->{hints};
+	}
         print 'var $_ = ', Jifty::JSON::objToJson( @_, { singlequote => 1 } );
     }
     elsif ($accept =~ qr{^(?:application/x-)?(?:perl|pl)$}i) {
@@ -592,6 +597,7 @@
     label
     hints
     mandatory
+    ajax_validates
     length
 );
 

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm	Wed Aug  1 12:25:47 2007
@@ -42,7 +42,7 @@
         Jifty->web->navigation->render_as_menu; };
 };
 
-template '__jifty/empty' => sub {
+template '__jifty/empty' => sub :Static {
         '';
 };
 

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/UUID.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/UUID.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,46 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::UUID;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
+
+=head1 NAME
+
+Jifty::Plugin::UUID
+
+=head1 SYNOPSIS
+
+In your model class schema description, add the following:
+
+    column photo => is UUID;
+
+
+=head1 DESCRIPTION
+
+This plugin provides user pictures for Jifty;
+
+
+=cut
+
+use Jifty::DBI::Schema;
+use Data::UUID;
+use Scalar::Defer;
+my $UUID_GEN = Data::UUID->new();
+
+my $UUID = lazy { $UUID_GEN->create_str() } ;
+sub _uuid {
+    my ($column, $from) = @_;
+    $column->readable(1);
+    $column->writable(1);
+    $column->default($UUID);
+    $column->type('varchar(32)');
+}
+
+Jifty::DBI::Schema->register_types(
+    UUID => sub {  _init_handler is \&_uuid,  render_as 'Jifty::Plugin::UUID::Widget'},
+);
+
+
+
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/UUID/Widget.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/UUID/Widget.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,43 @@
+use strict;
+use warnings;
+
+
+package Jifty::Plugin::UUID::Widget;
+
+use base qw/Jifty::Web::Form::Field/;
+
+=head1 NAME
+
+Jifty::Plugin::UUID::Widget - 
+
+=head1 METHODS
+
+
+=cut
+
+sub accessors { shift->SUPER::accessors() };
+
+=head2 render_widget
+
+Renders form fields as a uuid;
+
+=cut
+
+sub render_widget {
+    warn "Rendering form field";
+    my $self     = shift;
+    my $action   = $self->action;
+    my $readonly = 1;
+    
+        my $name = $self->name;
+    if ( $action->record->$name() ) {
+        Jifty->web->out(  $action->record->$name() );
+    } else { 
+        Jifty->web->out("<i>"._('No value yet')."</i>");
+
+    }
+    '';
+}
+
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/BaseClass.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/BaseClass.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/BaseClass.pm	Wed Aug  1 12:25:47 2007
@@ -4,8 +4,7 @@
 use warnings;
 use base qw/Exporter Jifty::View::Declare::Helpers/;
 use Scalar::Defer;
-use Template::Declare::Tags;
-
+use PadWalker;
 
 use Jifty::View::Declare::Helpers;
 
@@ -54,6 +53,40 @@
     }
 }
 
+sub _actual_td_code {
+    my $class = shift;
+    my $path = shift;
+    my $code = Template::Declare->resolve_template($path) or return;
+    my $closed_over = PadWalker::closed_over($code)->{'$coderef'};
+    return $closed_over ? $$closed_over : $code;
+}
+
+use Attribute::Handlers;
+my (%Static, %Action);
+sub Static :ATTR(CODE,BEGIN) {
+    $Static{$_[2]}++;
+}
+
+sub Action :ATTR(CODE,BEGIN) {
+    $Action{$_[2]}++;
+}
+
+=head2 client_cacheable
+
+Returns the type of cacheable object for client
+
+=cut
+
+sub client_cacheable {
+    my $self = shift;
+    my $path = shift;
+    my $code = $self->_actual_td_code($path) or return;
+
+    return 'static' if $Static{$code};
+    return 'action' if $Action{$code};
+    return;
+}
+
 
 =head2 show templatename arguments
 

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm	Wed Aug  1 12:25:47 2007
@@ -4,6 +4,14 @@
 package Jifty::View::Declare::CRUD;
 use Jifty::View::Declare -base;
 
+# XXX: should register 'template type' handler, so the
+# client_cache_content & the TD sub here agrees with the arguments.
+use Attribute::Handlers;
+my %VIEW;
+sub CRUDView :ATTR(CODE,BEGIN) {
+    $VIEW{$_[2]}++;
+}
+
 
 =head1 NAME
 
@@ -38,6 +46,19 @@
     *{$vclass."::object_type"} = sub { $model };
 }
 
+sub _dispatch_template {
+    my $class = shift;
+    my $code  = shift;
+    if ($VIEW{$code} && !UNIVERSAL::isa($_[0], 'Evil')) {
+	my ( $object_type, $id ) = ( $class->object_type, get('id') );
+	@_ = ($class, $class->_get_record($id), @_);
+    }
+    else {
+	unshift @_, $class;
+    }
+    goto $code;
+}
+
 
 =head2 object_type
 
@@ -163,26 +184,23 @@
         }
 };
 
-
 =head2 view
 
 This template displays the data held by a single model record.
 
 =cut
 
-template 'view' => sub {
-    my $self = shift;
-    my ( $object_type, $id ) = ( $self->object_type, get('id') );
-      my $record =   $self->_get_record($id);
+template 'view' => sub :CRUDView {
+    my ($self, $record) = @_;
     my $update = new_action(
-        class   => 'Update' . $object_type,
+        class   => 'Update' . $self->object_type,
         moniker => "update-" . Jifty->web->serial,
-        record  => $record 
+        record  => $record,
     );
 
     div {
         { class is 'crud read item inline' };
-        my @fields =$self->display_columns($update);
+        my @fields = $self->display_columns($update);
         render_action( $update, \@fields, { render_mode => 'read' } );
 
         show ('./view_item_controls', $record, $update); 
@@ -202,7 +220,7 @@
             class   => "editlink",
             onclick => {
                 replace_with => $self->fragment_for('update'),
-                args         => { object_type => $self->object_type, id => $record->id }
+                args         => { id => $record->id }
             },
         );
     }
@@ -317,6 +335,13 @@
 
 };
 
+=head2 per_page
+
+This routine returns how many items should be shown on each page of a listing.
+The default is 25.
+
+=cut
+
 sub per_page { 25 }
 
 sub _current_collection {

Added: jifty/branches/virtual-models/lib/Jifty/View/Declare/Compile.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Compile.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,210 @@
+package Jifty::View::Declare::Compile;
+use strict;
+use base 'B::Deparse';
+use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
+	 OPf_WANT OPf_WANT_VOID OPf_WANT_SCALAR OPf_WANT_LIST
+	 OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL OPf_MOD
+	 OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
+	 OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY
+	 OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER
+	 OPpSORT_REVERSE OPpSORT_INPLACE OPpSORT_DESCEND OPpITER_REVERSED
+	 SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG
+         CVf_METHOD CVf_LOCKED CVf_LVALUE CVf_ASSERTION
+	 PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE PMf_SKIPWHITE
+	 PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED);
+BEGIN {
+    die "You need a custom version of B::Deparse from http://svn.jifty.org/svn/jifty.org/B/"
+        unless B::Deparse->can('e_method')
+}
+sub is_scope { goto \&B::Deparse::is_scope }
+sub is_state { goto \&B::Deparse::is_state }
+sub null { goto \&B::Deparse::null }
+
+sub padname {
+    my $self = shift;
+    my $targ = shift;
+    return substr($self->padname_sv($targ)->PVX, 1);
+}
+
+require CGI;
+our %TAGS = (
+    map { $_ => +{} }
+        map {@{$_||[]}} @CGI::EXPORT_TAGS{qw/:html2 :html3 :html4 :netscape :form/}
+);
+
+sub deparse {
+    my $self = shift;
+    my $ret = $self->SUPER::deparse(@_);
+    return '' if $ret =~ m/use (strict|warnings)/;
+    return $ret;
+}
+
+sub loop_common {
+    my $self = shift;
+    my($op, $cx, $init) = @_;
+    my $enter = $op->first;
+    my $kid = $enter->sibling;
+    if ($enter->name eq "enteriter") { # foreach
+	my $ary = $enter->first->sibling; # first was pushmark
+	my $var = $ary->sibling;
+
+	if ($ary->name eq 'null' and $enter->private & OPpITER_REVERSED) {
+	    # "reverse" was optimised away
+	    return $self->SUPER::loop_common(@_);
+	} elsif ($enter->flags & OPf_STACKED
+	    and not null $ary->first->sibling->sibling)
+	{
+	    return $self->SUPER::loop_common(@_);
+	} else {
+	    $ary = $self->deparse($ary, 1);
+	}
+
+	if (null $var) {
+	    if ($enter->flags & OPf_SPECIAL) { # thread special var
+		$var = $self->pp_threadsv($enter, 1);
+	    } else { # regular my() variable
+		$var = $self->padname($enter->targ);
+	    }
+	} elsif ($var->name eq "rv2gv") {
+	    $var = $self->pp_rv2sv($var, 1);
+	    if ($enter->private & OPpOUR_INTRO) {
+		# our declarations don't have package names
+		$var =~ s/^(.).*::/$1/;
+		$var = "our $var";
+	    }
+	} elsif ($var->name eq "gv") {
+	    $var = $self->deparse($var, 1);
+	    $var = '$' . $var if $var eq '_';
+	}
+	else {
+	    return $self->SUPER::loop_common(@_);
+	}
+
+
+	my $body = $kid->first->first->sibling; # skip OP_AND and OP_ITER
+	# statement() foreach (@foo);
+	if (!is_state $body->first and $body->first->name ne "stub") {
+	    Carp::confess unless $var eq '$_';
+	    $body = $body->first;
+	    return "$ary.each(function (\$_) {".$self->deparse($body, 2)."} )";
+	}
+	# XXX not handling cont block here yet
+	return "$ary.each(function ($var) {".$self->deparse($body, 0)."} )";
+    }
+    return $self->SUPER::loop_common(@_);
+}
+
+sub maybe_my {
+    my $self = shift;
+    my($op, $cx, $text) = @_;
+    if ($op->private & OPpLVAL_INTRO and not $self->{'avoid_local'}{$$op}) {
+	if (B::Deparse::want_scalar($op)) {
+	    return "var $text";
+	} else {
+	    return $self->maybe_parens_func("my", $text, $cx, 16);
+	}
+    } else {
+	return $text;
+    }
+}
+
+sub maybe_parens_func {
+    my $self = shift;
+    my($func, $text, $cx, $prec) = @_;
+    return "$func($text)";
+
+}
+
+sub const {
+    my $self = shift;
+    my($sv, $cx) = @_;
+    if (class($sv) eq "NULL") {
+       return 'null';
+    }
+    return $self->SUPER::const(@_);
+}
+
+sub pp_undef { 'null' }
+sub pp_sne { shift->binop(@_, "!=", 14) }
+sub pp_grepwhile { shift->mapop(@_, "grep") }
+
+sub mapop {
+    my $self = shift;
+    my($op, $cx, $name) = @_;
+    return $self->SUPER::mapop(@_) unless $name eq 'grep';
+    my($expr, @exprs);
+    my $kid = $op->first; # this is the (map|grep)start
+    $kid = $kid->first->sibling; # skip a pushmark
+    my $code = $kid->first; # skip a null
+    if (is_scope $code) {
+	$code = "{" . $self->deparse($code, 0) . "} ";
+    } else {
+	$code = $self->deparse($code, 24) . ", ";
+    }
+    $kid = $kid->sibling;
+    for (; !null($kid); $kid = $kid->sibling) {
+	$expr = $self->deparse($kid, 6);
+	push @exprs, $expr if defined $expr;
+    }
+    return "(".join(", ", @exprs).").select(function (\$_) $code)";
+}
+
+sub e_anoncode {
+    my ($self, $info) = @_;
+    my $text = $self->deparse_sub($info->{code});
+    return "function () " . $text;
+}
+
+sub e_anonhash {
+    my ($self, $info) = @_;
+    my @exprs = @{$info->{exprs}};
+    my @pairs;
+    while (my @p = splice(@exprs, 0, 2)) {
+	push @pairs, join(': ', map { $self->deparse($_, 6) } @p);
+    }
+    return '{' . join(", ", @pairs) . '}';
+}
+
+sub pp_entersub {
+    my $self = shift;
+    my $ret = $self->SUPER::pp_entersub(@_);
+    $ret =~ s/return\s*\((.*)\)/return [$1]/ if $ret =~ m/^attr/;
+
+    return $ret;
+}
+
+sub e_method {
+    my ($self, $info) = @_;
+    my $obj = $info->{object};
+    if ($obj->name eq 'const') {
+        $obj = $self->const_sv($obj)->PV;
+    }
+    else {
+        $obj = $self->deparse($obj, 24);
+    }
+
+    my $meth = $info->{method};
+    $meth = $self->deparse($meth, 1) if $info->{variable_method};
+    my $args = join(", ", map { $self->deparse($_, 6) } @{$info->{args}} );
+    my $kid = $obj . "." . $meth;
+    return $kid . "(" . $args . ")"; # parens mandatory
+}
+
+sub walk_lineseq {
+    my ($self, $op, $kids, $callback) = @_;
+    my $xcallback = $callback;
+    if ((!$op || $op->next->name eq 'grepwhile') && $kids->[-1]->name ne 'return') {
+	$callback = sub { my ($expr, $index) = @_;
+			  $expr = "return ($expr)" if $index == $#{$kids};
+			  $xcallback->($expr, $index) };
+    }
+    $self->SUPER::walk_lineseq($op, $kids, $callback);
+}
+
+sub compile_to_js {
+    my $class = shift;
+    my $code = shift;
+    return 'function() '.$class->new->coderef2text($code);
+}
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm	Wed Aug  1 12:25:47 2007
@@ -2,10 +2,10 @@
 use strict;
 
 package Jifty::View::Declare::Helpers;
-use base qw/Template::Declare Exporter/;
 use Template::Declare::Tags;
+use base qw/Template::Declare Exporter/;
 
-our @EXPORT = ( qw(form hyperlink tangent redirect new_action form_submit form_return form_next_page page content wrapper request get set render_param current_user render_action render_region), @Template::Declare::Tags::EXPORT);
+our @EXPORT = ( qw(hyperlink tangent redirect new_action form_submit form_return form_next_page page content wrapper request get set render_param current_user render_action render_region), @Template::Declare::Tags::EXPORT);
 
 =head1 NAME
 
@@ -29,7 +29,7 @@
 
  {
     no warnings qw/redefine/;
-    sub form (&) {
+    sub form (&;$) {
         my $code = shift;
 
         smart_tag_wrapper {

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm	Wed Aug  1 12:25:47 2007
@@ -2,7 +2,6 @@
 use strict;
 use warnings;
 use base qw/Template::Declare Class::Accessor::Fast/;
-use Template::Declare::Tags;
 
 =head1 NAME
 

Modified: jifty/branches/virtual-models/lib/Jifty/Web.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web.pm	Wed Aug  1 12:25:47 2007
@@ -50,6 +50,7 @@
     scriptaculous/effects.js
     scriptaculous/controls.js
     formatDate.js
+    template_declare.js
     jifty.js
     jifty_utils.js
     jifty_subs.js

Modified: jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm	Wed Aug  1 12:25:47 2007
@@ -384,4 +384,30 @@
     return "#region-" . $self->qualified_name . ' ' . join(' ', @_);
 }
 
+my $can_compile = eval 'use Jifty::View::Declare::Compile; 1' ? 1 : 0;
+
+=head2 client_cacheable
+
+=cut
+
+sub client_cacheable {
+    my $self = shift;
+    return unless $can_compile;
+
+    return Jifty::View::Declare::BaseClass->client_cacheable($self->path);
+}
+
+=head2 client_cacheable
+
+=cut
+
+sub client_cache_content {
+    my $self = shift;
+    return unless $can_compile;
+
+    return Jifty::View::Declare::Compile->compile_to_js(
+        Jifty::View::Declare::BaseClass->_actual_td_code($self->path)
+    );
+}
+
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Web/Session.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/Session.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/Session.pm	Wed Aug  1 12:25:47 2007
@@ -76,6 +76,44 @@
     $self->{cache} = undef;
 }
 
+=head2 load_by_kv key => value OR key, default, callback
+
+Load up the current session from the given (key, value) pair. If no matching
+session could be found, it will create a new session with the key, value set.
+Be sure that what you're loading by is unique. If you're loading a session
+based on, say, a timestamp, then you're asking for trouble.
+
+The second form is used when you have a complex value. Each value for the
+given key is passed to the callback. The callback should return true if
+there's a match. The default value is used to create a new entry when no
+value was chosen.
+
+=cut
+
+sub load_by_kv {
+    my $self = shift;
+    my $k = shift;
+    my $v = shift;
+    my $callback = shift || sub { $_[0] eq $v };
+    my $session_id;
+
+    # tried doing this with load_by_cols but it never returned any rows
+    my $sessions = Jifty::Model::SessionCollection->new;
+    $sessions->limit(column => 'key_type', value => 'key');
+    $sessions->limit(column => 'data_key', value => $k );
+
+    while (my $row = $sessions->next) {
+        my $match = $callback->($row->value);
+        if ($match) {
+            $session_id = $row->session_id;
+            last;
+        }
+    }
+
+    $self->load($session_id);
+    $self->set($k => $v) if !$session_id;
+}
+
 sub _get_session_id_from_client {
         my $self = shift;
         my %cookies    = CGI::Cookie->fetch();

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/MochiKit.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/MochiKit.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,4802 @@
+/***
+
+    MochiKit.MochiKit 1.3.1 : PACKED VERSION
+
+    THIS FILE IS AUTOMATICALLY GENERATED.  If creating patches, please
+    diff against the source tree, not this file.
+
+    See <http://mochikit.com/> for documentation, downloads, license, etc.
+
+    (c) 2005 Bob Ippolito.  All rights Reserved.
+
+***/
+
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Base");
+}
+if(typeof (MochiKit)=="undefined"){
+MochiKit={};
+}
+if(typeof (MochiKit.Base)=="undefined"){
+MochiKit.Base={};
+}
+MochiKit.Base.VERSION="1.3.1";
+MochiKit.Base.NAME="MochiKit.Base";
+MochiKit.Base.update=function(_1,_2){
+if(_1===null){
+_1={};
+}
+for(var i=1;i<arguments.length;i++){
+var o=arguments[i];
+if(typeof (o)!="undefined"&&o!==null){
+for(var k in o){
+_1[k]=o[k];
+}
+}
+}
+return _1;
+};
+MochiKit.Base.update(MochiKit.Base,{__repr__:function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+},toString:function(){
+return this.__repr__();
+},counter:function(n){
+if(arguments.length===0){
+n=1;
+}
+return function(){
+return n++;
+};
+},clone:function(_7){
+var me=arguments.callee;
+if(arguments.length==1){
+me.prototype=_7;
+return new me();
+}
+},flattenArguments:function(_9){
+var res=[];
+var m=MochiKit.Base;
+var _12=m.extend(null,arguments);
+while(_12.length){
+var o=_12.shift();
+if(o&&typeof (o)=="object"&&typeof (o.length)=="number"){
+for(var i=o.length-1;i>=0;i--){
+_12.unshift(o[i]);
+}
+}else{
+res.push(o);
+}
+}
+return res;
+},extend:function(_13,obj,_15){
+if(!_15){
+_15=0;
+}
+if(obj){
+var l=obj.length;
+if(typeof (l)!="number"){
+if(typeof (MochiKit.Iter)!="undefined"){
+obj=MochiKit.Iter.list(obj);
+l=obj.length;
+}else{
+throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
+}
+}
+if(!_13){
+_13=[];
+}
+for(var i=_15;i<l;i++){
+_13.push(obj[i]);
+}
+}
+return _13;
+},updatetree:function(_17,obj){
+if(_17===null){
+_17={};
+}
+for(var i=1;i<arguments.length;i++){
+var o=arguments[i];
+if(typeof (o)!="undefined"&&o!==null){
+for(var k in o){
+var v=o[k];
+if(typeof (_17[k])=="object"&&typeof (v)=="object"){
+arguments.callee(_17[k],v);
+}else{
+_17[k]=v;
+}
+}
+}
+}
+return _17;
+},setdefault:function(_19,obj){
+if(_19===null){
+_19={};
+}
+for(var i=1;i<arguments.length;i++){
+var o=arguments[i];
+for(var k in o){
+if(!(k in _19)){
+_19[k]=o[k];
+}
+}
+}
+return _19;
+},keys:function(obj){
+var _20=[];
+for(var _21 in obj){
+_20.push(_21);
+}
+return _20;
+},items:function(obj){
+var _22=[];
+var e;
+for(var _24 in obj){
+var v;
+try{
+v=obj[_24];
+}
+catch(e){
+continue;
+}
+_22.push([_24,v]);
+}
+return _22;
+},_newNamedError:function(_25,_26,_27){
+_27.prototype=new MochiKit.Base.NamedError(_25.NAME+"."+_26);
+_25[_26]=_27;
+},operator:{truth:function(a){
+return !!a;
+},lognot:function(a){
+return !a;
+},identity:function(a){
+return a;
+},not:function(a){
+return ~a;
+},neg:function(a){
+return -a;
+},add:function(a,b){
+return a+b;
+},sub:function(a,b){
+return a-b;
+},div:function(a,b){
+return a/b;
+},mod:function(a,b){
+return a%b;
+},mul:function(a,b){
+return a*b;
+},and:function(a,b){
+return a&b;
+},or:function(a,b){
+return a|b;
+},xor:function(a,b){
+return a^b;
+},lshift:function(a,b){
+return a<<b;
+},rshift:function(a,b){
+return a>>b;
+},zrshift:function(a,b){
+return a>>>b;
+},eq:function(a,b){
+return a==b;
+},ne:function(a,b){
+return a!=b;
+},gt:function(a,b){
+return a>b;
+},ge:function(a,b){
+return a>=b;
+},lt:function(a,b){
+return a<b;
+},le:function(a,b){
+return a<=b;
+},ceq:function(a,b){
+return MochiKit.Base.compare(a,b)===0;
+},cne:function(a,b){
+return MochiKit.Base.compare(a,b)!==0;
+},cgt:function(a,b){
+return MochiKit.Base.compare(a,b)==1;
+},cge:function(a,b){
+return MochiKit.Base.compare(a,b)!=-1;
+},clt:function(a,b){
+return MochiKit.Base.compare(a,b)==-1;
+},cle:function(a,b){
+return MochiKit.Base.compare(a,b)!=1;
+},logand:function(a,b){
+return a&&b;
+},logor:function(a,b){
+return a||b;
+},contains:function(a,b){
+return b in a;
+}},forwardCall:function(_30){
+return function(){
+return this[_30].apply(this,arguments);
+};
+},itemgetter:function(_31){
+return function(arg){
+return arg[_31];
+};
+},typeMatcher:function(){
+var _33={};
+for(var i=0;i<arguments.length;i++){
+var typ=arguments[i];
+_33[typ]=typ;
+}
+return function(){
+for(var i=0;i<arguments.length;i++){
+if(!(typeof (arguments[i]) in _33)){
+return false;
+}
+}
+return true;
+};
+},isNull:function(){
+for(var i=0;i<arguments.length;i++){
+if(arguments[i]!==null){
+return false;
+}
+}
+return true;
+},isUndefinedOrNull:function(){
+for(var i=0;i<arguments.length;i++){
+var o=arguments[i];
+if(!(typeof (o)=="undefined"||o===null)){
+return false;
+}
+}
+return true;
+},isEmpty:function(obj){
+return !MochiKit.Base.isNotEmpty.apply(this,arguments);
+},isNotEmpty:function(obj){
+for(var i=0;i<arguments.length;i++){
+var o=arguments[i];
+if(!(o&&o.length)){
+return false;
+}
+}
+return true;
+},isArrayLike:function(){
+for(var i=0;i<arguments.length;i++){
+var o=arguments[i];
+var typ=typeof (o);
+if((typ!="object"&&!(typ=="function"&&typeof (o.item)=="function"))||o===null||typeof (o.length)!="number"){
+return false;
+}
+}
+return true;
+},isDateLike:function(){
+for(var i=0;i<arguments.length;i++){
+var o=arguments[i];
+if(typeof (o)!="object"||o===null||typeof (o.getTime)!="function"){
+return false;
+}
+}
+return true;
+},xmap:function(fn){
+if(fn===null){
+return MochiKit.Base.extend(null,arguments,1);
+}
+var _36=[];
+for(var i=1;i<arguments.length;i++){
+_36.push(fn(arguments[i]));
+}
+return _36;
+},map:function(fn,lst){
+var m=MochiKit.Base;
+var itr=MochiKit.Iter;
+var _39=m.isArrayLike;
+if(arguments.length<=2){
+if(!_39(lst)){
+if(itr){
+lst=itr.list(lst);
+if(fn===null){
+return lst;
+}
+}else{
+throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
+}
+}
+if(fn===null){
+return m.extend(null,lst);
+}
+var _40=[];
+for(var i=0;i<lst.length;i++){
+_40.push(fn(lst[i]));
+}
+return _40;
+}else{
+if(fn===null){
+fn=Array;
+}
+var _41=null;
+for(i=1;i<arguments.length;i++){
+if(!_39(arguments[i])){
+if(itr){
+return itr.list(itr.imap.apply(null,arguments));
+}else{
+throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
+}
+}
+var l=arguments[i].length;
+if(_41===null||_41>l){
+_41=l;
+}
+}
+_40=[];
+for(i=0;i<_41;i++){
+var _42=[];
+for(var j=1;j<arguments.length;j++){
+_42.push(arguments[j][i]);
+}
+_40.push(fn.apply(this,_42));
+}
+return _40;
+}
+},xfilter:function(fn){
+var _44=[];
+if(fn===null){
+fn=MochiKit.Base.operator.truth;
+}
+for(var i=1;i<arguments.length;i++){
+var o=arguments[i];
+if(fn(o)){
+_44.push(o);
+}
+}
+return _44;
+},filter:function(fn,lst,_45){
+var _46=[];
+var m=MochiKit.Base;
+if(!m.isArrayLike(lst)){
+if(MochiKit.Iter){
+lst=MochiKit.Iter.list(lst);
+}else{
+throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
+}
+}
+if(fn===null){
+fn=m.operator.truth;
+}
+if(typeof (Array.prototype.filter)=="function"){
+return Array.prototype.filter.call(lst,fn,_45);
+}else{
+if(typeof (_45)=="undefined"||_45===null){
+for(var i=0;i<lst.length;i++){
+var o=lst[i];
+if(fn(o)){
+_46.push(o);
+}
+}
+}else{
+for(i=0;i<lst.length;i++){
+o=lst[i];
+if(fn.call(_45,o)){
+_46.push(o);
+}
+}
+}
+}
+return _46;
+},_wrapDumbFunction:function(_47){
+return function(){
+switch(arguments.length){
+case 0:
+return _47();
+case 1:
+return _47(arguments[0]);
+case 2:
+return _47(arguments[0],arguments[1]);
+case 3:
+return _47(arguments[0],arguments[1],arguments[2]);
+}
+var _48=[];
+for(var i=0;i<arguments.length;i++){
+_48.push("arguments["+i+"]");
+}
+return eval("(func("+_48.join(",")+"))");
+};
+},method:function(_49,_50){
+var m=MochiKit.Base;
+return m.bind.apply(this,m.extend([_50,_49],arguments,2));
+},bind:function(_51,_52){
+if(typeof (_51)=="string"){
+_51=_52[_51];
+}
+var _53=_51.im_func;
+var _54=_51.im_preargs;
+var _55=_51.im_self;
+var m=MochiKit.Base;
+if(typeof (_51)=="function"&&typeof (_51.apply)=="undefined"){
+_51=m._wrapDumbFunction(_51);
+}
+if(typeof (_53)!="function"){
+_53=_51;
+}
+if(typeof (_52)!="undefined"){
+_55=_52;
+}
+if(typeof (_54)=="undefined"){
+_54=[];
+}else{
+_54=_54.slice();
+}
+m.extend(_54,arguments,2);
+var _56=function(){
+var _57=arguments;
+var me=arguments.callee;
+if(me.im_preargs.length>0){
+_57=m.concat(me.im_preargs,_57);
+}
+var _52=me.im_self;
+if(!_52){
+_52=this;
+}
+return me.im_func.apply(_52,_57);
+};
+_56.im_self=_55;
+_56.im_func=_53;
+_56.im_preargs=_54;
+return _56;
+},bindMethods:function(_58){
+var _59=MochiKit.Base.bind;
+for(var k in _58){
+var _60=_58[k];
+if(typeof (_60)=="function"){
+_58[k]=_59(_60,_58);
+}
+}
+},registerComparator:function(_61,_62,_63,_64){
+MochiKit.Base.comparatorRegistry.register(_61,_62,_63,_64);
+},_primitives:{"boolean":true,"string":true,"number":true},compare:function(a,b){
+if(a==b){
+return 0;
+}
+var _65=(typeof (a)=="undefined"||a===null);
+var _66=(typeof (b)=="undefined"||b===null);
+if(_65&&_66){
+return 0;
+}else{
+if(_65){
+return -1;
+}else{
+if(_66){
+return 1;
+}
+}
+}
+var m=MochiKit.Base;
+var _67=m._primitives;
+if(!(typeof (a) in _67&&typeof (b) in _67)){
+try{
+return m.comparatorRegistry.match(a,b);
+}
+catch(e){
+if(e!=m.NotFound){
+throw e;
+}
+}
+}
+if(a<b){
+return -1;
+}else{
+if(a>b){
+return 1;
+}
+}
+var _68=m.repr;
+throw new TypeError(_68(a)+" and "+_68(b)+" can not be compared");
+},compareDateLike:function(a,b){
+return MochiKit.Base.compare(a.getTime(),b.getTime());
+},compareArrayLike:function(a,b){
+var _69=MochiKit.Base.compare;
+var _70=a.length;
+var _71=0;
+if(_70>b.length){
+_71=1;
+_70=b.length;
+}else{
+if(_70<b.length){
+_71=-1;
+}
+}
+for(var i=0;i<_70;i++){
+var cmp=_69(a[i],b[i]);
+if(cmp){
+return cmp;
+}
+}
+return _71;
+},registerRepr:function(_73,_74,_75,_76){
+MochiKit.Base.reprRegistry.register(_73,_74,_75,_76);
+},repr:function(o){
+if(typeof (o)=="undefined"){
+return "undefined";
+}else{
+if(o===null){
+return "null";
+}
+}
+try{
+if(typeof (o.__repr__)=="function"){
+return o.__repr__();
+}else{
+if(typeof (o.repr)=="function"&&o.repr!=arguments.callee){
+return o.repr();
+}
+}
+return MochiKit.Base.reprRegistry.match(o);
+}
+catch(e){
+if(typeof (o.NAME)=="string"&&(o.toString==Function.prototype.toString||o.toString==Object.prototype.toString)){
+return o.NAME;
+}
+}
+try{
+var _77=(o+"");
+}
+catch(e){
+return "["+typeof (o)+"]";
+}
+if(typeof (o)=="function"){
+o=_77.replace(/^\s+/,"");
+var idx=o.indexOf("{");
+if(idx!=-1){
+o=o.substr(0,idx)+"{...}";
+}
+}
+return _77;
+},reprArrayLike:function(o){
+var m=MochiKit.Base;
+return "["+m.map(m.repr,o).join(", ")+"]";
+},reprString:function(o){
+return ("\""+o.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r");
+},reprNumber:function(o){
+return o+"";
+},registerJSON:function(_79,_80,_81,_82){
+MochiKit.Base.jsonRegistry.register(_79,_80,_81,_82);
+},evalJSON:function(){
+return eval("("+arguments[0]+")");
+},serializeJSON:function(o){
+var _83=typeof (o);
+if(_83=="undefined"){
+return "undefined";
+}else{
+if(_83=="number"||_83=="boolean"){
+return o+"";
+}else{
+if(o===null){
+return "null";
+}
+}
+}
+var m=MochiKit.Base;
+var _84=m.reprString;
+if(_83=="string"){
+return _84(o);
+}
+var me=arguments.callee;
+var _85;
+if(typeof (o.__json__)=="function"){
+_85=o.__json__();
+if(o!==_85){
+return me(_85);
+}
+}
+if(typeof (o.json)=="function"){
+_85=o.json();
+if(o!==_85){
+return me(_85);
+}
+}
+if(_83!="function"&&typeof (o.length)=="number"){
+var res=[];
+for(var i=0;i<o.length;i++){
+var val=me(o[i]);
+if(typeof (val)!="string"){
+val="undefined";
+}
+res.push(val);
+}
+return "["+res.join(", ")+"]";
+}
+try{
+_85=m.jsonRegistry.match(o);
+return me(_85);
+}
+catch(e){
+if(e!=m.NotFound){
+throw e;
+}
+}
+if(_83=="function"){
+return null;
+}
+res=[];
+for(var k in o){
+var _87;
+if(typeof (k)=="number"){
+_87="\""+k+"\"";
+}else{
+if(typeof (k)=="string"){
+_87=_84(k);
+}else{
+continue;
+}
+}
+val=me(o[k]);
+if(typeof (val)!="string"){
+continue;
+}
+res.push(_87+":"+val);
+}
+return "{"+res.join(", ")+"}";
+},objEqual:function(a,b){
+return (MochiKit.Base.compare(a,b)===0);
+},arrayEqual:function(_88,arr){
+if(_88.length!=arr.length){
+return false;
+}
+return (MochiKit.Base.compare(_88,arr)===0);
+},concat:function(){
+var _90=[];
+var _91=MochiKit.Base.extend;
+for(var i=0;i<arguments.length;i++){
+_91(_90,arguments[i]);
+}
+return _90;
+},keyComparator:function(key){
+var m=MochiKit.Base;
+var _93=m.compare;
+if(arguments.length==1){
+return function(a,b){
+return _93(a[key],b[key]);
+};
+}
+var _94=m.extend(null,arguments);
+return function(a,b){
+var _95=0;
+for(var i=0;(_95===0)&&(i<_94.length);i++){
+var key=_94[i];
+_95=_93(a[key],b[key]);
+}
+return _95;
+};
+},reverseKeyComparator:function(key){
+var _96=MochiKit.Base.keyComparator.apply(this,arguments);
+return function(a,b){
+return _96(b,a);
+};
+},partial:function(_97){
+var m=MochiKit.Base;
+return m.bind.apply(this,m.extend([_97,undefined],arguments,1));
+},listMinMax:function(_98,lst){
+if(lst.length===0){
+return null;
+}
+var cur=lst[0];
+var _100=MochiKit.Base.compare;
+for(var i=1;i<lst.length;i++){
+var o=lst[i];
+if(_100(o,cur)==_98){
+cur=o;
+}
+}
+return cur;
+},objMax:function(){
+return MochiKit.Base.listMinMax(1,arguments);
+},objMin:function(){
+return MochiKit.Base.listMinMax(-1,arguments);
+},findIdentical:function(lst,_101,_102,end){
+if(typeof (end)=="undefined"||end===null){
+end=lst.length;
+}
+for(var i=(_102||0);i<end;i++){
+if(lst[i]===_101){
+return i;
+}
+}
+return -1;
+},findValue:function(lst,_104,_105,end){
+if(typeof (end)=="undefined"||end===null){
+end=lst.length;
+}
+var cmp=MochiKit.Base.compare;
+for(var i=(_105||0);i<end;i++){
+if(cmp(lst[i],_104)===0){
+return i;
+}
+}
+return -1;
+},nodeWalk:function(node,_107){
+var _108=[node];
+var _109=MochiKit.Base.extend;
+while(_108.length){
+var res=_107(_108.shift());
+if(res){
+_109(_108,res);
+}
+}
+},nameFunctions:function(_110){
+var base=_110.NAME;
+if(typeof (base)=="undefined"){
+base="";
+}else{
+base=base+".";
+}
+for(var name in _110){
+var o=_110[name];
+if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){
+try{
+o.NAME=base+name;
+}
+catch(e){
+}
+}
+}
+},queryString:function(_113,_114){
+if(typeof (MochiKit.DOM)!="undefined"&&arguments.length==1&&(typeof (_113)=="string"||(typeof (_113.nodeType)!="undefined"&&_113.nodeType>0))){
+var kv=MochiKit.DOM.formContents(_113);
+_113=kv[0];
+_114=kv[1];
+}else{
+if(arguments.length==1){
+var o=_113;
+_113=[];
+_114=[];
+for(var k in o){
+var v=o[k];
+if(typeof (v)!="function"){
+_113.push(k);
+_114.push(v);
+}
+}
+}
+}
+var rval=[];
+var len=Math.min(_113.length,_114.length);
+var _118=MochiKit.Base.urlEncode;
+for(var i=0;i<len;i++){
+v=_114[i];
+if(typeof (v)!="undefined"&&v!==null){
+rval.push(_118(_113[i])+"="+_118(v));
+}
+}
+return rval.join("&");
+},parseQueryString:function(_119,_120){
+var _121=_119.replace(/\+/g,"%20").split("&");
+var o={};
+var _122;
+if(typeof (decodeURIComponent)!="undefined"){
+_122=decodeURIComponent;
+}else{
+_122=unescape;
+}
+if(_120){
+for(var i=0;i<_121.length;i++){
+var pair=_121[i].split("=");
+var name=_122(pair[0]);
+var arr=o[name];
+if(!(arr instanceof Array)){
+arr=[];
+o[name]=arr;
+}
+arr.push(_122(pair[1]));
+}
+}else{
+for(i=0;i<_121.length;i++){
+pair=_121[i].split("=");
+o[_122(pair[0])]=_122(pair[1]);
+}
+}
+return o;
+}});
+MochiKit.Base.AdapterRegistry=function(){
+this.pairs=[];
+};
+MochiKit.Base.AdapterRegistry.prototype={register:function(name,_124,wrap,_126){
+if(_126){
+this.pairs.unshift([name,_124,wrap]);
+}else{
+this.pairs.push([name,_124,wrap]);
+}
+},match:function(){
+for(var i=0;i<this.pairs.length;i++){
+var pair=this.pairs[i];
+if(pair[1].apply(this,arguments)){
+return pair[2].apply(this,arguments);
+}
+}
+throw MochiKit.Base.NotFound;
+},unregister:function(name){
+for(var i=0;i<this.pairs.length;i++){
+var pair=this.pairs[i];
+if(pair[0]==name){
+this.pairs.splice(i,1);
+return true;
+}
+}
+return false;
+}};
+MochiKit.Base.EXPORT=["counter","clone","extend","update","updatetree","setdefault","keys","items","NamedError","operator","forwardCall","itemgetter","typeMatcher","isCallable","isUndefined","isUndefinedOrNull","isNull","isEmpty","isNotEmpty","isArrayLike","isDateLike","xmap","map","xfilter","filter","bind","bindMethods","NotFound","AdapterRegistry","registerComparator","compare","registerRepr","repr","objEqual","arrayEqual","concat","keyComparator","reverseKeyComparator","partial","merge","listMinMax","listMax","listMin","objMax","objMin","nodeWalk","zip","urlEncode","queryString","serializeJSON","registerJSON","evalJSON","parseQueryString","findValue","findIdentical","flattenArguments","method"];
+MochiKit.Base.EXPORT_OK=["nameFunctions","comparatorRegistry","reprRegistry","jsonRegistry","compareDateLike","compareArrayLike","reprArrayLike","reprString","reprNumber"];
+MochiKit.Base._exportSymbols=function(_127,_128){
+if(typeof (MochiKit.__export__)=="undefined"){
+MochiKit.__export__=(MochiKit.__compat__||(typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined"));
+}
+if(!MochiKit.__export__){
+return;
+}
+var all=_128.EXPORT_TAGS[":all"];
+for(var i=0;i<all.length;i++){
+_127[all[i]]=_128[all[i]];
+}
+};
+MochiKit.Base.__new__=function(){
+var m=this;
+m.forward=m.forwardCall;
+m.find=m.findValue;
+if(typeof (encodeURIComponent)!="undefined"){
+m.urlEncode=function(_130){
+return encodeURIComponent(_130).replace(/\'/g,"%27");
+};
+}else{
+m.urlEncode=function(_131){
+return escape(_131).replace(/\+/g,"%2B").replace(/\"/g,"%22").rval.replace(/\'/g,"%27");
+};
+}
+m.NamedError=function(name){
+this.message=name;
+this.name=name;
+};
+m.NamedError.prototype=new Error();
+m.update(m.NamedError.prototype,{repr:function(){
+if(this.message&&this.message!=this.name){
+return this.name+"("+m.repr(this.message)+")";
+}else{
+return this.name+"()";
+}
+},toString:m.forwardCall("repr")});
+m.NotFound=new m.NamedError("MochiKit.Base.NotFound");
+m.listMax=m.partial(m.listMinMax,1);
+m.listMin=m.partial(m.listMinMax,-1);
+m.isCallable=m.typeMatcher("function");
+m.isUndefined=m.typeMatcher("undefined");
+m.merge=m.partial(m.update,null);
+m.zip=m.partial(m.map,null);
+m.comparatorRegistry=new m.AdapterRegistry();
+m.registerComparator("dateLike",m.isDateLike,m.compareDateLike);
+m.registerComparator("arrayLike",m.isArrayLike,m.compareArrayLike);
+m.reprRegistry=new m.AdapterRegistry();
+m.registerRepr("arrayLike",m.isArrayLike,m.reprArrayLike);
+m.registerRepr("string",m.typeMatcher("string"),m.reprString);
+m.registerRepr("numbers",m.typeMatcher("number","boolean"),m.reprNumber);
+m.jsonRegistry=new m.AdapterRegistry();
+var all=m.concat(m.EXPORT,m.EXPORT_OK);
+m.EXPORT_TAGS={":common":m.concat(m.EXPORT_OK),":all":all};
+m.nameFunctions(this);
+};
+MochiKit.Base.__new__();
+if(!MochiKit.__compat__){
+compare=MochiKit.Base.compare;
+}
+MochiKit.Base._exportSymbols(this,MochiKit.Base);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Iter");
+dojo.require("MochiKit.Base");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Iter depends on MochiKit.Base!";
+}
+if(typeof (MochiKit.Iter)=="undefined"){
+MochiKit.Iter={};
+}
+MochiKit.Iter.NAME="MochiKit.Iter";
+MochiKit.Iter.VERSION="1.3.1";
+MochiKit.Base.update(MochiKit.Iter,{__repr__:function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+},toString:function(){
+return this.__repr__();
+},registerIteratorFactory:function(name,_132,_133,_134){
+MochiKit.Iter.iteratorRegistry.register(name,_132,_133,_134);
+},iter:function(_135,_136){
+var self=MochiKit.Iter;
+if(arguments.length==2){
+return self.takewhile(function(a){
+return a!=_136;
+},_135);
+}
+if(typeof (_135.next)=="function"){
+return _135;
+}else{
+if(typeof (_135.iter)=="function"){
+return _135.iter();
+}
+}
+try{
+return self.iteratorRegistry.match(_135);
+}
+catch(e){
+var m=MochiKit.Base;
+if(e==m.NotFound){
+e=new TypeError(typeof (_135)+": "+m.repr(_135)+" is not iterable");
+}
+throw e;
+}
+},count:function(n){
+if(!n){
+n=0;
+}
+var m=MochiKit.Base;
+return {repr:function(){
+return "count("+n+")";
+},toString:m.forwardCall("repr"),next:m.counter(n)};
+},cycle:function(p){
+var self=MochiKit.Iter;
+var m=MochiKit.Base;
+var lst=[];
+var _139=self.iter(p);
+return {repr:function(){
+return "cycle(...)";
+},toString:m.forwardCall("repr"),next:function(){
+try{
+var rval=_139.next();
+lst.push(rval);
+return rval;
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+if(lst.length===0){
+this.next=function(){
+throw self.StopIteration;
+};
+}else{
+var i=-1;
+this.next=function(){
+i=(i+1)%lst.length;
+return lst[i];
+};
+}
+return this.next();
+}
+}};
+},repeat:function(elem,n){
+var m=MochiKit.Base;
+if(typeof (n)=="undefined"){
+return {repr:function(){
+return "repeat("+m.repr(elem)+")";
+},toString:m.forwardCall("repr"),next:function(){
+return elem;
+}};
+}
+return {repr:function(){
+return "repeat("+m.repr(elem)+", "+n+")";
+},toString:m.forwardCall("repr"),next:function(){
+if(n<=0){
+throw MochiKit.Iter.StopIteration;
+}
+n-=1;
+return elem;
+}};
+},next:function(_141){
+return _141.next();
+},izip:function(p,q){
+var m=MochiKit.Base;
+var next=MochiKit.Iter.next;
+var _144=m.map(iter,arguments);
+return {repr:function(){
+return "izip(...)";
+},toString:m.forwardCall("repr"),next:function(){
+return m.map(next,_144);
+}};
+},ifilter:function(pred,seq){
+var m=MochiKit.Base;
+seq=MochiKit.Iter.iter(seq);
+if(pred===null){
+pred=m.operator.truth;
+}
+return {repr:function(){
+return "ifilter(...)";
+},toString:m.forwardCall("repr"),next:function(){
+while(true){
+var rval=seq.next();
+if(pred(rval)){
+return rval;
+}
+}
+return undefined;
+}};
+},ifilterfalse:function(pred,seq){
+var m=MochiKit.Base;
+seq=MochiKit.Iter.iter(seq);
+if(pred===null){
+pred=m.operator.truth;
+}
+return {repr:function(){
+return "ifilterfalse(...)";
+},toString:m.forwardCall("repr"),next:function(){
+while(true){
+var rval=seq.next();
+if(!pred(rval)){
+return rval;
+}
+}
+return undefined;
+}};
+},islice:function(seq){
+var self=MochiKit.Iter;
+var m=MochiKit.Base;
+seq=self.iter(seq);
+var _147=0;
+var stop=0;
+var step=1;
+var i=-1;
+if(arguments.length==2){
+stop=arguments[1];
+}else{
+if(arguments.length==3){
+_147=arguments[1];
+stop=arguments[2];
+}else{
+_147=arguments[1];
+stop=arguments[2];
+step=arguments[3];
+}
+}
+return {repr:function(){
+return "islice("+["...",_147,stop,step].join(", ")+")";
+},toString:m.forwardCall("repr"),next:function(){
+var rval;
+while(i<_147){
+rval=seq.next();
+i++;
+}
+if(_147>=stop){
+throw self.StopIteration;
+}
+_147+=step;
+return rval;
+}};
+},imap:function(fun,p,q){
+var m=MochiKit.Base;
+var self=MochiKit.Iter;
+var _151=m.map(self.iter,m.extend(null,arguments,1));
+var map=m.map;
+var next=self.next;
+return {repr:function(){
+return "imap(...)";
+},toString:m.forwardCall("repr"),next:function(){
+return fun.apply(this,map(next,_151));
+}};
+},applymap:function(fun,seq,self){
+seq=MochiKit.Iter.iter(seq);
+var m=MochiKit.Base;
+return {repr:function(){
+return "applymap(...)";
+},toString:m.forwardCall("repr"),next:function(){
+return fun.apply(self,seq.next());
+}};
+},chain:function(p,q){
+var self=MochiKit.Iter;
+var m=MochiKit.Base;
+if(arguments.length==1){
+return self.iter(arguments[0]);
+}
+var _153=m.map(self.iter,arguments);
+return {repr:function(){
+return "chain(...)";
+},toString:m.forwardCall("repr"),next:function(){
+while(_153.length>1){
+try{
+return _153[0].next();
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+_153.shift();
+}
+}
+if(_153.length==1){
+var arg=_153.shift();
+this.next=m.bind("next",arg);
+return this.next();
+}
+throw self.StopIteration;
+}};
+},takewhile:function(pred,seq){
+var self=MochiKit.Iter;
+seq=self.iter(seq);
+return {repr:function(){
+return "takewhile(...)";
+},toString:MochiKit.Base.forwardCall("repr"),next:function(){
+var rval=seq.next();
+if(!pred(rval)){
+this.next=function(){
+throw self.StopIteration;
+};
+this.next();
+}
+return rval;
+}};
+},dropwhile:function(pred,seq){
+seq=MochiKit.Iter.iter(seq);
+var m=MochiKit.Base;
+var bind=m.bind;
+return {"repr":function(){
+return "dropwhile(...)";
+},"toString":m.forwardCall("repr"),"next":function(){
+while(true){
+var rval=seq.next();
+if(!pred(rval)){
+break;
+}
+}
+this.next=bind("next",seq);
+return rval;
+}};
+},_tee:function(_155,sync,_157){
+sync.pos[_155]=-1;
+var m=MochiKit.Base;
+var _158=m.listMin;
+return {repr:function(){
+return "tee("+_155+", ...)";
+},toString:m.forwardCall("repr"),next:function(){
+var rval;
+var i=sync.pos[_155];
+if(i==sync.max){
+rval=_157.next();
+sync.deque.push(rval);
+sync.max+=1;
+sync.pos[_155]+=1;
+}else{
+rval=sync.deque[i-sync.min];
+sync.pos[_155]+=1;
+if(i==sync.min&&_158(sync.pos)!=sync.min){
+sync.min+=1;
+sync.deque.shift();
+}
+}
+return rval;
+}};
+},tee:function(_159,n){
+var rval=[];
+var sync={"pos":[],"deque":[],"max":-1,"min":-1};
+if(arguments.length==1){
+n=2;
+}
+var self=MochiKit.Iter;
+_159=self.iter(_159);
+var _tee=self._tee;
+for(var i=0;i<n;i++){
+rval.push(_tee(i,sync,_159));
+}
+return rval;
+},list:function(_161){
+var m=MochiKit.Base;
+if(typeof (_161.slice)=="function"){
+return _161.slice();
+}else{
+if(m.isArrayLike(_161)){
+return m.concat(_161);
+}
+}
+var self=MochiKit.Iter;
+_161=self.iter(_161);
+var rval=[];
+try{
+while(true){
+rval.push(_161.next());
+}
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+return rval;
+}
+return undefined;
+},reduce:function(fn,_162,_163){
+var i=0;
+var x=_163;
+var self=MochiKit.Iter;
+_162=self.iter(_162);
+if(arguments.length<3){
+try{
+x=_162.next();
+}
+catch(e){
+if(e==self.StopIteration){
+e=new TypeError("reduce() of empty sequence with no initial value");
+}
+throw e;
+}
+i++;
+}
+try{
+while(true){
+x=fn(x,_162.next());
+}
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+}
+return x;
+},range:function(){
+var _165=0;
+var stop=0;
+var step=1;
+if(arguments.length==1){
+stop=arguments[0];
+}else{
+if(arguments.length==2){
+_165=arguments[0];
+stop=arguments[1];
+}else{
+if(arguments.length==3){
+_165=arguments[0];
+stop=arguments[1];
+step=arguments[2];
+}else{
+throw new TypeError("range() takes 1, 2, or 3 arguments!");
+}
+}
+}
+if(step===0){
+throw new TypeError("range() step must not be 0");
+}
+return {next:function(){
+if((step>0&&_165>=stop)||(step<0&&_165<=stop)){
+throw MochiKit.Iter.StopIteration;
+}
+var rval=_165;
+_165+=step;
+return rval;
+},repr:function(){
+return "range("+[_165,stop,step].join(", ")+")";
+},toString:MochiKit.Base.forwardCall("repr")};
+},sum:function(_166,_167){
+var x=_167||0;
+var self=MochiKit.Iter;
+_166=self.iter(_166);
+try{
+while(true){
+x+=_166.next();
+}
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+}
+return x;
+},exhaust:function(_168){
+var self=MochiKit.Iter;
+_168=self.iter(_168);
+try{
+while(true){
+_168.next();
+}
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+}
+},forEach:function(_169,func,self){
+var m=MochiKit.Base;
+if(arguments.length>2){
+func=m.bind(func,self);
+}
+if(m.isArrayLike(_169)){
+try{
+for(var i=0;i<_169.length;i++){
+func(_169[i]);
+}
+}
+catch(e){
+if(e!=MochiKit.Iter.StopIteration){
+throw e;
+}
+}
+}else{
+self=MochiKit.Iter;
+self.exhaust(self.imap(func,_169));
+}
+},every:function(_171,func){
+var self=MochiKit.Iter;
+try{
+self.ifilterfalse(func,_171).next();
+return false;
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+return true;
+}
+},sorted:function(_172,cmp){
+var rval=MochiKit.Iter.list(_172);
+if(arguments.length==1){
+cmp=MochiKit.Base.compare;
+}
+rval.sort(cmp);
+return rval;
+},reversed:function(_173){
+var rval=MochiKit.Iter.list(_173);
+rval.reverse();
+return rval;
+},some:function(_174,func){
+var self=MochiKit.Iter;
+try{
+self.ifilter(func,_174).next();
+return true;
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+return false;
+}
+},iextend:function(lst,_175){
+if(MochiKit.Base.isArrayLike(_175)){
+for(var i=0;i<_175.length;i++){
+lst.push(_175[i]);
+}
+}else{
+var self=MochiKit.Iter;
+_175=self.iter(_175);
+try{
+while(true){
+lst.push(_175.next());
+}
+}
+catch(e){
+if(e!=self.StopIteration){
+throw e;
+}
+}
+}
+return lst;
+},groupby:function(_176,_177){
+var m=MochiKit.Base;
+var self=MochiKit.Iter;
+if(arguments.length<2){
+_177=m.operator.identity;
+}
+_176=self.iter(_176);
+var pk=undefined;
+var k=undefined;
+var v;
+function fetch(){
+v=_176.next();
+k=_177(v);
+}
+function eat(){
+var ret=v;
+v=undefined;
+return ret;
+}
+var _180=true;
+return {repr:function(){
+return "groupby(...)";
+},next:function(){
+while(k==pk){
+fetch();
+if(_180){
+_180=false;
+break;
+}
+}
+pk=k;
+return [k,{next:function(){
+if(v==undefined){
+fetch();
+}
+if(k!=pk){
+throw self.StopIteration;
+}
+return eat();
+}}];
+}};
+},groupby_as_array:function(_181,_182){
+var m=MochiKit.Base;
+var self=MochiKit.Iter;
+if(arguments.length<2){
+_182=m.operator.identity;
+}
+_181=self.iter(_181);
+var _183=[];
+var _184=true;
+var _185;
+while(true){
+try{
+var _186=_181.next();
+var key=_182(_186);
+}
+catch(e){
+if(e==self.StopIteration){
+break;
+}
+throw e;
+}
+if(_184||key!=_185){
+var _187=[];
+_183.push([key,_187]);
+}
+_187.push(_186);
+_184=false;
+_185=key;
+}
+return _183;
+},arrayLikeIter:function(_188){
+var i=0;
+return {repr:function(){
+return "arrayLikeIter(...)";
+},toString:MochiKit.Base.forwardCall("repr"),next:function(){
+if(i>=_188.length){
+throw MochiKit.Iter.StopIteration;
+}
+return _188[i++];
+}};
+},hasIterateNext:function(_189){
+return (_189&&typeof (_189.iterateNext)=="function");
+},iterateNextIter:function(_190){
+return {repr:function(){
+return "iterateNextIter(...)";
+},toString:MochiKit.Base.forwardCall("repr"),next:function(){
+var rval=_190.iterateNext();
+if(rval===null||rval===undefined){
+throw MochiKit.Iter.StopIteration;
+}
+return rval;
+}};
+}});
+MochiKit.Iter.EXPORT_OK=["iteratorRegistry","arrayLikeIter","hasIterateNext","iterateNextIter",];
+MochiKit.Iter.EXPORT=["StopIteration","registerIteratorFactory","iter","count","cycle","repeat","next","izip","ifilter","ifilterfalse","islice","imap","applymap","chain","takewhile","dropwhile","tee","list","reduce","range","sum","exhaust","forEach","every","sorted","reversed","some","iextend","groupby","groupby_as_array"];
+MochiKit.Iter.__new__=function(){
+var m=MochiKit.Base;
+this.StopIteration=new m.NamedError("StopIteration");
+this.iteratorRegistry=new m.AdapterRegistry();
+this.registerIteratorFactory("arrayLike",m.isArrayLike,this.arrayLikeIter);
+this.registerIteratorFactory("iterateNext",this.hasIterateNext,this.iterateNextIter);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+m.nameFunctions(this);
+};
+MochiKit.Iter.__new__();
+if(!MochiKit.__compat__){
+reduce=MochiKit.Iter.reduce;
+}
+MochiKit.Base._exportSymbols(this,MochiKit.Iter);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Logging");
+dojo.require("MochiKit.Base");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Logging depends on MochiKit.Base!";
+}
+if(typeof (MochiKit.Logging)=="undefined"){
+MochiKit.Logging={};
+}
+MochiKit.Logging.NAME="MochiKit.Logging";
+MochiKit.Logging.VERSION="1.3.1";
+MochiKit.Logging.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.Logging.toString=function(){
+return this.__repr__();
+};
+MochiKit.Logging.EXPORT=["LogLevel","LogMessage","Logger","alertListener","logger","log","logError","logDebug","logFatal","logWarning"];
+MochiKit.Logging.EXPORT_OK=["logLevelAtLeast","isLogMessage","compareLogMessage"];
+MochiKit.Logging.LogMessage=function(num,_192,info){
+this.num=num;
+this.level=_192;
+this.info=info;
+this.timestamp=new Date();
+};
+MochiKit.Logging.LogMessage.prototype={repr:function(){
+var m=MochiKit.Base;
+return "LogMessage("+m.map(m.repr,[this.num,this.level,this.info]).join(", ")+")";
+},toString:MochiKit.Base.forwardCall("repr")};
+MochiKit.Base.update(MochiKit.Logging,{logLevelAtLeast:function(_194){
+var self=MochiKit.Logging;
+if(typeof (_194)=="string"){
+_194=self.LogLevel[_194];
+}
+return function(msg){
+var _196=msg.level;
+if(typeof (_196)=="string"){
+_196=self.LogLevel[_196];
+}
+return _196>=_194;
+};
+},isLogMessage:function(){
+var _197=MochiKit.Logging.LogMessage;
+for(var i=0;i<arguments.length;i++){
+if(!(arguments[i] instanceof _197)){
+return false;
+}
+}
+return true;
+},compareLogMessage:function(a,b){
+return MochiKit.Base.compare([a.level,a.info],[b.level,b.info]);
+},alertListener:function(msg){
+alert("num: "+msg.num+"\nlevel: "+msg.level+"\ninfo: "+msg.info.join(" "));
+}});
+MochiKit.Logging.Logger=function(_198){
+this.counter=0;
+if(typeof (_198)=="undefined"||_198===null){
+_198=-1;
+}
+this.maxSize=_198;
+this._messages=[];
+this.listeners={};
+this.useNativeConsole=false;
+};
+MochiKit.Logging.Logger.prototype={clear:function(){
+this._messages.splice(0,this._messages.length);
+},logToConsole:function(msg){
+if(typeof (window)!="undefined"&&window.console&&window.console.log){
+window.console.log(msg);
+}else{
+if(typeof (opera)!="undefined"&&opera.postError){
+opera.postError(msg);
+}else{
+if(typeof (printfire)=="function"){
+printfire(msg);
+}
+}
+}
+},dispatchListeners:function(msg){
+for(var k in this.listeners){
+var pair=this.listeners[k];
+if(pair.ident!=k||(pair[0]&&!pair[0](msg))){
+continue;
+}
+pair[1](msg);
+}
+},addListener:function(_199,_200,_201){
+if(typeof (_200)=="string"){
+_200=MochiKit.Logging.logLevelAtLeast(_200);
+}
+var _202=[_200,_201];
+_202.ident=_199;
+this.listeners[_199]=_202;
+},removeListener:function(_203){
+delete this.listeners[_203];
+},baseLog:function(_204,_205){
+var msg=new MochiKit.Logging.LogMessage(this.counter,_204,MochiKit.Base.extend(null,arguments,1));
+this._messages.push(msg);
+this.dispatchListeners(msg);
+if(this.useNativeConsole){
+this.logToConsole(msg.level+": "+msg.info.join(" "));
+}
+this.counter+=1;
+while(this.maxSize>=0&&this._messages.length>this.maxSize){
+this._messages.shift();
+}
+},getMessages:function(_206){
+var _207=0;
+if(!(typeof (_206)=="undefined"||_206===null)){
+_207=Math.max(0,this._messages.length-_206);
+}
+return this._messages.slice(_207);
+},getMessageText:function(_208){
+if(typeof (_208)=="undefined"||_208===null){
+_208=30;
+}
+var _209=this.getMessages(_208);
+if(_209.length){
+var lst=map(function(m){
+return "\n  ["+m.num+"] "+m.level+": "+m.info.join(" ");
+},_209);
+lst.unshift("LAST "+_209.length+" MESSAGES:");
+return lst.join("");
+}
+return "";
+},debuggingBookmarklet:function(_210){
+if(typeof (MochiKit.LoggingPane)=="undefined"){
+alert(this.getMessageText());
+}else{
+MochiKit.LoggingPane.createLoggingPane(_210||false);
+}
+}};
+MochiKit.Logging.__new__=function(){
+this.LogLevel={ERROR:40,FATAL:50,WARNING:30,INFO:20,DEBUG:10};
+var m=MochiKit.Base;
+m.registerComparator("LogMessage",this.isLogMessage,this.compareLogMessage);
+var _211=m.partial;
+var _212=this.Logger;
+var _213=_212.prototype.baseLog;
+m.update(this.Logger.prototype,{debug:_211(_213,"DEBUG"),log:_211(_213,"INFO"),error:_211(_213,"ERROR"),fatal:_211(_213,"FATAL"),warning:_211(_213,"WARNING")});
+var self=this;
+var _214=function(name){
+return function(){
+self.logger[name].apply(self.logger,arguments);
+};
+};
+this.log=_214("log");
+this.logError=_214("error");
+this.logDebug=_214("debug");
+this.logFatal=_214("fatal");
+this.logWarning=_214("warning");
+this.logger=new _212();
+this.logger.useNativeConsole=true;
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+m.nameFunctions(this);
+};
+if(typeof (printfire)=="undefined"&&typeof (document)!="undefined"&&document.createEvent&&typeof (dispatchEvent)!="undefined"){
+printfire=function(){
+printfire.args=arguments;
+var ev=document.createEvent("Events");
+ev.initEvent("printfire",false,true);
+dispatchEvent(ev);
+};
+}
+MochiKit.Logging.__new__();
+MochiKit.Base._exportSymbols(this,MochiKit.Logging);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.DateTime");
+}
+if(typeof (MochiKit)=="undefined"){
+MochiKit={};
+}
+if(typeof (MochiKit.DateTime)=="undefined"){
+MochiKit.DateTime={};
+}
+MochiKit.DateTime.NAME="MochiKit.DateTime";
+MochiKit.DateTime.VERSION="1.3.1";
+MochiKit.DateTime.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.DateTime.toString=function(){
+return this.__repr__();
+};
+MochiKit.DateTime.isoDate=function(str){
+str=str+"";
+if(typeof (str)!="string"||str.length===0){
+return null;
+}
+var iso=str.split("-");
+if(iso.length===0){
+return null;
+}
+return new Date(iso[0],iso[1]-1,iso[2]);
+};
+MochiKit.DateTime._isoRegexp=/(\d{4,})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d+))?)?(?:(Z)|([+-])(\d{1,2})(?::(\d{1,2}))?)?)?)?)?/;
+MochiKit.DateTime.isoTimestamp=function(str){
+str=str+"";
+if(typeof (str)!="string"||str.length===0){
+return null;
+}
+var res=str.match(MochiKit.DateTime._isoRegexp);
+if(typeof (res)=="undefined"||res===null){
+return null;
+}
+var year,month,day,hour,min,sec,msec;
+year=parseInt(res[1],10);
+if(typeof (res[2])=="undefined"||res[2]===""){
+return new Date(year);
+}
+month=parseInt(res[2],10)-1;
+day=parseInt(res[3],10);
+if(typeof (res[4])=="undefined"||res[4]===""){
+return new Date(year,month,day);
+}
+hour=parseInt(res[4],10);
+min=parseInt(res[5],10);
+sec=(typeof (res[6])!="undefined"&&res[6]!=="")?parseInt(res[6],10):0;
+if(typeof (res[7])!="undefined"&&res[7]!==""){
+msec=Math.round(1000*parseFloat("0."+res[7]));
+}else{
+msec=0;
+}
+if((typeof (res[8])=="undefined"||res[8]==="")&&(typeof (res[9])=="undefined"||res[9]==="")){
+return new Date(year,month,day,hour,min,sec,msec);
+}
+var ofs;
+if(typeof (res[9])!="undefined"&&res[9]!==""){
+ofs=parseInt(res[10],10)*3600000;
+if(typeof (res[11])!="undefined"&&res[11]!==""){
+ofs+=parseInt(res[11],10)*60000;
+}
+if(res[9]=="-"){
+ofs=-ofs;
+}
+}else{
+ofs=0;
+}
+return new Date(Date.UTC(year,month,day,hour,min,sec,msec)-ofs);
+};
+MochiKit.DateTime.toISOTime=function(date,_221){
+if(typeof (date)=="undefined"||date===null){
+return null;
+}
+var hh=date.getHours();
+var mm=date.getMinutes();
+var ss=date.getSeconds();
+var lst=[((_221&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)];
+return lst.join(":");
+};
+MochiKit.DateTime.toISOTimestamp=function(date,_225){
+if(typeof (date)=="undefined"||date===null){
+return null;
+}
+var sep=_225?"T":" ";
+var foot=_225?"Z":"";
+if(_225){
+date=new Date(date.getTime()+(date.getTimezoneOffset()*60000));
+}
+return MochiKit.DateTime.toISODate(date)+sep+MochiKit.DateTime.toISOTime(date,_225)+foot;
+};
+MochiKit.DateTime.toISODate=function(date){
+if(typeof (date)=="undefined"||date===null){
+return null;
+}
+var _228=MochiKit.DateTime._padTwo;
+return [date.getFullYear(),_228(date.getMonth()+1),_228(date.getDate())].join("-");
+};
+MochiKit.DateTime.americanDate=function(d){
+d=d+"";
+if(typeof (d)!="string"||d.length===0){
+return null;
+}
+var a=d.split("/");
+return new Date(a[2],a[0]-1,a[1]);
+};
+MochiKit.DateTime._padTwo=function(n){
+return (n>9)?n:"0"+n;
+};
+MochiKit.DateTime.toPaddedAmericanDate=function(d){
+if(typeof (d)=="undefined"||d===null){
+return null;
+}
+var _230=MochiKit.DateTime._padTwo;
+return [_230(d.getMonth()+1),_230(d.getDate()),d.getFullYear()].join("/");
+};
+MochiKit.DateTime.toAmericanDate=function(d){
+if(typeof (d)=="undefined"||d===null){
+return null;
+}
+return [d.getMonth()+1,d.getDate(),d.getFullYear()].join("/");
+};
+MochiKit.DateTime.EXPORT=["isoDate","isoTimestamp","toISOTime","toISOTimestamp","toISODate","americanDate","toPaddedAmericanDate","toAmericanDate"];
+MochiKit.DateTime.EXPORT_OK=[];
+MochiKit.DateTime.EXPORT_TAGS={":common":MochiKit.DateTime.EXPORT,":all":MochiKit.DateTime.EXPORT};
+MochiKit.DateTime.__new__=function(){
+var base=this.NAME+".";
+for(var k in this){
+var o=this[k];
+if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){
+try{
+o.NAME=base+k;
+}
+catch(e){
+}
+}
+}
+};
+MochiKit.DateTime.__new__();
+if(typeof (MochiKit.Base)!="undefined"){
+MochiKit.Base._exportSymbols(this,MochiKit.DateTime);
+}else{
+(function(_231,_232){
+if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(typeof (MochiKit.__compat__)=="boolean"&&MochiKit.__compat__)){
+var all=_232.EXPORT_TAGS[":all"];
+for(var i=0;i<all.length;i++){
+_231[all[i]]=_232[all[i]];
+}
+}
+})(this,MochiKit.DateTime);
+}
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Format");
+}
+if(typeof (MochiKit)=="undefined"){
+MochiKit={};
+}
+if(typeof (MochiKit.Format)=="undefined"){
+MochiKit.Format={};
+}
+MochiKit.Format.NAME="MochiKit.Format";
+MochiKit.Format.VERSION="1.3.1";
+MochiKit.Format.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.Format.toString=function(){
+return this.__repr__();
+};
+MochiKit.Format._numberFormatter=function(_233,_234,_235,_236,_237,_238,_239,_240,_241){
+return function(num){
+num=parseFloat(num);
+if(typeof (num)=="undefined"||num===null||isNaN(num)){
+return _233;
+}
+var _242=_234;
+var _243=_235;
+if(num<0){
+num=-num;
+}else{
+_242=_242.replace(/-/,"");
+}
+var me=arguments.callee;
+var fmt=MochiKit.Format.formatLocale(_236);
+if(_237){
+num=num*100;
+_243=fmt.percent+_243;
+}
+num=MochiKit.Format.roundToFixed(num,_238);
+var _245=num.split(/\./);
+var _246=_245[0];
+var frac=(_245.length==1)?"":_245[1];
+var res="";
+while(_246.length<_239){
+_246="0"+_246;
+}
+if(_240){
+while(_246.length>_240){
+var i=_246.length-_240;
+res=fmt.separator+_246.substring(i,_246.length)+res;
+_246=_246.substring(0,i);
+}
+}
+res=_246+res;
+if(_238>0){
+while(frac.length<_241){
+frac=frac+"0";
+}
+res=res+fmt.decimal+frac;
+}
+return _242+res+_243;
+};
+};
+MochiKit.Format.numberFormatter=function(_248,_249,_250){
+if(typeof (_249)=="undefined"){
+_249="";
+}
+var _251=_248.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/);
+if(!_251){
+throw TypeError("Invalid pattern");
+}
+var _252=_248.substr(0,_251.index);
+var _253=_248.substr(_251.index+_251[0].length);
+if(_252.search(/-/)==-1){
+_252=_252+"-";
+}
+var _254=_251[1];
+var frac=(typeof (_251[2])=="string"&&_251[2]!="")?_251[2]:"";
+var _255=(typeof (_251[3])=="string"&&_251[3]!="");
+var tmp=_254.split(/,/);
+var _257;
+if(typeof (_250)=="undefined"){
+_250="default";
+}
+if(tmp.length==1){
+_257=null;
+}else{
+_257=tmp[1].length;
+}
+var _258=_254.length-_254.replace(/0/g,"").length;
+var _259=frac.length-frac.replace(/0/g,"").length;
+var _260=frac.length;
+var rval=MochiKit.Format._numberFormatter(_249,_252,_253,_250,_255,_260,_258,_257,_259);
+var m=MochiKit.Base;
+if(m){
+var fn=arguments.callee;
+var args=m.concat(arguments);
+rval.repr=function(){
+return [self.NAME,"(",map(m.repr,args).join(", "),")"].join("");
+};
+}
+return rval;
+};
+MochiKit.Format.formatLocale=function(_262){
+if(typeof (_262)=="undefined"||_262===null){
+_262="default";
+}
+if(typeof (_262)=="string"){
+var rval=MochiKit.Format.LOCALE[_262];
+if(typeof (rval)=="string"){
+rval=arguments.callee(rval);
+MochiKit.Format.LOCALE[_262]=rval;
+}
+return rval;
+}else{
+return _262;
+}
+};
+MochiKit.Format.twoDigitAverage=function(_263,_264){
+if(_264){
+var res=_263/_264;
+if(!isNaN(res)){
+return MochiKit.Format.twoDigitFloat(_263/_264);
+}
+}
+return "0";
+};
+MochiKit.Format.twoDigitFloat=function(_265){
+var sign=(_265<0?"-":"");
+var s=Math.floor(Math.abs(_265)*100).toString();
+if(s=="0"){
+return s;
+}
+if(s.length<3){
+while(s.charAt(s.length-1)=="0"){
+s=s.substring(0,s.length-1);
+}
+return sign+"0."+s;
+}
+var head=sign+s.substring(0,s.length-2);
+var tail=s.substring(s.length-2,s.length);
+if(tail=="00"){
+return head;
+}else{
+if(tail.charAt(1)=="0"){
+return head+"."+tail.charAt(0);
+}else{
+return head+"."+tail;
+}
+}
+};
+MochiKit.Format.lstrip=function(str,_270){
+str=str+"";
+if(typeof (str)!="string"){
+return null;
+}
+if(!_270){
+return str.replace(/^\s+/,"");
+}else{
+return str.replace(new RegExp("^["+_270+"]+"),"");
+}
+};
+MochiKit.Format.rstrip=function(str,_271){
+str=str+"";
+if(typeof (str)!="string"){
+return null;
+}
+if(!_271){
+return str.replace(/\s+$/,"");
+}else{
+return str.replace(new RegExp("["+_271+"]+$"),"");
+}
+};
+MochiKit.Format.strip=function(str,_272){
+var self=MochiKit.Format;
+return self.rstrip(self.lstrip(str,_272),_272);
+};
+MochiKit.Format.truncToFixed=function(_273,_274){
+_273=Math.floor(_273*Math.pow(10,_274));
+var res=(_273*Math.pow(10,-_274)).toFixed(_274);
+if(res.charAt(0)=="."){
+res="0"+res;
+}
+return res;
+};
+MochiKit.Format.roundToFixed=function(_275,_276){
+return MochiKit.Format.truncToFixed(_275+0.5*Math.pow(10,-_276),_276);
+};
+MochiKit.Format.percentFormat=function(_277){
+return MochiKit.Format.twoDigitFloat(100*_277)+"%";
+};
+MochiKit.Format.EXPORT=["truncToFixed","roundToFixed","numberFormatter","formatLocale","twoDigitAverage","twoDigitFloat","percentFormat","lstrip","rstrip","strip"];
+MochiKit.Format.LOCALE={en_US:{separator:",",decimal:".",percent:"%"},de_DE:{separator:".",decimal:",",percent:"%"},fr_FR:{separator:" ",decimal:",",percent:"%"},"default":"en_US"};
+MochiKit.Format.EXPORT_OK=[];
+MochiKit.Format.EXPORT_TAGS={":all":MochiKit.Format.EXPORT,":common":MochiKit.Format.EXPORT};
+MochiKit.Format.__new__=function(){
+var base=this.NAME+".";
+var k,v,o;
+for(k in this.LOCALE){
+o=this.LOCALE[k];
+if(typeof (o)=="object"){
+o.repr=function(){
+return this.NAME;
+};
+o.NAME=base+"LOCALE."+k;
+}
+}
+for(k in this){
+o=this[k];
+if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){
+try{
+o.NAME=base+k;
+}
+catch(e){
+}
+}
+}
+};
+MochiKit.Format.__new__();
+if(typeof (MochiKit.Base)!="undefined"){
+MochiKit.Base._exportSymbols(this,MochiKit.Format);
+}else{
+(function(_278,_279){
+if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(typeof (MochiKit.__compat__)=="boolean"&&MochiKit.__compat__)){
+var all=_279.EXPORT_TAGS[":all"];
+for(var i=0;i<all.length;i++){
+_278[all[i]]=_279[all[i]];
+}
+}
+})(this,MochiKit.Format);
+}
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Async");
+dojo.require("MochiKit.Base");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Async depends on MochiKit.Base!";
+}
+if(typeof (MochiKit.Async)=="undefined"){
+MochiKit.Async={};
+}
+MochiKit.Async.NAME="MochiKit.Async";
+MochiKit.Async.VERSION="1.3.1";
+MochiKit.Async.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.Async.toString=function(){
+return this.__repr__();
+};
+MochiKit.Async.Deferred=function(_280){
+this.chain=[];
+this.id=this._nextId();
+this.fired=-1;
+this.paused=0;
+this.results=[null,null];
+this.canceller=_280;
+this.silentlyCancelled=false;
+this.chained=false;
+};
+MochiKit.Async.Deferred.prototype={repr:function(){
+var _281;
+if(this.fired==-1){
+_281="unfired";
+}else{
+if(this.fired===0){
+_281="success";
+}else{
+_281="error";
+}
+}
+return "Deferred("+this.id+", "+_281+")";
+},toString:MochiKit.Base.forwardCall("repr"),_nextId:MochiKit.Base.counter(),cancel:function(){
+var self=MochiKit.Async;
+if(this.fired==-1){
+if(this.canceller){
+this.canceller(this);
+}else{
+this.silentlyCancelled=true;
+}
+if(this.fired==-1){
+this.errback(new self.CancelledError(this));
+}
+}else{
+if((this.fired===0)&&(this.results[0] instanceof self.Deferred)){
+this.results[0].cancel();
+}
+}
+},_pause:function(){
+this.paused++;
+},_unpause:function(){
+this.paused--;
+if((this.paused===0)&&(this.fired>=0)){
+this._fire();
+}
+},_continue:function(res){
+this._resback(res);
+this._unpause();
+},_resback:function(res){
+this.fired=((res instanceof Error)?1:0);
+this.results[this.fired]=res;
+this._fire();
+},_check:function(){
+if(this.fired!=-1){
+if(!this.silentlyCancelled){
+throw new MochiKit.Async.AlreadyCalledError(this);
+}
+this.silentlyCancelled=false;
+return;
+}
+},callback:function(res){
+this._check();
+if(res instanceof MochiKit.Async.Deferred){
+throw new Error("Deferred instances can only be chained if they are the result of a callback");
+}
+this._resback(res);
+},errback:function(res){
+this._check();
+var self=MochiKit.Async;
+if(res instanceof self.Deferred){
+throw new Error("Deferred instances can only be chained if they are the result of a callback");
+}
+if(!(res instanceof Error)){
+res=new self.GenericError(res);
+}
+this._resback(res);
+},addBoth:function(fn){
+if(arguments.length>1){
+fn=MochiKit.Base.partial.apply(null,arguments);
+}
+return this.addCallbacks(fn,fn);
+},addCallback:function(fn){
+if(arguments.length>1){
+fn=MochiKit.Base.partial.apply(null,arguments);
+}
+return this.addCallbacks(fn,null);
+},addErrback:function(fn){
+if(arguments.length>1){
+fn=MochiKit.Base.partial.apply(null,arguments);
+}
+return this.addCallbacks(null,fn);
+},addCallbacks:function(cb,eb){
+if(this.chained){
+throw new Error("Chained Deferreds can not be re-used");
+}
+this.chain.push([cb,eb]);
+if(this.fired>=0){
+this._fire();
+}
+return this;
+},_fire:function(){
+var _284=this.chain;
+var _285=this.fired;
+var res=this.results[_285];
+var self=this;
+var cb=null;
+while(_284.length>0&&this.paused===0){
+var pair=_284.shift();
+var f=pair[_285];
+if(f===null){
+continue;
+}
+try{
+res=f(res);
+_285=((res instanceof Error)?1:0);
+if(res instanceof MochiKit.Async.Deferred){
+cb=function(res){
+self._continue(res);
+};
+this._pause();
+}
+}
+catch(err){
+_285=1;
+if(!(err instanceof Error)){
+err=new MochiKit.Async.GenericError(err);
+}
+res=err;
+}
+}
+this.fired=_285;
+this.results[_285]=res;
+if(cb&&this.paused){
+res.addBoth(cb);
+res.chained=true;
+}
+}};
+MochiKit.Base.update(MochiKit.Async,{evalJSONRequest:function(){
+return eval("("+arguments[0].responseText+")");
+},succeed:function(_287){
+var d=new MochiKit.Async.Deferred();
+d.callback.apply(d,arguments);
+return d;
+},fail:function(_288){
+var d=new MochiKit.Async.Deferred();
+d.errback.apply(d,arguments);
+return d;
+},getXMLHttpRequest:function(){
+var self=arguments.callee;
+if(!self.XMLHttpRequest){
+var _289=[function(){
+return new XMLHttpRequest();
+},function(){
+return new ActiveXObject("Msxml2.XMLHTTP");
+},function(){
+return new ActiveXObject("Microsoft.XMLHTTP");
+},function(){
+return new ActiveXObject("Msxml2.XMLHTTP.4.0");
+},function(){
+throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest");
+}];
+for(var i=0;i<_289.length;i++){
+var func=_289[i];
+try{
+self.XMLHttpRequest=func;
+return func();
+}
+catch(e){
+}
+}
+}
+return self.XMLHttpRequest();
+},_nothing:function(){
+},_xhr_onreadystatechange:function(d){
+if(this.readyState==4){
+try{
+this.onreadystatechange=null;
+}
+catch(e){
+try{
+this.onreadystatechange=MochiKit.Async._nothing;
+}
+catch(e){
+}
+}
+var _290=null;
+try{
+_290=this.status;
+if(!_290&&MochiKit.Base.isNotEmpty(this.responseText)){
+_290=304;
+}
+}
+catch(e){
+}
+if(_290==200||_290==304){
+d.callback(this);
+}else{
+var err=new MochiKit.Async.XMLHttpRequestError(this,"Request failed");
+if(err.number){
+d.errback(err);
+}else{
+d.errback(err);
+}
+}
+}
+},_xhr_canceller:function(req){
+try{
+req.onreadystatechange=null;
+}
+catch(e){
+try{
+req.onreadystatechange=MochiKit.Async._nothing;
+}
+catch(e){
+}
+}
+req.abort();
+},sendXMLHttpRequest:function(req,_293){
+if(typeof (_293)=="undefined"||_293===null){
+_293="";
+}
+var m=MochiKit.Base;
+var self=MochiKit.Async;
+var d=new self.Deferred(m.partial(self._xhr_canceller,req));
+try{
+req.onreadystatechange=m.bind(self._xhr_onreadystatechange,req,d);
+req.send(_293);
+}
+catch(e){
+try{
+req.onreadystatechange=null;
+}
+catch(ignore){
+}
+d.errback(e);
+}
+return d;
+},doSimpleXMLHttpRequest:function(url){
+var self=MochiKit.Async;
+var req=self.getXMLHttpRequest();
+if(arguments.length>1){
+var m=MochiKit.Base;
+var qs=m.queryString.apply(null,m.extend(null,arguments,1));
+if(qs){
+url+="?"+qs;
+}
+}
+req.open("GET",url,true);
+return self.sendXMLHttpRequest(req);
+},loadJSONDoc:function(url){
+var self=MochiKit.Async;
+var d=self.doSimpleXMLHttpRequest.apply(self,arguments);
+d=d.addCallback(self.evalJSONRequest);
+return d;
+},wait:function(_296,_297){
+var d=new MochiKit.Async.Deferred();
+var m=MochiKit.Base;
+if(typeof (_297)!="undefined"){
+d.addCallback(function(){
+return _297;
+});
+}
+var _298=setTimeout(m.bind("callback",d),Math.floor(_296*1000));
+d.canceller=function(){
+try{
+clearTimeout(_298);
+}
+catch(e){
+}
+};
+return d;
+},callLater:function(_299,func){
+var m=MochiKit.Base;
+var _300=m.partial.apply(m,m.extend(null,arguments,1));
+return MochiKit.Async.wait(_299).addCallback(function(res){
+return _300();
+});
+}});
+MochiKit.Async.DeferredLock=function(){
+this.waiting=[];
+this.locked=false;
+this.id=this._nextId();
+};
+MochiKit.Async.DeferredLock.prototype={__class__:MochiKit.Async.DeferredLock,acquire:function(){
+d=new MochiKit.Async.Deferred();
+if(this.locked){
+this.waiting.push(d);
+}else{
+this.locked=true;
+d.callback(this);
+}
+return d;
+},release:function(){
+if(!this.locked){
+throw TypeError("Tried to release an unlocked DeferredLock");
+}
+this.locked=false;
+if(this.waiting.length>0){
+this.locked=true;
+this.waiting.shift().callback(this);
+}
+},_nextId:MochiKit.Base.counter(),repr:function(){
+var _301;
+if(this.locked){
+_301="locked, "+this.waiting.length+" waiting";
+}else{
+_301="unlocked";
+}
+return "DeferredLock("+this.id+", "+_301+")";
+},toString:MochiKit.Base.forwardCall("repr")};
+MochiKit.Async.DeferredList=function(list,_303,_304,_305,_306){
+this.list=list;
+this.resultList=new Array(this.list.length);
+this.chain=[];
+this.id=this._nextId();
+this.fired=-1;
+this.paused=0;
+this.results=[null,null];
+this.canceller=_306;
+this.silentlyCancelled=false;
+if(this.list.length===0&&!_303){
+this.callback(this.resultList);
+}
+this.finishedCount=0;
+this.fireOnOneCallback=_303;
+this.fireOnOneErrback=_304;
+this.consumeErrors=_305;
+var _307=0;
+MochiKit.Base.map(MochiKit.Base.bind(function(d){
+d.addCallback(MochiKit.Base.bind(this._cbDeferred,this),_307,true);
+d.addErrback(MochiKit.Base.bind(this._cbDeferred,this),_307,false);
+_307+=1;
+},this),this.list);
+};
+MochiKit.Base.update(MochiKit.Async.DeferredList.prototype,MochiKit.Async.Deferred.prototype);
+MochiKit.Base.update(MochiKit.Async.DeferredList.prototype,{_cbDeferred:function(_308,_309,_310){
+this.resultList[_308]=[_309,_310];
+this.finishedCount+=1;
+if(this.fired!==0){
+if(_309&&this.fireOnOneCallback){
+this.callback([_308,_310]);
+}else{
+if(!_309&&this.fireOnOneErrback){
+this.errback(_310);
+}else{
+if(this.finishedCount==this.list.length){
+this.callback(this.resultList);
+}
+}
+}
+}
+if(!_309&&this.consumeErrors){
+_310=null;
+}
+return _310;
+}});
+MochiKit.Async.gatherResults=function(_311){
+var d=new MochiKit.Async.DeferredList(_311,false,true,false);
+d.addCallback(function(_312){
+var ret=[];
+for(var i=0;i<_312.length;i++){
+ret.push(_312[i][1]);
+}
+return ret;
+});
+return d;
+};
+MochiKit.Async.maybeDeferred=function(func){
+var self=MochiKit.Async;
+var _313;
+try{
+var r=func.apply(null,MochiKit.Base.extend([],arguments,1));
+if(r instanceof self.Deferred){
+_313=r;
+}else{
+if(r instanceof Error){
+_313=self.fail(r);
+}else{
+_313=self.succeed(r);
+}
+}
+}
+catch(e){
+_313=self.fail(e);
+}
+return _313;
+};
+MochiKit.Async.EXPORT=["AlreadyCalledError","CancelledError","BrowserComplianceError","GenericError","XMLHttpRequestError","Deferred","succeed","fail","getXMLHttpRequest","doSimpleXMLHttpRequest","loadJSONDoc","wait","callLater","sendXMLHttpRequest","DeferredLock","DeferredList","gatherResults","maybeDeferred"];
+MochiKit.Async.EXPORT_OK=["evalJSONRequest"];
+MochiKit.Async.__new__=function(){
+var m=MochiKit.Base;
+var ne=m.partial(m._newNamedError,this);
+ne("AlreadyCalledError",function(_316){
+this.deferred=_316;
+});
+ne("CancelledError",function(_317){
+this.deferred=_317;
+});
+ne("BrowserComplianceError",function(msg){
+this.message=msg;
+});
+ne("GenericError",function(msg){
+this.message=msg;
+});
+ne("XMLHttpRequestError",function(req,msg){
+this.req=req;
+this.message=msg;
+try{
+this.number=req.status;
+}
+catch(e){
+}
+});
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+m.nameFunctions(this);
+};
+MochiKit.Async.__new__();
+MochiKit.Base._exportSymbols(this,MochiKit.Async);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.DOM");
+dojo.require("MochiKit.Iter");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Iter",[]);
+}
+try{
+if(typeof (MochiKit.Iter)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.DOM depends on MochiKit.Iter!";
+}
+if(typeof (MochiKit.DOM)=="undefined"){
+MochiKit.DOM={};
+}
+MochiKit.DOM.NAME="MochiKit.DOM";
+MochiKit.DOM.VERSION="1.3.1";
+MochiKit.DOM.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.DOM.toString=function(){
+return this.__repr__();
+};
+MochiKit.DOM.EXPORT=["formContents","currentWindow","currentDocument","withWindow","withDocument","registerDOMConverter","coerceToDOM","createDOM","createDOMFunc","getNodeAttribute","setNodeAttribute","updateNodeAttributes","appendChildNodes","replaceChildNodes","removeElement","swapDOM","BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG","getElement","$","computedStyle","getElementsByTagAndClassName","addToCallStack","addLoadEvent","focusOnLoad","setElementClass","toggleElementClass","addElementClass","removeElementClass","swapElementClass","hasElementClass","escapeHTML","toHTML","emitHTML","setDisplayForElement","hideElement","showElement","scrapeText","elementDimensions","elementPosition","setElementDimensions","setElementPosition","getViewportDimensions","setOpacity"];
+MochiKit.DOM.EXPORT_OK=["domConverters"];
+MochiKit.DOM.Dimensions=function(w,h){
+this.w=w;
+this.h=h;
+};
+MochiKit.DOM.Dimensions.prototype.repr=function(){
+var repr=MochiKit.Base.repr;
+return "{w: "+repr(this.w)+", h: "+repr(this.h)+"}";
+};
+MochiKit.DOM.Coordinates=function(x,y){
+this.x=x;
+this.y=y;
+};
+MochiKit.DOM.Coordinates.prototype.repr=function(){
+var repr=MochiKit.Base.repr;
+return "{x: "+repr(this.x)+", y: "+repr(this.y)+"}";
+};
+MochiKit.DOM.Coordinates.prototype.toString=function(){
+return this.repr();
+};
+MochiKit.Base.update(MochiKit.DOM,{setOpacity:function(elem,o){
+elem=MochiKit.DOM.getElement(elem);
+MochiKit.DOM.updateNodeAttributes(elem,{"style":{"opacity":o,"-moz-opacity":o,"-khtml-opacity":o,"filter":" alpha(opacity="+(o*100)+")"}});
+},getViewportDimensions:function(){
+var d=new MochiKit.DOM.Dimensions();
+var w=MochiKit.DOM._window;
+var b=MochiKit.DOM._document.body;
+if(w.innerWidth){
+d.w=w.innerWidth;
+d.h=w.innerHeight;
+}else{
+if(b.parentElement.clientWidth){
+d.w=b.parentElement.clientWidth;
+d.h=b.parentElement.clientHeight;
+}else{
+if(b&&b.clientWidth){
+d.w=b.clientWidth;
+d.h=b.clientHeight;
+}
+}
+}
+return d;
+},elementDimensions:function(elem){
+var self=MochiKit.DOM;
+if(typeof (elem.w)=="number"||typeof (elem.h)=="number"){
+return new self.Dimensions(elem.w||0,elem.h||0);
+}
+elem=self.getElement(elem);
+if(!elem){
+return undefined;
+}
+if(self.computedStyle(elem,"display")!="none"){
+return new self.Dimensions(elem.offsetWidth||0,elem.offsetHeight||0);
+}
+var s=elem.style;
+var _322=s.visibility;
+var _323=s.position;
+s.visibility="hidden";
+s.position="absolute";
+s.display="";
+var _324=elem.offsetWidth;
+var _325=elem.offsetHeight;
+s.display="none";
+s.position=_323;
+s.visibility=_322;
+return new self.Dimensions(_324,_325);
+},elementPosition:function(elem,_326){
+var self=MochiKit.DOM;
+elem=self.getElement(elem);
+if(!elem){
+return undefined;
+}
+var c=new self.Coordinates(0,0);
+if(elem.x&&elem.y){
+c.x+=elem.x||0;
+c.y+=elem.y||0;
+return c;
+}else{
+if(elem.parentNode===null||self.computedStyle(elem,"display")=="none"){
+return undefined;
+}
+}
+var box=null;
+var _329=null;
+var d=MochiKit.DOM._document;
+var de=d.documentElement;
+var b=d.body;
+if(elem.getBoundingClientRect){
+box=elem.getBoundingClientRect();
+c.x+=box.left+(de.scrollLeft||b.scrollLeft)-(de.clientLeft||b.clientLeft);
+c.y+=box.top+(de.scrollTop||b.scrollTop)-(de.clientTop||b.clientTop);
+}else{
+if(d.getBoxObjectFor){
+box=d.getBoxObjectFor(elem);
+c.x+=box.x;
+c.y+=box.y;
+}else{
+if(elem.offsetParent){
+c.x+=elem.offsetLeft;
+c.y+=elem.offsetTop;
+_329=elem.offsetParent;
+if(_329!=elem){
+while(_329){
+c.x+=_329.offsetLeft;
+c.y+=_329.offsetTop;
+_329=_329.offsetParent;
+}
+}
+var ua=navigator.userAgent.toLowerCase();
+if((typeof (opera)!="undefined"&&parseFloat(opera.version())<9)||(ua.indexOf("safari")!=-1&&self.computedStyle(elem,"position")=="absolute")){
+c.x-=b.offsetLeft;
+c.y-=b.offsetTop;
+}
+}
+}
+}
+if(typeof (_326)!="undefined"){
+_326=arguments.callee(_326);
+if(_326){
+c.x-=(_326.x||0);
+c.y-=(_326.y||0);
+}
+}
+if(elem.parentNode){
+_329=elem.parentNode;
+}else{
+_329=null;
+}
+while(_329&&_329.tagName!="BODY"&&_329.tagName!="HTML"){
+c.x-=_329.scrollLeft;
+c.y-=_329.scrollTop;
+if(_329.parentNode){
+_329=_329.parentNode;
+}else{
+_329=null;
+}
+}
+return c;
+},setElementDimensions:function(elem,_332,_333){
+elem=MochiKit.DOM.getElement(elem);
+if(typeof (_333)=="undefined"){
+_333="px";
+}
+MochiKit.DOM.updateNodeAttributes(elem,{"style":{"width":_332.w+_333,"height":_332.h+_333}});
+},setElementPosition:function(elem,_334,_335){
+elem=MochiKit.DOM.getElement(elem);
+if(typeof (_335)=="undefined"){
+_335="px";
+}
+MochiKit.DOM.updateNodeAttributes(elem,{"style":{"left":_334.x+_335,"top":_334.y+_335}});
+},currentWindow:function(){
+return MochiKit.DOM._window;
+},currentDocument:function(){
+return MochiKit.DOM._document;
+},withWindow:function(win,func){
+var self=MochiKit.DOM;
+var _337=self._document;
+var _338=self._win;
+var rval;
+try{
+self._window=win;
+self._document=win.document;
+rval=func();
+}
+catch(e){
+self._window=_338;
+self._document=_337;
+throw e;
+}
+self._window=_338;
+self._document=_337;
+return rval;
+},formContents:function(elem){
+var _339=[];
+var _340=[];
+var m=MochiKit.Base;
+var self=MochiKit.DOM;
+if(typeof (elem)=="undefined"||elem===null){
+elem=self._document;
+}else{
+elem=self.getElement(elem);
+}
+m.nodeWalk(elem,function(elem){
+var name=elem.name;
+if(m.isNotEmpty(name)){
+var _341=elem.nodeName;
+if(_341=="INPUT"&&(elem.type=="radio"||elem.type=="checkbox")&&!elem.checked){
+return null;
+}
+if(_341=="SELECT"){
+if(elem.selectedIndex>=0){
+var opt=elem.options[elem.selectedIndex];
+_339.push(name);
+_340.push((opt.value)?opt.value:opt.text);
+return null;
+}
+_339.push(name);
+_340.push("");
+return null;
+}
+if(_341=="FORM"||_341=="P"||_341=="SPAN"||_341=="DIV"){
+return elem.childNodes;
+}
+_339.push(name);
+_340.push(elem.value||"");
+return null;
+}
+return elem.childNodes;
+});
+return [_339,_340];
+},withDocument:function(doc,func){
+var self=MochiKit.DOM;
+var _344=self._document;
+var rval;
+try{
+self._document=doc;
+rval=func();
+}
+catch(e){
+self._document=_344;
+throw e;
+}
+self._document=_344;
+return rval;
+},registerDOMConverter:function(name,_345,wrap,_346){
+MochiKit.DOM.domConverters.register(name,_345,wrap,_346);
+},coerceToDOM:function(node,ctx){
+var im=MochiKit.Iter;
+var self=MochiKit.DOM;
+var iter=im.iter;
+var _350=im.repeat;
+var imap=im.imap;
+var _352=self.domConverters;
+var _353=self.coerceToDOM;
+var _354=MochiKit.Base.NotFound;
+while(true){
+if(typeof (node)=="undefined"||node===null){
+return null;
+}
+if(typeof (node.nodeType)!="undefined"&&node.nodeType>0){
+return node;
+}
+if(typeof (node)=="number"||typeof (node)=="boolean"){
+node=node.toString();
+}
+if(typeof (node)=="string"){
+return self._document.createTextNode(node);
+}
+if(typeof (node.toDOM)=="function"){
+node=node.toDOM(ctx);
+continue;
+}
+if(typeof (node)=="function"){
+node=node(ctx);
+continue;
+}
+var _355=null;
+try{
+_355=iter(node);
+}
+catch(e){
+}
+if(_355){
+return imap(_353,_355,_350(ctx));
+}
+try{
+node=_352.match(node,ctx);
+continue;
+}
+catch(e){
+if(e!=_354){
+throw e;
+}
+}
+return self._document.createTextNode(node.toString());
+}
+return undefined;
+},setNodeAttribute:function(node,attr,_357){
+var o={};
+o[attr]=_357;
+try{
+return MochiKit.DOM.updateNodeAttributes(node,o);
+}
+catch(e){
+}
+return null;
+},getNodeAttribute:function(node,attr){
+var self=MochiKit.DOM;
+var _358=self.attributeArray.renames[attr];
+node=self.getElement(node);
+try{
+if(_358){
+return node[_358];
+}
+return node.getAttribute(attr);
+}
+catch(e){
+}
+return null;
+},updateNodeAttributes:function(node,_359){
+var elem=node;
+var self=MochiKit.DOM;
+if(typeof (node)=="string"){
+elem=self.getElement(node);
+}
+if(_359){
+var _360=MochiKit.Base.updatetree;
+if(self.attributeArray.compliant){
+for(var k in _359){
+var v=_359[k];
+if(typeof (v)=="object"&&typeof (elem[k])=="object"){
+_360(elem[k],v);
+}else{
+if(k.substring(0,2)=="on"){
+if(typeof (v)=="string"){
+v=new Function(v);
+}
+elem[k]=v;
+}else{
+elem.setAttribute(k,v);
+}
+}
+}
+}else{
+var _361=self.attributeArray.renames;
+for(k in _359){
+v=_359[k];
+var _362=_361[k];
+if(k=="style"&&typeof (v)=="string"){
+elem.style.cssText=v;
+}else{
+if(typeof (_362)=="string"){
+elem[_362]=v;
+}else{
+if(typeof (elem[k])=="object"&&typeof (v)=="object"){
+_360(elem[k],v);
+}else{
+if(k.substring(0,2)=="on"){
+if(typeof (v)=="string"){
+v=new Function(v);
+}
+elem[k]=v;
+}else{
+elem.setAttribute(k,v);
+}
+}
+}
+}
+}
+}
+}
+return elem;
+},appendChildNodes:function(node){
+var elem=node;
+var self=MochiKit.DOM;
+if(typeof (node)=="string"){
+elem=self.getElement(node);
+}
+var _363=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)];
+var _364=MochiKit.Base.concat;
+while(_363.length){
+var n=_363.shift();
+if(typeof (n)=="undefined"||n===null){
+}else{
+if(typeof (n.nodeType)=="number"){
+elem.appendChild(n);
+}else{
+_363=_364(n,_363);
+}
+}
+}
+return elem;
+},replaceChildNodes:function(node){
+var elem=node;
+var self=MochiKit.DOM;
+if(typeof (node)=="string"){
+elem=self.getElement(node);
+arguments[0]=elem;
+}
+var _365;
+while((_365=elem.firstChild)){
+elem.removeChild(_365);
+}
+if(arguments.length<2){
+return elem;
+}else{
+return self.appendChildNodes.apply(this,arguments);
+}
+},createDOM:function(name,_366){
+var elem;
+var self=MochiKit.DOM;
+var m=MochiKit.Base;
+if(typeof (_366)=="string"||typeof (_366)=="number"){
+var args=m.extend([name,null],arguments,1);
+return arguments.callee.apply(this,args);
+}
+if(typeof (name)=="string"){
+if(_366&&"name" in _366&&!self.attributeArray.compliant){
+name=("<"+name+" name=\""+self.escapeHTML(_366.name)+"\">");
+}
+elem=self._document.createElement(name);
+}else{
+elem=name;
+}
+if(_366){
+self.updateNodeAttributes(elem,_366);
+}
+if(arguments.length<=2){
+return elem;
+}else{
+var args=m.extend([elem],arguments,2);
+return self.appendChildNodes.apply(this,args);
+}
+},createDOMFunc:function(){
+var m=MochiKit.Base;
+return m.partial.apply(this,m.extend([MochiKit.DOM.createDOM],arguments));
+},swapDOM:function(dest,src){
+var self=MochiKit.DOM;
+dest=self.getElement(dest);
+var _369=dest.parentNode;
+if(src){
+src=self.getElement(src);
+_369.replaceChild(src,dest);
+}else{
+_369.removeChild(dest);
+}
+return src;
+},getElement:function(id){
+var self=MochiKit.DOM;
+if(arguments.length==1){
+return ((typeof (id)=="string")?self._document.getElementById(id):id);
+}else{
+return MochiKit.Base.map(self.getElement,arguments);
+}
+},computedStyle:function(_371,_372,_373){
+if(arguments.length==2){
+_373=_372;
+}
+var self=MochiKit.DOM;
+var el=self.getElement(_371);
+var _375=self._document;
+if(!el||el==_375){
+return undefined;
+}
+if(el.currentStyle){
+return el.currentStyle[_372];
+}
+if(typeof (_375.defaultView)=="undefined"){
+return undefined;
+}
+if(_375.defaultView===null){
+return undefined;
+}
+var _376=_375.defaultView.getComputedStyle(el,null);
+if(typeof (_376)=="undefined"||_376===null){
+return undefined;
+}
+return _376.getPropertyValue(_373);
+},getElementsByTagAndClassName:function(_377,_378,_379){
+var self=MochiKit.DOM;
+if(typeof (_377)=="undefined"||_377===null){
+_377="*";
+}
+if(typeof (_379)=="undefined"||_379===null){
+_379=self._document;
+}
+_379=self.getElement(_379);
+var _380=(_379.getElementsByTagName(_377)||self._document.all);
+if(typeof (_378)=="undefined"||_378===null){
+return MochiKit.Base.extend(null,_380);
+}
+var _381=[];
+for(var i=0;i<_380.length;i++){
+var _382=_380[i];
+var _383=_382.className.split(" ");
+for(var j=0;j<_383.length;j++){
+if(_383[j]==_378){
+_381.push(_382);
+break;
+}
+}
+}
+return _381;
+},_newCallStack:function(path,once){
+var rval=function(){
+var _386=arguments.callee.callStack;
+for(var i=0;i<_386.length;i++){
+if(_386[i].apply(this,arguments)===false){
+break;
+}
+}
+if(once){
+try{
+this[path]=null;
+}
+catch(e){
+}
+}
+};
+rval.callStack=[];
+return rval;
+},addToCallStack:function(_387,path,func,once){
+var self=MochiKit.DOM;
+var _388=_387[path];
+var _389=_388;
+if(!(typeof (_388)=="function"&&typeof (_388.callStack)=="object"&&_388.callStack!==null)){
+_389=self._newCallStack(path,once);
+if(typeof (_388)=="function"){
+_389.callStack.push(_388);
+}
+_387[path]=_389;
+}
+_389.callStack.push(func);
+},addLoadEvent:function(func){
+var self=MochiKit.DOM;
+self.addToCallStack(self._window,"onload",func,true);
+},focusOnLoad:function(_390){
+var self=MochiKit.DOM;
+self.addLoadEvent(function(){
+_390=self.getElement(_390);
+if(_390){
+_390.focus();
+}
+});
+},setElementClass:function(_391,_392){
+var self=MochiKit.DOM;
+var obj=self.getElement(_391);
+if(self.attributeArray.compliant){
+obj.setAttribute("class",_392);
+}else{
+obj.setAttribute("className",_392);
+}
+},toggleElementClass:function(_393){
+var self=MochiKit.DOM;
+for(var i=1;i<arguments.length;i++){
+var obj=self.getElement(arguments[i]);
+if(!self.addElementClass(obj,_393)){
+self.removeElementClass(obj,_393);
+}
+}
+},addElementClass:function(_394,_395){
+var self=MochiKit.DOM;
+var obj=self.getElement(_394);
+var cls=obj.className;
+if(cls.length===0){
+self.setElementClass(obj,_395);
+return true;
+}
+if(cls==_395){
+return false;
+}
+var _397=obj.className.split(" ");
+for(var i=0;i<_397.length;i++){
+if(_397[i]==_395){
+return false;
+}
+}
+self.setElementClass(obj,cls+" "+_395);
+return true;
+},removeElementClass:function(_398,_399){
+var self=MochiKit.DOM;
+var obj=self.getElement(_398);
+var cls=obj.className;
+if(cls.length===0){
+return false;
+}
+if(cls==_399){
+self.setElementClass(obj,"");
+return true;
+}
+var _400=obj.className.split(" ");
+for(var i=0;i<_400.length;i++){
+if(_400[i]==_399){
+_400.splice(i,1);
+self.setElementClass(obj,_400.join(" "));
+return true;
+}
+}
+return false;
+},swapElementClass:function(_401,_402,_403){
+var obj=MochiKit.DOM.getElement(_401);
+var res=MochiKit.DOM.removeElementClass(obj,_402);
+if(res){
+MochiKit.DOM.addElementClass(obj,_403);
+}
+return res;
+},hasElementClass:function(_404,_405){
+var obj=MochiKit.DOM.getElement(_404);
+var _406=obj.className.split(" ");
+for(var i=1;i<arguments.length;i++){
+var good=false;
+for(var j=0;j<_406.length;j++){
+if(_406[j]==arguments[i]){
+good=true;
+break;
+}
+}
+if(!good){
+return false;
+}
+}
+return true;
+},escapeHTML:function(s){
+return s.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
+},toHTML:function(dom){
+return MochiKit.DOM.emitHTML(dom).join("");
+},emitHTML:function(dom,lst){
+if(typeof (lst)=="undefined"||lst===null){
+lst=[];
+}
+var _409=[dom];
+var self=MochiKit.DOM;
+var _410=self.escapeHTML;
+var _411=self.attributeArray;
+while(_409.length){
+dom=_409.pop();
+if(typeof (dom)=="string"){
+lst.push(dom);
+}else{
+if(dom.nodeType==1){
+lst.push("<"+dom.nodeName.toLowerCase());
+var _412=[];
+var _413=_411(dom);
+for(var i=0;i<_413.length;i++){
+var a=_413[i];
+_412.push([" ",a.name,"=\"",_410(a.value),"\""]);
+}
+_412.sort();
+for(i=0;i<_412.length;i++){
+var _414=_412[i];
+for(var j=0;j<_414.length;j++){
+lst.push(_414[j]);
+}
+}
+if(dom.hasChildNodes()){
+lst.push(">");
+_409.push("</"+dom.nodeName.toLowerCase()+">");
+var _415=dom.childNodes;
+for(i=_415.length-1;i>=0;i--){
+_409.push(_415[i]);
+}
+}else{
+lst.push("/>");
+}
+}else{
+if(dom.nodeType==3){
+lst.push(_410(dom.nodeValue));
+}
+}
+}
+}
+return lst;
+},setDisplayForElement:function(_416,_417){
+var m=MochiKit.Base;
+var _418=m.extend(null,arguments,1);
+MochiKit.Iter.forEach(m.filter(null,m.map(MochiKit.DOM.getElement,_418)),function(_417){
+_417.style.display=_416;
+});
+},scrapeText:function(node,_419){
+var rval=[];
+(function(node){
+var cn=node.childNodes;
+if(cn){
+for(var i=0;i<cn.length;i++){
+arguments.callee.call(this,cn[i]);
+}
+}
+var _421=node.nodeValue;
+if(typeof (_421)=="string"){
+rval.push(_421);
+}
+})(MochiKit.DOM.getElement(node));
+if(_419){
+return rval;
+}else{
+return rval.join("");
+}
+},__new__:function(win){
+var m=MochiKit.Base;
+this._document=document;
+this._window=win;
+this.domConverters=new m.AdapterRegistry();
+var _422=this._document.createElement("span");
+var _423;
+if(_422&&_422.attributes&&_422.attributes.length>0){
+var _424=m.filter;
+_423=function(node){
+return _424(_423.ignoreAttrFilter,node.attributes);
+};
+_423.ignoreAttr={};
+MochiKit.Iter.forEach(_422.attributes,function(a){
+_423.ignoreAttr[a.name]=a.value;
+});
+_423.ignoreAttrFilter=function(a){
+return (_423.ignoreAttr[a.name]!=a.value);
+};
+_423.compliant=false;
+_423.renames={"class":"className","checked":"defaultChecked","usemap":"useMap","for":"htmlFor"};
+}else{
+_423=function(node){
+return node.attributes;
+};
+_423.compliant=true;
+_423.renames={};
+}
+this.attributeArray=_423;
+var _425=this.createDOMFunc;
+this.UL=_425("ul");
+this.OL=_425("ol");
+this.LI=_425("li");
+this.TD=_425("td");
+this.TR=_425("tr");
+this.TBODY=_425("tbody");
+this.THEAD=_425("thead");
+this.TFOOT=_425("tfoot");
+this.TABLE=_425("table");
+this.TH=_425("th");
+this.INPUT=_425("input");
+this.SPAN=_425("span");
+this.A=_425("a");
+this.DIV=_425("div");
+this.IMG=_425("img");
+this.BUTTON=_425("button");
+this.TT=_425("tt");
+this.PRE=_425("pre");
+this.H1=_425("h1");
+this.H2=_425("h2");
+this.H3=_425("h3");
+this.BR=_425("br");
+this.HR=_425("hr");
+this.LABEL=_425("label");
+this.TEXTAREA=_425("textarea");
+this.FORM=_425("form");
+this.P=_425("p");
+this.SELECT=_425("select");
+this.OPTION=_425("option");
+this.OPTGROUP=_425("optgroup");
+this.LEGEND=_425("legend");
+this.FIELDSET=_425("fieldset");
+this.STRONG=_425("strong");
+this.CANVAS=_425("canvas");
+this.hideElement=m.partial(this.setDisplayForElement,"none");
+this.showElement=m.partial(this.setDisplayForElement,"block");
+this.removeElement=this.swapDOM;
+this.$=this.getElement;
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+m.nameFunctions(this);
+}});
+MochiKit.DOM.__new__(((typeof (window)=="undefined")?this:window));
+if(!MochiKit.__compat__){
+withWindow=MochiKit.DOM.withWindow;
+withDocument=MochiKit.DOM.withDocument;
+}
+MochiKit.Base._exportSymbols(this,MochiKit.DOM);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.LoggingPane");
+dojo.require("MochiKit.Logging");
+dojo.require("MochiKit.Base");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Logging",[]);
+JSAN.use("MochiKit.Base",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"||typeof (MochiKit.Logging)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.LoggingPane depends on MochiKit.Base and MochiKit.Logging!";
+}
+if(typeof (MochiKit.LoggingPane)=="undefined"){
+MochiKit.LoggingPane={};
+}
+MochiKit.LoggingPane.NAME="MochiKit.LoggingPane";
+MochiKit.LoggingPane.VERSION="1.3.1";
+MochiKit.LoggingPane.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.LoggingPane.toString=function(){
+return this.__repr__();
+};
+MochiKit.LoggingPane.createLoggingPane=function(_426){
+var m=MochiKit.LoggingPane;
+_426=!(!_426);
+if(m._loggingPane&&m._loggingPane.inline!=_426){
+m._loggingPane.closePane();
+m._loggingPane=null;
+}
+if(!m._loggingPane||m._loggingPane.closed){
+m._loggingPane=new m.LoggingPane(_426,MochiKit.Logging.logger);
+}
+return m._loggingPane;
+};
+MochiKit.LoggingPane.LoggingPane=function(_427,_428){
+if(typeof (_428)=="undefined"||_428===null){
+_428=MochiKit.Logging.logger;
+}
+this.logger=_428;
+var _429=MochiKit.Base.update;
+var _430=MochiKit.Base.updatetree;
+var bind=MochiKit.Base.bind;
+var _431=MochiKit.Base.clone;
+var win=window;
+var uid="_MochiKit_LoggingPane";
+if(typeof (MochiKit.DOM)!="undefined"){
+win=MochiKit.DOM.currentWindow();
+}
+if(!_427){
+var url=win.location.href.split("?")[0].replace(/[:\/.><&]/g,"_");
+var name=uid+"_"+url;
+var nwin=win.open("",name,"dependent,resizable,height=200");
+if(!nwin){
+alert("Not able to open debugging window due to pop-up blocking.");
+return undefined;
+}
+nwin.document.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "+"\"http://www.w3.org/TR/html4/loose.dtd\">"+"<html><head><title>[MochiKit.LoggingPane]</title></head>"+"<body></body></html>");
+nwin.document.close();
+nwin.document.title+=" "+win.document.title;
+win=nwin;
+}
+var doc=win.document;
+this.doc=doc;
+var _434=doc.getElementById(uid);
+var _435=!!_434;
+if(_434&&typeof (_434.loggingPane)!="undefined"){
+_434.loggingPane.logger=this.logger;
+_434.loggingPane.buildAndApplyFilter();
+return _434.loggingPane;
+}
+if(_435){
+var _436;
+while((_436=_434.firstChild)){
+_434.removeChild(_436);
+}
+}else{
+_434=doc.createElement("div");
+_434.id=uid;
+}
+_434.loggingPane=this;
+var _437=doc.createElement("input");
+var _438=doc.createElement("input");
+var _439=doc.createElement("button");
+var _440=doc.createElement("button");
+var _441=doc.createElement("button");
+var _442=doc.createElement("button");
+var _443=doc.createElement("div");
+var _444=doc.createElement("div");
+var _445=uid+"_Listener";
+this.colorTable=_431(this.colorTable);
+var _446=[];
+var _447=null;
+var _448=function(msg){
+var _449=msg.level;
+if(typeof (_449)=="number"){
+_449=MochiKit.Logging.LogLevel[_449];
+}
+return _449;
+};
+var _450=function(msg){
+return msg.info.join(" ");
+};
+var _451=bind(function(msg){
+var _452=_448(msg);
+var text=_450(msg);
+var c=this.colorTable[_452];
+var p=doc.createElement("span");
+p.className="MochiKit-LogMessage MochiKit-LogLevel-"+_452;
+p.style.cssText="margin: 0px; white-space: -moz-pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; wrap-option: emergency; color: "+c;
+p.appendChild(doc.createTextNode(_452+": "+text));
+_444.appendChild(p);
+_444.appendChild(doc.createElement("br"));
+if(_443.offsetHeight>_443.scrollHeight){
+_443.scrollTop=0;
+}else{
+_443.scrollTop=_443.scrollHeight;
+}
+},this);
+var _454=function(msg){
+_446[_446.length]=msg;
+_451(msg);
+};
+var _455=function(){
+var _456,infore;
+try{
+_456=new RegExp(_437.value);
+infore=new RegExp(_438.value);
+}
+catch(e){
+logDebug("Error in filter regex: "+e.message);
+return null;
+}
+return function(msg){
+return (_456.test(_448(msg))&&infore.test(_450(msg)));
+};
+};
+var _457=function(){
+while(_444.firstChild){
+_444.removeChild(_444.firstChild);
+}
+};
+var _458=function(){
+_446=[];
+_457();
+};
+var _459=bind(function(){
+if(this.closed){
+return;
+}
+this.closed=true;
+if(MochiKit.LoggingPane._loggingPane==this){
+MochiKit.LoggingPane._loggingPane=null;
+}
+this.logger.removeListener(_445);
+_434.loggingPane=null;
+if(_427){
+_434.parentNode.removeChild(_434);
+}else{
+this.win.close();
+}
+},this);
+var _460=function(){
+_457();
+for(var i=0;i<_446.length;i++){
+var msg=_446[i];
+if(_447===null||_447(msg)){
+_451(msg);
+}
+}
+};
+this.buildAndApplyFilter=function(){
+_447=_455();
+_460();
+this.logger.removeListener(_445);
+this.logger.addListener(_445,_447,_454);
+};
+var _461=bind(function(){
+_446=this.logger.getMessages();
+_460();
+},this);
+var _462=bind(function(_463){
+_463=_463||window.event;
+key=_463.which||_463.keyCode;
+if(key==13){
+this.buildAndApplyFilter();
+}
+},this);
+var _464="display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: "+this.logFont;
+if(_427){
+_464+="; height: 10em; border-top: 2px solid black";
+}else{
+_464+="; height: 100%;";
+}
+_434.style.cssText=_464;
+if(!_435){
+doc.body.appendChild(_434);
+}
+_464={"cssText":"width: 33%; display: inline; font: "+this.logFont};
+_430(_437,{"value":"FATAL|ERROR|WARNING|INFO|DEBUG","onkeypress":_462,"style":_464});
+_434.appendChild(_437);
+_430(_438,{"value":".*","onkeypress":_462,"style":_464});
+_434.appendChild(_438);
+_464="width: 8%; display:inline; font: "+this.logFont;
+_439.appendChild(doc.createTextNode("Filter"));
+_439.onclick=bind("buildAndApplyFilter",this);
+_439.style.cssText=_464;
+_434.appendChild(_439);
+_440.appendChild(doc.createTextNode("Load"));
+_440.onclick=_461;
+_440.style.cssText=_464;
+_434.appendChild(_440);
+_441.appendChild(doc.createTextNode("Clear"));
+_441.onclick=_458;
+_441.style.cssText=_464;
+_434.appendChild(_441);
+_442.appendChild(doc.createTextNode("Close"));
+_442.onclick=_459;
+_442.style.cssText=_464;
+_434.appendChild(_442);
+_443.style.cssText="overflow: auto; width: 100%";
+_444.style.cssText="width: 100%; height: "+(_427?"8em":"100%");
+_443.appendChild(_444);
+_434.appendChild(_443);
+this.buildAndApplyFilter();
+_461();
+if(_427){
+this.win=undefined;
+}else{
+this.win=win;
+}
+this.inline=_427;
+this.closePane=_459;
+this.closed=false;
+return this;
+};
+MochiKit.LoggingPane.LoggingPane.prototype={"logFont":"8pt Verdana,sans-serif","colorTable":{"ERROR":"red","FATAL":"darkred","WARNING":"blue","INFO":"black","DEBUG":"green"}};
+MochiKit.LoggingPane.EXPORT_OK=["LoggingPane"];
+MochiKit.LoggingPane.EXPORT=["createLoggingPane"];
+MochiKit.LoggingPane.__new__=function(){
+this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)};
+MochiKit.Base.nameFunctions(this);
+MochiKit.LoggingPane._loggingPane=null;
+};
+MochiKit.LoggingPane.__new__();
+MochiKit.Base._exportSymbols(this,MochiKit.LoggingPane);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Color");
+dojo.require("MochiKit.Base");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Color depends on MochiKit.Base";
+}
+if(typeof (MochiKit.Color)=="undefined"){
+MochiKit.Color={};
+}
+MochiKit.Color.NAME="MochiKit.Color";
+MochiKit.Color.VERSION="1.3.1";
+MochiKit.Color.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.Color.toString=function(){
+return this.__repr__();
+};
+MochiKit.Color.Color=function(red,_466,blue,_468){
+if(typeof (_468)=="undefined"||_468===null){
+_468=1;
+}
+this.rgb={r:red,g:_466,b:blue,a:_468};
+};
+MochiKit.Color.Color.prototype={__class__:MochiKit.Color.Color,colorWithAlpha:function(_469){
+var rgb=this.rgb;
+var m=MochiKit.Color;
+return m.Color.fromRGB(rgb.r,rgb.g,rgb.b,_469);
+},colorWithHue:function(hue){
+var hsl=this.asHSL();
+hsl.h=hue;
+var m=MochiKit.Color;
+return m.Color.fromHSL(hsl);
+},colorWithSaturation:function(_473){
+var hsl=this.asHSL();
+hsl.s=_473;
+var m=MochiKit.Color;
+return m.Color.fromHSL(hsl);
+},colorWithLightness:function(_474){
+var hsl=this.asHSL();
+hsl.l=_474;
+var m=MochiKit.Color;
+return m.Color.fromHSL(hsl);
+},darkerColorWithLevel:function(_475){
+var hsl=this.asHSL();
+hsl.l=Math.max(hsl.l-_475,0);
+var m=MochiKit.Color;
+return m.Color.fromHSL(hsl);
+},lighterColorWithLevel:function(_476){
+var hsl=this.asHSL();
+hsl.l=Math.min(hsl.l+_476,1);
+var m=MochiKit.Color;
+return m.Color.fromHSL(hsl);
+},blendedColor:function(_477,_478){
+if(typeof (_478)=="undefined"||_478===null){
+_478=0.5;
+}
+var sf=1-_478;
+var s=this.rgb;
+var d=_477.rgb;
+var df=_478;
+return MochiKit.Color.Color.fromRGB((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df));
+},compareRGB:function(_481){
+var a=this.asRGB();
+var b=_481.asRGB();
+return MochiKit.Base.compare([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a]);
+},isLight:function(){
+return this.asHSL().b>0.5;
+},isDark:function(){
+return (!this.isLight());
+},toHSLString:function(){
+var c=this.asHSL();
+var ccc=MochiKit.Color.clampColorComponent;
+var rval=this._hslString;
+if(!rval){
+var mid=(ccc(c.h,360).toFixed(0)+","+ccc(c.s,100).toPrecision(4)+"%"+","+ccc(c.l,100).toPrecision(4)+"%");
+var a=c.a;
+if(a>=1){
+a=1;
+rval="hsl("+mid+")";
+}else{
+if(a<=0){
+a=0;
+}
+rval="hsla("+mid+","+a+")";
+}
+this._hslString=rval;
+}
+return rval;
+},toRGBString:function(){
+var c=this.rgb;
+var ccc=MochiKit.Color.clampColorComponent;
+var rval=this._rgbString;
+if(!rval){
+var mid=(ccc(c.r,255).toFixed(0)+","+ccc(c.g,255).toFixed(0)+","+ccc(c.b,255).toFixed(0));
+if(c.a!=1){
+rval="rgba("+mid+","+c.a+")";
+}else{
+rval="rgb("+mid+")";
+}
+this._rgbString=rval;
+}
+return rval;
+},asRGB:function(){
+return MochiKit.Base.clone(this.rgb);
+},toHexString:function(){
+var m=MochiKit.Color;
+var c=this.rgb;
+var ccc=MochiKit.Color.clampColorComponent;
+var rval=this._hexString;
+if(!rval){
+rval=("#"+m.toColorPart(ccc(c.r,255))+m.toColorPart(ccc(c.g,255))+m.toColorPart(ccc(c.b,255)));
+this._hexString=rval;
+}
+return rval;
+},asHSV:function(){
+var hsv=this.hsv;
+var c=this.rgb;
+if(typeof (hsv)=="undefined"||hsv===null){
+hsv=MochiKit.Color.rgbToHSV(this.rgb);
+this.hsv=hsv;
+}
+return MochiKit.Base.clone(hsv);
+},asHSL:function(){
+var hsl=this.hsl;
+var c=this.rgb;
+if(typeof (hsl)=="undefined"||hsl===null){
+hsl=MochiKit.Color.rgbToHSL(this.rgb);
+this.hsl=hsl;
+}
+return MochiKit.Base.clone(hsl);
+},toString:function(){
+return this.toRGBString();
+},repr:function(){
+var c=this.rgb;
+var col=[c.r,c.g,c.b,c.a];
+return this.__class__.NAME+"("+col.join(", ")+")";
+}};
+MochiKit.Base.update(MochiKit.Color.Color,{fromRGB:function(red,_486,blue,_487){
+var _488=MochiKit.Color.Color;
+if(arguments.length==1){
+var rgb=red;
+red=rgb.r;
+_486=rgb.g;
+blue=rgb.b;
+if(typeof (rgb.a)=="undefined"){
+_487=undefined;
+}else{
+_487=rgb.a;
+}
+}
+return new _488(red,_486,blue,_487);
+},fromHSL:function(hue,_489,_490,_491){
+var m=MochiKit.Color;
+return m.Color.fromRGB(m.hslToRGB.apply(m,arguments));
+},fromHSV:function(hue,_492,_493,_494){
+var m=MochiKit.Color;
+return m.Color.fromRGB(m.hsvToRGB.apply(m,arguments));
+},fromName:function(name){
+var _495=MochiKit.Color.Color;
+if(name.charAt(0)=="\""){
+name=name.substr(1,name.length-2);
+}
+var _496=_495._namedColors[name.toLowerCase()];
+if(typeof (_496)=="string"){
+return _495.fromHexString(_496);
+}else{
+if(name=="transparent"){
+return _495.transparentColor();
+}
+}
+return null;
+},fromString:function(_497){
+var self=MochiKit.Color.Color;
+var _498=_497.substr(0,3);
+if(_498=="rgb"){
+return self.fromRGBString(_497);
+}else{
+if(_498=="hsl"){
+return self.fromHSLString(_497);
+}else{
+if(_497.charAt(0)=="#"){
+return self.fromHexString(_497);
+}
+}
+}
+return self.fromName(_497);
+},fromHexString:function(_499){
+if(_499.charAt(0)=="#"){
+_499=_499.substring(1);
+}
+var _500=[];
+var i,hex;
+if(_499.length==3){
+for(i=0;i<3;i++){
+hex=_499.substr(i,1);
+_500.push(parseInt(hex+hex,16)/255);
+}
+}else{
+for(i=0;i<6;i+=2){
+hex=_499.substr(i,2);
+_500.push(parseInt(hex,16)/255);
+}
+}
+var _501=MochiKit.Color.Color;
+return _501.fromRGB.apply(_501,_500);
+},_fromColorString:function(pre,_503,_504,_505){
+if(_505.indexOf(pre)===0){
+_505=_505.substring(_505.indexOf("(",3)+1,_505.length-1);
+}
+var _506=_505.split(/\s*,\s*/);
+var _507=[];
+for(var i=0;i<_506.length;i++){
+var c=_506[i];
+var val;
+var _508=c.substring(c.length-3);
+if(c.charAt(c.length-1)=="%"){
+val=0.01*parseFloat(c.substring(0,c.length-1));
+}else{
+if(_508=="deg"){
+val=parseFloat(c)/360;
+}else{
+if(_508=="rad"){
+val=parseFloat(c)/(Math.PI*2);
+}else{
+val=_504[i]*parseFloat(c);
+}
+}
+}
+_507.push(val);
+}
+return this[_503].apply(this,_507);
+},fromComputedStyle:function(elem,_509,_510){
+var d=MochiKit.DOM;
+var cls=MochiKit.Color.Color;
+for(elem=d.getElement(elem);elem;elem=elem.parentNode){
+var _511=d.computedStyle.apply(d,arguments);
+if(!_511){
+continue;
+}
+var _512=cls.fromString(_511);
+if(!_512){
+break;
+}
+if(_512.asRGB().a>0){
+return _512;
+}
+}
+return null;
+},fromBackground:function(elem){
+var cls=MochiKit.Color.Color;
+return cls.fromComputedStyle(elem,"backgroundColor","background-color")||cls.whiteColor();
+},fromText:function(elem){
+var cls=MochiKit.Color.Color;
+return cls.fromComputedStyle(elem,"color","color")||cls.blackColor();
+},namedColors:function(){
+return MochiKit.Base.clone(MochiKit.Color.Color._namedColors);
+}});
+MochiKit.Base.update(MochiKit.Color,{clampColorComponent:function(v,_513){
+v*=_513;
+if(v<0){
+return 0;
+}else{
+if(v>_513){
+return _513;
+}else{
+return v;
+}
+}
+},_hslValue:function(n1,n2,hue){
+if(hue>6){
+hue-=6;
+}else{
+if(hue<0){
+hue+=6;
+}
+}
+var val;
+if(hue<1){
+val=n1+(n2-n1)*hue;
+}else{
+if(hue<3){
+val=n2;
+}else{
+if(hue<4){
+val=n1+(n2-n1)*(4-hue);
+}else{
+val=n1;
+}
+}
+}
+return val;
+},hsvToRGB:function(hue,_516,_517,_518){
+if(arguments.length==1){
+var hsv=hue;
+hue=hsv.h;
+_516=hsv.s;
+_517=hsv.v;
+_518=hsv.a;
+}
+var red;
+var _519;
+var blue;
+if(_516===0){
+red=0;
+_519=0;
+blue=0;
+}else{
+var i=Math.floor(hue*6);
+var f=(hue*6)-i;
+var p=_517*(1-_516);
+var q=_517*(1-(_516*f));
+var t=_517*(1-(_516*(1-f)));
+switch(i){
+case 1:
+red=q;
+_519=_517;
+blue=p;
+break;
+case 2:
+red=p;
+_519=_517;
+blue=t;
+break;
+case 3:
+red=p;
+_519=q;
+blue=_517;
+break;
+case 4:
+red=t;
+_519=p;
+blue=_517;
+break;
+case 5:
+red=_517;
+_519=p;
+blue=q;
+break;
+case 6:
+case 0:
+red=_517;
+_519=t;
+blue=p;
+break;
+}
+}
+return {r:red,g:_519,b:blue,a:_518};
+},hslToRGB:function(hue,_521,_522,_523){
+if(arguments.length==1){
+var hsl=hue;
+hue=hsl.h;
+_521=hsl.s;
+_522=hsl.l;
+_523=hsl.a;
+}
+var red;
+var _524;
+var blue;
+if(_521===0){
+red=_522;
+_524=_522;
+blue=_522;
+}else{
+var m2;
+if(_522<=0.5){
+m2=_522*(1+_521);
+}else{
+m2=_522+_521-(_522*_521);
+}
+var m1=(2*_522)-m2;
+var f=MochiKit.Color._hslValue;
+var h6=hue*6;
+red=f(m1,m2,h6+2);
+_524=f(m1,m2,h6);
+blue=f(m1,m2,h6-2);
+}
+return {r:red,g:_524,b:blue,a:_523};
+},rgbToHSV:function(red,_528,blue,_529){
+if(arguments.length==1){
+var rgb=red;
+red=rgb.r;
+_528=rgb.g;
+blue=rgb.b;
+_529=rgb.a;
+}
+var max=Math.max(Math.max(red,_528),blue);
+var min=Math.min(Math.min(red,_528),blue);
+var hue;
+var _532;
+var _533=max;
+if(min==max){
+hue=0;
+_532=0;
+}else{
+var _534=(max-min);
+_532=_534/max;
+if(red==max){
+hue=(_528-blue)/_534;
+}else{
+if(_528==max){
+hue=2+((blue-red)/_534);
+}else{
+hue=4+((red-_528)/_534);
+}
+}
+hue/=6;
+if(hue<0){
+hue+=1;
+}
+if(hue>1){
+hue-=1;
+}
+}
+return {h:hue,s:_532,v:_533,a:_529};
+},rgbToHSL:function(red,_535,blue,_536){
+if(arguments.length==1){
+var rgb=red;
+red=rgb.r;
+_535=rgb.g;
+blue=rgb.b;
+_536=rgb.a;
+}
+var max=Math.max(red,Math.max(_535,blue));
+var min=Math.min(red,Math.min(_535,blue));
+var hue;
+var _537;
+var _538=(max+min)/2;
+var _539=max-min;
+if(_539===0){
+hue=0;
+_537=0;
+}else{
+if(_538<=0.5){
+_537=_539/(max+min);
+}else{
+_537=_539/(2-max-min);
+}
+if(red==max){
+hue=(_535-blue)/_539;
+}else{
+if(_535==max){
+hue=2+((blue-red)/_539);
+}else{
+hue=4+((red-_535)/_539);
+}
+}
+hue/=6;
+if(hue<0){
+hue+=1;
+}
+if(hue>1){
+hue-=1;
+}
+}
+return {h:hue,s:_537,l:_538,a:_536};
+},toColorPart:function(num){
+num=Math.round(num);
+var _540=num.toString(16);
+if(num<16){
+return "0"+_540;
+}
+return _540;
+},__new__:function(){
+var m=MochiKit.Base;
+this.Color.fromRGBString=m.bind(this.Color._fromColorString,this.Color,"rgb","fromRGB",[1/255,1/255,1/255,1]);
+this.Color.fromHSLString=m.bind(this.Color._fromColorString,this.Color,"hsl","fromHSL",[1/360,0.01,0.01,1]);
+var _541=1/3;
+var _542={black:[0,0,0],blue:[0,0,1],brown:[0.6,0.4,0.2],cyan:[0,1,1],darkGray:[_541,_541,_541],gray:[0.5,0.5,0.5],green:[0,1,0],lightGray:[2*_541,2*_541,2*_541],magenta:[1,0,1],orange:[1,0.5,0],purple:[0.5,0,0.5],red:[1,0,0],transparent:[0,0,0,0],white:[1,1,1],yellow:[1,1,0]};
+var _543=function(name,r,g,b,a){
+var rval=this.fromRGB(r,g,b,a);
+this[name]=function(){
+return rval;
+};
+return rval;
+};
+for(var k in _542){
+var name=k+"Color";
+var _545=m.concat([_543,this.Color,name],_542[k]);
+this.Color[name]=m.bind.apply(null,_545);
+}
+var _546=function(){
+for(var i=0;i<arguments.length;i++){
+if(!(arguments[i] instanceof Color)){
+return false;
+}
+}
+return true;
+};
+var _547=function(a,b){
+return a.compareRGB(b);
+};
+m.nameFunctions(this);
+m.registerComparator(this.Color.NAME,_546,_547);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+}});
+MochiKit.Color.EXPORT=["Color"];
+MochiKit.Color.EXPORT_OK=["clampColorComponent","rgbToHSL","hslToRGB","rgbToHSV","hsvToRGB","toColorPart"];
+MochiKit.Color.__new__();
+MochiKit.Base._exportSymbols(this,MochiKit.Color);
+MochiKit.Color.Color._namedColors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Signal");
+dojo.require("MochiKit.Base");
+dojo.require("MochiKit.DOM");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+JSAN.use("MochiKit.DOM",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Signal depends on MochiKit.Base!";
+}
+try{
+if(typeof (MochiKit.DOM)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Signal depends on MochiKit.DOM!";
+}
+if(typeof (MochiKit.Signal)=="undefined"){
+MochiKit.Signal={};
+}
+MochiKit.Signal.NAME="MochiKit.Signal";
+MochiKit.Signal.VERSION="1.3.1";
+MochiKit.Signal._observers=[];
+MochiKit.Signal.Event=function(src,e){
+this._event=e||window.event;
+this._src=src;
+};
+MochiKit.Base.update(MochiKit.Signal.Event.prototype,{__repr__:function(){
+var repr=MochiKit.Base.repr;
+var str="{event(): "+repr(this.event())+", src(): "+repr(this.src())+", type(): "+repr(this.type())+", target(): "+repr(this.target())+", modifier(): "+"{alt: "+repr(this.modifier().alt)+", ctrl: "+repr(this.modifier().ctrl)+", meta: "+repr(this.modifier().meta)+", shift: "+repr(this.modifier().shift)+", any: "+repr(this.modifier().any)+"}";
+if(this.type()&&this.type().indexOf("key")===0){
+str+=", key(): {code: "+repr(this.key().code)+", string: "+repr(this.key().string)+"}";
+}
+if(this.type()&&(this.type().indexOf("mouse")===0||this.type().indexOf("click")!=-1||this.type()=="contextmenu")){
+str+=", mouse(): {page: "+repr(this.mouse().page)+", client: "+repr(this.mouse().client);
+if(this.type()!="mousemove"){
+str+=", button: {left: "+repr(this.mouse().button.left)+", middle: "+repr(this.mouse().button.middle)+", right: "+repr(this.mouse().button.right)+"}}";
+}else{
+str+="}";
+}
+}
+if(this.type()=="mouseover"||this.type()=="mouseout"){
+str+=", relatedTarget(): "+repr(this.relatedTarget());
+}
+str+="}";
+return str;
+},toString:function(){
+return this.__repr__();
+},src:function(){
+return this._src;
+},event:function(){
+return this._event;
+},type:function(){
+return this._event.type||undefined;
+},target:function(){
+return this._event.target||this._event.srcElement;
+},relatedTarget:function(){
+if(this.type()=="mouseover"){
+return (this._event.relatedTarget||this._event.fromElement);
+}else{
+if(this.type()=="mouseout"){
+return (this._event.relatedTarget||this._event.toElement);
+}
+}
+return undefined;
+},modifier:function(){
+var m={};
+m.alt=this._event.altKey;
+m.ctrl=this._event.ctrlKey;
+m.meta=this._event.metaKey||false;
+m.shift=this._event.shiftKey;
+m.any=m.alt||m.ctrl||m.shift||m.meta;
+return m;
+},key:function(){
+var k={};
+if(this.type()&&this.type().indexOf("key")===0){
+if(this.type()=="keydown"||this.type()=="keyup"){
+k.code=this._event.keyCode;
+k.string=(MochiKit.Signal._specialKeys[k.code]||"KEY_UNKNOWN");
+return k;
+}else{
+if(this.type()=="keypress"){
+k.code=0;
+k.string="";
+if(typeof (this._event.charCode)!="undefined"&&this._event.charCode!==0&&!MochiKit.Signal._specialMacKeys[this._event.charCode]){
+k.code=this._event.charCode;
+k.string=String.fromCharCode(k.code);
+}else{
+if(this._event.keyCode&&typeof (this._event.charCode)=="undefined"){
+k.code=this._event.keyCode;
+k.string=String.fromCharCode(k.code);
+}
+}
+return k;
+}
+}
+}
+return undefined;
+},mouse:function(){
+var m={};
+var e=this._event;
+if(this.type()&&(this.type().indexOf("mouse")===0||this.type().indexOf("click")!=-1||this.type()=="contextmenu")){
+m.client=new MochiKit.DOM.Coordinates(0,0);
+if(e.clientX||e.clientY){
+m.client.x=(!e.clientX||e.clientX<0)?0:e.clientX;
+m.client.y=(!e.clientY||e.clientY<0)?0:e.clientY;
+}
+m.page=new MochiKit.DOM.Coordinates(0,0);
+if(e.pageX||e.pageY){
+m.page.x=(!e.pageX||e.pageX<0)?0:e.pageX;
+m.page.y=(!e.pageY||e.pageY<0)?0:e.pageY;
+}else{
+var de=MochiKit.DOM._document.documentElement;
+var b=MochiKit.DOM._document.body;
+m.page.x=e.clientX+(de.scrollLeft||b.scrollLeft)-(de.clientLeft||b.clientLeft);
+m.page.y=e.clientY+(de.scrollTop||b.scrollTop)-(de.clientTop||b.clientTop);
+}
+if(this.type()!="mousemove"){
+m.button={};
+m.button.left=false;
+m.button.right=false;
+m.button.middle=false;
+if(e.which){
+m.button.left=(e.which==1);
+m.button.middle=(e.which==2);
+m.button.right=(e.which==3);
+}else{
+m.button.left=!!(e.button&1);
+m.button.right=!!(e.button&2);
+m.button.middle=!!(e.button&4);
+}
+}
+return m;
+}
+return undefined;
+},stop:function(){
+this.stopPropagation();
+this.preventDefault();
+},stopPropagation:function(){
+if(this._event.stopPropagation){
+this._event.stopPropagation();
+}else{
+this._event.cancelBubble=true;
+}
+},preventDefault:function(){
+if(this._event.preventDefault){
+this._event.preventDefault();
+}else{
+this._event.returnValue=false;
+}
+}});
+MochiKit.Signal._specialMacKeys={3:"KEY_ENTER",63289:"KEY_NUM_PAD_CLEAR",63276:"KEY_PAGE_UP",63277:"KEY_PAGE_DOWN",63275:"KEY_END",63273:"KEY_HOME",63234:"KEY_ARROW_LEFT",63232:"KEY_ARROW_UP",63235:"KEY_ARROW_RIGHT",63233:"KEY_ARROW_DOWN",63302:"KEY_INSERT",63272:"KEY_DELETE"};
+for(i=63236;i<=63242;i++){
+MochiKit.Signal._specialMacKeys[i]="KEY_F"+(i-63236+1);
+}
+MochiKit.Signal._specialKeys={8:"KEY_BACKSPACE",9:"KEY_TAB",12:"KEY_NUM_PAD_CLEAR",13:"KEY_ENTER",16:"KEY_SHIFT",17:"KEY_CTRL",18:"KEY_ALT",19:"KEY_PAUSE",20:"KEY_CAPS_LOCK",27:"KEY_ESCAPE",32:"KEY_SPACEBAR",33:"KEY_PAGE_UP",34:"KEY_PAGE_DOWN",35:"KEY_END",36:"KEY_HOME",37:"KEY_ARROW_LEFT",38:"KEY_ARROW_UP",39:"KEY_ARROW_RIGHT",40:"KEY_ARROW_DOWN",44:"KEY_PRINT_SCREEN",45:"KEY_INSERT",46:"KEY_DELETE",59:"KEY_SEMICOLON",91:"KEY_WINDOWS_LEFT",92:"KEY_WINDOWS_RIGHT",93:"KEY_SELECT",106:"KEY_NUM_PAD_ASTERISK",107:"KEY_NUM_PAD_PLUS_SIGN",109:"KEY_NUM_PAD_HYPHEN-MINUS",110:"KEY_NUM_PAD_FULL_STOP",111:"KEY_NUM_PAD_SOLIDUS",144:"KEY_NUM_LOCK",145:"KEY_SCROLL_LOCK",186:"KEY_SEMICOLON",187:"KEY_EQUALS_SIGN",188:"KEY_COMMA",189:"KEY_HYPHEN-MINUS",190:"KEY_FULL_STOP",191:"KEY_SOLIDUS",192:"KEY_GRAVE_ACCENT",219:"KEY_LEFT_SQUARE_BRACKET",220:"KEY_REVERSE_SOLIDUS",221:"KEY_RIGHT_SQUARE_BRACKET",222:"KEY_APOSTROPHE"};
+for(var i=48;i<=57;i++){
+MochiKit.Signal._specialKeys[i]="KEY_"+(i-48);
+}
+for(i=65;i<=90;i++){
+MochiKit.Signal._specialKeys[i]="KEY_"+String.fromCharCode(i);
+}
+for(i=96;i<=105;i++){
+MochiKit.Signal._specialKeys[i]="KEY_NUM_PAD_"+(i-96);
+}
+for(i=112;i<=123;i++){
+MochiKit.Signal._specialKeys[i]="KEY_F"+(i-112+1);
+}
+MochiKit.Base.update(MochiKit.Signal,{__repr__:function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+},toString:function(){
+return this.__repr__();
+},_unloadCache:function(){
+var self=MochiKit.Signal;
+var _548=self._observers;
+for(var i=0;i<_548.length;i++){
+self._disconnect(_548[i]);
+}
+delete self._observers;
+try{
+window.onload=undefined;
+}
+catch(e){
+}
+try{
+window.onunload=undefined;
+}
+catch(e){
+}
+},_listener:function(src,func,obj,_549){
+var E=MochiKit.Signal.Event;
+if(!_549){
+return MochiKit.Base.bind(func,obj);
+}
+obj=obj||src;
+if(typeof (func)=="string"){
+return function(_551){
+obj[func].apply(obj,[new E(src,_551)]);
+};
+}else{
+return function(_552){
+func.apply(obj,[new E(src,_552)]);
+};
+}
+},connect:function(src,sig,_554,_555){
+src=MochiKit.DOM.getElement(src);
+var self=MochiKit.Signal;
+if(typeof (sig)!="string"){
+throw new Error("'sig' must be a string");
+}
+var obj=null;
+var func=null;
+if(typeof (_555)!="undefined"){
+obj=_554;
+func=_555;
+if(typeof (_555)=="string"){
+if(typeof (_554[_555])!="function"){
+throw new Error("'funcOrStr' must be a function on 'objOrFunc'");
+}
+}else{
+if(typeof (_555)!="function"){
+throw new Error("'funcOrStr' must be a function or string");
+}
+}
+}else{
+if(typeof (_554)!="function"){
+throw new Error("'objOrFunc' must be a function if 'funcOrStr' is not given");
+}else{
+func=_554;
+}
+}
+if(typeof (obj)=="undefined"||obj===null){
+obj=src;
+}
+var _556=!!(src.addEventListener||src.attachEvent);
+var _557=self._listener(src,func,obj,_556);
+if(src.addEventListener){
+src.addEventListener(sig.substr(2),_557,false);
+}else{
+if(src.attachEvent){
+src.attachEvent(sig,_557);
+}
+}
+var _558=[src,sig,_557,_556,_554,_555];
+self._observers.push(_558);
+return _558;
+},_disconnect:function(_559){
+if(!_559[3]){
+return;
+}
+var src=_559[0];
+var sig=_559[1];
+var _560=_559[2];
+if(src.removeEventListener){
+src.removeEventListener(sig.substr(2),_560,false);
+}else{
+if(src.detachEvent){
+src.detachEvent(sig,_560);
+}else{
+throw new Error("'src' must be a DOM element");
+}
+}
+},disconnect:function(_561){
+var self=MochiKit.Signal;
+var _562=self._observers;
+var m=MochiKit.Base;
+if(arguments.length>1){
+var src=MochiKit.DOM.getElement(arguments[0]);
+var sig=arguments[1];
+var obj=arguments[2];
+var func=arguments[3];
+for(var i=_562.length-1;i>=0;i--){
+var o=_562[i];
+if(o[0]===src&&o[1]===sig&&o[4]===obj&&o[5]===func){
+self._disconnect(o);
+_562.splice(i,1);
+return true;
+}
+}
+}else{
+var idx=m.findIdentical(_562,_561);
+if(idx>=0){
+self._disconnect(_561);
+_562.splice(idx,1);
+return true;
+}
+}
+return false;
+},disconnectAll:function(src,sig){
+src=MochiKit.DOM.getElement(src);
+var m=MochiKit.Base;
+var _563=m.flattenArguments(m.extend(null,arguments,1));
+var self=MochiKit.Signal;
+var _564=self._disconnect;
+var _565=self._observers;
+if(_563.length===0){
+for(var i=_565.length-1;i>=0;i--){
+var _566=_565[i];
+if(_566[0]===src){
+_564(_566);
+_565.splice(i,1);
+}
+}
+}else{
+var sigs={};
+for(var i=0;i<_563.length;i++){
+sigs[_563[i]]=true;
+}
+for(var i=_565.length-1;i>=0;i--){
+var _566=_565[i];
+if(_566[0]===src&&_566[1] in sigs){
+_564(_566);
+_565.splice(i,1);
+}
+}
+}
+},signal:function(src,sig){
+var _568=MochiKit.Signal._observers;
+src=MochiKit.DOM.getElement(src);
+var args=MochiKit.Base.extend(null,arguments,2);
+var _569=[];
+for(var i=0;i<_568.length;i++){
+var _570=_568[i];
+if(_570[0]===src&&_570[1]===sig){
+try{
+_570[2].apply(src,args);
+}
+catch(e){
+_569.push(e);
+}
+}
+}
+if(_569.length==1){
+throw _569[0];
+}else{
+if(_569.length>1){
+var e=new Error("Multiple errors thrown in handling 'sig', see errors property");
+e.errors=_569;
+throw e;
+}
+}
+}});
+MochiKit.Signal.EXPORT_OK=[];
+MochiKit.Signal.EXPORT=["connect","disconnect","signal","disconnectAll"];
+MochiKit.Signal.__new__=function(win){
+var m=MochiKit.Base;
+this._document=document;
+this._window=win;
+try{
+this.connect(window,"onunload",this._unloadCache);
+}
+catch(e){
+}
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+m.nameFunctions(this);
+};
+MochiKit.Signal.__new__(this);
+if(!MochiKit.__compat__){
+connect=MochiKit.Signal.connect;
+disconnect=MochiKit.Signal.disconnect;
+disconnectAll=MochiKit.Signal.disconnectAll;
+signal=MochiKit.Signal.signal;
+}
+MochiKit.Base._exportSymbols(this,MochiKit.Signal);
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.Visual");
+dojo.require("MochiKit.Base");
+dojo.require("MochiKit.DOM");
+dojo.require("MochiKit.Color");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+JSAN.use("MochiKit.DOM",[]);
+JSAN.use("MochiKit.Color",[]);
+}
+try{
+if(typeof (MochiKit.Base)=="undefined"||typeof (MochiKit.DOM)=="undefined"||typeof (MochiKit.Color)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM and MochiKit.Color!";
+}
+if(typeof (MochiKit.Visual)=="undefined"){
+MochiKit.Visual={};
+}
+MochiKit.Visual.NAME="MochiKit.Visual";
+MochiKit.Visual.VERSION="1.3.1";
+MochiKit.Visual.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.Visual.toString=function(){
+return this.__repr__();
+};
+MochiKit.Visual._RoundCorners=function(e,_571){
+e=MochiKit.DOM.getElement(e);
+this._setOptions(_571);
+if(this.options.__unstable__wrapElement){
+e=this._doWrap(e);
+}
+var _572=this.options.color;
+var C=MochiKit.Color.Color;
+if(this.options.color=="fromElement"){
+_572=C.fromBackground(e);
+}else{
+if(!(_572 instanceof C)){
+_572=C.fromString(_572);
+}
+}
+this.isTransparent=(_572.asRGB().a<=0);
+var _574=this.options.bgColor;
+if(this.options.bgColor=="fromParent"){
+_574=C.fromBackground(e.offsetParent);
+}else{
+if(!(_574 instanceof C)){
+_574=C.fromString(_574);
+}
+}
+this._roundCornersImpl(e,_572,_574);
+};
+MochiKit.Visual._RoundCorners.prototype={_doWrap:function(e){
+var _575=e.parentNode;
+var doc=MochiKit.DOM.currentDocument();
+if(typeof (doc.defaultView)=="undefined"||doc.defaultView===null){
+return e;
+}
+var _576=doc.defaultView.getComputedStyle(e,null);
+if(typeof (_576)=="undefined"||_576===null){
+return e;
+}
+var _577=MochiKit.DOM.DIV({"style":{display:"block",marginTop:_576.getPropertyValue("padding-top"),marginRight:_576.getPropertyValue("padding-right"),marginBottom:_576.getPropertyValue("padding-bottom"),marginLeft:_576.getPropertyValue("padding-left"),padding:"0px"}});
+_577.innerHTML=e.innerHTML;
+e.innerHTML="";
+e.appendChild(_577);
+return e;
+},_roundCornersImpl:function(e,_578,_579){
+if(this.options.border){
+this._renderBorder(e,_579);
+}
+if(this._isTopRounded()){
+this._roundTopCorners(e,_578,_579);
+}
+if(this._isBottomRounded()){
+this._roundBottomCorners(e,_578,_579);
+}
+},_renderBorder:function(el,_580){
+var _581="1px solid "+this._borderColor(_580);
+var _582="border-left: "+_581;
+var _583="border-right: "+_581;
+var _584="style='"+_582+";"+_583+"'";
+el.innerHTML="<div "+_584+">"+el.innerHTML+"</div>";
+},_roundTopCorners:function(el,_585,_586){
+var _587=this._createCorner(_586);
+for(var i=0;i<this.options.numSlices;i++){
+_587.appendChild(this._createCornerSlice(_585,_586,i,"top"));
+}
+el.style.paddingTop=0;
+el.insertBefore(_587,el.firstChild);
+},_roundBottomCorners:function(el,_588,_589){
+var _590=this._createCorner(_589);
+for(var i=(this.options.numSlices-1);i>=0;i--){
+_590.appendChild(this._createCornerSlice(_588,_589,i,"bottom"));
+}
+el.style.paddingBottom=0;
+el.appendChild(_590);
+},_createCorner:function(_591){
+var dom=MochiKit.DOM;
+return dom.DIV({style:{backgroundColor:_591.toString()}});
+},_createCornerSlice:function(_592,_593,n,_594){
+var _595=MochiKit.DOM.SPAN();
+var _596=_595.style;
+_596.backgroundColor=_592.toString();
+_596.display="block";
+_596.height="1px";
+_596.overflow="hidden";
+_596.fontSize="1px";
+var _597=this._borderColor(_592,_593);
+if(this.options.border&&n===0){
+_596.borderTopStyle="solid";
+_596.borderTopWidth="1px";
+_596.borderLeftWidth="0px";
+_596.borderRightWidth="0px";
+_596.borderBottomWidth="0px";
+_596.height="0px";
+_596.borderColor=_597.toString();
+}else{
+if(_597){
+_596.borderColor=_597.toString();
+_596.borderStyle="solid";
+_596.borderWidth="0px 1px";
+}
+}
+if(!this.options.compact&&(n==(this.options.numSlices-1))){
+_596.height="2px";
+}
+this._setMargin(_595,n,_594);
+this._setBorder(_595,n,_594);
+return _595;
+},_setOptions:function(_598){
+this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false,__unstable__wrapElement:false};
+MochiKit.Base.update(this.options,_598);
+this.options.numSlices=(this.options.compact?2:4);
+},_whichSideTop:function(){
+var _599=this.options.corners;
+if(this._hasString(_599,"all","top")){
+return "";
+}
+var _600=(_599.indexOf("tl")!=-1);
+var _601=(_599.indexOf("tr")!=-1);
+if(_600&&_601){
+return "";
+}
+if(_600){
+return "left";
+}
+if(_601){
+return "right";
+}
+return "";
+},_whichSideBottom:function(){
+var _602=this.options.corners;
+if(this._hasString(_602,"all","bottom")){
+return "";
+}
+var _603=(_602.indexOf("bl")!=-1);
+var _604=(_602.indexOf("br")!=-1);
+if(_603&&_604){
+return "";
+}
+if(_603){
+return "left";
+}
+if(_604){
+return "right";
+}
+return "";
+},_borderColor:function(_605,_606){
+if(_605=="transparent"){
+return _606;
+}else{
+if(this.options.border){
+return this.options.border;
+}else{
+if(this.options.blend){
+return _606.blendedColor(_605);
+}
+}
+}
+return "";
+},_setMargin:function(el,n,_607){
+var _608=this._marginSize(n)+"px";
+var _609=(_607=="top"?this._whichSideTop():this._whichSideBottom());
+var _610=el.style;
+if(_609=="left"){
+_610.marginLeft=_608;
+_610.marginRight="0px";
+}else{
+if(_609=="right"){
+_610.marginRight=_608;
+_610.marginLeft="0px";
+}else{
+_610.marginLeft=_608;
+_610.marginRight=_608;
+}
+}
+},_setBorder:function(el,n,_611){
+var _612=this._borderSize(n)+"px";
+var _613=(_611=="top"?this._whichSideTop():this._whichSideBottom());
+var _614=el.style;
+if(_613=="left"){
+_614.borderLeftWidth=_612;
+_614.borderRightWidth="0px";
+}else{
+if(_613=="right"){
+_614.borderRightWidth=_612;
+_614.borderLeftWidth="0px";
+}else{
+_614.borderLeftWidth=_612;
+_614.borderRightWidth=_612;
+}
+}
+},_marginSize:function(n){
+if(this.isTransparent){
+return 0;
+}
+var o=this.options;
+if(o.compact&&o.blend){
+var _615=[1,0];
+return _615[n];
+}else{
+if(o.compact){
+var _616=[2,1];
+return _616[n];
+}else{
+if(o.blend){
+var _617=[3,2,1,0];
+return _617[n];
+}else{
+var _618=[5,3,2,1];
+return _618[n];
+}
+}
+}
+},_borderSize:function(n){
+var o=this.options;
+var _619;
+if(o.compact&&(o.blend||this.isTransparent)){
+return 1;
+}else{
+if(o.compact){
+_619=[1,0];
+}else{
+if(o.blend){
+_619=[2,1,1,1];
+}else{
+if(o.border){
+_619=[0,2,0,0];
+}else{
+if(this.isTransparent){
+_619=[5,3,2,1];
+}else{
+return 0;
+}
+}
+}
+}
+}
+return _619[n];
+},_hasString:function(str){
+for(var i=1;i<arguments.length;i++){
+if(str.indexOf(arguments[i])!=-1){
+return true;
+}
+}
+return false;
+},_isTopRounded:function(){
+return this._hasString(this.options.corners,"all","top","tl","tr");
+},_isBottomRounded:function(){
+return this._hasString(this.options.corners,"all","bottom","bl","br");
+},_hasSingleTextChild:function(el){
+return (el.childNodes.length==1&&el.childNodes[0].nodeType==3);
+}};
+MochiKit.Visual.roundElement=function(e,_620){
+new MochiKit.Visual._RoundCorners(e,_620);
+};
+MochiKit.Visual.roundClass=function(_621,_622,_623){
+var _624=MochiKit.DOM.getElementsByTagAndClassName(_621,_622);
+for(var i=0;i<_624.length;i++){
+MochiKit.Visual.roundElement(_624[i],_623);
+}
+};
+MochiKit.Visual.Color=MochiKit.Color.Color;
+MochiKit.Visual.getElementsComputedStyle=MochiKit.DOM.computedStyle;
+MochiKit.Visual.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+MochiKit.Visual.EXPORT=["roundElement","roundClass"];
+MochiKit.Visual.EXPORT_OK=[];
+MochiKit.Visual.__new__();
+MochiKit.Base._exportSymbols(this,MochiKit.Visual);
+if(typeof (MochiKit)=="undefined"){
+MochiKit={};
+}
+if(typeof (MochiKit.MochiKit)=="undefined"){
+MochiKit.MochiKit={};
+}
+MochiKit.MochiKit.NAME="MochiKit.MochiKit";
+MochiKit.MochiKit.VERSION="1.3.1";
+MochiKit.MochiKit.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+MochiKit.MochiKit.toString=function(){
+return this.__repr__();
+};
+MochiKit.MochiKit.SUBMODULES=["Base","Iter","Logging","DateTime","Format","Async","DOM","LoggingPane","Color","Signal","Visual"];
+if(typeof (JSAN)!="undefined"||typeof (dojo)!="undefined"){
+if(typeof (dojo)!="undefined"){
+dojo.provide("MochiKit.MochiKit");
+dojo.require("MochiKit.*");
+}
+if(typeof (JSAN)!="undefined"){
+JSAN.use("MochiKit.Base",[]);
+JSAN.use("MochiKit.Iter",[]);
+JSAN.use("MochiKit.Logging",[]);
+JSAN.use("MochiKit.DateTime",[]);
+JSAN.use("MochiKit.Format",[]);
+JSAN.use("MochiKit.Async",[]);
+JSAN.use("MochiKit.DOM",[]);
+JSAN.use("MochiKit.LoggingPane",[]);
+JSAN.use("MochiKit.Color",[]);
+JSAN.use("MochiKit.Signal",[]);
+JSAN.use("MochiKit.Visual",[]);
+}
+(function(){
+var _625=MochiKit.Base.extend;
+var self=MochiKit.MochiKit;
+var _626=self.SUBMODULES;
+var _627=[];
+var _628=[];
+var _629={};
+var i,k,m,all;
+for(i=0;i<_626.length;i++){
+m=MochiKit[_626[i]];
+_625(_627,m.EXPORT);
+_625(_628,m.EXPORT_OK);
+for(k in m.EXPORT_TAGS){
+_629[k]=_625(_629[k],m.EXPORT_TAGS[k]);
+}
+all=m.EXPORT_TAGS[":all"];
+if(!all){
+all=_625(null,m.EXPORT,m.EXPORT_OK);
+}
+var j;
+for(j=0;j<all.length;j++){
+k=all[j];
+self[k]=m[k];
+}
+}
+self.EXPORT=_627;
+self.EXPORT_OK=_628;
+self.EXPORT_TAGS=_629;
+}());
+}else{
+if(typeof (MochiKit.__compat__)=="undefined"){
+MochiKit.__compat__=true;
+}
+(function(){
+var _630=document.getElementsByTagName("script");
+var _631="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var base=null;
+var _632=null;
+var _633={};
+var i;
+for(i=0;i<_630.length;i++){
+var src=_630[i].getAttribute("src");
+if(!src){
+continue;
+}
+_633[src]=true;
+if(src.match(/MochiKit.js$/)){
+base=src.substring(0,src.lastIndexOf("MochiKit.js"));
+_632=_630[i];
+}
+}
+if(base===null){
+return;
+}
+var _634=MochiKit.MochiKit.SUBMODULES;
+for(var i=0;i<_634.length;i++){
+if(MochiKit[_634[i]]){
+continue;
+}
+var uri=base+_634[i]+".js";
+if(uri in _633){
+continue;
+}
+if(document.documentElement&&document.documentElement.namespaceURI==_631){
+var s=document.createElementNS(_631,"script");
+s.setAttribute("id","MochiKit_"+base+_634[i]);
+s.setAttribute("src",uri);
+s.setAttribute("type","application/x-javascript");
+_632.parentNode.appendChild(s);
+}else{
+document.write("<script src=\""+uri+"\" type=\"text/javascript\"></script>");
+}
+}
+})();
+}
+
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/__package__.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/MochiKit/__package__.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,2 @@
+dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
+dojo.hostenv.moduleLoaded("MochiKit.*");

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Base.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Base.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,406 @@
+/*
+    PlotKit
+    =======
+    PlotKit is a collection of Javascript classes that allows
+    you to quickly visualise data using different types of charts.
+
+    For license/info/documentation: http://www.liquidx.net/plotkit/
+
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+*/
+
+// --------------------------------------------------------------------
+// Check required components
+// --------------------------------------------------------------------
+
+try {    
+    if (typeof(MochiKit.Base) == 'undefined'   ||
+        typeof(MochiKit.DOM) == 'undefined'    ||
+        typeof(MochiKit.Color) == 'undefined'  ||
+        typeof(MochiKit.Format) == 'undefined')
+    {
+        throw "";    
+    }
+} 
+catch (e) {    
+    throw "PlotKit depends on MochiKit.{Base,Color,DOM,Format}"
+}
+
+// -------------------------------------------------------------------
+// Inject Common Shortcuts we use into MochiKit.Color.Color
+// -------------------------------------------------------------------
+
+MochiKit.Base.update(MochiKit.Color.Color.prototype, {
+    asFillColor: function() {
+        return this.lighterColorWithLevel(0.3);
+    },
+        
+    asStrokeColor: function() {
+        return this.darkerColorWithLevel(0.1);
+    },
+
+    asPointColor: function() {
+        return this.lighterColorWithLevel(0.1);
+    }
+});
+
+
+// -------------------------------------------------------------------
+// Define our own PlotKit namespace
+// -------------------------------------------------------------------
+
+if (typeof(PlotKit) == 'undefined') {
+    PlotKit = {};
+}
+
+PlotKit.NAME = "PlotKit";
+PlotKit.VERSION = "0.8";
+PlotKit.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.toString = function() {
+    return this.__repr__();
+}
+
+// -------------------------------------------------------------------
+//  Encapsulate all our utility function into it's own namespace.
+// -------------------------------------------------------------------
+
+if (typeof(PlotKit.Base) == 'undefined') {
+    PlotKit.Base = {};
+}
+
+PlotKit.Base.NAME = 'PlotKit.Base';
+PlotKit.Base.VERSION = PlotKit.VERSION;
+
+PlotKit.Base.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.Base.toString = function() {
+    return this.__repr__();
+}
+
+
+// Detect whether we are using prototype.js
+PlotKit.Base.usingPrototype =  function() {
+    try {
+        return (typeof(Object.extend) == 'function');
+    }
+    catch (e) {
+        return false;
+    }
+}
+
+
+MochiKit.Base.update(PlotKit.Base, {
+    roundInterval: function(range, intervals, precision) {
+        // We want to make the interval look regular,
+        var trunc = MochiKit.Format.roundToFixed;
+        var sep = range/intervals;
+        return parseFloat(trunc(sep, precision));
+    },
+
+    collapse: function(lst) {
+        var m = MochiKit.Base;
+        var biggerList = new Array();
+        for (var i = 0; i < lst.length; i++) {
+            biggerList = m.concat(biggerList, lst[i]);
+        }
+        if (PlotKit.Base.usingPrototype()) {
+            delete biggerList.extend;
+            delete biggerList.from;
+            delete biggerList.inspect;
+        }
+        
+        return biggerList;
+    },
+    
+    uniq: function(sortedList) {
+        // get unique elements in list, exactly the same as unix shell's uniq.
+        var m = MochiKit.Base;
+        
+        if (!m.isArrayLike(sortedList) || (sortedList.length < 1))
+            return new Array();
+
+        var uniq = new Array();
+        var lastElem = sortedList[0];    
+        uniq.push(sortedList[0]);
+        for (var i = 1; i < sortedList.length; i++) {
+            if (m.compare(sortedList[i], lastElem) != 0) {
+                lastElem = sortedList[i];
+                uniq.push(sortedList[i]);            
+            }
+        }
+        return uniq;
+    },
+    
+    colorScheme: function() {
+        var mb = MochiKit.Base;
+        var mc = MochiKit.Color
+        var scheme = ["red", "orange", "yellow", "green", "cyan", "blue", "purple", "magenta"];
+        
+        var makeColor = function(name) {
+            return mc.Color[name + "Color"]()
+        };
+        
+        return mb.map(makeColor, scheme);
+    },
+
+    baseDarkPrimaryColors: function () {
+        var hexColor = MochiKit.Color.Color.fromHexString;
+        return [hexColor("#ad3f40"),
+                hexColor("#ddac2c"),
+                hexColor("#dfdd0c"),
+                hexColor("#5276c4"),
+                hexColor("#739c5a")];
+    },
+
+    basePrimaryColors: function () {
+        var hexColor = MochiKit.Color.Color.fromHexString;
+        return [hexColor("#d24c4d"),
+                hexColor("#f2b32f"),
+                hexColor("#ece90e"),
+                hexColor("#5d83da"),
+                hexColor("#78a15d")];
+    },
+    
+    baseBlueColors: function () {
+         var hexColor = MochiKit.Color.Color.fromHexString;
+         return [hexColor("#4b6b94"), hexColor("#5d81b4"), hexColor("#acbad2")];
+    },
+
+    palette: function(baseColor, fromLevel, toLevel, increment) {
+        var isNil = MochiKit.Base.isUndefinedOrNull;
+        var fractions = new Array();
+        if (isNil(increment))
+            increment = 0.1;
+        if (isNil(toLevel))
+            toLevel = 0.4;
+        if (isNil(fromLevel))
+            fromLevel = -0.2;
+
+        var level = fromLevel;
+        while (level <= toLevel) {
+            fractions.push(level);
+            level += increment;
+        }
+            
+        var makeColor = function(color, fraction) {
+            return color.lighterColorWithLevel(fraction);
+        };
+        return MochiKit.Base.map(partial(makeColor, baseColor), fractions);
+    },
+    
+    excanvasSupported: function() {
+         if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+             return true;
+         }
+         return false;
+    },
+
+    // The following functions are from quirksmode.org
+    // http://www.quirksmode.org/js/findpos.html
+
+    findPosX: function(obj) {
+        var curleft = 0;
+        if (obj.offsetParent) {
+            while (obj.offsetParent) {
+                    curleft += obj.offsetLeft
+                        obj = obj.offsetParent;
+            }
+        }
+        else if (obj.x)
+            curleft += obj.x;
+        return curleft;
+    },
+                       
+   findPosY: function(obj) {
+       var curtop = 0;
+       if (obj.offsetParent) {
+           while (obj.offsetParent) {
+               curtop += obj.offsetTop
+               obj = obj.offsetParent;
+           }
+       }
+       else if (obj.y)
+           curtop += obj.y;
+       return curtop;
+   },
+   
+   isFuncLike: function(obj) {
+       return (typeof(obj) == 'function');
+   }
+});    
+
+//
+// Prototype.js aware (crippled) versions of map and items.
+//
+
+PlotKit.Base.map = function(fn, lst) {
+    if (PlotKit.Base.usingPrototype()) {
+        var rval = [];
+        for (var x in lst) {
+            if (typeof(lst[x]) == 'function') continue;
+            rval.push(fn(lst[x]));
+        }
+        return rval;
+    }
+    else {
+        return MochiKit.Base.map(fn, lst);
+    }
+};
+
+PlotKit.Base.items = function(lst) {
+    if (PlotKit.Base.usingPrototype()) {
+        var rval = [];
+         for (var x in lst) {
+             if (typeof(lst[x]) == 'function') continue;
+             rval.push([x, lst[x]]);
+         }
+         return rval;
+    }
+    else {
+        return MochiKit.Base.items(lst);
+    }
+};
+
+PlotKit.Base.keys = function(lst) {
+    if (PlotKit.Base.usingPrototype()) {
+        var rval = [];
+         for (var x in lst) {
+             if (typeof(lst[x]) == 'function') continue;
+             rval.push(x);
+         }
+         return rval;
+    }
+    else {
+        return MochiKit.Base.keys(lst);
+    }
+};
+
+// 
+// colour schemes
+//
+
+PlotKit.Base.baseColors = function () {
+   var hexColor = MochiKit.Color.Color.fromHexString;
+   return [hexColor("#476fb2"),
+           hexColor("#be2c2b"),
+           hexColor("#85b730"),
+           hexColor("#734a99"),
+           hexColor("#26a1c5"),
+           hexColor("#fb8707"),
+           hexColor("#000000")];
+};
+
+PlotKit.Base.officeBaseStyle = {
+    "axisLineWidth": 2.0,
+    "axisLabelColor": Color.grayColor(),
+    "axisLineColor": Color.whiteColor(),
+    "padding": {top: 5, bottom: 10, left: 30, right: 30}
+};    
+
+MochiKit.Base.update(PlotKit.Base,{
+    officeBlue: function() {
+        var r = {
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),
+        "backgroundColor": PlotKit.Base.baseColors()[0].lighterColorWithLevel(0.45)
+        };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    officeRed: function() {
+        var r = {
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[1]),
+        "backgroundColor": PlotKit.Base.baseColors()[1].lighterColorWithLevel(0.5)
+        };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    officeGreen: function() {
+        var r = {
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[2]),
+        "backgroundColor": PlotKit.Base.baseColors()[2].lighterColorWithLevel(0.5)
+        };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    officePurple: function() {
+        var r = {
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[3]),
+        "backgroundColor": PlotKit.Base.baseColors()[3].lighterColorWithLevel(0.5)
+        };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    
+    officeCyan: function() {
+        var r = {
+            "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[4]),
+            "backgroundColor": PlotKit.Base.baseColors()[4].lighterColorWithLevel(0.5)
+            };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    
+    officeOrange: function() {
+        var r = {
+            "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[5]),
+            "backgroundColor": PlotKit.Base.baseColors()[5].lighterColorWithLevel(0.4)
+            };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    },
+    
+    officeBlack: function() {
+        var r = {
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[6], 0.0, 0.6),
+        "backgroundColor": PlotKit.Base.baseColors()[6].lighterColorWithLevel(0.9)
+        };
+        MochiKit.Base.update(r, PlotKit.Base.officeBaseStyle);
+        return r;
+    }
+});
+
+
+PlotKit.Base.EXPORT = [
+   "baseColors",
+   "collapse",
+   "colorScheme",
+   "findPosX",
+   "findPosY",
+   "officeBaseStyle",
+   "officeBlue",
+   "officeRed",
+   "officeGreen",
+   "officePurple",
+   "officeCyan",
+   "officeOrange",
+   "officeBlack",
+   "roundInterval",
+   "uniq",
+   "isFuncLike",
+   "excanvasSupported"
+];
+
+PlotKit.Base.EXPORT_OK = [];
+
+PlotKit.Base.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.Base.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.Base);
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Canvas.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Canvas.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,683 @@
+/* 
+    PlotKit Canvas
+    ==============
+    
+    Provides HTML Canvas Renderer. This is supported under:
+    
+    - Safari 2.0
+    - Mozilla Firefox 1.5
+    - Opera 9.0 preview 2
+    - IE 6 (via VML Emulation)
+    
+    It uses DIVs for labels.
+    
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+    
+*/
+// --------------------------------------------------------------------
+// Check required components
+// --------------------------------------------------------------------
+
+try {    
+    if ((typeof(PlotKit.Base) == 'undefined') ||
+        (typeof(PlotKit.Layout) == 'undefined'))
+    {
+        throw "";    
+    }
+} 
+catch (e) {    
+    throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Base,Layout}"
+}
+
+
+// ------------------------------------------------------------------------
+//  Defines the renderer class
+// ------------------------------------------------------------------------
+
+if (typeof(PlotKit.CanvasRenderer) == 'undefined') {
+    PlotKit.CanvasRenderer = {};
+}
+
+PlotKit.CanvasRenderer.NAME = "PlotKit.CanvasRenderer";
+PlotKit.CanvasRenderer.VERSION = PlotKit.VERSION;
+
+PlotKit.CanvasRenderer.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.CanvasRenderer.toString = function() {
+    return this.__repr__();
+}
+
+PlotKit.CanvasRenderer = function(element, layout, options) {
+    if (arguments.length  > 0)
+        this.__init__(element, layout, options);
+};
+
+PlotKit.CanvasRenderer.prototype.__init__ = function(element, layout, options) {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+    var Color = MochiKit.Color.Color;
+    
+    // default options
+    this.options = {
+        "drawBackground": true,
+        "backgroundColor": Color.whiteColor(),
+        "padding": {left: 30, right: 30, top: 5, bottom: 10},
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),
+        "strokeColor": Color.whiteColor(),
+        "strokeColorTransform": "asStrokeColor",
+        "strokeWidth": 0.5,
+        "shouldFill": true,
+        "shouldStroke": true,
+        "drawXAxis": true,
+        "drawYAxis": true,
+        "axisLineColor": Color.blackColor(),
+        "axisLineWidth": 0.5,
+        "axisTickSize": 3,
+        "axisLabelColor": Color.blackColor(),
+        "axisLabelFont": "Arial",
+        "axisLabelFontSize": 9,
+		"axisLabelWidth": 50,
+		"pieRadius": 0.4,
+        "enableEvents": true
+    };
+    MochiKit.Base.update(this.options, options ? options : {});
+
+    this.layout = layout;
+    this.element = MochiKit.DOM.getElement(element);
+    this.container = this.element.parentNode;
+
+    // Stuff relating to Canvas on IE support    
+    this.isIE = PlotKit.Base.excanvasSupported();
+
+    if (this.isIE && !isNil(G_vmlCanvasManager)) {
+        this.IEDelay = 0.5;
+        this.maxTries = 5;
+        this.renderDelay = null;
+        this.clearDelay = null;
+        this.element = G_vmlCanvasManager.initElement(this.element);
+    }
+
+    this.height = this.element.height;
+    this.width = this.element.width;
+
+    // --- check whether everything is ok before we return
+
+    if (isNil(this.element))
+        throw "CanvasRenderer() - passed canvas is not found";
+
+    if (!this.isIE && !(PlotKit.CanvasRenderer.isSupported(this.element)))
+        throw "CanvasRenderer() - Canvas is not supported.";
+
+    if (isNil(this.container) || (this.container.nodeName.toLowerCase() != "div"))
+        throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
+
+    // internal state
+    this.xlabels = new Array();
+    this.ylabels = new Array();
+    this.isFirstRender = true;
+
+    this.area = {
+        x: this.options.padding.left,
+        y: this.options.padding.top,
+        w: this.width - this.options.padding.left - this.options.padding.right,
+        h: this.height - this.options.padding.top - this.options.padding.bottom
+    };
+
+    MochiKit.DOM.updateNodeAttributes(this.container, 
+    {"style":{ "position": "relative", "width": this.width + "px"}});
+
+    // load event system if we have Signals
+    /* Disabled until we have a proper implementation
+    try {
+        this.event_isinside = null;
+        if (MochiKit.Signal && this.options.enableEvents) {
+            this._initialiseEvents();
+        }
+    }
+    catch (e) {
+        // still experimental
+    }
+    */
+};
+
+PlotKit.CanvasRenderer.prototype.render = function() {
+    if (this.isIE) {
+        // VML takes a while to start up, so we just poll every this.IEDelay
+        try {
+            if (this.renderDelay) {
+                this.renderDelay.cancel();
+                this.renderDelay = null;
+            }
+            var context = this.element.getContext("2d");
+        }
+        catch (e) {
+            this.isFirstRender = false;
+            if (this.maxTries-- > 0) {
+                this.renderDelay = MochiKit.Async.wait(this.IEDelay);
+                this.renderDelay.addCallback(bind(this.render, this));
+            }
+            return;
+        }
+    }
+
+    if (this.options.drawBackground)
+        this._renderBackground();
+
+    if (this.layout.style == "bar") {
+        this._renderBarChart();
+		this._renderBarAxis(); 
+	}
+    else if (this.layout.style == "pie") {
+        this._renderPieChart();
+		this._renderPieAxis();
+	}
+    else if (this.layout.style == "line") {
+        this._renderLineChart();
+		this._renderLineAxis();
+	}
+};
+
+PlotKit.CanvasRenderer.prototype._renderBarChartWrap = function(data, plotFunc) {
+    var context = this.element.getContext("2d");
+    var colorCount = this.options.colorScheme.length;
+    var colorScheme = this.options.colorScheme;
+    var setNames = MochiKit.Base.keys(this.layout.datasets);
+    var setCount = setNames.length;
+
+    for (var i = 0; i < setCount; i++) {
+        var setName = setNames[i];
+        var color = colorScheme[i%colorCount];
+        context.save();
+        context.fillStyle = color.toRGBString();
+        if (this.options.strokeColor)
+            context.strokeStyle = this.options.strokeColor.toRGBString();
+        else if (this.options.strokeColorTransform) 
+            context.strokeStyle = color[this.options.strokeColorTransform]().toRGBString();
+        
+        context.lineWidth = this.options.strokeWidth;
+        var forEachFunc = function(obj) {
+            if (obj.name == setName)
+                plotFunc(context, obj);
+        };                
+
+        MochiKit.Iter.forEach(data, bind(forEachFunc, this));
+        context.restore();
+    }
+};
+
+PlotKit.CanvasRenderer.prototype._renderBarChart = function() {
+    var bind = MochiKit.Base.bind;
+
+    var drawRect = function(context, bar) {
+        var x = this.area.w * bar.x + this.area.x;
+        var y = this.area.h * bar.y + this.area.y;
+        var w = this.area.w * bar.w;
+        var h = this.area.h * bar.h;       
+        if ((w < 1) || (h < 1))
+            return;
+        if (this.options.shouldFill)
+            context.fillRect(x, y, w, h);
+        if (this.options.shouldStroke)
+            context.strokeRect(x, y, w, h);                
+    };
+    this._renderBarChartWrap(this.layout.bars, bind(drawRect, this));
+};
+
+PlotKit.CanvasRenderer.prototype._renderLineChart = function() {
+    var context = this.element.getContext("2d");
+    var colorCount = this.options.colorScheme.length;
+    var colorScheme = this.options.colorScheme;
+    var setNames = MochiKit.Base.keys(this.layout.datasets);
+    var setCount = setNames.length;
+    var bind = MochiKit.Base.bind;
+    var partial = MochiKit.Base.partial;
+
+    for (var i = 0; i < setCount; i++) {
+        var setName = setNames[i];
+        var color = colorScheme[i%colorCount];
+        var strokeX = this.options.strokeColorTransform;
+
+        // setup graphics context
+        context.save();
+        context.fillStyle = color.toRGBString();
+        if (this.options.strokeColor)
+            context.strokeStyle = this.options.strokeColor.toRGBString();
+        else if (this.options.strokeColorTransform) 
+            context.strokeStyle = color[strokeX]().toRGBString();
+        
+        context.lineWidth = this.options.strokeWidth;
+        
+        // create paths
+        var makePath = function(ctx) {
+            ctx.beginPath();
+            ctx.moveTo(this.area.x, this.area.y + this.area.h);
+            var addPoint = function(ctx_, point) {
+                if (point.name == setName)
+                    ctx_.lineTo(this.area.w * point.x + this.area.x,
+                                this.area.h * point.y + this.area.y);
+            };
+            MochiKit.Iter.forEach(this.layout.points, partial(addPoint, ctx), this);
+            ctx.lineTo(this.area.w + this.area.x,
+                           this.area.h + this.area.y);
+            ctx.lineTo(this.area.x, this.area.y + this.area.h);
+            ctx.closePath();
+        };
+
+        if (this.options.shouldFill) {
+            bind(makePath, this)(context);
+            context.fill();
+        }
+        if (this.options.shouldStroke) {
+            bind(makePath, this)(context);
+            context.stroke();
+        }
+
+        context.restore();
+    }
+};
+
+PlotKit.CanvasRenderer.prototype._renderPieChart = function() {
+    var context = this.element.getContext("2d");
+    var colorCount = this.options.colorScheme.length;
+    var slices = this.layout.slices;
+
+    var centerx = this.area.x + this.area.w * 0.5;
+    var centery = this.area.y + this.area.h * 0.5;
+    var radius = Math.min(this.area.w * this.options.pieRadius, 
+                          this.area.h * this.options.pieRadius);
+
+    if (this.isIE) {
+        centerx = parseInt(centerx);
+        centery = parseInt(centery);
+        radius = parseInt(radius);
+    }
+
+
+	// NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1
+	// so we have to subtract 90 degrees to make it start at y = 1, x = 0
+
+    for (var i = 0; i < slices.length; i++) {
+        var color = this.options.colorScheme[i%colorCount];
+        context.save();
+        context.fillStyle = color.toRGBString();
+
+        var makePath = function() {
+            context.beginPath();
+            context.moveTo(centerx, centery);
+            context.arc(centerx, centery, radius, 
+                        slices[i].startAngle - Math.PI/2,
+                        slices[i].endAngle - Math.PI/2,
+                        false);
+            context.lineTo(centerx, centery);
+            context.closePath();
+        };
+
+        if (Math.abs(slices[i].startAngle - slices[i].endAngle) > 0.001) {
+            if (this.options.shouldFill) {
+                makePath();
+                context.fill();
+            }
+            
+            if (this.options.shouldStroke) {
+                makePath();
+                context.lineWidth = this.options.strokeWidth;
+                if (this.options.strokeColor)
+                    context.strokeStyle = this.options.strokeColor.toRGBString();
+                else if (this.options.strokeColorTransform)
+                    context.strokeStyle = color[this.options.strokeColorTransform]().toRGBString();
+                context.stroke();
+            }
+        }
+        context.restore();
+    }
+};
+
+PlotKit.CanvasRenderer.prototype._renderBarAxis = function() {
+	this._renderAxis();
+}
+
+PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
+	this._renderAxis();
+};
+
+
+PlotKit.CanvasRenderer.prototype._renderAxis = function() {
+    if (!this.options.drawXAxis && !this.options.drawYAxis)
+        return;
+
+    var context = this.element.getContext("2d");
+
+    var labelStyle = {"style":
+         {"position": "absolute",
+          "fontSize": this.options.axisLabelFontSize + "px",
+          "zIndex": 10,
+          "color": this.options.axisLabelColor.toRGBString(),
+          "width": this.options.axisLabelWidth + "px",
+          "overflow": "hidden"
+         }
+    };
+
+    // axis lines
+    context.save();
+    context.strokeStyle = this.options.axisLineColor.toRGBString();
+    context.lineWidth = this.options.axisLineWidth;
+
+
+    if (this.options.drawYAxis) {
+        if (this.layout.yticks) {
+            var drawTick = function(tick) {
+                if (typeof(tick) == "function") return;
+                var x = this.area.x;
+                var y = this.area.y + tick[0] * this.area.h;
+                context.beginPath();
+                context.moveTo(x, y);
+                context.lineTo(x - this.options.axisTickSize, y);
+                context.closePath();
+                context.stroke();
+
+                var label = DIV(labelStyle, tick[1]);
+                label.style.top = (y - this.options.axisLabelFontSize) + "px";
+                label.style.left = (x - this.options.padding.left - this.options.axisTickSize) + "px";
+                label.style.textAlign = "right";
+                label.style.width = (this.options.padding.left - this.options.axisTickSize * 2) + "px";
+                MochiKit.DOM.appendChildNodes(this.container, label);
+                this.ylabels.push(label);
+            };
+            
+            MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
+        }
+
+        context.beginPath();
+        context.moveTo(this.area.x, this.area.y);
+        context.lineTo(this.area.x, this.area.y + this.area.h);
+        context.closePath();
+        context.stroke();
+    }
+
+    if (this.options.drawXAxis) {
+        if (this.layout.xticks) {
+            var drawTick = function(tick) {
+                if (typeof(dataset) == "function") return;
+                
+                var x = this.area.x + tick[0] * this.area.w;
+                var y = this.area.y + this.area.h;
+                context.beginPath();
+                context.moveTo(x, y);
+                context.lineTo(x, y + this.options.axisTickSize);
+                context.closePath();
+                context.stroke();
+
+                var label = DIV(labelStyle, tick[1]);
+                label.style.top = (y + this.options.axisTickSize) + "px";
+                label.style.left = (x - this.options.axisLabelWidth/2) + "px";
+                label.style.textAlign = "center";
+                label.style.width = this.options.axisLabelWidth + "px";
+                MochiKit.DOM.appendChildNodes(this.container, label);
+                this.xlabels.push(label);
+            };
+            
+            MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
+        }
+
+        context.beginPath();
+        context.moveTo(this.area.x, this.area.y + this.area.h);
+        context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h);
+        context.closePath();
+        context.stroke();
+    }
+
+    context.restore();
+
+};
+
+PlotKit.CanvasRenderer.prototype._renderPieAxis = function() {
+    if (!this.options.drawXAxis)
+        return;
+
+	if (this.layout.xticks) {
+		// make a lookup dict for x->slice values
+		var lookup = new Array();
+		for (var i = 0; i < this.layout.slices.length; i++) {
+			lookup[this.layout.slices[i].xval] = this.layout.slices[i];
+		}
+		
+		var centerx = this.area.x + this.area.w * 0.5;
+	    var centery = this.area.y + this.area.h * 0.5;
+	    var radius = Math.min(this.area.w * this.options.pieRadius,
+	                          this.area.h * this.options.pieRadius);
+		var labelWidth = this.options.axisLabelWidth;
+		
+		for (var i = 0; i < this.layout.xticks.length; i++) {
+			var slice = lookup[this.layout.xticks[i][0]];
+			if (MochiKit.Base.isUndefinedOrNull(slice))
+				continue;
+				
+				
+			var angle = (slice.startAngle + slice.endAngle)/2;
+			// normalize the angle
+			var normalisedAngle = angle;
+			if (normalisedAngle > Math.PI * 2)
+				normalisedAngle = normalisedAngle - Math.PI * 2;
+			else if (normalisedAngle < 0)
+				normalisedAngle = normalisedAngle + Math.PI * 2;
+				
+			var labelx = centerx + Math.sin(normalisedAngle) * (radius + 10);
+	        var labely = centery - Math.cos(normalisedAngle) * (radius + 10);
+
+			var attrib = {"position": "absolute",
+	                      "zIndex": 11,
+	                      "width": labelWidth + "px",
+	                      "fontSize": this.options.axisLabelFontSize + "px",
+	                      "overflow": "hidden",
+						  "color": this.options.axisLabelColor.toHexString()
+						};
+
+			if (normalisedAngle <= Math.PI * 0.5) {
+	            // text on top and align left
+	            attrib["textAlign"] = "left";
+	            attrib["verticalAlign"] = "top";
+	            attrib["left"] = labelx + "px";
+	            attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
+	        }
+	        else if ((normalisedAngle > Math.PI * 0.5) && (normalisedAngle <= Math.PI)) {
+	            // text on bottom and align left
+	            attrib["textAlign"] = "left";
+	            attrib["verticalAlign"] = "bottom";     
+	            attrib["left"] = labelx + "px";
+	            attrib["top"] = labely + "px";
+
+	        }
+	        else if ((normalisedAngle > Math.PI) && (normalisedAngle <= Math.PI*1.5)) {
+	            // text on bottom and align right
+	            attrib["textAlign"] = "right";
+	            attrib["verticalAlign"] = "bottom"; 
+	            attrib["left"] = (labelx  - labelWidth) + "px";
+	            attrib["top"] = labely + "px";
+	        }
+	        else {
+	            // text on top and align right
+	            attrib["textAlign"] = "right";
+	            attrib["verticalAlign"] = "bottom";  
+	            attrib["left"] = (labelx  - labelWidth) + "px";
+	            attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
+	        }
+	
+			var label = DIV({'style': attrib}, this.layout.xticks[i][1]);
+			this.xlabels.push(label);
+			MochiKit.DOM.appendChildNodes(this.container, label);
+	  }
+		
+	}
+};
+
+PlotKit.CanvasRenderer.prototype._renderBackground = function() {
+    var context = this.element.getContext("2d");
+    context.save();
+    context.fillStyle = this.options.backgroundColor.toRGBString();
+    context.fillRect(0, 0, this.width, this.height);
+    context.restore();
+};
+
+PlotKit.CanvasRenderer.prototype.clear = function() {
+    if (this.isIE) {
+        // VML takes a while to start up, so we just poll every this.IEDelay
+        try {
+            if (this.clearDelay) {
+                this.clearDelay.cancel();
+                this.clearDelay = null;
+            }
+            var context = this.element.getContext("2d");
+        }
+        catch (e) {
+            this.isFirstRender = false;
+            this.clearDelay = MochiKit.Async.wait(this.IEDelay);
+            this.clearDelay.addCallback(bind(this.clear, this));
+            return;
+        }
+    }
+
+    var context = this.element.getContext("2d");
+    context.clearRect(0, 0, this.width, this.height);
+
+    MochiKit.Iter.forEach(this.xlabels, MochiKit.DOM.removeElement);
+    MochiKit.Iter.forEach(this.ylabels, MochiKit.DOM.removeElement);
+    this.xlabels = new Array();
+    this.ylabels = new Array();
+};
+
+// ----------------------------------------------------------------
+//  Everything below here is experimental and undocumented.
+// ----------------------------------------------------------------
+
+PlotKit.CanvasRenderer.prototype._initialiseEvents = function() {
+    var connect = MochiKit.Signal.connect;
+    var bind = MochiKit.Base.bind;
+    //MochiKit.Signal.registerSignals(this, ['onmouseover', 'onclick', 'onmouseout', 'onmousemove']);
+    //connect(this.element, 'onmouseover', bind(this.onmouseover, this));
+    //connect(this.element, 'onmouseout', bind(this.onmouseout, this));
+    //connect(this.element, 'onmousemove', bind(this.onmousemove, this));
+    connect(this.element, 'onclick', bind(this.onclick, this));
+};
+
+PlotKit.CanvasRenderer.prototype._resolveObject = function(e) {
+    // does not work in firefox
+	//var x = (e.event().offsetX - this.area.x) / this.area.w;
+	//var y = (e.event().offsetY - this.area.y) / this.area.h;
+
+    var x = (e.mouse().page.x - PlotKit.Base.findPosX(this.element) - this.area.x) / this.area.w;
+    var y = (e.mouse().page.y - PlotKit.Base.findPosY(this.element) - this.area.y) / this.area.h;
+	
+    //log(x, y);
+
+    var isHit = this.layout.hitTest(x, y);
+    if (isHit)
+        return isHit;
+    return null;
+};
+
+PlotKit.CanvasRenderer.prototype._createEventObject = function(layoutObj, e) {
+    if (layoutObj == null) {
+        return null;
+    }
+
+    e.chart = layoutObj
+    return e;
+};
+
+
+PlotKit.CanvasRenderer.prototype.onclick = function(e) {
+    var layoutObject = this._resolveObject(e);
+    var eventObject = this._createEventObject(layoutObject, e);
+    if (eventObject != null)
+        MochiKit.Signal.signal(this, "onclick", eventObject);
+};
+
+PlotKit.CanvasRenderer.prototype.onmouseover = function(e) {
+    var layoutObject = this._resolveObject(e);
+    var eventObject = this._createEventObject(layoutObject, e);
+    if (eventObject != null) 
+        signal(this, "onmouseover", eventObject);
+};
+
+PlotKit.CanvasRenderer.prototype.onmouseout = function(e) {
+    var layoutObject = this._resolveObject(e);
+    var eventObject = this._createEventObject(layoutObject, e);
+    if (eventObject == null)
+        signal(this, "onmouseout", e);
+    else 
+        signal(this, "onmouseout", eventObject);
+
+};
+
+PlotKit.CanvasRenderer.prototype.onmousemove = function(e) {
+    var layoutObject = this._resolveObject(e);
+    var eventObject = this._createEventObject(layoutObject, e);
+
+    if ((layoutObject == null) && (this.event_isinside == null)) {
+        // TODO: should we emit an event anyway?
+        return;
+    }
+
+    if ((layoutObject != null) && (this.event_isinside == null))
+        signal(this, "onmouseover", eventObject);
+
+    if ((layoutObject == null) && (this.event_isinside != null))
+        signal(this, "onmouseout", eventObject);
+
+    if ((layoutObject != null) && (this.event_isinside != null))
+        signal(this, "onmousemove", eventObject);
+
+    this.event_isinside = layoutObject;
+    //log("move", x, y);    
+};
+
+PlotKit.CanvasRenderer.isSupported = function(canvasName) {
+    var canvas = null;
+    try {
+        if (MochiKit.Base.isUndefinedOrNull(canvasName)) 
+            canvas = MochiKit.DOM.CANVAS({});
+        else
+            canvas = MochiKit.DOM.getElement(canvasName);
+        var context = canvas.getContext("2d");
+    }
+    catch (e) {
+        var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
+        var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
+        if ((!ie) || (ie[1] < 6) || (opera))
+            return false;
+        return true;
+    }
+    return true;
+};
+
+// Namespace Iniitialisation
+
+PlotKit.Canvas = {}
+PlotKit.Canvas.CanvasRenderer = PlotKit.CanvasRenderer;
+
+PlotKit.Canvas.EXPORT = [
+    "CanvasRenderer"
+];
+
+PlotKit.Canvas.EXPORT_OK = [
+    "CanvasRenderer"
+];
+
+PlotKit.Canvas.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.Canvas.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.Canvas);
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/EasyPlot.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/EasyPlot.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,161 @@
+/* 
+    PlotKit EasyPlot
+    ================
+
+    User friendly wrapper around the common plotting functions.
+
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+    
+*/
+
+try {    
+    if (typeof(PlotKit.CanvasRenderer) == 'undefined')
+    {
+        throw ""
+    }
+} 
+catch (e) {    
+    throw "PlotKit.EasyPlot depends on all of PlotKit's components";
+}
+
+// --------------------------------------------------------------------
+// Start of EasyPlot definition
+// --------------------------------------------------------------------
+
+if (typeof(PlotKit.EasyPlot) == 'undefined') {
+    PlotKit.EasyPlot = {};
+}
+
+PlotKit.EasyPlot.NAME = "PlotKit.EasyPlot";
+PlotKit.EasyPlot.VERSION = PlotKit.VERSION;
+
+PlotKit.EasyPlot.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.EasyPlot.toString = function() {
+    return this.__repr__();
+}
+
+// --------------------------------------------------------------------
+// Start of EasyPlot definition
+// --------------------------------------------------------------------
+
+PlotKit.EasyPlot = function(style, options, divElem, datasources) {
+    this.layout = new Layout(style, options);
+    this.divElem = divElem;
+    this.width = parseInt(divElem.getAttribute('width'));
+    this.height = parseInt(divElem.getAttribute('height'));
+    this.deferredCount = 0;
+
+    // make sure we have non-zero width
+    if (this.width < 1) {
+        this.width = this.divElem.width ? this.divElem.width : 300;
+    }
+    
+    if (this.height < 1) {
+        this.height = this.divElem.height ? this.divElem.height : 300;
+    }
+    
+    // load data sources
+    if (isArrayLike(datasources)) {
+        for (var i = 0; i < datasources.length; i++) {
+            if (typeof(datasources[i]) == "string") {
+                this.deferredCount++;
+                // load CSV via ajax
+                var d = MochiKit.Async.doSimpleXMLHttpRequest(datasources[i]);
+                d.addCallback(MochiKit.Base.bind(PlotKit.EasyPlot.onDataLoaded, this));
+            }
+            else if (isArrayLike(datasources[i])) {
+                this.layout.addDataset("data-" + i, datasources[i]);
+            }
+        }
+    }
+    else if (!isUndefinedOrNull(datasources)) {
+        throw "Passed datasources are not Array like";
+    }
+    
+    // setup canvas to render
+    
+    if (CanvasRenderer.isSupported()) {
+        this.element = CANVAS({"id": this.divElem.getAttribute("id") + "-canvas",
+                               "width": this.width,
+                               "height": this.height}, "");
+        this.divElem.appendChild(this.element);
+        this.renderer = new SweetCanvasRenderer(this.element, this.layout, options);
+    }
+    else if (SVGRenderer.isSupported()) {
+        this.element = SVGRenderer.SVG({"id": this.divElem.getAttribute("id") + "-svg",
+                                        "width": this.width,
+                                        "height": this.height,
+                                        "version": "1.1",
+                                        "baseProfile": "full"}, "");
+        this.divElem.appendChild(this.element);
+        this.renderer = new SweetSVGRenderer(this.element, this.layout, options);
+    }
+    
+    if ((this.deferredCount == 0) && (PlotKit.Base.keys(this.layout.datasets).length > 0)) {
+        this.layout.evaluate();
+        this.renderer.clear();
+        this.renderer.render();    
+    }
+    
+};
+
+PlotKit.EasyPlot.onDataLoaded = function(request) {
+    
+    // very primitive CSV parser, should fix to make it more compliant.
+    var table = new Array();
+    var lines = request.responseText.split('\n');
+    for (var i = 0; i < lines.length; i++) {
+        var stripped = MochiKit.Format.strip(lines[i]);
+        if ((stripped.length > 1) && (stripped.charAt(0) != '#')) {
+            table.push(stripped.split(','));
+        }
+    }
+  
+    this.layout.addDataset("data-ajax-" + this.deferredCount, table);
+    this.deferredCount--;
+    
+    if ((this.deferredCount == 0) && (PlotKit.Base.keys(this.layout.datasets).length > 0)) {
+        this.layout.evaluate();
+        this.renderer.clear();
+        this.renderer.render();
+    }
+};
+
+PlotKit.EasyPlot.prototype.reload = function() {
+    this.layout.evaluate();
+    this.renderer.clear();
+    this.renderer.render();
+};
+
+// Namespace Iniitialisation
+
+PlotKit.EasyPlotModule = {};
+PlotKit.EasyPlotModule.EasyPlot = PlotKit.EasyPlot;
+
+PlotKit.EasyPlotModule.EXPORT = [
+    "EasyPlot"
+];
+
+PlotKit.EasyPlotModule.EXPORT_OK = [];
+
+PlotKit.EasyPlotModule.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.EasyPlotModule.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.EasyPlotModule);
+
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Layout.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/Layout.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,756 @@
+/* 
+    PlotKit Layout
+    ==============
+    
+    Handles laying out data on to a virtual canvas square canvas between 0.0 
+    and 1.0. If you want to add new chart/plot types such as point plots,
+    you need to add them here.
+    
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+    
+*/
+
+try {    
+    if (typeof(PlotKit.Base) == 'undefined')
+    {
+        throw ""
+    }
+} 
+catch (e) {    
+    throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Base"
+}
+
+// --------------------------------------------------------------------
+// Start of Layout definition
+// --------------------------------------------------------------------
+
+if (typeof(PlotKit.Layout) == 'undefined') {
+    PlotKit.Layout = {};
+}
+
+PlotKit.Layout.NAME = "PlotKit.Layout";
+PlotKit.Layout.VERSION = PlotKit.VERSION;
+
+PlotKit.Layout.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.Layout.toString = function() {
+    return this.__repr__();
+}
+
+PlotKit.Layout.valid_styles = ["bar", "line", "pie", "point"];
+
+// --------------------------------------------------------------------
+// Start of Layout definition
+// --------------------------------------------------------------------
+
+PlotKit.Layout = function(style, options) {
+  
+    this.options = {
+        "barWidthFillFraction": 0.75,
+        "barOrientation": "vertical",
+        "xOriginIsZero": true,
+        "yOriginIsZero": true,
+        "xAxis": null, // [xmin, xmax]
+        "yAxis": null, // [ymin, ymax]
+        "xTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
+        "yTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
+        "xNumberOfTicks": 10,
+        "yNumberOfTicks": 5,
+        "xTickPrecision": 1,
+        "yTickPrecision": 1,
+        "pieRadius": 0.4
+    };
+
+    // valid external options : TODO: input verification
+    this.style = style; 
+    MochiKit.Base.update(this.options, options ? options : {});
+
+    // externally visible states
+    // overriden if xAxis and yAxis are set in options
+    if (!MochiKit.Base.isUndefinedOrNull(this.options.xAxis)) {
+        this.minxval = this.options.xAxis[0];
+        this.maxxval = this.options.xAxis[1];
+        this.xscale = this.maxxval - this.minxval; 
+    }
+    else {
+        this.minxval = 0;
+        this.maxxval = null;
+        this.xscale = null; // val -> pos factor (eg, xval * xscale = xpos)
+    }
+
+    if (!MochiKit.Base.isUndefinedOrNull(this.options.yAxis)) {
+        this.minyval = this.options.yAxis[0];
+        this.maxyval = this.options.yAxis[1];
+        this.yscale = this.maxyval - this.minyval;
+    }
+    else {
+        this.minyval = 0;
+        this.maxyval = null;
+        this.yscale = null;
+    }
+
+    this.bars = new Array();   // array of bars to plot for bar charts
+    this.points = new Array(); // array of points to plot for line plots
+    this.slices = new Array(); // array of slices to draw for pie charts
+
+    this.xticks = new Array();
+    this.yticks = new Array();
+
+    // internal states
+    this.datasets = new Array();
+    this.minxdelta = 0;
+    this.xrange = 1;
+    this.yrange = 1;
+
+    this.hitTestCache = {x2maxy: null};
+    
+};
+
+// --------------------------------------------------------------------
+// Dataset Manipulation
+// --------------------------------------------------------------------
+
+
+PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
+    this.datasets[setname] = set_xy;
+};
+
+PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
+    delete this.datasets[setname];
+};
+
+PlotKit.Layout.prototype.addDatasetFromTable = function(name, tableElement, xcol, ycol,  lcol) {
+	var isNil = MochiKit.Base.isUndefinedOrNull;
+	var scrapeText = MochiKit.DOM.scrapeText;
+	var strip = MochiKit.Format.strip;
+	
+	if (isNil(xcol))
+		xcol = 0;
+	if (isNil(ycol))
+		ycol = 1;
+	if (isNil(lcol))
+	    lcol = -1;
+        
+    var rows = tableElement.tBodies[0].rows;
+    var data = new Array();
+    var labels = new Array();
+    
+    if (!isNil(rows)) {
+        for (var i = 0; i < rows.length; i++) {
+            data.push([parseFloat(strip(scrapeText(rows[i].cells[xcol]))),
+                       parseFloat(strip(scrapeText(rows[i].cells[ycol])))]);
+            if (lcol >= 0){
+               labels.push({v: parseFloat(strip(scrapeText(rows[i].cells[xcol]))),
+                            label:  strip(scrapeText(rows[i].cells[lcol]))});
+            }
+        }
+        this.addDataset(name, data);
+        if (lcol >= 0) {
+            this.options.xTicks = labels;
+        }
+        return true;
+    }
+    return false;
+};
+
+// --------------------------------------------------------------------
+// Evaluates the layout for the current data and style.
+// --------------------------------------------------------------------
+
+PlotKit.Layout.prototype.evaluate = function() {
+    this._evaluateLimits();
+    this._evaluateScales();
+    if (this.style == "bar") {
+        if (this.options.barOrientation == "horizontal") {
+            this._evaluateHorizBarCharts();
+        }
+        else {
+            this._evaluateBarCharts();
+        }
+        this._evaluateBarTicks();
+    }
+    else if (this.style == "line") {
+        this._evaluateLineCharts();
+        this._evaluateLineTicks();
+    }
+    else if (this.style == "pie") {
+        this._evaluatePieCharts();
+        this._evaluatePieTicks();
+    }
+};
+
+
+
+// Given the fractional x, y positions, report the corresponding
+// x, y values.
+PlotKit.Layout.prototype.hitTest = function(x, y) {
+    // TODO: make this more efficient with better datastructures
+    //       for this.bars, this.points and this.slices
+
+    var f = MochiKit.Format.twoDigitFloat;
+
+    if ((this.style == "bar") && this.bars && (this.bars.length > 0)) {
+        for (var i = 0; i < this.bars.length; i++) {
+            var bar = this.bars[i];
+            if ((x >= bar.x) && (x <= bar.x + bar.w) 
+                && (y >= bar.y) && (y - bar.y <= bar.h))
+                return bar;
+        }
+    }
+
+    else if (this.style == "line") {
+        if (this.hitTestCache.x2maxy == null) {
+            this._regenerateHitTestCache();
+        }
+
+        // 1. find the xvalues that equal or closest to the give x
+        var xval = x / this.xscale;
+        var xvalues = this.hitTestCache.xvalues;
+        var xbefore = null;
+        var xafter = null;
+
+        for (var i = 1; i < xvalues.length; i++) {
+            if (xvalues[i] > xval) {
+                xbefore = xvalues[i-1];
+                xafter = xvalues[i];
+                break;
+            }
+        }
+
+        if ((xbefore != null)) {
+            var ybefore = this.hitTestCache.x2maxy[xbefore];
+            var yafter = this.hitTestCache.x2maxy[xafter];
+            var yval = (1.0 - y)/this.yscale;
+
+            // interpolate whether we will fall inside or outside
+            var gradient = (yafter - ybefore) / (xafter - xbefore);
+            var projmaxy = ybefore + gradient * (xval - xbefore);
+            if (projmaxy >= yval) {
+                // inside the highest curve (roughly)
+                var obj = {xval: xval, yval: yval,
+                           xafter: xafter, yafter: yafter,
+                           xbefore: xbefore, ybefore: ybefore,
+                           yprojected: projmaxy
+                };
+                return obj;
+            }
+        }
+    }
+
+    else if (this.style == "pie") {
+        var dist = Math.sqrt((y-0.5)*(y-0.5) + (x-0.5)*(x-0.5));
+        if (dist > this.options.pieRadius)
+            return null;
+
+        // TODO: actually doesn't work if we don't know how the Canvas
+        //       lays it out, need to fix!
+        var angle = Math.atan2(y - 0.5, x - 0.5) - Math.PI/2;
+        for (var i = 0; i < this.slices.length; i++) {
+            var slice = this.slices[i];
+            if (slice.startAngle < angle && slice.endAngle >= angle)
+                return slice;
+        }
+    }
+
+    return null;
+};
+
+// Reports valid position rectangle for X value (only valid for bar charts)
+PlotKit.Layout.prototype.rectForX = function(x) {
+    return null;
+};
+
+// Reports valid angles through which X value encloses (only valid for pie charts)
+PlotKit.Layout.prototype.angleRangeForX = function(x) {
+    return null;
+};
+
+// --------------------------------------------------------------------
+// START Internal Functions
+// --------------------------------------------------------------------
+
+PlotKit.Layout.prototype._evaluateLimits = function() {
+    // take all values from all datasets and find max and min
+    var map = PlotKit.Base.map;
+    var items = PlotKit.Base.items;
+    var itemgetter = MochiKit.Base.itemgetter;
+    var collapse = PlotKit.Base.collapse;
+    var listMin = MochiKit.Base.listMin;
+    var listMax = MochiKit.Base.listMax;
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+
+
+    var all = collapse(map(itemgetter(1), items(this.datasets)));
+    if (isNil(this.options.xAxis)) {
+        if (this.options.xOriginIsZero)
+            this.minxval = 0;
+        else
+            this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
+
+        this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
+    }
+    else {
+        this.minxval = this.options.xAxis[0];
+        this.maxxval = this.options.xAxis[1];
+        this.xscale = this.maxval - this.minxval;
+    }
+    
+    if (isNil(this.options.yAxis)) {
+        if (this.options.yOriginIsZero)
+            this.minyval = 0;
+        else
+            this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
+
+        this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
+    }
+    else {
+        this.minyval = this.options.yAxis[0];
+        this.maxyval = this.options.yAxis[1];
+        this.yscale = this.maxyval - this.minyval;
+    }
+
+};
+
+PlotKit.Layout.prototype._evaluateScales = function() {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+
+    this.xrange = this.maxxval - this.minxval;
+    if (this.xrange == 0)
+        this.xscale = 1.0;
+    else
+        this.xscale = 1/this.xrange;
+
+    this.yrange = this.maxyval - this.minyval;
+    if (this.yrange == 0)
+        this.yscale = 1.0;
+    else
+        this.yscale = 1/this.yrange;
+};
+
+PlotKit.Layout.prototype._uniqueXValues = function() {
+    var collapse = PlotKit.Base.collapse;
+    var map = PlotKit.Base.map;
+    var uniq = PlotKit.Base.uniq;
+    var getter = MochiKit.Base.itemgetter;
+    var items = PlotKit.Base.items;
+    
+    var xvalues = map(parseFloat, map(getter(0), collapse(map(getter(1), items(this.datasets)))));
+    xvalues.sort(MochiKit.Base.compare);
+    return uniq(xvalues);
+};
+
+// Create the bars
+PlotKit.Layout.prototype._evaluateBarCharts = function() {
+    var items = PlotKit.Base.items;
+
+    var setCount = items(this.datasets).length;
+
+    // work out how far separated values are
+    var xdelta = 10000000;
+    var xvalues = this._uniqueXValues();
+    for (var i = 1; i < xvalues.length; i++) {
+        xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
+    }
+
+    var barWidth = 0;
+    var barWidthForSet = 0;
+    var barMargin = 0;
+    if (xvalues.length == 1) {
+        // note we have to do something smarter if we only plot one value
+        xdelta = 1.0;
+        this.xscale = 1.0;
+        this.minxval = xvalues[0];
+        barWidth = 1.0 * this.options.barWidthFillFraction;
+        barWidthForSet = barWidth/setCount;
+        barMargin = (1.0 - this.options.barWidthFillFraction)/2;
+    }
+    else {
+        // readjust xscale to fix with bar charts
+        if (this.xrange == 1) {
+            this.xscale = 0.5;
+        }
+        else if (this.xrange == 2) {
+            this.xscale = 1/3.0;
+        }
+        else {
+            this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
+        }
+        barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
+        barWidthForSet = barWidth / setCount;
+        barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2;
+    }
+    
+    this.minxdelta = xdelta; // need this for tick positions
+
+    // add all the rects
+    this.bars = new Array();
+    var i = 0;
+    for (var setName in this.datasets) {
+        var dataset = this.datasets[setName];
+        if (PlotKit.Base.isFuncLike(dataset)) continue;
+        for (var j = 0; j < dataset.length; j++) {
+            var item = dataset[j];
+            var rect = {
+                x: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin,
+                y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
+                w: barWidthForSet,
+                h: ((parseFloat(item[1]) - this.minyval) * this.yscale),
+                xval: parseFloat(item[0]),
+                yval: parseFloat(item[1]),
+                name: setName
+            };
+            if ((rect.x >= 0.0) && (rect.x <= 1.0) && 
+                (rect.y >= 0.0) && (rect.y <= 1.0)) {
+                this.bars.push(rect);
+            }
+        }
+        i++;
+    }
+};
+
+// Create the horizontal bars
+PlotKit.Layout.prototype._evaluateHorizBarCharts = function() {
+    var items = PlotKit.Base.items;
+
+    var setCount = items(this.datasets).length;
+
+    // work out how far separated values are
+    var xdelta = 10000000;
+    var xvalues = this._uniqueXValues();
+    for (var i = 1; i < xvalues.length; i++) {
+        xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
+    }
+
+    var barWidth = 0;
+    var barWidthForSet = 0;
+    var barMargin = 0;
+    
+    // work out how far each far each bar is separated
+    if (xvalues.length == 1) {
+        // do something smarter if we only plot one value
+        xdelta = 1.0;
+        this.xscale = 1.0;
+        this.minxval = xvalues[0];
+        barWidth = 1.0 * this.options.barWidthFillFraction;
+        barWidthForSet = barWidth/setCount;
+        barMargin = (1.0 - this.options.barWidthFillFraction)/2;
+    }
+    else {
+        // readjust yscale to fix with bar charts
+        this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
+        barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
+        barWidthForSet = barWidth / setCount;
+        barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2;
+    }
+
+    this.minxdelta = xdelta; // need this for tick positions
+
+    // add all the rects
+    this.bars = new Array();
+    var i = 0;
+    for (var setName in this.datasets) {
+        var dataset = this.datasets[setName];
+        if (PlotKit.Base.isFuncLike(dataset)) continue;
+        for (var j = 0; j < dataset.length; j++) {
+            var item = dataset[j];
+            var rect = {
+                y: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin,
+                x: 0.0,
+                h: barWidthForSet,
+                w: ((parseFloat(item[1]) - this.minyval) * this.yscale),
+                xval: parseFloat(item[0]),
+                yval: parseFloat(item[1]),
+                name: setName
+            };
+
+            // limit the x, y values so they do not overdraw
+            if (rect.y <= 0.0) {
+                rect.y = 0.0;
+            }
+            if (rect.y >= 1.0) {
+                rect.y = 1.0;
+            }
+            if ((rect.x >= 0.0) && (rect.x <= 1.0)) {
+                this.bars.push(rect);
+            }
+        }
+        i++;
+    }
+};
+
+
+// Create the line charts
+PlotKit.Layout.prototype._evaluateLineCharts = function() {
+    var items = PlotKit.Base.items;
+
+    var setCount = items(this.datasets).length;
+
+    // add all the rects
+    this.points = new Array();
+    var i = 0;
+    for (var setName in this.datasets) {
+        var dataset = this.datasets[setName];
+        if (PlotKit.Base.isFuncLike(dataset)) continue;
+        dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
+        for (var j = 0; j < dataset.length; j++) {
+            var item = dataset[j];
+            var point = {
+                x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
+                y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
+                xval: parseFloat(item[0]),
+                yval: parseFloat(item[1]),
+                name: setName
+            };
+
+            // limit the x, y values so they do not overdraw
+            if (point.y <= 0.0) {
+                point.y = 0.0;
+            }
+            if (point.y >= 1.0) {
+                point.y = 1.0;
+            }
+            if ((point.x >= 0.0) && (point.x <= 1.0)) {
+                this.points.push(point);
+            }
+        }
+        i++;
+    }
+};
+
+// Create the pie charts
+PlotKit.Layout.prototype._evaluatePieCharts = function() {
+    var items = PlotKit.Base.items;
+    var sum = MochiKit.Iter.sum;
+    var getter = MochiKit.Base.itemgetter;
+
+    var setCount = items(this.datasets).length;
+
+    // we plot the y values of the first dataset
+    var dataset = items(this.datasets)[0][1];
+    var total = sum(map(getter(1), dataset));
+
+    this.slices = new Array();
+    var currentAngle = 0.0;
+    for (var i = 0; i < dataset.length; i++) {
+        var fraction = dataset[i][1] / total;
+		var startAngle = currentAngle * Math.PI * 2;
+		var endAngle = (currentAngle + fraction) * Math.PI * 2;
+			
+        var slice = {fraction: fraction,
+                     xval: dataset[i][0],
+                     yval: dataset[i][1],
+                     startAngle: startAngle,
+                     endAngle: endAngle
+        };
+        if (dataset[i][1] != 0) {
+            this.slices.push(slice);
+        }
+        currentAngle += fraction;
+    }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+    
+    if (this.options.xTicks) {
+        // we use use specified ticks with optional labels
+
+        this.xticks = new Array();
+        var makeTicks = function(tick) {
+            var label = tick.label;
+            if (isNil(label))
+                label = tick.v.toString();
+            var pos = this.xscale * (tick.v - this.minxval);
+            if ((pos >= 0.0) && (pos <= 1.0)) {
+                this.xticks.push([pos, label]);
+            }
+        };
+        MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
+    }
+    else if (this.options.xNumberOfTicks) {
+        // we use defined number of ticks as hint to auto generate
+        var xvalues = this._uniqueXValues();
+        var roughSeparation = this.xrange / this.options.xNumberOfTicks;
+        var tickCount = 0;
+
+        this.xticks = new Array();
+        for (var i = 0; i <= xvalues.length; i++) {
+            if ((xvalues[i] - this.minxval) >= (tickCount * roughSeparation)) {
+                var pos = this.xscale * (xvalues[i] - this.minxval);
+                if ((pos > 1.0) || (pos < 0.0))
+                    continue;
+                this.xticks.push([pos, xvalues[i]]);
+                tickCount++;
+            }
+            if (tickCount > this.options.xNumberOfTicks)
+                break;
+        }
+    }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+
+
+    if (this.options.yTicks) {
+        this.yticks = new Array();
+        var makeTicks = function(tick) {
+            var label = tick.label;
+            if (isNil(label))
+                label = tick.v.toString();
+            var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
+            if ((pos >= 0.0) && (pos <= 1.0)) {
+                this.yticks.push([pos, label]);
+            }
+        };
+        MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
+    }
+    else if (this.options.yNumberOfTicks) {
+        // We use the optionally defined number of ticks as a guide        
+        this.yticks = new Array();
+
+        // if we get this separation right, we'll have good looking graphs
+        var roundInt = PlotKit.Base.roundInterval;
+        var prec = this.options.yTickPrecision;
+        var roughSeparation = roundInt(this.yrange, 
+                                       this.options.yNumberOfTicks, prec);
+
+        // round off each value of the y-axis to the precision
+        // eg. 1.3333 at precision 1 -> 1.3
+        for (var i = 0; i <= this.options.yNumberOfTicks; i++) {
+            var yval = this.minyval + (i * roughSeparation);
+            var pos = 1.0 - ((yval - this.minyval) * this.yscale);
+            if ((pos > 1.0) || (pos < 0.0))
+                continue;
+            this.yticks.push([pos, MochiKit.Format.roundToFixed(yval, prec)]);
+        }
+    }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicks = function() {
+    this._evaluateLineTicksForXAxis();
+    this._evaluateLineTicksForYAxis();
+};
+
+PlotKit.Layout.prototype._evaluateBarTicks = function() {
+    this._evaluateLineTicks();
+    var centerInBar = function(tick) {
+        return [tick[0] + (this.minxdelta * this.xscale)/2, tick[1]];
+    };
+    this.xticks = MochiKit.Base.map(bind(centerInBar, this), this.xticks);
+    
+    if (this.options.barOrientation == "horizontal") {
+        // swap scales
+        var tempticks = this.xticks;
+        this.xticks = this.yticks;
+        this.yticks = tempticks;
+
+        // we need to invert the "yaxis" (which is now the xaxis when drawn)
+        var invert = function(tick) {
+            return [1.0 - tick[0], tick[1]];
+        }
+        this.xticks = MochiKit.Base.map(invert, this.xticks);
+    }
+};
+
+PlotKit.Layout.prototype._evaluatePieTicks = function() {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+	var formatter = MochiKit.Format.numberFormatter("#%");
+
+    this.xticks = new Array();
+	if (this.options.xTicks) {
+		// make a lookup dict for x->slice values
+		var lookup = new Array();
+		for (var i = 0; i < this.slices.length; i++) {
+			lookup[this.slices[i].xval] = this.slices[i];
+		}
+		
+		for (var i =0; i < this.options.xTicks.length; i++) {
+			var tick = this.options.xTicks[i];
+			var slice = lookup[tick.v]; 
+            var label = tick.label;
+			if (slice) {
+                if (isNil(label))
+                    label = tick.v.toString();
+				label += " (" + formatter(slice.fraction) + ")";
+				this.xticks.push([tick.v, label]);
+			}
+		}
+	}
+	else {
+		// we make our own labels from all the slices
+		for (var i =0; i < this.slices.length; i++) {
+			var slice = this.slices[i];
+			var label = slice.xval + " (" + formatter(slice.fraction) + ")";
+			this.xticks.push([slice.xval, label]);
+		}
+	}
+};
+
+PlotKit.Layout.prototype._regenerateHitTestCache = function() {
+    this.hitTestCache.xvalues = this._uniqueXValues();
+    this.hitTestCache.xlookup = new Array();
+    this.hitTestCache.x2maxy = new Array();
+
+    var listMax = MochiKit.Base.listMax;
+    var itemgetter = MochiKit.Base.itemgetter;
+    var map = MochiKit.Base.map;
+
+    // generate a lookup table for x values to y values
+    var setNames = keys(this.datasets);
+    for (var i = 0; i < setNames.length; i++) {
+        var dataset = this.datasets[setNames[i]];
+        for (var j = 0; j < dataset.length; j++) {
+            var xval = dataset[j][0];
+            var yval = dataset[j][1];
+            if (this.hitTestCache.xlookup[xval])
+                this.hitTestCache.xlookup[xval].push([yval, setNames[i]]);
+            else 
+                this.hitTestCache.xlookup[xval] = [[yval, setNames[i]]];
+        }
+    }
+
+    for (var x in this.hitTestCache.xlookup) {
+        var yvals = this.hitTestCache.xlookup[x];
+        this.hitTestCache.x2maxy[x] = listMax(map(itemgetter(0), yvals));
+    }
+
+
+};
+
+// --------------------------------------------------------------------
+// END Internal Functions
+// --------------------------------------------------------------------
+
+
+// Namespace Iniitialisation
+
+PlotKit.LayoutModule = {};
+PlotKit.LayoutModule.Layout = PlotKit.Layout;
+
+PlotKit.LayoutModule.EXPORT = [
+    "Layout"
+];
+
+PlotKit.LayoutModule.EXPORT_OK = [];
+
+PlotKit.LayoutModule.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.LayoutModule.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
+
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,151 @@
+/***
+
+PlotKit Autoload Javascript Module.
+
+This file was adapted from MochiKit.
+See <http://mochikit.com/> for documentation, downloads, license, etc.
+(c) 2005 Bob Ippolito.  All rights Reserved.
+
+Modified by Alastair Tse, 2006, for PlotKit.
+
+***/
+
+if (typeof(PlotKit) == 'undefined') {
+    PlotKit = {};
+}
+
+if (typeof(PlotKit.PlotKit) == 'undefined') {
+    PlotKit.PlotKit = {};
+}
+
+PlotKit.PlotKit.NAME = "PlotKit.PlotKit";
+PlotKit.PlotKit.VERSION = "0.9.1";
+PlotKit.PlotKit.__repr__ = function () {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.PlotKit.toString = function () {
+    return this.__repr__();
+};
+
+PlotKit.PlotKit.SUBMODULES = [
+    "Base",
+    "Layout",
+    "Canvas",
+    "SVG",
+    "SweetCanvas",
+    "SweetSVG",
+    "EasyPlot"
+];
+
+if (typeof(JSAN) != 'undefined' || typeof(dojo) != 'undefined') {
+    if (typeof(dojo) != 'undefined') {
+        dojo.provide('PlotKit.PlotKit');
+        dojo.require("PlotKit.*");
+    }
+    if (typeof(JSAN) != 'undefined') {
+        // hopefully this makes it easier for static analysis?
+        JSAN.use("PlotKit.Base", []);
+        JSAN.use("PlotKit.Layout", []);
+        JSAN.use("PlotKit.Canvas", []);
+        JSAN.use("PlotKit.SweetCanvas", []);
+        JSAN.use("PlotKit.SVG", []);
+        JSAN.use("PlotKit.SweetSVG", []);
+    }
+    (function () {
+        var extend = MochiKit.Base.extend;
+        var self = PlotKit.PlotKit;
+        var modules = self.SUBMODULES;
+        var EXPORT = [];
+        var EXPORT_OK = [];
+        var EXPORT_TAGS = {};
+        var i, k, m, all;
+        for (i = 0; i < modules.length; i++) {
+            m = PlotKit[modules[i]];
+            extend(EXPORT, m.EXPORT);
+            extend(EXPORT_OK, m.EXPORT_OK);
+            for (k in m.EXPORT_TAGS) {
+                EXPORT_TAGS[k] = extend(EXPORT_TAGS[k], m.EXPORT_TAGS[k]);
+            }
+            all = m.EXPORT_TAGS[":all"];
+            if (!all) {
+                all = extend(null, m.EXPORT, m.EXPORT_OK);
+            }
+            var j;
+            for (j = 0; j < all.length; j++) {
+                k = all[j];
+                self[k] = m[k];
+            }
+        }
+        self.EXPORT = EXPORT;
+        self.EXPORT_OK = EXPORT_OK;
+        self.EXPORT_TAGS = EXPORT_TAGS;
+    }());
+    
+} else {
+    if (typeof(PlotKit.__compat__) == 'undefined') {
+        PlotKit.__compat__ = true;
+    }
+    (function () {
+        if (typeof(document) == "undefined") {
+              return;
+        }
+        
+        var scripts = document.getElementsByTagName("script");
+        var kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+        var base = null;
+        var baseElem = null;
+        var allScripts = {};
+        var i;
+        for (i = 0; i < scripts.length; i++) {
+            var src = scripts[i].getAttribute("src");
+            if (!src) {
+                continue;
+            }
+            allScripts[src] = true;
+            if (src.match(/PlotKit.js$/)) {
+                base = src.substring(0, src.lastIndexOf('PlotKit.js'));
+                baseElem = scripts[i];
+            }
+
+        }
+
+        if (base === null) {
+            return;
+        }
+        var modules = PlotKit.PlotKit.SUBMODULES;
+        for (var i = 0; i < modules.length; i++) {
+            if (PlotKit[modules[i]]) {
+                continue;
+            }
+            var uri = base + modules[i] + '.js';
+            if (uri in allScripts) {
+                continue;
+            }
+            if (document.documentElement &&
+                document.documentElement.namespaceURI == kXULNSURI) {
+                // XUL
+                var s = document.createElementNS(kXULNSURI, 'script');
+                s.setAttribute("id", "PlotKit_" + base + modules[i]);
+                s.setAttribute("src", uri);
+                s.setAttribute("type", "application/x-javascript");
+                baseElem.parentNode.appendChild(s);
+            } else {
+                // HTML
+                /*
+                    DOM can not be used here because Safari does
+                    deferred loading of scripts unless they are
+                    in the document or inserted with document.write
+
+                    This is not XHTML compliant.  If you want XHTML
+                    compliance then you must use the packed version of MochiKit
+                    or include each script individually (basically unroll
+                    these document.write calls into your XHTML source)
+
+                */
+                document.write('<script src="' + uri +
+                    '" type="text/javascript"></script>');
+            }
+        };
+    })();
+}

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit_Packed.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/PlotKit_Packed.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,2177 @@
+/***
+
+    PlotKit.PlotKit 0.9.1 : PACKED VERSION
+
+    THIS FILE IS AUTOMATICALLY GENERATED.  If creating patches, please
+    diff against the source tree, not this file.
+
+    For more information, <http://www.liquidx.net/plotkit/>.
+    
+    Copyright (c) 2006. Alastair Tse.
+
+***/
+
+try{
+if(typeof (MochiKit.Base)=="undefined"||typeof (MochiKit.DOM)=="undefined"||typeof (MochiKit.Color)=="undefined"||typeof (MochiKit.Format)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "PlotKit depends on MochiKit.{Base,Color,DOM,Format}";
+}
+MochiKit.Base.update(MochiKit.Color.Color.prototype,{asFillColor:function(){
+return this.lighterColorWithLevel(0.3);
+},asStrokeColor:function(){
+return this.darkerColorWithLevel(0.1);
+},asPointColor:function(){
+return this.lighterColorWithLevel(0.1);
+}});
+if(typeof (PlotKit)=="undefined"){
+PlotKit={};
+}
+PlotKit.NAME="PlotKit";
+PlotKit.VERSION="0.8";
+PlotKit.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.toString=function(){
+return this.__repr__();
+};
+if(typeof (PlotKit.Base)=="undefined"){
+PlotKit.Base={};
+}
+PlotKit.Base.NAME="PlotKit.Base";
+PlotKit.Base.VERSION=PlotKit.VERSION;
+PlotKit.Base.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.Base.toString=function(){
+return this.__repr__();
+};
+PlotKit.Base.usingPrototype=function(){
+try{
+return (typeof (Object.extend)=="function");
+}
+catch(e){
+return false;
+}
+};
+MochiKit.Base.update(PlotKit.Base,{roundInterval:function(_1,_2,_3){
+var _4=MochiKit.Format.roundToFixed;
+var _5=_1/_2;
+return parseFloat(_4(_5,_3));
+},collapse:function(_6){
+var m=MochiKit.Base;
+var _8=new Array();
+for(var i=0;i<_6.length;i++){
+_8=m.concat(_8,_6[i]);
+}
+if(PlotKit.Base.usingPrototype()){
+delete _8.extend;
+delete _8.from;
+delete _8.inspect;
+}
+return _8;
+},uniq:function(_10){
+var m=MochiKit.Base;
+if(!m.isArrayLike(_10)||(_10.length<1)){
+return new Array();
+}
+var _11=new Array();
+var _12=_10[0];
+_11.push(_10[0]);
+for(var i=1;i<_10.length;i++){
+if(m.compare(_10[i],_12)!=0){
+_12=_10[i];
+_11.push(_10[i]);
+}
+}
+return _11;
+},colorScheme:function(){
+var mb=MochiKit.Base;
+var mc=MochiKit.Color;
+var _15=["red","orange","yellow","green","cyan","blue","purple","magenta"];
+var _16=function(_17){
+return mc.Color[_17+"Color"]();
+};
+return mb.map(_16,_15);
+},baseDarkPrimaryColors:function(){
+var _18=MochiKit.Color.Color.fromHexString;
+return [_18("#ad3f40"),_18("#ddac2c"),_18("#dfdd0c"),_18("#5276c4"),_18("#739c5a")];
+},basePrimaryColors:function(){
+var _19=MochiKit.Color.Color.fromHexString;
+return [_19("#d24c4d"),_19("#f2b32f"),_19("#ece90e"),_19("#5d83da"),_19("#78a15d")];
+},baseBlueColors:function(){
+var _20=MochiKit.Color.Color.fromHexString;
+return [_20("#4b6b94"),_20("#5d81b4"),_20("#acbad2")];
+},palette:function(_21,_22,_23,_24){
+var _25=MochiKit.Base.isUndefinedOrNull;
+var _26=new Array();
+if(_25(_24)){
+_24=0.1;
+}
+if(_25(_23)){
+_23=0.4;
+}
+if(_25(_22)){
+_22=-0.2;
+}
+var _27=_22;
+while(_27<=_23){
+_26.push(_27);
+_27+=_24;
+}
+var _28=function(_29,_30){
+return _29.lighterColorWithLevel(_30);
+};
+return MochiKit.Base.map(partial(_28,_21),_26);
+},excanvasSupported:function(){
+if(/MSIE/.test(navigator.userAgent)&&!window.opera){
+return true;
+}
+return false;
+},findPosX:function(obj){
+var _32=0;
+if(obj.offsetParent){
+while(obj.offsetParent){
+_32+=obj.offsetLeft;
+obj=obj.offsetParent;
+}
+}else{
+if(obj.x){
+_32+=obj.x;
+}
+}
+return _32;
+},findPosY:function(obj){
+var _33=0;
+if(obj.offsetParent){
+while(obj.offsetParent){
+_33+=obj.offsetTop;
+obj=obj.offsetParent;
+}
+}else{
+if(obj.y){
+_33+=obj.y;
+}
+}
+return _33;
+},isFuncLike:function(obj){
+return (typeof (obj)=="function");
+}});
+PlotKit.Base.map=function(fn,lst){
+if(PlotKit.Base.usingPrototype()){
+var _36=[];
+for(var x in lst){
+if(typeof (lst[x])=="function"){
+continue;
+}
+_36.push(fn(lst[x]));
+}
+return _36;
+}else{
+return MochiKit.Base.map(fn,lst);
+}
+};
+PlotKit.Base.items=function(lst){
+if(PlotKit.Base.usingPrototype()){
+var _38=[];
+for(var x in lst){
+if(typeof (lst[x])=="function"){
+continue;
+}
+_38.push([x,lst[x]]);
+}
+return _38;
+}else{
+return MochiKit.Base.items(lst);
+}
+};
+PlotKit.Base.keys=function(lst){
+if(PlotKit.Base.usingPrototype()){
+var _39=[];
+for(var x in lst){
+if(typeof (lst[x])=="function"){
+continue;
+}
+_39.push(x);
+}
+return _39;
+}else{
+return MochiKit.Base.keys(lst);
+}
+};
+PlotKit.Base.baseColors=function(){
+var _40=MochiKit.Color.Color.fromHexString;
+return [_40("#476fb2"),_40("#be2c2b"),_40("#85b730"),_40("#734a99"),_40("#26a1c5"),_40("#fb8707"),_40("#000000")];
+};
+PlotKit.Base.officeBaseStyle={"axisLineWidth":2,"axisLabelColor":Color.grayColor(),"axisLineColor":Color.whiteColor(),"padding":{top:5,bottom:10,left:30,right:30}};
+MochiKit.Base.update(PlotKit.Base,{officeBlue:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),"backgroundColor":PlotKit.Base.baseColors()[0].lighterColorWithLevel(0.45)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officeRed:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[1]),"backgroundColor":PlotKit.Base.baseColors()[1].lighterColorWithLevel(0.5)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officeGreen:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[2]),"backgroundColor":PlotKit.Base.baseColors()[2].lighterColorWithLevel(0.5)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officePurple:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[3]),"backgroundColor":PlotKit.Base.baseColors()[3].lighterColorWithLevel(0.5)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officeCyan:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[4]),"backgroundColor":PlotKit.Base.baseColors()[4].lighterColorWithLevel(0.5)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officeOrange:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[5]),"backgroundColor":PlotKit.Base.baseColors()[5].lighterColorWithLevel(0.4)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+},officeBlack:function(){
+var r={"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[6],0,0.6),"backgroundColor":PlotKit.Base.baseColors()[6].lighterColorWithLevel(0.9)};
+MochiKit.Base.update(r,PlotKit.Base.officeBaseStyle);
+return r;
+}});
+PlotKit.Base.EXPORT=["baseColors","collapse","colorScheme","findPosX","findPosY","officeBaseStyle","officeBlue","officeRed","officeGreen","officePurple","officeCyan","officeOrange","officeBlack","roundInterval","uniq","isFuncLike","excanvasSupported"];
+PlotKit.Base.EXPORT_OK=[];
+PlotKit.Base.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.Base.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.Base);
+try{
+if(typeof (PlotKit.Base)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Base";
+}
+if(typeof (PlotKit.Layout)=="undefined"){
+PlotKit.Layout={};
+}
+PlotKit.Layout.NAME="PlotKit.Layout";
+PlotKit.Layout.VERSION=PlotKit.VERSION;
+PlotKit.Layout.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.Layout.toString=function(){
+return this.__repr__();
+};
+PlotKit.Layout.valid_styles=["bar","line","pie","point"];
+PlotKit.Layout=function(_42,_43){
+this.options={"barWidthFillFraction":0.75,"barOrientation":"vertical","xOriginIsZero":true,"yOriginIsZero":true,"xAxis":null,"yAxis":null,"xTicks":null,"yTicks":null,"xNumberOfTicks":10,"yNumberOfTicks":5,"xTickPrecision":1,"yTickPrecision":1,"pieRadius":0.4};
+this.style=_42;
+MochiKit.Base.update(this.options,_43?_43:{});
+if(!MochiKit.Base.isUndefinedOrNull(this.options.xAxis)){
+this.minxval=this.options.xAxis[0];
+this.maxxval=this.options.xAxis[1];
+this.xscale=this.maxxval-this.minxval;
+}else{
+this.minxval=0;
+this.maxxval=null;
+this.xscale=null;
+}
+if(!MochiKit.Base.isUndefinedOrNull(this.options.yAxis)){
+this.minyval=this.options.yAxis[0];
+this.maxyval=this.options.yAxis[1];
+this.yscale=this.maxyval-this.minyval;
+}else{
+this.minyval=0;
+this.maxyval=null;
+this.yscale=null;
+}
+this.bars=new Array();
+this.points=new Array();
+this.slices=new Array();
+this.xticks=new Array();
+this.yticks=new Array();
+this.datasets=new Array();
+this.minxdelta=0;
+this.xrange=1;
+this.yrange=1;
+this.hitTestCache={x2maxy:null};
+};
+PlotKit.Layout.prototype.addDataset=function(_44,_45){
+this.datasets[_44]=_45;
+};
+PlotKit.Layout.prototype.removeDataset=function(_46,_47){
+delete this.datasets[_46];
+};
+PlotKit.Layout.prototype.addDatasetFromTable=function(_48,_49,_50,_51,_52){
+var _53=MochiKit.Base.isUndefinedOrNull;
+var _54=MochiKit.DOM.scrapeText;
+var _55=MochiKit.Format.strip;
+if(_53(_50)){
+_50=0;
+}
+if(_53(_51)){
+_51=1;
+}
+if(_53(_52)){
+_52=-1;
+}
+var _56=_49.tBodies[0].rows;
+var _57=new Array();
+var _58=new Array();
+if(!_53(_56)){
+for(var i=0;i<_56.length;i++){
+_57.push([parseFloat(_55(_54(_56[i].cells[_50]))),parseFloat(_55(_54(_56[i].cells[_51])))]);
+if(_52>=0){
+_58.push({v:parseFloat(_55(_54(_56[i].cells[_50]))),label:_55(_54(_56[i].cells[_52]))});
+}
+}
+this.addDataset(_48,_57);
+if(_52>=0){
+this.options.xTicks=_58;
+}
+return true;
+}
+return false;
+};
+PlotKit.Layout.prototype.evaluate=function(){
+this._evaluateLimits();
+this._evaluateScales();
+if(this.style=="bar"){
+if(this.options.barOrientation=="horizontal"){
+this._evaluateHorizBarCharts();
+}else{
+this._evaluateBarCharts();
+}
+this._evaluateBarTicks();
+}else{
+if(this.style=="line"){
+this._evaluateLineCharts();
+this._evaluateLineTicks();
+}else{
+if(this.style=="pie"){
+this._evaluatePieCharts();
+this._evaluatePieTicks();
+}
+}
+}
+};
+PlotKit.Layout.prototype.hitTest=function(x,y){
+var f=MochiKit.Format.twoDigitFloat;
+if((this.style=="bar")&&this.bars&&(this.bars.length>0)){
+for(var i=0;i<this.bars.length;i++){
+var bar=this.bars[i];
+if((x>=bar.x)&&(x<=bar.x+bar.w)&&(y>=bar.y)&&(y-bar.y<=bar.h)){
+return bar;
+}
+}
+}else{
+if(this.style=="line"){
+if(this.hitTestCache.x2maxy==null){
+this._regenerateHitTestCache();
+}
+var _62=x/this.xscale;
+var _63=this.hitTestCache.xvalues;
+var _64=null;
+var _65=null;
+for(var i=1;i<_63.length;i++){
+if(_63[i]>_62){
+_64=_63[i-1];
+_65=_63[i];
+break;
+}
+}
+if((_64!=null)){
+var _66=this.hitTestCache.x2maxy[_64];
+var _67=this.hitTestCache.x2maxy[_65];
+var _68=(1-y)/this.yscale;
+var _69=(_67-_66)/(_65-_64);
+var _70=_66+_69*(_62-_64);
+if(_70>=_68){
+var obj={xval:_62,yval:_68,xafter:_65,yafter:_67,xbefore:_64,ybefore:_66,yprojected:_70};
+return obj;
+}
+}
+}else{
+if(this.style=="pie"){
+var _71=Math.sqrt((y-0.5)*(y-0.5)+(x-0.5)*(x-0.5));
+if(_71>this.options.pieRadius){
+return null;
+}
+var _72=Math.atan2(y-0.5,x-0.5)-Math.PI/2;
+for(var i=0;i<this.slices.length;i++){
+var _73=this.slices[i];
+if(_73.startAngle<_72&&_73.endAngle>=_72){
+return _73;
+}
+}
+}
+}
+}
+return null;
+};
+PlotKit.Layout.prototype.rectForX=function(x){
+return null;
+};
+PlotKit.Layout.prototype.angleRangeForX=function(x){
+return null;
+};
+PlotKit.Layout.prototype._evaluateLimits=function(){
+var map=PlotKit.Base.map;
+var _75=PlotKit.Base.items;
+var _76=MochiKit.Base.itemgetter;
+var _77=PlotKit.Base.collapse;
+var _78=MochiKit.Base.listMin;
+var _79=MochiKit.Base.listMax;
+var _80=MochiKit.Base.isUndefinedOrNull;
+var all=_77(map(_76(1),_75(this.datasets)));
+if(_80(this.options.xAxis)){
+if(this.options.xOriginIsZero){
+this.minxval=0;
+}else{
+this.minxval=_78(map(parseFloat,map(_76(0),all)));
+}
+this.maxxval=_79(map(parseFloat,map(_76(0),all)));
+}else{
+this.minxval=this.options.xAxis[0];
+this.maxxval=this.options.xAxis[1];
+this.xscale=this.maxval-this.minxval;
+}
+if(_80(this.options.yAxis)){
+if(this.options.yOriginIsZero){
+this.minyval=0;
+}else{
+this.minyval=_78(map(parseFloat,map(_76(1),all)));
+}
+this.maxyval=_79(map(parseFloat,map(_76(1),all)));
+}else{
+this.minyval=this.options.yAxis[0];
+this.maxyval=this.options.yAxis[1];
+this.yscale=this.maxyval-this.minyval;
+}
+};
+PlotKit.Layout.prototype._evaluateScales=function(){
+var _82=MochiKit.Base.isUndefinedOrNull;
+this.xrange=this.maxxval-this.minxval;
+if(this.xrange==0){
+this.xscale=1;
+}else{
+this.xscale=1/this.xrange;
+}
+this.yrange=this.maxyval-this.minyval;
+if(this.yrange==0){
+this.yscale=1;
+}else{
+this.yscale=1/this.yrange;
+}
+};
+PlotKit.Layout.prototype._uniqueXValues=function(){
+var _83=PlotKit.Base.collapse;
+var map=PlotKit.Base.map;
+var _84=PlotKit.Base.uniq;
+var _85=MochiKit.Base.itemgetter;
+var _86=PlotKit.Base.items;
+var _87=map(parseFloat,map(_85(0),_83(map(_85(1),_86(this.datasets)))));
+_87.sort(MochiKit.Base.compare);
+return _84(_87);
+};
+PlotKit.Layout.prototype._evaluateBarCharts=function(){
+var _88=PlotKit.Base.items;
+var _89=_88(this.datasets).length;
+var _90=10000000;
+var _91=this._uniqueXValues();
+for(var i=1;i<_91.length;i++){
+_90=Math.min(Math.abs(_91[i]-_91[i-1]),_90);
+}
+var _92=0;
+var _93=0;
+var _94=0;
+if(_91.length==1){
+_90=1;
+this.xscale=1;
+this.minxval=_91[0];
+_92=1*this.options.barWidthFillFraction;
+_93=_92/_89;
+_94=(1-this.options.barWidthFillFraction)/2;
+}else{
+if(this.xrange==1){
+this.xscale=0.5;
+}else{
+if(this.xrange==2){
+this.xscale=1/3;
+}else{
+this.xscale=(1-_90/this.xrange)/this.xrange;
+}
+}
+_92=_90*this.xscale*this.options.barWidthFillFraction;
+_93=_92/_89;
+_94=_90*this.xscale*(1-this.options.barWidthFillFraction)/2;
+}
+this.minxdelta=_90;
+this.bars=new Array();
+var i=0;
+for(var _95 in this.datasets){
+var _96=this.datasets[_95];
+if(PlotKit.Base.isFuncLike(_96)){
+continue;
+}
+for(var j=0;j<_96.length;j++){
+var _98=_96[j];
+var _99={x:((parseFloat(_98[0])-this.minxval)*this.xscale)+(i*_93)+_94,y:1-((parseFloat(_98[1])-this.minyval)*this.yscale),w:_93,h:((parseFloat(_98[1])-this.minyval)*this.yscale),xval:parseFloat(_98[0]),yval:parseFloat(_98[1]),name:_95};
+if((_99.x>=0)&&(_99.x<=1)&&(_99.y>=0)&&(_99.y<=1)){
+this.bars.push(_99);
+}
+}
+i++;
+}
+};
+PlotKit.Layout.prototype._evaluateHorizBarCharts=function(){
+var _100=PlotKit.Base.items;
+var _101=_100(this.datasets).length;
+var _102=10000000;
+var _103=this._uniqueXValues();
+for(var i=1;i<_103.length;i++){
+_102=Math.min(Math.abs(_103[i]-_103[i-1]),_102);
+}
+var _104=0;
+var _105=0;
+var _106=0;
+if(_103.length==1){
+_102=1;
+this.xscale=1;
+this.minxval=_103[0];
+_104=1*this.options.barWidthFillFraction;
+_105=_104/_101;
+_106=(1-this.options.barWidthFillFraction)/2;
+}else{
+this.xscale=(1-_102/this.xrange)/this.xrange;
+_104=_102*this.xscale*this.options.barWidthFillFraction;
+_105=_104/_101;
+_106=_102*this.xscale*(1-this.options.barWidthFillFraction)/2;
+}
+this.minxdelta=_102;
+this.bars=new Array();
+var i=0;
+for(var _107 in this.datasets){
+var _108=this.datasets[_107];
+if(PlotKit.Base.isFuncLike(_108)){
+continue;
+}
+for(var j=0;j<_108.length;j++){
+var item=_108[j];
+var rect={y:((parseFloat(item[0])-this.minxval)*this.xscale)+(i*_105)+_106,x:0,h:_105,w:((parseFloat(item[1])-this.minyval)*this.yscale),xval:parseFloat(item[0]),yval:parseFloat(item[1]),name:_107};
+if(rect.y<=0){
+rect.y=0;
+}
+if(rect.y>=1){
+rect.y=1;
+}
+if((rect.x>=0)&&(rect.x<=1)){
+this.bars.push(rect);
+}
+}
+i++;
+}
+};
+PlotKit.Layout.prototype._evaluateLineCharts=function(){
+var _111=PlotKit.Base.items;
+var _112=_111(this.datasets).length;
+this.points=new Array();
+var i=0;
+for(var _113 in this.datasets){
+var _114=this.datasets[_113];
+if(PlotKit.Base.isFuncLike(_114)){
+continue;
+}
+_114.sort(function(a,b){
+return compare(parseFloat(a[0]),parseFloat(b[0]));
+});
+for(var j=0;j<_114.length;j++){
+var item=_114[j];
+var _117={x:((parseFloat(item[0])-this.minxval)*this.xscale),y:1-((parseFloat(item[1])-this.minyval)*this.yscale),xval:parseFloat(item[0]),yval:parseFloat(item[1]),name:_113};
+if(_117.y<=0){
+_117.y=0;
+}
+if(_117.y>=1){
+_117.y=1;
+}
+if((_117.x>=0)&&(_117.x<=1)){
+this.points.push(_117);
+}
+}
+i++;
+}
+};
+PlotKit.Layout.prototype._evaluatePieCharts=function(){
+var _118=PlotKit.Base.items;
+var sum=MochiKit.Iter.sum;
+var _120=MochiKit.Base.itemgetter;
+var _121=_118(this.datasets).length;
+var _122=_118(this.datasets)[0][1];
+var _123=sum(map(_120(1),_122));
+this.slices=new Array();
+var _124=0;
+for(var i=0;i<_122.length;i++){
+var _125=_122[i][1]/_123;
+var _126=_124*Math.PI*2;
+var _127=(_124+_125)*Math.PI*2;
+var _128={fraction:_125,xval:_122[i][0],yval:_122[i][1],startAngle:_126,endAngle:_127};
+if(_122[i][1]!=0){
+this.slices.push(_128);
+}
+_124+=_125;
+}
+};
+PlotKit.Layout.prototype._evaluateLineTicksForXAxis=function(){
+var _129=MochiKit.Base.isUndefinedOrNull;
+if(this.options.xTicks){
+this.xticks=new Array();
+var _130=function(tick){
+var _132=tick.label;
+if(_129(_132)){
+_132=tick.v.toString();
+}
+var pos=this.xscale*(tick.v-this.minxval);
+if((pos>=0)&&(pos<=1)){
+this.xticks.push([pos,_132]);
+}
+};
+MochiKit.Iter.forEach(this.options.xTicks,bind(_130,this));
+}else{
+if(this.options.xNumberOfTicks){
+var _134=this._uniqueXValues();
+var _135=this.xrange/this.options.xNumberOfTicks;
+var _136=0;
+this.xticks=new Array();
+for(var i=0;i<=_134.length;i++){
+if((_134[i]-this.minxval)>=(_136*_135)){
+var pos=this.xscale*(_134[i]-this.minxval);
+if((pos>1)||(pos<0)){
+continue;
+}
+this.xticks.push([pos,_134[i]]);
+_136++;
+}
+if(_136>this.options.xNumberOfTicks){
+break;
+}
+}
+}
+}
+};
+PlotKit.Layout.prototype._evaluateLineTicksForYAxis=function(){
+var _137=MochiKit.Base.isUndefinedOrNull;
+if(this.options.yTicks){
+this.yticks=new Array();
+var _138=function(tick){
+var _139=tick.label;
+if(_137(_139)){
+_139=tick.v.toString();
+}
+var pos=1-(this.yscale*(tick.v-this.minyval));
+if((pos>=0)&&(pos<=1)){
+this.yticks.push([pos,_139]);
+}
+};
+MochiKit.Iter.forEach(this.options.yTicks,bind(_138,this));
+}else{
+if(this.options.yNumberOfTicks){
+this.yticks=new Array();
+var _140=PlotKit.Base.roundInterval;
+var prec=this.options.yTickPrecision;
+var _142=_140(this.yrange,this.options.yNumberOfTicks,prec);
+for(var i=0;i<=this.options.yNumberOfTicks;i++){
+var yval=this.minyval+(i*_142);
+var pos=1-((yval-this.minyval)*this.yscale);
+if((pos>1)||(pos<0)){
+continue;
+}
+this.yticks.push([pos,MochiKit.Format.roundToFixed(yval,prec)]);
+}
+}
+}
+};
+PlotKit.Layout.prototype._evaluateLineTicks=function(){
+this._evaluateLineTicksForXAxis();
+this._evaluateLineTicksForYAxis();
+};
+PlotKit.Layout.prototype._evaluateBarTicks=function(){
+this._evaluateLineTicks();
+var _144=function(tick){
+return [tick[0]+(this.minxdelta*this.xscale)/2,tick[1]];
+};
+this.xticks=MochiKit.Base.map(bind(_144,this),this.xticks);
+if(this.options.barOrientation=="horizontal"){
+var _145=this.xticks;
+this.xticks=this.yticks;
+this.yticks=_145;
+var _146=function(tick){
+return [1-tick[0],tick[1]];
+};
+this.xticks=MochiKit.Base.map(_146,this.xticks);
+}
+};
+PlotKit.Layout.prototype._evaluatePieTicks=function(){
+var _147=MochiKit.Base.isUndefinedOrNull;
+var _148=MochiKit.Format.numberFormatter("#%");
+this.xticks=new Array();
+if(this.options.xTicks){
+var _149=new Array();
+for(var i=0;i<this.slices.length;i++){
+_149[this.slices[i].xval]=this.slices[i];
+}
+for(var i=0;i<this.options.xTicks.length;i++){
+var tick=this.options.xTicks[i];
+var _150=_149[tick.v];
+var _151=tick.label;
+if(_150){
+if(_147(_151)){
+_151=tick.v.toString();
+}
+_151+=" ("+_148(_150.fraction)+")";
+this.xticks.push([tick.v,_151]);
+}
+}
+}else{
+for(var i=0;i<this.slices.length;i++){
+var _150=this.slices[i];
+var _151=_150.xval+" ("+_148(_150.fraction)+")";
+this.xticks.push([_150.xval,_151]);
+}
+}
+};
+PlotKit.Layout.prototype._regenerateHitTestCache=function(){
+this.hitTestCache.xvalues=this._uniqueXValues();
+this.hitTestCache.xlookup=new Array();
+this.hitTestCache.x2maxy=new Array();
+var _152=MochiKit.Base.listMax;
+var _153=MochiKit.Base.itemgetter;
+var map=MochiKit.Base.map;
+var _154=keys(this.datasets);
+for(var i=0;i<_154.length;i++){
+var _155=this.datasets[_154[i]];
+for(var j=0;j<_155.length;j++){
+var xval=_155[j][0];
+var yval=_155[j][1];
+if(this.hitTestCache.xlookup[xval]){
+this.hitTestCache.xlookup[xval].push([yval,_154[i]]);
+}else{
+this.hitTestCache.xlookup[xval]=[[yval,_154[i]]];
+}
+}
+}
+for(var x in this.hitTestCache.xlookup){
+var _157=this.hitTestCache.xlookup[x];
+this.hitTestCache.x2maxy[x]=_152(map(_153(0),_157));
+}
+};
+PlotKit.LayoutModule={};
+PlotKit.LayoutModule.Layout=PlotKit.Layout;
+PlotKit.LayoutModule.EXPORT=["Layout"];
+PlotKit.LayoutModule.EXPORT_OK=[];
+PlotKit.LayoutModule.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.LayoutModule.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.LayoutModule);
+try{
+if((typeof (PlotKit.Base)=="undefined")||(typeof (PlotKit.Layout)=="undefined")){
+throw "";
+}
+}
+catch(e){
+throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Base,Layout}";
+}
+if(typeof (PlotKit.CanvasRenderer)=="undefined"){
+PlotKit.CanvasRenderer={};
+}
+PlotKit.CanvasRenderer.NAME="PlotKit.CanvasRenderer";
+PlotKit.CanvasRenderer.VERSION=PlotKit.VERSION;
+PlotKit.CanvasRenderer.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.CanvasRenderer.toString=function(){
+return this.__repr__();
+};
+PlotKit.CanvasRenderer=function(_158,_159,_160){
+if(arguments.length>0){
+this.__init__(_158,_159,_160);
+}
+};
+PlotKit.CanvasRenderer.prototype.__init__=function(_161,_162,_163){
+var _164=MochiKit.Base.isUndefinedOrNull;
+var _165=MochiKit.Color.Color;
+this.options={"drawBackground":true,"backgroundColor":_165.whiteColor(),"padding":{left:30,right:30,top:5,bottom:10},"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),"strokeColor":_165.whiteColor(),"strokeColorTransform":"asStrokeColor","strokeWidth":0.5,"shouldFill":true,"shouldStroke":true,"drawXAxis":true,"drawYAxis":true,"axisLineColor":_165.blackColor(),"axisLineWidth":0.5,"axisTickSize":3,"axisLabelColor":_165.blackColor(),"axisLabelFont":"Arial","axisLabelFontSize":9,"axisLabelWidth":50,"pieRadius":0.4,"enableEvents":true};
+MochiKit.Base.update(this.options,_163?_163:{});
+this.layout=_162;
+this.element=MochiKit.DOM.getElement(_161);
+this.container=this.element.parentNode;
+this.isIE=PlotKit.Base.excanvasSupported();
+if(this.isIE&&!_164(G_vmlCanvasManager)){
+this.IEDelay=0.5;
+this.maxTries=5;
+this.renderDelay=null;
+this.clearDelay=null;
+this.element=G_vmlCanvasManager.initElement(this.element);
+}
+this.height=this.element.height;
+this.width=this.element.width;
+if(_164(this.element)){
+throw "CanvasRenderer() - passed canvas is not found";
+}
+if(!this.isIE&&!(PlotKit.CanvasRenderer.isSupported(this.element))){
+throw "CanvasRenderer() - Canvas is not supported.";
+}
+if(_164(this.container)||(this.container.nodeName.toLowerCase()!="div")){
+throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
+}
+this.xlabels=new Array();
+this.ylabels=new Array();
+this.isFirstRender=true;
+this.area={x:this.options.padding.left,y:this.options.padding.top,w:this.width-this.options.padding.left-this.options.padding.right,h:this.height-this.options.padding.top-this.options.padding.bottom};
+MochiKit.DOM.updateNodeAttributes(this.container,{"style":{"position":"relative","width":this.width+"px"}});
+};
+PlotKit.CanvasRenderer.prototype.render=function(){
+if(this.isIE){
+try{
+if(this.renderDelay){
+this.renderDelay.cancel();
+this.renderDelay=null;
+}
+var _166=this.element.getContext("2d");
+}
+catch(e){
+this.isFirstRender=false;
+if(this.maxTries-->0){
+this.renderDelay=MochiKit.Async.wait(this.IEDelay);
+this.renderDelay.addCallback(bind(this.render,this));
+}
+return;
+}
+}
+if(this.options.drawBackground){
+this._renderBackground();
+}
+if(this.layout.style=="bar"){
+this._renderBarChart();
+this._renderBarAxis();
+}else{
+if(this.layout.style=="pie"){
+this._renderPieChart();
+this._renderPieAxis();
+}else{
+if(this.layout.style=="line"){
+this._renderLineChart();
+this._renderLineAxis();
+}
+}
+}
+};
+PlotKit.CanvasRenderer.prototype._renderBarChartWrap=function(data,_168){
+var _169=this.element.getContext("2d");
+var _170=this.options.colorScheme.length;
+var _171=this.options.colorScheme;
+var _172=MochiKit.Base.keys(this.layout.datasets);
+var _173=_172.length;
+for(var i=0;i<_173;i++){
+var _174=_172[i];
+var _175=_171[i%_170];
+_169.save();
+_169.fillStyle=_175.toRGBString();
+if(this.options.strokeColor){
+_169.strokeStyle=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_169.strokeStyle=_175[this.options.strokeColorTransform]().toRGBString();
+}
+}
+_169.lineWidth=this.options.strokeWidth;
+var _176=function(obj){
+if(obj.name==_174){
+_168(_169,obj);
+}
+};
+MochiKit.Iter.forEach(data,bind(_176,this));
+_169.restore();
+}
+};
+PlotKit.CanvasRenderer.prototype._renderBarChart=function(){
+var bind=MochiKit.Base.bind;
+var _178=function(_179,bar){
+var x=this.area.w*bar.x+this.area.x;
+var y=this.area.h*bar.y+this.area.y;
+var w=this.area.w*bar.w;
+var h=this.area.h*bar.h;
+if((w<1)||(h<1)){
+return;
+}
+if(this.options.shouldFill){
+_179.fillRect(x,y,w,h);
+}
+if(this.options.shouldStroke){
+_179.strokeRect(x,y,w,h);
+}
+};
+this._renderBarChartWrap(this.layout.bars,bind(_178,this));
+};
+PlotKit.CanvasRenderer.prototype._renderLineChart=function(){
+var _182=this.element.getContext("2d");
+var _183=this.options.colorScheme.length;
+var _184=this.options.colorScheme;
+var _185=MochiKit.Base.keys(this.layout.datasets);
+var _186=_185.length;
+var bind=MochiKit.Base.bind;
+var _187=MochiKit.Base.partial;
+for(var i=0;i<_186;i++){
+var _188=_185[i];
+var _189=_184[i%_183];
+var _190=this.options.strokeColorTransform;
+_182.save();
+_182.fillStyle=_189.toRGBString();
+if(this.options.strokeColor){
+_182.strokeStyle=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_182.strokeStyle=_189[_190]().toRGBString();
+}
+}
+_182.lineWidth=this.options.strokeWidth;
+var _191=function(ctx){
+ctx.beginPath();
+ctx.moveTo(this.area.x,this.area.y+this.area.h);
+var _193=function(ctx_,_195){
+if(_195.name==_188){
+ctx_.lineTo(this.area.w*_195.x+this.area.x,this.area.h*_195.y+this.area.y);
+}
+};
+MochiKit.Iter.forEach(this.layout.points,_187(_193,ctx),this);
+ctx.lineTo(this.area.w+this.area.x,this.area.h+this.area.y);
+ctx.lineTo(this.area.x,this.area.y+this.area.h);
+ctx.closePath();
+};
+if(this.options.shouldFill){
+bind(_191,this)(_182);
+_182.fill();
+}
+if(this.options.shouldStroke){
+bind(_191,this)(_182);
+_182.stroke();
+}
+_182.restore();
+}
+};
+PlotKit.CanvasRenderer.prototype._renderPieChart=function(){
+var _196=this.element.getContext("2d");
+var _197=this.options.colorScheme.length;
+var _198=this.layout.slices;
+var _199=this.area.x+this.area.w*0.5;
+var _200=this.area.y+this.area.h*0.5;
+var _201=Math.min(this.area.w*this.options.pieRadius,this.area.h*this.options.pieRadius);
+if(this.isIE){
+_199=parseInt(_199);
+_200=parseInt(_200);
+_201=parseInt(_201);
+}
+for(var i=0;i<_198.length;i++){
+var _202=this.options.colorScheme[i%_197];
+_196.save();
+_196.fillStyle=_202.toRGBString();
+var _203=function(){
+_196.beginPath();
+_196.moveTo(_199,_200);
+_196.arc(_199,_200,_201,_198[i].startAngle-Math.PI/2,_198[i].endAngle-Math.PI/2,false);
+_196.lineTo(_199,_200);
+_196.closePath();
+};
+if(Math.abs(_198[i].startAngle-_198[i].endAngle)>0.001){
+if(this.options.shouldFill){
+_203();
+_196.fill();
+}
+if(this.options.shouldStroke){
+_203();
+_196.lineWidth=this.options.strokeWidth;
+if(this.options.strokeColor){
+_196.strokeStyle=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_196.strokeStyle=_202[this.options.strokeColorTransform]().toRGBString();
+}
+}
+_196.stroke();
+}
+}
+_196.restore();
+}
+};
+PlotKit.CanvasRenderer.prototype._renderBarAxis=function(){
+this._renderAxis();
+};
+PlotKit.CanvasRenderer.prototype._renderLineAxis=function(){
+this._renderAxis();
+};
+PlotKit.CanvasRenderer.prototype._renderAxis=function(){
+if(!this.options.drawXAxis&&!this.options.drawYAxis){
+return;
+}
+var _204=this.element.getContext("2d");
+var _205={"style":{"position":"absolute","fontSize":this.options.axisLabelFontSize+"px","zIndex":10,"color":this.options.axisLabelColor.toRGBString(),"width":this.options.axisLabelWidth+"px","overflow":"hidden"}};
+_204.save();
+_204.strokeStyle=this.options.axisLineColor.toRGBString();
+_204.lineWidth=this.options.axisLineWidth;
+if(this.options.drawYAxis){
+if(this.layout.yticks){
+var _206=function(tick){
+if(typeof (tick)=="function"){
+return;
+}
+var x=this.area.x;
+var y=this.area.y+tick[0]*this.area.h;
+_204.beginPath();
+_204.moveTo(x,y);
+_204.lineTo(x-this.options.axisTickSize,y);
+_204.closePath();
+_204.stroke();
+var _207=DIV(_205,tick[1]);
+_207.style.top=(y-this.options.axisLabelFontSize)+"px";
+_207.style.left=(x-this.options.padding.left-this.options.axisTickSize)+"px";
+_207.style.textAlign="right";
+_207.style.width=(this.options.padding.left-this.options.axisTickSize*2)+"px";
+MochiKit.DOM.appendChildNodes(this.container,_207);
+this.ylabels.push(_207);
+};
+MochiKit.Iter.forEach(this.layout.yticks,bind(_206,this));
+}
+_204.beginPath();
+_204.moveTo(this.area.x,this.area.y);
+_204.lineTo(this.area.x,this.area.y+this.area.h);
+_204.closePath();
+_204.stroke();
+}
+if(this.options.drawXAxis){
+if(this.layout.xticks){
+var _206=function(tick){
+if(typeof (dataset)=="function"){
+return;
+}
+var x=this.area.x+tick[0]*this.area.w;
+var y=this.area.y+this.area.h;
+_204.beginPath();
+_204.moveTo(x,y);
+_204.lineTo(x,y+this.options.axisTickSize);
+_204.closePath();
+_204.stroke();
+var _208=DIV(_205,tick[1]);
+_208.style.top=(y+this.options.axisTickSize)+"px";
+_208.style.left=(x-this.options.axisLabelWidth/2)+"px";
+_208.style.textAlign="center";
+_208.style.width=this.options.axisLabelWidth+"px";
+MochiKit.DOM.appendChildNodes(this.container,_208);
+this.xlabels.push(_208);
+};
+MochiKit.Iter.forEach(this.layout.xticks,bind(_206,this));
+}
+_204.beginPath();
+_204.moveTo(this.area.x,this.area.y+this.area.h);
+_204.lineTo(this.area.x+this.area.w,this.area.y+this.area.h);
+_204.closePath();
+_204.stroke();
+}
+_204.restore();
+};
+PlotKit.CanvasRenderer.prototype._renderPieAxis=function(){
+if(!this.options.drawXAxis){
+return;
+}
+if(this.layout.xticks){
+var _209=new Array();
+for(var i=0;i<this.layout.slices.length;i++){
+_209[this.layout.slices[i].xval]=this.layout.slices[i];
+}
+var _210=this.area.x+this.area.w*0.5;
+var _211=this.area.y+this.area.h*0.5;
+var _212=Math.min(this.area.w*this.options.pieRadius,this.area.h*this.options.pieRadius);
+var _213=this.options.axisLabelWidth;
+for(var i=0;i<this.layout.xticks.length;i++){
+var _214=_209[this.layout.xticks[i][0]];
+if(MochiKit.Base.isUndefinedOrNull(_214)){
+continue;
+}
+var _215=(_214.startAngle+_214.endAngle)/2;
+var _216=_215;
+if(_216>Math.PI*2){
+_216=_216-Math.PI*2;
+}else{
+if(_216<0){
+_216=_216+Math.PI*2;
+}
+}
+var _217=_210+Math.sin(_216)*(_212+10);
+var _218=_211-Math.cos(_216)*(_212+10);
+var _219={"position":"absolute","zIndex":11,"width":_213+"px","fontSize":this.options.axisLabelFontSize+"px","overflow":"hidden","color":this.options.axisLabelColor.toHexString()};
+if(_216<=Math.PI*0.5){
+_219["textAlign"]="left";
+_219["verticalAlign"]="top";
+_219["left"]=_217+"px";
+_219["top"]=(_218-this.options.axisLabelFontSize)+"px";
+}else{
+if((_216>Math.PI*0.5)&&(_216<=Math.PI)){
+_219["textAlign"]="left";
+_219["verticalAlign"]="bottom";
+_219["left"]=_217+"px";
+_219["top"]=_218+"px";
+}else{
+if((_216>Math.PI)&&(_216<=Math.PI*1.5)){
+_219["textAlign"]="right";
+_219["verticalAlign"]="bottom";
+_219["left"]=(_217-_213)+"px";
+_219["top"]=_218+"px";
+}else{
+_219["textAlign"]="right";
+_219["verticalAlign"]="bottom";
+_219["left"]=(_217-_213)+"px";
+_219["top"]=(_218-this.options.axisLabelFontSize)+"px";
+}
+}
+}
+var _220=DIV({"style":_219},this.layout.xticks[i][1]);
+this.xlabels.push(_220);
+MochiKit.DOM.appendChildNodes(this.container,_220);
+}
+}
+};
+PlotKit.CanvasRenderer.prototype._renderBackground=function(){
+var _221=this.element.getContext("2d");
+_221.save();
+_221.fillStyle=this.options.backgroundColor.toRGBString();
+_221.fillRect(0,0,this.width,this.height);
+_221.restore();
+};
+PlotKit.CanvasRenderer.prototype.clear=function(){
+if(this.isIE){
+try{
+if(this.clearDelay){
+this.clearDelay.cancel();
+this.clearDelay=null;
+}
+var _222=this.element.getContext("2d");
+}
+catch(e){
+this.isFirstRender=false;
+this.clearDelay=MochiKit.Async.wait(this.IEDelay);
+this.clearDelay.addCallback(bind(this.clear,this));
+return;
+}
+}
+var _222=this.element.getContext("2d");
+_222.clearRect(0,0,this.width,this.height);
+MochiKit.Iter.forEach(this.xlabels,MochiKit.DOM.removeElement);
+MochiKit.Iter.forEach(this.ylabels,MochiKit.DOM.removeElement);
+this.xlabels=new Array();
+this.ylabels=new Array();
+};
+PlotKit.CanvasRenderer.prototype._initialiseEvents=function(){
+var _223=MochiKit.Signal.connect;
+var bind=MochiKit.Base.bind;
+_223(this.element,"onclick",bind(this.onclick,this));
+};
+PlotKit.CanvasRenderer.prototype._resolveObject=function(e){
+var x=(e.mouse().page.x-PlotKit.Base.findPosX(this.element)-this.area.x)/this.area.w;
+var y=(e.mouse().page.y-PlotKit.Base.findPosY(this.element)-this.area.y)/this.area.h;
+var _225=this.layout.hitTest(x,y);
+if(_225){
+return _225;
+}
+return null;
+};
+PlotKit.CanvasRenderer.prototype._createEventObject=function(_226,e){
+if(_226==null){
+return null;
+}
+e.chart=_226;
+return e;
+};
+PlotKit.CanvasRenderer.prototype.onclick=function(e){
+var _227=this._resolveObject(e);
+var _228=this._createEventObject(_227,e);
+if(_228!=null){
+MochiKit.Signal.signal(this,"onclick",_228);
+}
+};
+PlotKit.CanvasRenderer.prototype.onmouseover=function(e){
+var _229=this._resolveObject(e);
+var _230=this._createEventObject(_229,e);
+if(_230!=null){
+signal(this,"onmouseover",_230);
+}
+};
+PlotKit.CanvasRenderer.prototype.onmouseout=function(e){
+var _231=this._resolveObject(e);
+var _232=this._createEventObject(_231,e);
+if(_232==null){
+signal(this,"onmouseout",e);
+}else{
+signal(this,"onmouseout",_232);
+}
+};
+PlotKit.CanvasRenderer.prototype.onmousemove=function(e){
+var _233=this._resolveObject(e);
+var _234=this._createEventObject(_233,e);
+if((_233==null)&&(this.event_isinside==null)){
+return;
+}
+if((_233!=null)&&(this.event_isinside==null)){
+signal(this,"onmouseover",_234);
+}
+if((_233==null)&&(this.event_isinside!=null)){
+signal(this,"onmouseout",_234);
+}
+if((_233!=null)&&(this.event_isinside!=null)){
+signal(this,"onmousemove",_234);
+}
+this.event_isinside=_233;
+};
+PlotKit.CanvasRenderer.isSupported=function(_235){
+var _236=null;
+try{
+if(MochiKit.Base.isUndefinedOrNull(_235)){
+_236=MochiKit.DOM.CANVAS({});
+}else{
+_236=MochiKit.DOM.getElement(_235);
+}
+var _237=_236.getContext("2d");
+}
+catch(e){
+var ie=navigator.appVersion.match(/MSIE (\d\.\d)/);
+var _239=(navigator.userAgent.toLowerCase().indexOf("opera")!=-1);
+if((!ie)||(ie[1]<6)||(_239)){
+return false;
+}
+return true;
+}
+return true;
+};
+PlotKit.Canvas={};
+PlotKit.Canvas.CanvasRenderer=PlotKit.CanvasRenderer;
+PlotKit.Canvas.EXPORT=["CanvasRenderer"];
+PlotKit.Canvas.EXPORT_OK=["CanvasRenderer"];
+PlotKit.Canvas.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.Canvas.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.Canvas);
+try{
+if(typeof (PlotKit.Layout)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "PlotKit depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Layout";
+}
+PlotKit.SVGRenderer=function(_240,_241,_242){
+if(arguments.length>0){
+this.__init__(_240,_241,_242);
+}
+};
+PlotKit.SVGRenderer.NAME="PlotKit.SVGRenderer";
+PlotKit.SVGRenderer.VERSION=PlotKit.VERSION;
+PlotKit.SVGRenderer.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.SVGRenderer.toString=function(){
+return this.__repr__();
+};
+PlotKit.SVGRenderer.SVGNS="http://www.w3.org/2000/svg";
+PlotKit.SVGRenderer.prototype.__init__=function(_243,_244,_245){
+var _246=MochiKit.Base.isUndefinedOrNull;
+this.options={"drawBackground":true,"backgroundColor":Color.whiteColor(),"padding":{left:30,right:30,top:5,bottom:10},"colorScheme":PlotKit.Base.palette(PlotKit.Base.baseColors()[1]),"strokeColor":Color.whiteColor(),"strokeColorTransform":"asStrokeColor","strokeWidth":0.5,"shouldFill":true,"shouldStroke":true,"drawXAxis":true,"drawYAxis":true,"axisLineColor":Color.blackColor(),"axisLineWidth":0.5,"axisTickSize":3,"axisLabelColor":Color.blackColor(),"axisLabelFont":"Arial","axisLabelFontSize":9,"axisLabelWidth":50,"axisLabelUseDiv":true,"pieRadius":0.4,"enableEvents":true};
+MochiKit.Base.update(this.options,_245?_245:{});
+this.layout=_244;
+this.element=MochiKit.DOM.getElement(_243);
+this.container=this.element.parentNode;
+this.height=parseInt(this.element.getAttribute("height"));
+this.width=parseInt(this.element.getAttribute("width"));
+this.document=document;
+this.root=this.element;
+try{
+this.document=this.element.getSVGDocument();
+this.root=_246(this.document.documentElement)?this.element:this.document.documentElement;
+}
+catch(e){
+}
+this.element.style.zIndex=1;
+if(_246(this.element)){
+throw "SVGRenderer() - passed SVG object is not found";
+}
+if(_246(this.container)||this.container.nodeName.toLowerCase()!="div"){
+throw "SVGRenderer() - No DIV's around the SVG.";
+}
+this.xlabels=new Array();
+this.ylabels=new Array();
+this.defs=this.createSVGElement("defs");
+this.area={x:this.options.padding.left,y:this.options.padding.top,w:this.width-this.options.padding.left-this.options.padding.right,h:this.height-this.options.padding.top-this.options.padding.bottom};
+MochiKit.DOM.updateNodeAttributes(this.container,{"style":{"position":"relative","width":this.width+"px"}});
+};
+PlotKit.SVGRenderer.prototype.render=function(){
+if(this.options.drawBackground){
+this._renderBackground();
+}
+if(this.layout.style=="bar"){
+this._renderBarChart();
+this._renderBarAxis();
+}else{
+if(this.layout.style=="pie"){
+this._renderPieChart();
+this._renderPieAxis();
+}else{
+if(this.layout.style=="line"){
+this._renderLineChart();
+this._renderLineAxis();
+}
+}
+}
+};
+PlotKit.SVGRenderer.prototype._renderBarOrLine=function(data,_247,_248,_249){
+var _250=this.options.colorScheme.length;
+var _251=this.options.colorScheme;
+var _252=MochiKit.Base.keys(this.layout.datasets);
+var _253=_252.length;
+for(var i=0;i<_253;i++){
+var _254=_252[i];
+var _255=new Array();
+var _256=_251[i%_250];
+if(this.options.shouldFill){
+_255["fill"]=_256.toRGBString();
+}else{
+_255["fill"]="none";
+}
+if(this.options.shouldStroke&&(this.options.strokeColor||this.options.strokeColorTransform)){
+if(this.options.strokeColor){
+_255["stroke"]=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_255["stroke"]=_256[this.options.strokeColorTransform]().toRGBString();
+}
+}
+_255["strokeWidth"]=this.options.strokeWidth;
+}
+if(_248){
+_248(_255);
+}
+var _257=function(obj){
+if(obj.name==_254){
+_247(_255,obj);
+}
+};
+MochiKit.Iter.forEach(data,bind(_257,this));
+if(_249){
+_249(_255);
+}
+}
+};
+PlotKit.SVGRenderer.prototype._renderBarChart=function(){
+var bind=MochiKit.Base.bind;
+var _258=function(_259,bar){
+var x=this.area.w*bar.x+this.area.x;
+var y=this.area.h*bar.y+this.area.y;
+var w=this.area.w*bar.w;
+var h=this.area.h*bar.h;
+this._drawRect(x,y,w,h,_259);
+};
+this._renderBarOrLine(this.layout.bars,bind(_258,this));
+};
+PlotKit.SVGRenderer.prototype._renderLineChart=function(){
+var bind=MochiKit.Base.bind;
+var _260=function(_261,_262){
+this._tempPointsBuffer+=(this.area.w*_262.x+this.area.x)+","+(this.area.h*_262.y+this.area.y)+" ";
+};
+var _263=function(_264){
+this._tempPointsBuffer="";
+this._tempPointsBuffer+=(this.area.x)+","+(this.area.y+this.area.h)+" ";
+};
+var _265=function(_266){
+this._tempPointsBuffer+=(this.area.w+this.area.x)+","+(this.area.h+this.area.y);
+_266["points"]=this._tempPointsBuffer;
+var elem=this.createSVGElement("polygon",_266);
+this.root.appendChild(elem);
+};
+this._renderBarOrLine(this.layout.points,bind(_260,this),bind(_263,this),bind(_265,this));
+};
+PlotKit.SVGRenderer.prototype._renderPieChart=function(){
+var _268=this.options.colorScheme.length;
+var _269=this.layout.slices;
+var _270=this.area.x+this.area.w*0.5;
+var _271=this.area.y+this.area.h*0.5;
+var _272=Math.min(this.area.w*this.options.pieRadius,this.area.h*this.options.pieRadius);
+if(_269.length==1&&(Math.abs(_269[0].startAngle)-Math.abs(_269[0].endAngle)<0.1)){
+var _273={"cx":_270,"cy":_271,"r":_272};
+var _274=this.options.colorScheme[0];
+if(this.options.shouldFill){
+_273["fill"]=_274.toRGBString();
+}else{
+_273["fill"]="none";
+}
+if(this.options.shouldStroke&&(this.options.strokeColor||this.options.strokeColorTransform)){
+if(this.options.strokeColor){
+_273["stroke"]=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_273["stroke"]=_274[this.options.strokeColorTransform]().toRGBString();
+}
+}
+_273["style"]="stroke-width: "+this.options.strokeWidth;
+}
+this.root.appendChild(this.createSVGElement("circle",_273));
+return;
+}
+for(var i=0;i<_269.length;i++){
+var _273=new Array();
+var _274=this.options.colorScheme[i%_268];
+if(this.options.shouldFill){
+_273["fill"]=_274.toRGBString();
+}else{
+_273["fill"]="none";
+}
+if(this.options.shouldStroke&&(this.options.strokeColor||this.options.strokeColorTransform)){
+if(this.options.strokeColor){
+_273["stroke"]=this.options.strokeColor.toRGBString();
+}else{
+if(this.options.strokeColorTransform){
+_273["stroke"]=_274[this.options.strokeColorTransform]().toRGBString();
+}
+}
+_273["style"]="stroke-width:"+this.options.strokeWidth;
+}
+var _275=0;
+if(Math.abs(_269[i].endAngle-_269[i].startAngle)>Math.PI){
+_275=1;
+}
+var x1=Math.cos(_269[i].startAngle-Math.PI/2)*_272;
+var y1=Math.sin(_269[i].startAngle-Math.PI/2)*_272;
+var x2=Math.cos(_269[i].endAngle-Math.PI/2)*_272;
+var y2=Math.sin(_269[i].endAngle-Math.PI/2)*_272;
+var rx=x2-x1;
+var ry=y2-y1;
+var _282="M"+_270+","+_271+" ";
+_282+="l"+x1+","+y1+" ";
+_282+="a"+_272+","+_272+" 0 "+_275+",1 "+rx+","+ry+" z";
+_273["d"]=_282;
+var elem=this.createSVGElement("path",_273);
+this.root.appendChild(elem);
+}
+};
+PlotKit.SVGRenderer.prototype._renderBarAxis=function(){
+this._renderAxis();
+};
+PlotKit.SVGRenderer.prototype._renderLineAxis=function(){
+this._renderAxis();
+};
+PlotKit.SVGRenderer.prototype._renderAxis=function(){
+if(!this.options.drawXAxis&&!this.options.drawYAxis){
+return;
+}
+var _283={"style":{"position":"absolute","textAlign":"center","fontSize":this.options.axisLabelFontSize+"px","zIndex":10,"color":this.options.axisLabelColor.toRGBString(),"width":this.options.axisLabelWidth+"px","overflow":"hidden"}};
+var _284={"stroke":this.options.axisLineColor.toRGBString(),"strokeWidth":this.options.axisLineWidth};
+if(this.options.drawYAxis){
+if(this.layout.yticks){
+var _285=function(tick){
+var x=this.area.x;
+var y=this.area.y+tick[0]*this.area.h;
+this._drawLine(x,y,x-3,y,_284);
+if(this.options.axisLabelUseDiv){
+var _286=DIV(_283,tick[1]);
+_286.style.top=(y-this.options.axisLabelFontSize)+"px";
+_286.style.left=(x-this.options.padding.left+this.options.axisTickSize)+"px";
+_286.style.textAlign="left";
+_286.style.width=(this.options.padding.left-3)+"px";
+MochiKit.DOM.appendChildNodes(this.container,_286);
+this.ylabels.push(_286);
+}else{
+var _287={y:y+3,x:(x-this.options.padding.left+3),width:(this.options.padding.left-this.options.axisTickSize)+"px",height:(this.options.axisLabelFontSize+3)+"px",fontFamily:"Arial",fontSize:this.options.axisLabelFontSize+"px",fill:this.options.axisLabelColor.toRGBString()};
+var _286=this.createSVGElement("text",_287);
+_286.appendChild(this.document.createTextNode(tick[1]));
+this.root.appendChild(_286);
+}
+};
+MochiKit.Iter.forEach(this.layout.yticks,bind(_285,this));
+}
+this._drawLine(this.area.x,this.area.y,this.area.x,this.area.y+this.area.h,_284);
+}
+if(this.options.drawXAxis){
+if(this.layout.xticks){
+var _285=function(tick){
+var x=this.area.x+tick[0]*this.area.w;
+var y=this.area.y+this.area.h;
+this._drawLine(x,y,x,y+this.options.axisTickSize,_284);
+if(this.options.axisLabelUseDiv){
+var _288=DIV(_283,tick[1]);
+_288.style.top=(y+this.options.axisTickSize)+"px";
+_288.style.left=(x-this.options.axisLabelWidth/2)+"px";
+_288.style.textAlign="center";
+_288.style.width=this.options.axisLabelWidth+"px";
+MochiKit.DOM.appendChildNodes(this.container,_288);
+this.xlabels.push(_288);
+}else{
+var _289={y:(y+this.options.axisTickSize+this.options.axisLabelFontSize),x:x-3,width:this.options.axisLabelWidth+"px",height:(this.options.axisLabelFontSize+3)+"px",fontFamily:"Arial",fontSize:this.options.axisLabelFontSize+"px",fill:this.options.axisLabelColor.toRGBString(),textAnchor:"middle"};
+var _288=this.createSVGElement("text",_289);
+_288.appendChild(this.document.createTextNode(tick[1]));
+this.root.appendChild(_288);
+}
+};
+MochiKit.Iter.forEach(this.layout.xticks,bind(_285,this));
+}
+this._drawLine(this.area.x,this.area.y+this.area.h,this.area.x+this.area.w,this.area.y+this.area.h,_284);
+}
+};
+PlotKit.SVGRenderer.prototype._renderPieAxis=function(){
+if(this.layout.xticks){
+var _290=new Array();
+for(var i=0;i<this.layout.slices.length;i++){
+_290[this.layout.slices[i].xval]=this.layout.slices[i];
+}
+var _291=this.area.x+this.area.w*0.5;
+var _292=this.area.y+this.area.h*0.5;
+var _293=Math.min(this.area.w*this.options.pieRadius+10,this.area.h*this.options.pieRadius+10);
+var _294=this.options.axisLabelWidth;
+for(var i=0;i<this.layout.xticks.length;i++){
+var _295=_290[this.layout.xticks[i][0]];
+if(MochiKit.Base.isUndefinedOrNull(_295)){
+continue;
+}
+var _296=(_295.startAngle+_295.endAngle)/2;
+var _297=_296;
+if(_297>Math.PI*2){
+_297=_297-Math.PI*2;
+}else{
+if(_297<0){
+_297=_297+Math.PI*2;
+}
+}
+var _298=_291+Math.sin(_297)*(_293+10);
+var _299=_292-Math.cos(_297)*(_293+10);
+var _300={"position":"absolute","zIndex":11,"width":_294+"px","fontSize":this.options.axisLabelFontSize+"px","overflow":"hidden","color":this.options.axisLabelColor.toHexString()};
+var _301={"width":_294+"px","fontSize":this.options.axisLabelFontSize+"px","height":(this.options.axisLabelFontSize+3)+"px","fill":this.options.axisLabelColor.toRGBString()};
+if(_297<=Math.PI*0.5){
+MochiKit.Base.update(_300,{"textAlign":"left","verticalAlign":"top","left":_298+"px","top":(_299-this.options.axisLabelFontSize)+"px"});
+MochiKit.Base.update(_301,{"x":_298,"y":(_299-this.options.axisLabelFontSize),"textAnchor":"left"});
+}else{
+if((_297>Math.PI*0.5)&&(_297<=Math.PI)){
+MochiKit.Base.update(_300,{"textAlign":"left","verticalAlign":"bottom","left":_298+"px","top":_299+"px"});
+MochiKit.Base.update(_301,{"textAnchor":"left","x":_298,"y":_299});
+}else{
+if((_297>Math.PI)&&(_297<=Math.PI*1.5)){
+MochiKit.Base.update(_300,{"textAlign":"right","verticalAlign":"bottom","left":_298+"px","top":_299+"px"});
+MochiKit.Base.update(_301,{"textAnchor":"right","x":_298-_294,"y":_299});
+}else{
+MochiKit.Base.update(_300,{"textAlign":"left","verticalAlign":"bottom","left":_298+"px","top":_299+"px"});
+MochiKit.Base.update(_301,{"textAnchor":"left","x":_298-_294,"y":_299-this.options.axisLabelFontSize});
+}
+}
+}
+if(this.options.axisLabelUseDiv){
+var _302=DIV({"style":_300},this.layout.xticks[i][1]);
+this.xlabels.push(_302);
+MochiKit.DOM.appendChildNodes(this.container,_302);
+}else{
+var _302=this.createSVGElement("text",_301);
+_302.appendChild(this.document.createTextNode(this.layout.xticks[i][1]));
+this.root.appendChild(_302);
+}
+}
+}
+};
+PlotKit.SVGRenderer.prototype._renderBackground=function(){
+var opts={"stroke":"none","fill":this.options.backgroundColor.toRGBString()};
+this._drawRect(0,0,this.width,this.height,opts);
+};
+PlotKit.SVGRenderer.prototype._drawRect=function(x,y,w,h,_304){
+var _305={x:x+"px",y:y+"px",width:w+"px",height:h+"px"};
+if(_304){
+MochiKit.Base.update(_305,_304);
+}
+var elem=this.createSVGElement("rect",_305);
+this.root.appendChild(elem);
+};
+PlotKit.SVGRenderer.prototype._drawLine=function(x1,y1,x2,y2,_306){
+var _307={x1:x1+"px",y1:y1+"px",x2:x2+"px",y2:y2+"px"};
+if(_306){
+MochiKit.Base.update(_307,_306);
+}
+var elem=this.createSVGElement("line",_307);
+this.root.appendChild(elem);
+};
+PlotKit.SVGRenderer.prototype.clear=function(){
+while(this.element.firstChild){
+this.element.removeChild(this.element.firstChild);
+}
+if(this.options.axisLabelUseDiv){
+for(var i=0;i<this.xlabels.length;i++){
+MochiKit.DOM.removeElement(this.xlabels[i]);
+}
+for(var i=0;i<this.ylabels.length;i++){
+MochiKit.DOM.removeElement(this.ylabels[i]);
+}
+}
+this.xlabels=new Array();
+this.ylabels=new Array();
+};
+PlotKit.SVGRenderer.prototype.createSVGElement=function(name,_309){
+var _310=MochiKit.Base.isUndefinedOrNull;
+var elem;
+var doc=_310(this.document)?document:this.document;
+try{
+elem=doc.createElementNS(PlotKit.SVGRenderer.SVGNS,name);
+}
+catch(e){
+elem=doc.createElement(name);
+elem.setAttribute("xmlns",PlotKit.SVGRenderer.SVGNS);
+}
+if(_309){
+MochiKit.DOM.updateNodeAttributes(elem,_309);
+}
+return elem;
+};
+PlotKit.SVGRenderer.SVG=function(_312){
+var ie=navigator.appVersion.match(/MSIE (\d\.\d)/);
+var _313=(navigator.userAgent.toLowerCase().indexOf("opera")!=-1);
+if(ie&&(ie[1]>=6)&&(!_313)){
+var _314=_312["width"]?_312["width"]:"100";
+var _315=_312["height"]?_312["height"]:"100";
+var eid=_312["id"]?_312["id"]:"notunique";
+var html="<svg:svg width=\""+_314+"\" height=\""+_315+"\" ";
+html+="id=\""+eid+"\" version=\"1.1\" baseProfile=\"full\" />";
+var _318=document.createElement(html);
+var _319=_318.getSVGDocument().createElementNS(PlotKit.SVGRenderer.SVGNS,"svg");
+_319.setAttribute("width",_314);
+_319.setAttribute("height",_315);
+_318.getSVGDocument().appendChild(_319);
+return _318;
+}else{
+return PlotKit.SVGRenderer.prototype.createSVGElement("svg",_312);
+}
+};
+PlotKit.SVGRenderer.isSupported=function(){
+var _320=(navigator.userAgent.toLowerCase().indexOf("opera")!=-1);
+var _321=navigator.appVersion.match(/MSIE (\d\.\d)/);
+var _322=navigator.userAgent.match(/AppleWebKit\/(\d+)/);
+var _323=navigator.userAgent.match(/Opera\/(\d*\.\d*)/);
+var _324=navigator.userAgent.match(/rv:(\d*\.\d*).*Gecko/);
+var _325="http://www.w3.org/TR/SVG11/feature#SVG";
+if(_321&&(_321[1]>=6)&&!_320){
+return document.implementation.hasFeature(_325,"1.1");
+}
+if(_323&&(_323[1]>8.9)){
+return true;
+}
+if(_324&&(_324>1.7)){
+return true;
+}
+return false;
+};
+PlotKit.SVG={};
+PlotKit.SVG.SVGRenderer=PlotKit.SVGRenderer;
+PlotKit.SVG.EXPORT=["SVGRenderer"];
+PlotKit.SVG.EXPORT_OK=["SVGRenderer"];
+PlotKit.SVG.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.SVG.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.SVG);
+try{
+if(typeof (PlotKit.CanvasRenderer)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "SweetCanvas depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Layout, Canvas}";
+}
+if(typeof (PlotKit.SweetCanvasRenderer)=="undefined"){
+PlotKit.SweetCanvasRenderer={};
+}
+PlotKit.SweetCanvasRenderer=function(_326,_327,_328){
+if(arguments.length>0){
+this.__init__(_326,_327,_328);
+}
+};
+PlotKit.SweetCanvasRenderer.NAME="PlotKit.SweetCanvasRenderer";
+PlotKit.SweetCanvasRenderer.VERSION=PlotKit.VERSION;
+PlotKit.SweetCanvasRenderer.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.SweetCanvasRenderer.toString=function(){
+return this.__repr__();
+};
+PlotKit.SweetCanvasRenderer.prototype=new PlotKit.CanvasRenderer();
+PlotKit.SweetCanvasRenderer.prototype.constructor=PlotKit.SweetCanvasRenderer;
+PlotKit.SweetCanvasRenderer.__super__=PlotKit.CanvasRenderer.prototype;
+PlotKit.SweetCanvasRenderer.prototype.__init__=function(el,_330,opts){
+var _331=PlotKit.Base.officeBlue();
+MochiKit.Base.update(_331,opts);
+PlotKit.SweetCanvasRenderer.__super__.__init__.call(this,el,_330,_331);
+};
+PlotKit.SweetCanvasRenderer.prototype._renderBarChart=function(){
+var bind=MochiKit.Base.bind;
+var _332=Color.blackColor().colorWithAlpha(0.1).toRGBString();
+var _333=function(_334,x,y,w,h){
+_334.fillStyle=_332;
+_334.fillRect(x-2,y-2,w+4,h+2);
+_334.fillStyle=_332;
+_334.fillRect(x-1,y-1,w+2,h+1);
+};
+var _335=this.options.colorScheme.length;
+var _336=this.options.colorScheme;
+var _337=PlotKit.Base.keys(this.layout.datasets);
+var _338=_337.length;
+var _339=function(name){
+for(var i=0;i<_338;i++){
+if(name==_337[i]){
+return _336[i%_335];
+}
+}
+return _336[0];
+};
+var _340=function(_341,bar){
+var x=this.area.w*bar.x+this.area.x;
+var y=this.area.h*bar.y+this.area.y;
+var w=this.area.w*bar.w;
+var h=this.area.h*bar.h;
+if((w<1)||(h<1)){
+return;
+}
+_341.save();
+_341.shadowBlur=5;
+_341.shadowColor=Color.fromHexString("#888888").toRGBString();
+if(this.isIE){
+_341.save();
+_341.fillStyle="#cccccc";
+_341.fillRect(x-2,y-2,w+4,h+2);
+_341.restore();
+}else{
+_333(_341,x,y,w,h);
+}
+if(this.options.shouldFill){
+_341.fillStyle=_339(bar.name).toRGBString();
+_341.fillRect(x,y,w,h);
+}
+_341.shadowBlur=0;
+_341.strokeStyle=Color.whiteColor().toRGBString();
+_341.lineWidth=2;
+if(this.options.shouldStroke){
+_341.strokeRect(x,y,w,h);
+}
+_341.restore();
+};
+this._renderBarChartWrap(this.layout.bars,bind(_340,this));
+};
+PlotKit.SweetCanvasRenderer.prototype._renderLineChart=function(){
+var _342=this.element.getContext("2d");
+var _343=this.options.colorScheme.length;
+var _344=this.options.colorScheme;
+var _345=PlotKit.Base.keys(this.layout.datasets);
+var _346=_345.length;
+var bind=MochiKit.Base.bind;
+for(var i=0;i<_346;i++){
+var _347=_345[i];
+var _348=_344[i%_343];
+var _349=this.options.strokeColorTransform;
+_342.save();
+var _350=function(ctx){
+ctx.beginPath();
+ctx.moveTo(this.area.x,this.area.y+this.area.h);
+var _351=function(ctx_,_352){
+if(_352.name==_347){
+ctx_.lineTo(this.area.w*_352.x+this.area.x,this.area.h*_352.y+this.area.y);
+}
+};
+MochiKit.Iter.forEach(this.layout.points,partial(_351,ctx),this);
+ctx.lineTo(this.area.w+this.area.x,this.area.h+this.area.y);
+ctx.lineTo(this.area.x,this.area.y+this.area.h);
+ctx.closePath();
+};
+if(this.options.shouldFill){
+_342.save();
+if(this.isIE){
+_342.fillStyle="#cccccc";
+}else{
+_342.fillStyle=Color.blackColor().colorWithAlpha(0.2).toRGBString();
+}
+_342.translate(-1,-2);
+bind(_350,this)(_342);
+if(this.options.shouldFill){
+_342.fill();
+}
+_342.restore();
+}
+_342.shadowBlur=5;
+_342.shadowColor=Color.fromHexString("#888888").toRGBString();
+_342.fillStyle=_348.toRGBString();
+_342.lineWidth=2;
+_342.strokeStyle=Color.whiteColor().toRGBString();
+if(this.options.shouldFill){
+bind(_350,this)(_342);
+_342.fill();
+}
+if(this.options.shouldStroke){
+bind(_350,this)(_342);
+_342.stroke();
+}
+_342.restore();
+}
+};
+PlotKit.SweetCanvasRenderer.prototype._renderPieChart=function(){
+var _353=this.element.getContext("2d");
+var _354=this.options.colorScheme.length;
+var _355=this.layout.slices;
+var _356=this.area.x+this.area.w*0.5;
+var _357=this.area.y+this.area.h*0.5;
+var _358=Math.min(this.area.w*this.options.pieRadius,this.area.h*this.options.pieRadius);
+if(this.isIE){
+_356=parseInt(_356);
+_357=parseInt(_357);
+_358=parseInt(_358);
+}
+if(!this.isIE){
+_353.save();
+var _359=Color.blackColor().colorWithAlpha(0.2);
+_353.fillStyle=_359.toRGBString();
+_353.shadowBlur=5;
+_353.shadowColor=Color.fromHexString("#888888").toRGBString();
+_353.translate(1,1);
+_353.beginPath();
+_353.moveTo(_356,_357);
+_353.arc(_356,_357,_358+2,0,Math.PI*2,false);
+_353.closePath();
+_353.fill();
+_353.restore();
+}
+_353.save();
+_353.strokeStyle=Color.whiteColor().toRGBString();
+_353.lineWidth=2;
+for(var i=0;i<_355.length;i++){
+var _360=this.options.colorScheme[i%_354];
+_353.fillStyle=_360.toRGBString();
+var _361=function(){
+_353.beginPath();
+_353.moveTo(_356,_357);
+_353.arc(_356,_357,_358,_355[i].startAngle-Math.PI/2,_355[i].endAngle-Math.PI/2,false);
+_353.lineTo(_356,_357);
+_353.closePath();
+};
+if(Math.abs(_355[i].startAngle-_355[i].endAngle)>0.0001){
+if(this.options.shouldFill){
+_361();
+_353.fill();
+}
+if(this.options.shouldStroke){
+_361();
+_353.stroke();
+}
+}
+}
+_353.restore();
+};
+PlotKit.SweetCanvasRenderer.prototype._renderBackground=function(){
+var _362=this.element.getContext("2d");
+if(this.layout.style=="bar"||this.layout.style=="line"){
+_362.save();
+_362.fillStyle=this.options.backgroundColor.toRGBString();
+_362.fillRect(this.area.x,this.area.y,this.area.w,this.area.h);
+_362.strokeStyle=this.options.axisLineColor.toRGBString();
+_362.lineWidth=1;
+var _363=this.layout.yticks;
+var _364=false;
+if(this.layout.style=="bar"&&this.layout.options.barOrientation=="horizontal"){
+_363=this.layout.xticks;
+_364=true;
+}
+for(var i=0;i<_363.length;i++){
+var x1=0;
+var y1=0;
+var x2=0;
+var y2=0;
+if(_364){
+x1=_363[i][0]*this.area.w+this.area.x;
+y1=this.area.y;
+x2=x1;
+y2=y1+this.area.h;
+}else{
+x1=this.area.x;
+y1=_363[i][0]*this.area.h+this.area.y;
+x2=x1+this.area.w;
+y2=y1;
+}
+_362.beginPath();
+_362.moveTo(x1,y1);
+_362.lineTo(x2,y2);
+_362.closePath();
+_362.stroke();
+}
+_362.restore();
+}else{
+PlotKit.SweetCanvasRenderer.__super__._renderBackground.call(this);
+}
+};
+PlotKit.SweetCanvas={};
+PlotKit.SweetCanvas.SweetCanvasRenderer=PlotKit.SweetCanvasRenderer;
+PlotKit.SweetCanvas.EXPORT=["SweetCanvasRenderer"];
+PlotKit.SweetCanvas.EXPORT_OK=["SweetCanvasRenderer"];
+PlotKit.SweetCanvas.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.SweetCanvas.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.SweetCanvas);
+try{
+if(typeof (PlotKit.SVGRenderer)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "SweetSVG depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Layout, SVG}";
+}
+if(typeof (PlotKit.SweetSVGRenderer)=="undefined"){
+PlotKit.SweetSVGRenderer={};
+}
+PlotKit.SweetSVGRenderer=function(_365,_366,_367){
+if(arguments.length>0){
+this.__init__(_365,_366,_367);
+}
+};
+PlotKit.SweetSVGRenderer.NAME="PlotKit.SweetSVGRenderer";
+PlotKit.SweetSVGRenderer.VERSION=PlotKit.VERSION;
+PlotKit.SweetSVGRenderer.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.SweetSVGRenderer.toString=function(){
+return this.__repr__();
+};
+PlotKit.SweetSVGRenderer.prototype=new PlotKit.SVGRenderer();
+PlotKit.SweetSVGRenderer.prototype.constructor=PlotKit.SweetSVGRenderer;
+PlotKit.SweetSVGRenderer.__super__=PlotKit.SVGRenderer.prototype;
+PlotKit.SweetSVGRenderer.prototype.__init__=function(_368,_369,_370){
+var _371=PlotKit.Base.officeBlue();
+MochiKit.Base.update(_371,_370);
+PlotKit.SweetSVGRenderer.__super__.__init__.call(this,_368,_369,_371);
+};
+PlotKit.SweetSVGRenderer.prototype._addDropShadowFilter=function(){
+var _372=this.createSVGElement("filter",{x:0,y:0,"id":"dropShadow"});
+var _373=this.createSVGElement("feOffset",{"in":"SourceGraphic","dx":0,"dy":0,"result":"topCopy"});
+var blur=this.createSVGElement("feGaussianBlur",{"in":"SourceAlpha","StdDeviation":2,"result":"shadow"});
+var _375=this.createSVGElement("feOffset",{"in":"shadow","dx":-1,"dy":-2,"result":"movedShadow"});
+var _376=this.createSVGElement("feMerge");
+var _377=this.createSVGElement("feMergeNode",{"in":"topCopy"});
+var _378=this.createSVGElement("feMergeNode",{"in":"movedShadow"});
+_376.appendChild(_377);
+_376.appendChild(_378);
+_372.appendChild(_373);
+_372.appendChild(blur);
+_372.appendChild(_375);
+_372.appendChild(_376);
+this.defs.appendChild(_372);
+};
+PlotKit.SweetSVGRenderer.prototype._renderBarChart=function(){
+var bind=MochiKit.Base.bind;
+var _379=Color.blackColor().toRGBString();
+var _380="fill:"+_379+";fill-opacity:0.15";
+var _381="stroke-width: 2.0; stroke:"+Color.whiteColor().toRGBString();
+var _382=function(_383,bar){
+var x=this.area.w*bar.x+this.area.x;
+var y=this.area.h*bar.y+this.area.y;
+var w=this.area.w*bar.w;
+var h=this.area.h*bar.h;
+if((w<1)||(h<1)){
+return;
+}
+_383["style"]=_381;
+this._drawRect(x-2,y-1,w+4,h+2,{"style":_380});
+this._drawRect(x,y,w,h,_383);
+};
+this._renderBarOrLine(this.layout.bars,bind(_382,this));
+};
+PlotKit.SweetSVGRenderer.prototype._renderLineChart=function(){
+var bind=MochiKit.Base.bind;
+var _384=Color.blackColor().toRGBString();
+var _385="fill:"+_384+";fill-opacity:0.15";
+var _386="stroke-width: 2.0; stroke:"+Color.whiteColor().toRGBString();
+var _387=function(_388,_389){
+this._tempPointsBuffer+=(this.area.w*_389.x+this.area.x)+","+(this.area.h*_389.y+this.area.y)+" ";
+};
+var _390=function(_391){
+this._tempPointsBuffer="";
+this._tempPointsBuffer+=(this.area.x)+","+(this.area.y+this.area.h)+" ";
+};
+var _392=function(_393){
+this._tempPointsBuffer+=(this.area.w+this.area.x)+","+(this.area.h+this.area.y);
+_393["points"]=this._tempPointsBuffer;
+_393["stroke"]="none";
+_393["transform"]="translate(-2, -1)";
+_393["style"]=_385;
+var _394=this.createSVGElement("polygon",_393);
+this.root.appendChild(_394);
+_393["transform"]="";
+_393["style"]=_386;
+var elem=this.createSVGElement("polygon",_393);
+this.root.appendChild(elem);
+};
+this._renderBarOrLine(this.layout.points,bind(_387,this),bind(_390,this),bind(_392,this));
+};
+PlotKit.SweetSVGRenderer.prototype._renderPieChart=function(){
+var _395=this.area.x+this.area.w*0.5;
+var _396=this.area.y+this.area.h*0.5;
+var _397=Color.blackColor().toRGBString();
+var _398=Math.min(this.area.w*this.options.pieRadius,this.area.h*this.options.pieRadius);
+var _399="fill:"+_397+";fill-opacity:0.15";
+var _400=this.createSVGElement("circle",{"style":_399,"cx":_395+1,"cy":_396+1,"r":_398+1});
+this.root.appendChild(_400);
+PlotKit.SweetSVGRenderer.__super__._renderPieChart.call(this);
+};
+PlotKit.SweetSVGRenderer.prototype._renderBackground=function(){
+var _401={"fill":this.options.backgroundColor.toRGBString(),"stroke":"none"};
+if(this.layout.style=="bar"||this.layout.style=="line"){
+this._drawRect(this.area.x,this.area.y,this.area.w,this.area.h,_401);
+var _402=this.layout.yticks;
+var _403=false;
+if(this.layout.style=="bar"&&this.layout.options.barOrientation=="horizontal"){
+_402=this.layout.xticks;
+_403=true;
+}
+for(var i=0;i<_402.length;i++){
+var x=0;
+var y=0;
+var w=0;
+var h=0;
+if(_403){
+x=_402[i][0]*this.area.w+this.area.x;
+y=this.area.y;
+w=1;
+h=this.area.w;
+}else{
+x=this.area.x;
+y=_402[i][0]*this.area.h+this.area.y;
+w=this.area.w;
+h=1;
+}
+this._drawRect(x,y,w,h,{"fill":this.options.axisLineColor.toRGBString()});
+}
+}else{
+PlotKit.SweetSVGRenderer.__super__._renderBackground.call(this);
+}
+};
+PlotKit.SweetSVG={};
+PlotKit.SweetSVG.SweetSVGRenderer=PlotKit.SweetSVGRenderer;
+PlotKit.SweetSVG.EXPORT=["SweetSVGRenderer"];
+PlotKit.SweetSVG.EXPORT_OK=["SweetSVGRenderer"];
+PlotKit.SweetSVG.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.SweetSVG.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.SweetSVG);
+try{
+if(typeof (PlotKit.CanvasRenderer)=="undefined"){
+throw "";
+}
+}
+catch(e){
+throw "PlotKit.EasyPlot depends on all of PlotKit's components";
+}
+if(typeof (PlotKit.EasyPlot)=="undefined"){
+PlotKit.EasyPlot={};
+}
+PlotKit.EasyPlot.NAME="PlotKit.EasyPlot";
+PlotKit.EasyPlot.VERSION=PlotKit.VERSION;
+PlotKit.EasyPlot.__repr__=function(){
+return "["+this.NAME+" "+this.VERSION+"]";
+};
+PlotKit.EasyPlot.toString=function(){
+return this.__repr__();
+};
+PlotKit.EasyPlot=function(_404,_405,_406,_407){
+this.layout=new Layout(_404,_405);
+this.divElem=_406;
+this.width=parseInt(_406.getAttribute("width"));
+this.height=parseInt(_406.getAttribute("height"));
+this.deferredCount=0;
+if(this.width<1){
+this.width=this.divElem.width?this.divElem.width:300;
+}
+if(this.height<1){
+this.height=this.divElem.height?this.divElem.height:300;
+}
+if(isArrayLike(_407)){
+for(var i=0;i<_407.length;i++){
+if(typeof (_407[i])=="string"){
+this.deferredCount++;
+var d=MochiKit.Async.doSimpleXMLHttpRequest(_407[i]);
+d.addCallback(MochiKit.Base.bind(PlotKit.EasyPlot.onDataLoaded,this));
+}else{
+if(isArrayLike(_407[i])){
+this.layout.addDataset("data-"+i,_407[i]);
+}
+}
+}
+}else{
+if(!isUndefinedOrNull(_407)){
+throw "Passed datasources are not Array like";
+}
+}
+if(CanvasRenderer.isSupported()){
+this.element=CANVAS({"id":this.divElem.getAttribute("id")+"-canvas","width":this.width,"height":this.height},"");
+this.divElem.appendChild(this.element);
+this.renderer=new SweetCanvasRenderer(this.element,this.layout,_405);
+}else{
+if(SVGRenderer.isSupported()){
+this.element=SVGRenderer.SVG({"id":this.divElem.getAttribute("id")+"-svg","width":this.width,"height":this.height,"version":"1.1","baseProfile":"full"},"");
+this.divElem.appendChild(this.element);
+this.renderer=new SweetSVGRenderer(this.element,this.layout,_405);
+}
+}
+if((this.deferredCount==0)&&(PlotKit.Base.keys(this.layout.datasets).length>0)){
+this.layout.evaluate();
+this.renderer.clear();
+this.renderer.render();
+}
+};
+PlotKit.EasyPlot.onDataLoaded=function(_409){
+var _410=new Array();
+var _411=_409.responseText.split("\n");
+for(var i=0;i<_411.length;i++){
+var _412=MochiKit.Format.strip(_411[i]);
+if((_412.length>1)&&(_412.charAt(0)!="#")){
+_410.push(_412.split(","));
+}
+}
+this.layout.addDataset("data-ajax-"+this.deferredCount,_410);
+this.deferredCount--;
+if((this.deferredCount==0)&&(PlotKit.Base.keys(this.layout.datasets).length>0)){
+this.layout.evaluate();
+this.renderer.clear();
+this.renderer.render();
+}
+};
+PlotKit.EasyPlot.prototype.reload=function(){
+this.layout.evaluate();
+this.renderer.clear();
+this.renderer.render();
+};
+PlotKit.EasyPlotModule={};
+PlotKit.EasyPlotModule.EasyPlot=PlotKit.EasyPlot;
+PlotKit.EasyPlotModule.EXPORT=["EasyPlot"];
+PlotKit.EasyPlotModule.EXPORT_OK=[];
+PlotKit.EasyPlotModule.__new__=function(){
+var m=MochiKit.Base;
+m.nameFunctions(this);
+this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)};
+};
+PlotKit.EasyPlotModule.__new__();
+MochiKit.Base._exportSymbols(this,PlotKit.EasyPlotModule);
+
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SVG.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SVG.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,705 @@
+/*
+    PlotKit SVG
+    ===========
+    SVG Renderer for PlotKit
+
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+*/
+
+// -------------------------------------------------------------------------
+// NOTES: - If you use XHTML1.1 strict, then you must include each MochiKit
+//          file individuall.
+//        - For IE support, you must include the AdobeSVG object hack.
+//          See tests/svg.html for details.
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
+// Check required components
+// -------------------------------------------------------------------------
+
+try {    
+    if (typeof(PlotKit.Layout) == 'undefined')
+    {
+        throw "";    
+    }
+} 
+catch (e) {    
+    throw "PlotKit depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Layout"
+}
+
+
+// ---------------------------------------------------------------------------
+//  SVG Renderer
+// ---------------------------------------------------------------------------
+
+PlotKit.SVGRenderer = function(element, layout, options) {
+    if (arguments.length > 0) 
+        this.__init__(element, layout, options);
+};
+
+PlotKit.SVGRenderer.NAME = "PlotKit.SVGRenderer";
+PlotKit.SVGRenderer.VERSION = PlotKit.VERSION;
+
+PlotKit.SVGRenderer.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.SVGRenderer.toString = function() {
+    return this.__repr__();
+}
+
+PlotKit.SVGRenderer.SVGNS = 'http://www.w3.org/2000/svg';
+
+PlotKit.SVGRenderer.prototype.__init__ = function(element, layout, options) {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+
+    // default options
+    this.options = {
+        "drawBackground": true,
+        "backgroundColor": Color.whiteColor(),
+        "padding": {left: 30, right: 30, top: 5, bottom: 10},
+        "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[1]),
+        "strokeColor": Color.whiteColor(),
+        "strokeColorTransform": "asStrokeColor",
+        "strokeWidth": 0.5,
+        "shouldFill": true,
+        "shouldStroke": true,
+        "drawXAxis": true,
+        "drawYAxis": true,
+        "axisLineColor": Color.blackColor(),
+        "axisLineWidth": 0.5,
+        "axisTickSize": 3,
+        "axisLabelColor": Color.blackColor(),
+        "axisLabelFont": "Arial",
+        "axisLabelFontSize": 9,
+        "axisLabelWidth": 50,
+        "axisLabelUseDiv": true,
+        "pieRadius": 0.4,
+        "enableEvents": true
+    };
+
+    MochiKit.Base.update(this.options, options ? options : {});
+    this.layout = layout;
+    this.element = MochiKit.DOM.getElement(element);
+    this.container = this.element.parentNode;
+    this.height = parseInt(this.element.getAttribute("height"));
+    this.width = parseInt(this.element.getAttribute("width"));
+    this.document = document;
+    this.root = this.element;
+
+    // Adobe SVG Support:
+    // - if an exception is thrown, then no Adobe SVG Plugin support.
+    try {
+        this.document = this.element.getSVGDocument();
+        this.root = isNil(this.document.documentElement) ? this.element : this.document.documentElement;
+    }
+    catch (e) {
+    }
+
+    this.element.style.zIndex = 1;
+
+    if (isNil(this.element))
+        throw "SVGRenderer() - passed SVG object is not found";
+
+    if (isNil(this.container) || this.container.nodeName.toLowerCase() != "div")
+        throw "SVGRenderer() - No DIV's around the SVG.";
+
+    // internal state
+    this.xlabels = new Array();
+    this.ylabels = new Array();
+
+    // initialise some meta structures in SVG
+    this.defs = this.createSVGElement("defs");
+
+    this.area = {
+        x: this.options.padding.left,
+        y: this.options.padding.top,
+        w: this.width - this.options.padding.left - this.options.padding.right,
+        h: this.height - this.options.padding.top - this.options.padding.bottom
+    };
+
+    MochiKit.DOM.updateNodeAttributes(this.container, 
+    {"style":{ "position": "relative", "width": this.width + "px"}});
+
+    
+};
+
+
+PlotKit.SVGRenderer.prototype.render = function() {
+    if (this.options.drawBackground)
+        this._renderBackground();
+
+    if (this.layout.style == "bar") {
+        this._renderBarChart();
+        this._renderBarAxis();
+    }
+    else if (this.layout.style == "pie") {
+        this._renderPieChart();
+        this._renderPieAxis();
+    }
+    else if (this.layout.style == "line") {
+        this._renderLineChart();
+        this._renderLineAxis();
+    }
+};
+
+PlotKit.SVGRenderer.prototype._renderBarOrLine = function(data, plotFunc, startFunc, endFunc) {
+    
+    var colorCount = this.options.colorScheme.length;
+    var colorScheme = this.options.colorScheme;
+    var setNames = MochiKit.Base.keys(this.layout.datasets);
+    var setCount = setNames.length;
+
+    for (var i = 0; i < setCount; i++) {
+        var setName = setNames[i];
+        var attrs = new Array();
+        var color = colorScheme[i%colorCount];
+
+        if (this.options.shouldFill)
+            attrs["fill"] = color.toRGBString();
+        else
+            attrs["fill"] = "none";
+
+        if (this.options.shouldStroke && 
+            (this.options.strokeColor || this.options.strokeColorTransform)) {
+            if (this.options.strokeColor)
+                attrs["stroke"] = this.options.strokeColor.toRGBString();
+            else if (this.options.strokeColorTransform)
+                attrs["stroke"] = color[this.options.strokeColorTransform]().toRGBString();
+            attrs["strokeWidth"] = this.options.strokeWidth;
+        }
+
+        if (startFunc)
+            startFunc(attrs);
+
+        var forEachFunc = function(obj) {
+            if (obj.name == setName)
+                plotFunc(attrs, obj);
+        };                
+
+        MochiKit.Iter.forEach(data, bind(forEachFunc, this));
+        if (endFunc)
+            endFunc(attrs);
+    }
+};
+
+PlotKit.SVGRenderer.prototype._renderBarChart = function() {
+    var bind = MochiKit.Base.bind;
+
+    var drawRect = function(attrs, bar) {
+        var x = this.area.w * bar.x + this.area.x;
+        var y = this.area.h * bar.y + this.area.y;
+        var w = this.area.w * bar.w;
+        var h = this.area.h * bar.h;
+        this._drawRect(x, y, w, h, attrs);
+    };
+    this._renderBarOrLine(this.layout.bars, bind(drawRect, this));
+};
+
+PlotKit.SVGRenderer.prototype._renderLineChart = function() {
+    var bind = MochiKit.Base.bind;
+
+    var addPoint = function(attrs, point) {
+        this._tempPointsBuffer += (this.area.w * point.x + this.area.x) + "," +
+                                 (this.area.h * point.y + this.area.y) + " ";
+    };
+
+    var startLine = function(attrs) {
+        this._tempPointsBuffer = "";
+        this._tempPointsBuffer += (this.area.x) + "," + (this.area.y+this.area.h) + " ";
+    };
+
+    var endLine = function(attrs) {
+        this._tempPointsBuffer += (this.area.w + this.area.x) + ","  +(this.area.h + this.area.y);
+        attrs["points"] = this._tempPointsBuffer;
+        var elem = this.createSVGElement("polygon", attrs);
+        this.root.appendChild(elem);
+    };
+
+    this._renderBarOrLine(this.layout.points, 
+                          bind(addPoint, this), 
+                          bind(startLine, this), 
+                          bind(endLine, this));
+};
+
+
+PlotKit.SVGRenderer.prototype._renderPieChart = function() {
+    var colorCount = this.options.colorScheme.length;
+    var slices = this.layout.slices;
+
+    var centerx = this.area.x + this.area.w * 0.5;
+    var centery = this.area.y + this.area.h * 0.5;
+    var radius = Math.min(this.area.w * this.options.pieRadius, 
+                          this.area.h * this.options.pieRadius);
+
+    // NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1
+    // so we have to subtract 90 degrees to make it start at y = 1, x = 0
+
+	// workaround if we only have 1 slice of 100%
+	if (slices.length == 1 && (Math.abs(slices[0].startAngle) - Math.abs(slices[0].endAngle) < 0.1)) {
+        var attrs = {"cx": centerx , "cy": centery , "r": radius };
+        var color = this.options.colorScheme[0];
+        if (this.options.shouldFill)
+            attrs["fill"] = color.toRGBString();
+        else
+            attrs["fill"] = "none";
+
+        if (this.options.shouldStroke && 
+            (this.options.strokeColor || this.options.strokeColorTransform)) {
+            if (this.options.strokeColor)
+                attrs["stroke"] = this.options.strokeColor.toRGBString();
+            else if (this.options.strokeColorTransform)
+                attrs["stroke"] = color[this.options.strokeColorTransform]().toRGBString();
+            attrs["style"] = "stroke-width: " + this.options.strokeWidth;
+        }
+
+        this.root.appendChild(this.createSVGElement("circle", attrs));
+        return;
+	}
+
+    for (var i = 0; i < slices.length; i++) {
+        var attrs = new Array();
+        var color = this.options.colorScheme[i%colorCount];
+        if (this.options.shouldFill)
+            attrs["fill"] = color.toRGBString();
+        else
+            attrs["fill"] = "none";
+
+        if (this.options.shouldStroke &&
+            (this.options.strokeColor || this.options.strokeColorTransform)) {
+            if (this.options.strokeColor)
+                attrs["stroke"] = this.options.strokeColor.toRGBString();
+            else if (this.options.strokeColorTransform)
+                attrs["stroke"] = color[this.options.strokeColorTransform]().toRGBString();
+            attrs["style"] = "stroke-width:" + this.options.strokeWidth;
+        }
+
+        var largearc = 0;
+        if (Math.abs(slices[i].endAngle - slices[i].startAngle) > Math.PI)
+            largearc = 1;
+        var x1 = Math.cos(slices[i].startAngle - Math.PI/2) * radius;
+        var y1 = Math.sin(slices[i].startAngle - Math.PI/2) * radius;
+        var x2 = Math.cos(slices[i].endAngle - Math.PI/2) * radius;
+        var y2 = Math.sin(slices[i].endAngle - Math.PI/2) * radius;
+        var rx = x2 - x1;
+        var ry = y2 - y1;
+
+        var pathString = "M" + centerx + "," + centery + " ";       
+        pathString += "l" + x1 + "," + y1 + " ";
+        pathString += "a" + radius + "," + radius + " 0 " + largearc + ",1 " + rx + "," + ry + " z";
+
+        attrs["d"] = pathString;
+
+        var elem = this.createSVGElement("path", attrs);
+        this.root.appendChild(elem);
+    }
+};
+
+PlotKit.SVGRenderer.prototype._renderBarAxis = function() {
+    this._renderAxis();
+}
+
+PlotKit.SVGRenderer.prototype._renderLineAxis = function() {
+    this._renderAxis();
+};
+
+
+PlotKit.SVGRenderer.prototype._renderAxis = function() {
+
+    if (!this.options.drawXAxis && !this.options.drawYAxis)
+        return;
+
+    var labelStyle = {"style":
+         {"position": "absolute",
+          "textAlign": "center",
+          "fontSize": this.options.axisLabelFontSize + "px",
+          "zIndex": 10,
+          "color": this.options.axisLabelColor.toRGBString(),
+          "width": this.options.axisLabelWidth + "px",
+          "overflow": "hidden"
+         }
+    };
+
+    // axis lines
+    var lineAttrs = {
+        "stroke": this.options.axisLineColor.toRGBString(),
+        "strokeWidth": this.options.axisLineWidth
+    };
+    
+
+    if (this.options.drawYAxis) {
+        if (this.layout.yticks) {
+            var drawTick = function(tick) {
+                var x = this.area.x;
+                var y = this.area.y + tick[0] * this.area.h;
+                this._drawLine(x, y, x - 3, y, lineAttrs);
+                
+                if (this.options.axisLabelUseDiv) {
+                    var label = DIV(labelStyle, tick[1]);
+                    label.style.top = (y - this.options.axisLabelFontSize) + "px";
+                    label.style.left = (x - this.options.padding.left + this.options.axisTickSize) + "px";
+                    label.style.textAlign = "left";
+                    label.style.width = (this.options.padding.left - 3) + "px";
+                    MochiKit.DOM.appendChildNodes(this.container, label);
+                    this.ylabels.push(label);
+                }
+                else {
+                    var attrs = {
+                        y: y + 3,
+                        x: (x - this.options.padding.left + 3),
+                        width: (this.options.padding.left - this.options.axisTickSize) + "px",
+                        height: (this.options.axisLabelFontSize + 3) + "px",
+                        fontFamily: "Arial",
+                        fontSize: this.options.axisLabelFontSize + "px",
+                        fill: this.options.axisLabelColor.toRGBString()
+                    };
+                    
+                    /* we can do clipping just like DIVs
+                    http://www.xml.com/pub/a/2004/06/02/svgtype.html */
+                    /*
+                    var mask = this.createSVGElement("mask", {id: "mask" + tick[0]});
+                    var maskShape = this.createSVGElement("rect",
+                        {y: y + 3,
+                         x: (x - this.options.padding.left + 3),
+                         width: (this.options.padding.left - this.options.axisTickSize) + "px",
+                         height: (this.options.axisLabelFontSize + 3) + "px",
+                         style: {"fill": "#ffffff", "stroke": "#000000"}});
+                    mask.appendChild(maskShape);
+                    this.defs.appendChild(mask);
+                    
+                    attrs["filter"] = "url(#mask" + tick[0] + ")";
+                    */
+                    
+                    var label = this.createSVGElement("text", attrs);
+                    label.appendChild(this.document.createTextNode(tick[1]));
+                    this.root.appendChild(label);
+                }
+            };
+            
+            MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
+        }
+
+        this._drawLine(this.area.x, this.area.y, this.area.x, this.area.y + this.area.h, lineAttrs);
+    }
+
+    if (this.options.drawXAxis) {
+        if (this.layout.xticks) {
+            var drawTick = function(tick) {
+                var x = this.area.x + tick[0] * this.area.w;
+                var y = this.area.y + this.area.h;
+                this._drawLine(x, y, x, y + this.options.axisTickSize, lineAttrs);
+
+                if (this.options.axisLabelUseDiv) {
+                    var label = DIV(labelStyle, tick[1]);
+                    label.style.top = (y + this.options.axisTickSize) + "px";
+                    label.style.left = (x - this.options.axisLabelWidth/2) + "px";
+                    label.style.textAlign = "center";
+                    label.style.width = this.options.axisLabelWidth + "px";
+                    MochiKit.DOM.appendChildNodes(this.container, label);
+                    this.xlabels.push(label);
+                }
+                else {
+                    var attrs = {
+                        y: (y + this.options.axisTickSize + this.options.axisLabelFontSize),
+                        x: x - 3,
+                        width: this.options.axisLabelWidth + "px",
+                        height: (this.options.axisLabelFontSize + 3) + "px",
+                        fontFamily: "Arial",
+                        fontSize: this.options.axisLabelFontSize + "px",
+                        fill: this.options.axisLabelColor.toRGBString(),
+                        textAnchor: "middle"
+                    };
+                    var label = this.createSVGElement("text", attrs);
+                    label.appendChild(this.document.createTextNode(tick[1]));
+                    this.root.appendChild(label);
+                }
+            };
+            
+            MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
+        }
+
+        this._drawLine(this.area.x, this.area.y + this.area.h, this.area.x + this.area.w, this.area.y + this.area.h, lineAttrs)
+    }
+};
+
+PlotKit.SVGRenderer.prototype._renderPieAxis = function() {
+
+    if (this.layout.xticks) {
+        // make a lookup dict for x->slice values
+        var lookup = new Array();
+        for (var i = 0; i < this.layout.slices.length; i++) {
+            lookup[this.layout.slices[i].xval] = this.layout.slices[i];
+        }
+        
+        var centerx = this.area.x + this.area.w * 0.5;
+        var centery = this.area.y + this.area.h * 0.5;
+        var radius = Math.min(this.area.w * this.options.pieRadius + 10, 
+                              this.area.h * this.options.pieRadius + 10);
+        var labelWidth = this.options.axisLabelWidth;
+        
+        for (var i = 0; i < this.layout.xticks.length; i++) {
+            var slice = lookup[this.layout.xticks[i][0]];
+            if (MochiKit.Base.isUndefinedOrNull(slice))
+                continue;
+                
+                
+            var angle = (slice.startAngle + slice.endAngle)/2;
+            // normalize the angle
+            var normalisedAngle = angle;
+            if (normalisedAngle > Math.PI * 2)
+                normalisedAngle = normalisedAngle - Math.PI * 2;
+            else if (normalisedAngle < 0)
+                normalisedAngle = normalisedAngle + Math.PI * 2;
+                
+            var labelx = centerx + Math.sin(normalisedAngle) * (radius + 10);
+            var labely = centery - Math.cos(normalisedAngle) * (radius + 10);
+
+            var attrib = {
+                "position": "absolute",
+                 "zIndex": 11,
+                "width": labelWidth + "px",
+                "fontSize": this.options.axisLabelFontSize + "px",
+                "overflow": "hidden",
+                "color": this.options.axisLabelColor.toHexString()
+            };
+
+            var svgattrib = {
+                "width": labelWidth + "px",
+                "fontSize": this.options.axisLabelFontSize + "px",
+                "height": (this.options.axisLabelFontSize + 3) + "px",
+                "fill": this.options.axisLabelColor.toRGBString()
+            };
+
+            if (normalisedAngle <= Math.PI * 0.5) {
+                // text on top and align left
+                MochiKit.Base.update(attrib, {
+                    'textAlign': 'left', 'verticalAlign': 'top',
+                    'left': labelx + 'px',
+                    'top':  (labely - this.options.axisLabelFontSize) + "px"
+                });
+                MochiKit.Base.update(svgattrib, {
+                    "x": labelx,
+                    "y" :(labely - this.options.axisLabelFontSize),
+                    "textAnchor": "left"
+                        });
+            }
+            else if ((normalisedAngle > Math.PI * 0.5) && (normalisedAngle <= Math.PI)) {
+                // text on bottom and align left
+                MochiKit.Base.update(attrib, {
+                    'textAlign': 'left', 'verticalAlign': 'bottom',
+                    'left': labelx + 'px',
+                    'top':  labely + "px"
+                });
+                MochiKit.Base.update(svgattrib, {
+                    'textAnchor': 'left',
+                    'x': labelx,
+                    'y':  labely
+                });
+            }
+            else if ((normalisedAngle > Math.PI) && (normalisedAngle <= Math.PI*1.5)) {
+                // text on bottom and align right
+                MochiKit.Base.update(attrib, {
+                    'textAlign': 'right', 'verticalAlign': 'bottom',
+                    'left': labelx + 'px',
+                    'top':  labely + "px"
+                });
+                MochiKit.Base.update(svgattrib, {
+                    'textAnchor': 'right',
+                    'x': labelx - labelWidth,
+                    'y':  labely
+                });
+            }
+            else {
+                // text on top and align right
+                MochiKit.Base.update(attrib, {
+                    'textAlign': 'left', 'verticalAlign': 'bottom',
+                    'left': labelx + 'px',
+                    'top':  labely + "px"
+                });
+                MochiKit.Base.update(svgattrib, {
+                    'textAnchor': 'left',
+                    'x': labelx - labelWidth,
+                    'y':  labely - this.options.axisLabelFontSize
+                });
+            }
+
+            if (this.options.axisLabelUseDiv) {
+                var label = DIV({'style': attrib}, this.layout.xticks[i][1]);
+                this.xlabels.push(label);
+                MochiKit.DOM.appendChildNodes(this.container, label);
+            }
+            else {
+                var label = this.createSVGElement("text", svgattrib);
+                label.appendChild(this.document.createTextNode(this.layout.xticks[i][1]))
+                this.root.appendChild(label);
+            }
+      }
+        
+    }
+};
+
+PlotKit.SVGRenderer.prototype._renderBackground = function() {
+    var opts = {"stroke": "none",
+                  "fill": this.options.backgroundColor.toRGBString()
+    };
+    this._drawRect(0, 0, this.width, this.height, opts);
+};
+
+PlotKit.SVGRenderer.prototype._drawRect = function(x, y, w, h, moreattrs) {
+    var attrs = {x: x + "px", y: y + "px", width: w + "px", height: h + "px"};
+    if (moreattrs)
+        MochiKit.Base.update(attrs, moreattrs);
+
+    var elem = this.createSVGElement("rect", attrs);
+    this.root.appendChild(elem);
+};
+
+PlotKit.SVGRenderer.prototype._drawLine = function(x1, y1, x2, y2, moreattrs) {
+    var attrs = {x1: x1 + "px", y1: y1 + "px", x2: x2 + "px", y2: y2 + "px"};
+    if (moreattrs)
+        MochiKit.Base.update(attrs, moreattrs);
+
+    var elem = this.createSVGElement("line", attrs);
+    this.root.appendChild(elem);
+}
+
+PlotKit.SVGRenderer.prototype.clear = function() {
+    while(this.element.firstChild) {
+        this.element.removeChild(this.element.firstChild);
+    }
+    
+    if (this.options.axisLabelUseDiv) {
+        for (var i = 0; i < this.xlabels.length; i++) {
+            MochiKit.DOM.removeElement(this.xlabels[i]);
+        }        
+        for (var i = 0; i < this.ylabels.length; i++) {
+            MochiKit.DOM.removeElement(this.ylabels[i]);
+        }            
+    }
+    this.xlabels = new Array();
+    this.ylabels = new Array();
+};
+
+
+PlotKit.SVGRenderer.prototype.createSVGElement = function(name, attrs) {
+    var isNil = MochiKit.Base.isUndefinedOrNull;
+    var elem;
+    var doc = isNil(this.document) ? document : this.document;
+
+    try {
+        elem = doc.createElementNS(PlotKit.SVGRenderer.SVGNS, name);
+    }
+    catch (e) {
+        elem = doc.createElement(name);
+        elem.setAttribute("xmlns", PlotKit.SVGRenderer.SVGNS);
+    }
+
+    if (attrs)
+        MochiKit.DOM.updateNodeAttributes(elem, attrs);
+
+    // TODO: we don't completely emulate the MochiKit.DOM.createElement
+    //       as we don't care about nodes contained. We really should though.
+
+    return elem;
+
+};
+
+
+PlotKit.SVGRenderer.SVG = function(attrs) {
+    // we have to do things differently for IE+AdobeSVG.
+    // My guess this works (via trial and error) is that we need to
+    // have an SVG object in order to use SVGDocument.createElementNS
+    // but IE doesn't allow us to that.
+
+    var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
+    var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
+    if (ie && (ie[1] >= 6) && (!opera)) {
+        var width = attrs["width"] ? attrs["width"] : "100";
+        var height = attrs["height"] ? attrs["height"] : "100";
+        var eid = attrs["id"] ? attrs["id"] : "notunique";
+        
+        var html = '<svg:svg width="' + width + '" height="' + height + '" ';
+        html += 'id="' + eid + '" version="1.1" baseProfile="full" />';
+
+        var canvas = document.createElement(html);
+
+        // create embedded SVG inside SVG.
+        var group = canvas.getSVGDocument().createElementNS(PlotKit.SVGRenderer.SVGNS, "svg");
+        group.setAttribute("width", width);
+        group.setAttribute("height", height);
+        canvas.getSVGDocument().appendChild(group);
+
+        return canvas;
+    }
+    else {
+        return PlotKit.SVGRenderer.prototype.createSVGElement("svg", attrs);
+    }
+};
+
+PlotKit.SVGRenderer.isSupported = function() {
+    var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
+    var ieVersion = navigator.appVersion.match(/MSIE (\d\.\d)/);
+    var safariVersion = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
+    var operaVersion = navigator.userAgent.match(/Opera\/(\d*\.\d*)/);
+    var mozillaVersion = navigator.userAgent.match(/rv:(\d*\.\d*).*Gecko/);
+    var svgFeature = "http://www.w3.org/TR/SVG11/feature#SVG";
+
+    if (ieVersion && (ieVersion[1] >= 6) && !isOpera) {
+        return document.implementation.hasFeature(svgFeature,"1.1");
+        /*
+        var dummysvg = document.createElement('<svg:svg width="1" height="1" baseProfile="full" version="1.1" id="dummy">');
+        try {
+            dummysvg.getSVGDocument();
+            dummysvg = null;
+            return true;
+        }
+        catch (e) {
+            return false;
+        }
+        */
+        
+    }
+    
+    /* support not really there yet. no text and paths are buggy
+    if (safariVersion && (safariVersion[1] > 419))
+        return true;
+    */
+
+    if (operaVersion && (operaVersion[1] > 8.9))
+        return true
+    
+    if (mozillaVersion && (mozillaVersion > 1.7))
+        return true;
+    
+    return false;
+};
+
+// Namespace Iniitialisation
+
+PlotKit.SVG = {}
+PlotKit.SVG.SVGRenderer = PlotKit.SVGRenderer;
+
+PlotKit.SVG.EXPORT = [
+    "SVGRenderer"
+];
+
+PlotKit.SVG.EXPORT_OK = [
+    "SVGRenderer"
+];
+
+PlotKit.SVG.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.SVG.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.SVG);
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetCanvas.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetCanvas.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,348 @@
+/*
+    PlotKit Sweet Canvas Renderer
+    =============================
+    Canvas Renderer for PlotKit which looks pretty!
+
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+*/
+
+// -------------------------------------------------------------------------
+// Check required components
+// -------------------------------------------------------------------------
+
+try {    
+    if (typeof(PlotKit.CanvasRenderer) == 'undefined')
+    {
+        throw "";    
+    }
+} 
+catch (e) {    
+    throw "SweetCanvas depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Layout, Canvas}"
+}
+
+
+if (typeof(PlotKit.SweetCanvasRenderer) == 'undefined') {
+    PlotKit.SweetCanvasRenderer = {};
+}
+
+PlotKit.SweetCanvasRenderer = function(element, layout, options) {
+    if (arguments.length > 0) {
+        this.__init__(element, layout, options);
+    }
+};
+
+PlotKit.SweetCanvasRenderer.NAME = "PlotKit.SweetCanvasRenderer";
+PlotKit.SweetCanvasRenderer.VERSION = PlotKit.VERSION;
+
+PlotKit.SweetCanvasRenderer.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.SweetCanvasRenderer.toString = function() {
+    return this.__repr__();
+};
+
+// ---------------------------------------------------------------------
+// Subclassing Magic
+// ---------------------------------------------------------------------
+
+PlotKit.SweetCanvasRenderer.prototype = new PlotKit.CanvasRenderer();
+PlotKit.SweetCanvasRenderer.prototype.constructor = PlotKit.SweetCanvasRenderer;
+PlotKit.SweetCanvasRenderer.__super__ = PlotKit.CanvasRenderer.prototype;
+
+// ---------------------------------------------------------------------
+// Constructor
+// ---------------------------------------------------------------------
+
+PlotKit.SweetCanvasRenderer.prototype.__init__ = function(el, layout, opts) { 
+    var moreOpts = PlotKit.Base.officeBlue();
+    MochiKit.Base.update(moreOpts, opts);
+    PlotKit.SweetCanvasRenderer.__super__.__init__.call(this, el, layout, moreOpts);
+};
+
+// ---------------------------------------------------------------------
+// Extended Plotting Functions
+// ---------------------------------------------------------------------
+
+PlotKit.SweetCanvasRenderer.prototype._renderBarChart = function() {
+    var bind = MochiKit.Base.bind;
+    var shadowColor = Color.blackColor().colorWithAlpha(0.1).toRGBString();
+
+    var prepareFakeShadow = function(context, x, y, w, h) {
+        context.fillStyle = shadowColor;
+        context.fillRect(x-2, y-2, w+4, h+2); 
+        context.fillStyle = shadowColor;
+        context.fillRect(x-1, y-1, w+2, h+1); 
+    };
+
+    var colorCount = this.options.colorScheme.length;
+    var colorScheme =  this.options.colorScheme;
+    var setNames = PlotKit.Base.keys(this.layout.datasets);
+    var setCount = setNames.length;
+
+    var chooseColor = function(name) {
+        for (var i = 0; i < setCount; i++) {
+            if (name == setNames[i])
+                return colorScheme[i%colorCount];
+        }
+        return colorScheme[0];
+    };
+
+    var drawRect = function(context, bar) {
+        var x = this.area.w * bar.x + this.area.x;
+        var y = this.area.h * bar.y + this.area.y;
+        var w = this.area.w * bar.w;
+        var h = this.area.h * bar.h;
+
+        if ((w < 1) || (h < 1))
+            return;        
+
+        context.save();
+
+        context.shadowBlur = 5.0;
+        context.shadowColor = Color.fromHexString("#888888").toRGBString();
+
+        if (this.isIE) {
+            context.save();
+            context.fillStyle = "#cccccc";
+            context.fillRect(x-2, y-2, w+4, h+2); 
+            context.restore();
+        }
+        else {
+            prepareFakeShadow(context, x, y, w, h);
+        }
+
+        if (this.options.shouldFill) {
+            context.fillStyle = chooseColor(bar.name).toRGBString();
+            context.fillRect(x, y, w, h);
+        }
+
+        context.shadowBlur = 0;
+        context.strokeStyle = Color.whiteColor().toRGBString();
+        context.lineWidth = 2.0;
+        
+        if (this.options.shouldStroke) {
+            context.strokeRect(x, y, w, h);                
+        }
+
+        context.restore();
+
+    };
+    this._renderBarChartWrap(this.layout.bars, bind(drawRect, this));
+};
+
+PlotKit.SweetCanvasRenderer.prototype._renderLineChart = function() {
+    var context = this.element.getContext("2d");
+    var colorCount = this.options.colorScheme.length;
+    var colorScheme = this.options.colorScheme;
+    var setNames = PlotKit.Base.keys(this.layout.datasets);
+    var setCount = setNames.length;
+    var bind = MochiKit.Base.bind;
+
+
+    for (var i = 0; i < setCount; i++) {
+        var setName = setNames[i];
+        var color = colorScheme[i%colorCount];
+        var strokeX = this.options.strokeColorTransform;
+
+        // setup graphics context
+        context.save();
+        
+        // create paths
+        var makePath = function(ctx) {
+            ctx.beginPath();
+            ctx.moveTo(this.area.x, this.area.y + this.area.h);
+            var addPoint = function(ctx_, point) {
+            if (point.name == setName)
+                ctx_.lineTo(this.area.w * point.x + this.area.x,
+                            this.area.h * point.y + this.area.y);
+            };
+            MochiKit.Iter.forEach(this.layout.points, partial(addPoint, ctx), this);
+            ctx.lineTo(this.area.w + this.area.x,
+                           this.area.h + this.area.y);
+            ctx.lineTo(this.area.x, this.area.y + this.area.h);
+            ctx.closePath();
+        };
+
+        // faux shadow for firefox
+        if (this.options.shouldFill) {
+            context.save();
+            if (this.isIE) {
+                context.fillStyle = "#cccccc";
+            }
+            else {
+                context.fillStyle = Color.blackColor().colorWithAlpha(0.2).toRGBString();
+            }
+            context.translate(-1, -2);
+            bind(makePath, this)(context);
+            if (this.options.shouldFill) {
+                context.fill();
+            }
+            context.restore();
+        }
+
+        context.shadowBlur = 5.0;
+        context.shadowColor = Color.fromHexString("#888888").toRGBString();
+        context.fillStyle = color.toRGBString();
+        context.lineWidth = 2.0;
+        context.strokeStyle = Color.whiteColor().toRGBString();
+
+        if (this.options.shouldFill) {
+            bind(makePath, this)(context);
+            context.fill();
+        }
+        if (this.options.shouldStroke) {
+            bind(makePath, this)(context);
+            context.stroke();
+        }
+        context.restore();
+    }
+};
+
+PlotKit.SweetCanvasRenderer.prototype._renderPieChart = function() {
+    var context = this.element.getContext("2d");
+
+    var colorCount = this.options.colorScheme.length;
+    var slices = this.layout.slices;
+
+    var centerx = this.area.x + this.area.w * 0.5;
+    var centery = this.area.y + this.area.h * 0.5;
+    var radius = Math.min(this.area.w * this.options.pieRadius, 
+                          this.area.h * this.options.pieRadius);
+
+    if (this.isIE) {
+        centerx = parseInt(centerx);
+        centery = parseInt(centery);
+        radius = parseInt(radius);
+    }
+
+	// NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1
+	// so we have to subtract 90 degrees to make it start at y = 1, x = 0
+
+    if (!this.isIE) {
+        context.save();
+        var shadowColor = Color.blackColor().colorWithAlpha(0.2);
+        context.fillStyle = shadowColor.toRGBString();
+        context.shadowBlur = 5.0;
+        context.shadowColor = Color.fromHexString("#888888").toRGBString();
+        context.translate(1, 1);
+        context.beginPath();
+        context.moveTo(centerx, centery);
+        context.arc(centerx, centery, radius + 2, 0, Math.PI*2, false);
+        context.closePath();
+        context.fill();
+        context.restore();
+    }
+
+    context.save();
+    context.strokeStyle = Color.whiteColor().toRGBString();
+    context.lineWidth = 2.0;    
+    for (var i = 0; i < slices.length; i++) {
+        var color = this.options.colorScheme[i%colorCount];
+        context.fillStyle = color.toRGBString();
+
+        var makePath = function() {
+            context.beginPath();
+            context.moveTo(centerx, centery);
+            context.arc(centerx, centery, radius, 
+                        slices[i].startAngle - Math.PI/2,
+                        slices[i].endAngle - Math.PI/2,
+                        false);
+            context.lineTo(centerx, centery);
+            context.closePath();
+        };
+
+        if (Math.abs(slices[i].startAngle - slices[i].endAngle) > 0.0001) {
+            if (this.options.shouldFill) {
+                makePath();
+                context.fill();
+            }
+            if (this.options.shouldStroke) {
+                makePath();
+                context.stroke();
+            }
+        }
+    }
+    context.restore();
+};
+
+PlotKit.SweetCanvasRenderer.prototype._renderBackground = function() {
+    var context = this.element.getContext("2d");
+   
+    if (this.layout.style == "bar" || this.layout.style == "line") {
+        context.save();
+        context.fillStyle = this.options.backgroundColor.toRGBString();
+        context.fillRect(this.area.x, this.area.y, this.area.w, this.area.h);
+        context.strokeStyle = this.options.axisLineColor.toRGBString();
+        context.lineWidth = 1.0;
+        
+        var ticks = this.layout.yticks;
+        var horiz = false;
+        if (this.layout.style == "bar" && 
+            this.layout.options.barOrientation == "horizontal") {
+                ticks = this.layout.xticks;
+                horiz = true;
+        }
+        
+        for (var i = 0; i < ticks.length; i++) {
+            var x1 = 0;
+            var y1 = 0;
+            var x2 = 0;
+            var y2 = 0;
+            
+            if (horiz) {
+                x1 = ticks[i][0] * this.area.w + this.area.x;
+                y1 = this.area.y;
+                x2 = x1;
+                y2 = y1 + this.area.h;
+            }
+            else {
+                x1 = this.area.x;
+                y1 = ticks[i][0] * this.area.h + this.area.y;
+                x2 = x1 + this.area.w;
+                y2 = y1;
+            }
+            
+            context.beginPath();
+            context.moveTo(x1, y1);
+            context.lineTo(x2, y2);
+            context.closePath();
+            context.stroke();
+        }
+        context.restore();
+    }
+    else {
+        PlotKit.SweetCanvasRenderer.__super__._renderBackground.call(this);
+    }
+};
+
+// Namespace Iniitialisation
+
+PlotKit.SweetCanvas = {}
+PlotKit.SweetCanvas.SweetCanvasRenderer = PlotKit.SweetCanvasRenderer;
+
+PlotKit.SweetCanvas.EXPORT = [
+    "SweetCanvasRenderer"
+];
+
+PlotKit.SweetCanvas.EXPORT_OK = [
+    "SweetCanvasRenderer"
+];
+
+PlotKit.SweetCanvas.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.SweetCanvas.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.SweetCanvas);
+

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetSVG.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/SweetSVG.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,247 @@
+/*
+    PlotKit Sweet SVG Renderer
+    ==========================
+    SVG Renderer for PlotKit which looks pretty!
+
+    Copyright
+    ---------
+    Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
+    For use under the BSD license. <http://www.liquidx.net/plotkit>
+*/
+
+
+// -------------------------------------------------------------------------
+// Check required components
+// -------------------------------------------------------------------------
+
+try {    
+    if (typeof(PlotKit.SVGRenderer) == 'undefined')
+    {
+        throw "";    
+    }
+} 
+catch (e) {    
+    throw "SweetSVG depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Layout, SVG}"
+}
+
+
+if (typeof(PlotKit.SweetSVGRenderer) == 'undefined') {
+    PlotKit.SweetSVGRenderer = {};
+}
+
+PlotKit.SweetSVGRenderer = function(element, layout, options) {
+    if (arguments.length > 0) {
+        this.__init__(element, layout, options);
+    }
+};
+
+PlotKit.SweetSVGRenderer.NAME = "PlotKit.SweetSVGRenderer";
+PlotKit.SweetSVGRenderer.VERSION = PlotKit.VERSION;
+
+PlotKit.SweetSVGRenderer.__repr__ = function() {
+    return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.SweetSVGRenderer.toString = function() {
+    return this.__repr__();
+};
+
+// ---------------------------------------------------------------------
+// Subclassing Magic
+// ---------------------------------------------------------------------
+
+PlotKit.SweetSVGRenderer.prototype = new PlotKit.SVGRenderer();
+PlotKit.SweetSVGRenderer.prototype.constructor = PlotKit.SweetSVGRenderer;
+PlotKit.SweetSVGRenderer.__super__ = PlotKit.SVGRenderer.prototype;
+
+// ---------------------------------------------------------------------
+// Constructor
+// ---------------------------------------------------------------------
+
+PlotKit.SweetSVGRenderer.prototype.__init__ = function(element, layout, options) { 
+    var moreOpts = PlotKit.Base.officeBlue();
+    MochiKit.Base.update(moreOpts, options);
+    PlotKit.SweetSVGRenderer.__super__.__init__.call(this, element, layout, moreOpts);
+    //this._addDropShadowFilter();
+};
+
+PlotKit.SweetSVGRenderer.prototype._addDropShadowFilter = function() {
+    var filter = this.createSVGElement("filter", {x: 0, y: 0, "id":"dropShadow"});
+    var goffset = this.createSVGElement("feOffset",
+        {"in": "SourceGraphic", "dx": 0, "dy": 0, "result": "topCopy"});
+    var blur = this.createSVGElement("feGaussianBlur",
+        {"in": "SourceAlpha", "StdDeviation": 2, "result": "shadow"});
+    var soffset = this.createSVGElement("feOffset",
+        {"in": "shadow", "dx": -1, "dy": -2, "result":"movedShadow"});
+    var merge = this.createSVGElement("feMerge");
+    var gmerge = this.createSVGElement("feMergeNode", {"in":"topCopy"});
+    var smerge = this.createSVGElement("feMergeNode", {"in":"movedShadow"});
+    
+    merge.appendChild(gmerge);
+    merge.appendChild(smerge);
+    filter.appendChild(goffset);
+    filter.appendChild(blur);
+    filter.appendChild(soffset);
+    filter.appendChild(merge);
+    this.defs.appendChild(filter);
+};
+
+// ---------------------------------------------------------------------
+// Extended Plotting Functions
+// ---------------------------------------------------------------------
+
+PlotKit.SweetSVGRenderer.prototype._renderBarChart = function() {
+    var bind = MochiKit.Base.bind;
+    var shadowColor = Color.blackColor().toRGBString();
+    var shadowStyle = "fill:" + shadowColor + ";fill-opacity:0.15";
+    var strokeStyle = "stroke-width: 2.0; stroke:" + Color.whiteColor().toRGBString();
+    
+    var drawRect = function(attrs, bar) {
+        var x = this.area.w * bar.x + this.area.x;
+        var y = this.area.h * bar.y + this.area.y;
+        var w = this.area.w * bar.w;
+        var h = this.area.h * bar.h;
+
+        if ((w < 1) || (h < 1))
+            return;        
+
+        //attrs["filter"] = "url(#dropShadow)";
+        attrs["style"] = strokeStyle;
+        this._drawRect(x - 2, y - 1, w+4, h+2, {"style":shadowStyle});
+        this._drawRect(x, y, w, h, attrs);
+    };
+    this._renderBarOrLine(this.layout.bars, bind(drawRect, this));
+
+};
+
+PlotKit.SweetSVGRenderer.prototype._renderLineChart = function() {
+    var bind = MochiKit.Base.bind;
+    var shadowColor = Color.blackColor().toRGBString();
+    var shadowStyle = "fill:" + shadowColor + ";fill-opacity:0.15";
+    var strokeStyle = "stroke-width: 2.0; stroke:" + Color.whiteColor().toRGBString();
+
+    var addPoint = function(attrs, point) {
+        this._tempPointsBuffer += (this.area.w * point.x + this.area.x) + "," +
+                                 (this.area.h * point.y + this.area.y) + " ";
+    };
+
+    var startLine = function(attrs) {
+        this._tempPointsBuffer = "";
+        this._tempPointsBuffer += (this.area.x) + "," + (this.area.y+this.area.h) + " ";
+    };
+
+    var endLine = function(attrs) {
+        this._tempPointsBuffer += (this.area.w + this.area.x) + ","  +(this.area.h + this.area.y);
+        attrs["points"] = this._tempPointsBuffer;    
+            
+        attrs["stroke"] = "none";
+        attrs["transform"] = "translate(-2, -1)";
+        attrs["style"] = shadowStyle;
+        var shadow = this.createSVGElement("polygon", attrs);
+        this.root.appendChild(shadow);
+        
+        attrs["transform"] = "";
+        attrs["style"] = strokeStyle;
+        var elem = this.createSVGElement("polygon", attrs);
+        this.root.appendChild(elem);
+        
+       
+    };
+
+    this._renderBarOrLine(this.layout.points, 
+                             bind(addPoint, this), 
+                             bind(startLine, this), 
+                             bind(endLine, this));
+};
+
+PlotKit.SweetSVGRenderer.prototype._renderPieChart = function() {
+    var centerx = this.area.x + this.area.w * 0.5;
+    var centery = this.area.y + this.area.h * 0.5;
+    var shadowColor = Color.blackColor().toRGBString();
+    var radius = Math.min(this.area.w * this.options.pieRadius, 
+                          this.area.h * this.options.pieRadius);
+    var shadowStyle = "fill:" + shadowColor + ";fill-opacity:0.15";
+    
+    var shadow = this.createSVGElement("circle", 
+        {"style": shadowStyle, "cx": centerx + 1, "cy": centery + 1, "r": radius + 1});
+    this.root.appendChild(shadow);
+                             
+    PlotKit.SweetSVGRenderer.__super__._renderPieChart.call(this);
+};
+    
+
+PlotKit.SweetSVGRenderer.prototype._renderBackground = function() {
+    var attrs = {
+        "fill": this.options.backgroundColor.toRGBString(),
+        "stroke": "none"
+    };
+    
+
+    if (this.layout.style == "bar" || this.layout.style == "line") {
+        this._drawRect(this.area.x, this.area.y, 
+                       this.area.w, this.area.h, attrs);
+                       
+        var ticks = this.layout.yticks;
+        var horiz = false;
+        if (this.layout.style == "bar" && 
+            this.layout.options.barOrientation == "horizontal") {
+                ticks = this.layout.xticks;
+                horiz = true;
+        }
+        
+        for (var i = 0; i < ticks.length; i++) {
+            var x = 0;
+            var y = 0;
+            var w = 0;
+            var h = 0;
+            
+            if (horiz) {
+                x = ticks[i][0] * this.area.w + this.area.x;
+                y = this.area.y;
+                w = 1;
+                h = this.area.w;
+            }
+            else {
+                x = this.area.x;
+                y = ticks[i][0] * this.area.h + this.area.y;
+                w = this.area.w;
+                h = 1;
+            }
+            
+            this._drawRect(x, y, w, h,
+                           {"fill": this.options.axisLineColor.toRGBString()});
+        }
+    }
+    else {
+        PlotKit.SweetSVGRenderer.__super__._renderBackground.call(this);
+        
+    }
+    
+};
+
+// Namespace Iniitialisation
+
+PlotKit.SweetSVG = {}
+PlotKit.SweetSVG.SweetSVGRenderer = PlotKit.SweetSVGRenderer;
+
+PlotKit.SweetSVG.EXPORT = [
+    "SweetSVGRenderer"
+];
+
+PlotKit.SweetSVG.EXPORT_OK = [
+    "SweetSVGRenderer"
+];
+
+PlotKit.SweetSVG.__new__ = function() {
+    var m = MochiKit.Base;
+    
+    m.nameFunctions(this);
+    
+    this.EXPORT_TAGS = {
+        ":common": this.EXPORT,
+        ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+    };
+};
+
+PlotKit.SweetSVG.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.SweetSVG);

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/dummy.svg
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/dummy.svg	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink">
+</svg>

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/excanvas.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/PlotKit/excanvas.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,723 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: Patterns
+// TODO: Radial gradient
+// TODO: Clipping paths
+// TODO: Coordsize (still need to support stretching)
+// TODO: Painting mode
+// TODO: Optimize
+// TODO: canvas width/height sets content size in moz, border size in ie
+
+// only add this code if we do not already have a canvas implementation
+if (!window.CanvasRenderingContext2D) {
+
+(function () {
+
+  // alias some functions to make (compiled) code shorter
+  var m = Math;
+  var mr = m.round;
+  var ms = m.sin;
+  var mc = m.cos;
+
+  var G_vmlCanvasManager_ = {
+    init: function (opt_doc) {
+      var doc = opt_doc || document;
+      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+        var self = this;
+        doc.attachEvent("onreadystatechange", function () {
+          self.init_(doc);
+        });
+      }
+    },
+
+    init_: function (doc, e) {
+      if (doc.readyState == "complete") {
+        // create xmlns
+        if (!doc.namespaces["g_vml_"]) {
+          doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
+        }
+
+        // setup default css
+        var ss = doc.createStyleSheet();
+        ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
+            "text-align:left;}" +
+            "g_vml_\\:*{behavior:url(#default#VML)}";
+
+        // find all canvas elements
+        var els = doc.getElementsByTagName("canvas");
+        for (var i = 0; i < els.length; i++) {
+          if (!els[i].getContext) {
+            this.initElement(els[i]);
+          }
+        }
+      }
+    },
+
+    fixElement_: function (el) {
+      // in IE before version 5.5 we would need to add HTML: to the tag name
+      // but we do not care about IE before version 6
+      var outerHTML = el.outerHTML;
+      var newEl = document.createElement(outerHTML);
+      // if the tag is still open IE has created the children as siblings and
+      // it has also created a tag with the name "/FOO"
+      if (outerHTML.slice(-2) != "/>") {
+        var tagName = "/" + el.tagName;
+        var ns;
+        // remove content
+        while ((ns = el.nextSibling) && ns.tagName != tagName) {
+          ns.removeNode();
+        }
+        // remove the incorrect closing tag
+        if (ns) {
+          ns.removeNode();
+        }
+      }
+      el.parentNode.replaceChild(newEl, el);
+      return newEl;
+    },
+
+    /**
+     * Public initializes a canvas element so that it can be used as canvas
+     * element from now on. This is called automatically before the page is
+     * loaded but if you are creating elements using createElement you need to
+     * make sure this is called on the element.
+     * @param {HTMLElement} el The canvas element to initialize.
+     * @return {HTMLElement} the element that was created.
+     */
+    initElement: function (el) {
+      el = this.fixElement_(el);
+      el.getContext = function () {
+        if (this.context_) {
+          return this.context_;
+        }
+        return this.context_ = new CanvasRenderingContext2D_(this);
+      };
+
+      // do not use inline function because that will leak memory
+      // el.attachEvent('onpropertychange', onPropertyChange)
+      el.attachEvent('onresize', onResize);
+
+      var attrs = el.attributes;
+      if (attrs.width && attrs.width.specified) {
+        // TODO: use runtimeStyle and coordsize
+        // el.getContext().setWidth_(attrs.width.nodeValue);
+        el.style.width = attrs.width.nodeValue + "px";
+      }
+      if (attrs.height && attrs.height.specified) {
+        // TODO: use runtimeStyle and coordsize
+        // el.getContext().setHeight_(attrs.height.nodeValue);
+        el.style.height = attrs.height.nodeValue + "px";
+      }
+      //el.getContext().setCoordsize_()
+      return el;
+    }
+  };
+
+  function onPropertyChange(e) {
+    // we need to watch changes to width and height
+    switch (e.propertyName) {
+      case 'width':
+      case 'height':
+        // TODO: coordsize and size
+        break;
+    }
+  }
+
+  function onResize(e) {
+    var el = e.srcElement;
+    if (el.firstChild) {
+      el.firstChild.style.width =  el.clientWidth + 'px';
+      el.firstChild.style.height = el.clientHeight + 'px';
+    }
+  }
+
+  G_vmlCanvasManager_.init();
+
+  // precompute "00" to "FF"
+  var dec2hex = [];
+  for (var i = 0; i < 16; i++) {
+    for (var j = 0; j < 16; j++) {
+      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
+    }
+  }
+
+  function createMatrixIdentity() {
+    return [
+      [1, 0, 0],
+      [0, 1, 0],
+      [0, 0, 1]
+    ];
+  }
+
+  function matrixMultiply(m1, m2) {
+    var result = createMatrixIdentity();
+
+    for (var x = 0; x < 3; x++) {
+      for (var y = 0; y < 3; y++) {
+        var sum = 0;
+
+        for (var z = 0; z < 3; z++) {
+          sum += m1[x][z] * m2[z][y];
+        }
+
+        result[x][y] = sum;
+      }
+    }
+    return result;
+  }
+
+  function copyState(o1, o2) {
+    o2.fillStyle     = o1.fillStyle;
+    o2.lineCap       = o1.lineCap;
+    o2.lineJoin      = o1.lineJoin;
+    o2.lineWidth     = o1.lineWidth;
+    o2.miterLimit    = o1.miterLimit;
+    o2.shadowBlur    = o1.shadowBlur;
+    o2.shadowColor   = o1.shadowColor;
+    o2.shadowOffsetX = o1.shadowOffsetX;
+    o2.shadowOffsetY = o1.shadowOffsetY;
+    o2.strokeStyle   = o1.strokeStyle;
+  }
+
+  function processStyle(styleString) {
+    var str, alpha = 1;
+
+    styleString = String(styleString);
+    if (styleString.substring(0, 3) == "rgb") {
+      var start = styleString.indexOf("(", 3);
+      var end = styleString.indexOf(")", start + 1);
+      var guts = styleString.substring(start + 1, end).split(",");
+
+      str = "#";
+      for (var i = 0; i < 3; i++) {
+        str += dec2hex[parseInt(guts[i])];
+      }
+
+      if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
+        alpha = guts[3];
+      }
+    } else {
+      str = styleString;
+    }
+
+    return [str, alpha];
+  }
+
+  function processLineCap(lineCap) {
+    switch (lineCap) {
+      case "butt":
+        return "flat";
+      case "round":
+        return "round";
+      case "square":
+      default:
+        return "square";
+    }
+  }
+
+  /**
+   * This class implements CanvasRenderingContext2D interface as described by
+   * the WHATWG.
+   * @param {HTMLElement} surfaceElement The element that the 2D context should
+   * be associated with
+   */
+   function CanvasRenderingContext2D_(surfaceElement) {
+    this.m_ = createMatrixIdentity();
+
+    this.mStack_ = [];
+    this.aStack_ = [];
+    this.currentPath_ = [];
+
+    // Canvas context properties
+    this.strokeStyle = "#000";
+    this.fillStyle = "#ccc";
+
+    this.lineWidth = 1;
+    this.lineJoin = "miter";
+    this.lineCap = "butt";
+    this.miterLimit = 10;
+    this.globalAlpha = 1;
+
+    var el = document.createElement('div');
+    el.style.width =  surfaceElement.clientWidth + 'px';
+    el.style.height = surfaceElement.clientHeight + 'px';
+    el.style.overflow = 'hidden';
+    el.style.position = 'absolute';
+    surfaceElement.appendChild(el);
+
+    this.element_ = el;
+    this.arcScaleX_ = 1;
+    this.arcScaleY_ = 1;
+  };
+
+  var contextPrototype = CanvasRenderingContext2D_.prototype;
+  contextPrototype.clearRect = function() {
+    this.element_.innerHTML = "";
+    this.currentPath_ = [];
+  };
+
+  contextPrototype.beginPath = function() {
+    // TODO: Branch current matrix so that save/restore has no effect
+    //       as per safari docs.
+
+    this.currentPath_ = [];
+  };
+
+  contextPrototype.moveTo = function(aX, aY) {
+    this.currentPath_.push({type: "moveTo", x: aX, y: aY});
+  };
+
+  contextPrototype.lineTo = function(aX, aY) {
+    this.currentPath_.push({type: "lineTo", x: aX, y: aY});
+  };
+
+  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+                                            aCP2x, aCP2y,
+                                            aX, aY) {
+    this.currentPath_.push({type: "bezierCurveTo",
+                           cp1x: aCP1x,
+                           cp1y: aCP1y,
+                           cp2x: aCP2x,
+                           cp2y: aCP2y,
+                           x: aX,
+                           y: aY});
+  };
+
+  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+    // VML's qb produces different output to Firefox's
+    // FF's behaviour seems to have changed in 1.5.0.1, check this
+    this.bezierCurveTo(aCPx, aCPy, aCPx, aCPy, aX, aY);
+  };
+
+  contextPrototype.arc = function(aX, aY, aRadius,
+                                  aStartAngle, aEndAngle, aClockwise) {
+    aRadius *= 10;
+    var arcType = aClockwise ? "at" : "wa";
+
+    var xStart = aX + (mc(aStartAngle) * aRadius) - 5;
+    var yStart = aY + (ms(aStartAngle) * aRadius) - 5;
+
+    var xEnd = aX + (mc(aEndAngle) * aRadius) - 5;
+    var yEnd = aY + (ms(aEndAngle) * aRadius) - 5;
+
+    this.currentPath_.push({type: arcType,
+                           x: aX,
+                           y: aY,
+                           radius: aRadius,
+                           xStart: xStart,
+                           yStart: yStart,
+                           xEnd: xEnd,
+                           yEnd: yEnd});
+
+  };
+
+  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+  };
+
+  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+    // Will destroy any existing path (same as FF behaviour)
+    this.beginPath();
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+    this.stroke();
+  };
+
+  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+    // Will destroy any existing path (same as FF behaviour)
+    this.beginPath();
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+    this.fill();
+  };
+
+  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+    var gradient = new CanvasGradient_("gradient");
+    return gradient;
+  };
+
+  contextPrototype.createRadialGradient = function(aX0, aY0,
+                                                   aR0, aX1,
+                                                   aY1, aR1) {
+    var gradient = new CanvasGradient_("gradientradial");
+    gradient.radius1_ = aR0;
+    gradient.radius2_ = aR1;
+    gradient.focus_.x = aX0;
+    gradient.focus_.y = aY0;
+    return gradient;
+  };
+
+  contextPrototype.drawImage = function (image, var_args) {
+    var dx, dy, dw, dh, sx, sy, sw, sh;
+    var w = image.width;
+    var h = image.height;
+
+    if (arguments.length == 3) {
+      dx = arguments[1];
+      dy = arguments[2];
+      sx = sy = 0;
+      sw = dw = w;
+      sh = dh = h;
+    } else if (arguments.length == 5) {
+      dx = arguments[1];
+      dy = arguments[2];
+      dw = arguments[3];
+      dh = arguments[4];
+      sx = sy = 0;
+      sw = w;
+      sh = h;
+    } else if (arguments.length == 9) {
+      sx = arguments[1];
+      sy = arguments[2];
+      sw = arguments[3];
+      sh = arguments[4];
+      dx = arguments[5];
+      dy = arguments[6];
+      dw = arguments[7];
+      dh = arguments[8];
+    } else {
+      throw "Invalid number of arguments";
+    }
+
+    var d = this.getCoords_(dx, dy);
+
+    var w2 = (sw / 2);
+    var h2 = (sh / 2);
+
+    var vmlStr = [];
+
+    // For some reason that I've now forgotten, using divs didn't work
+    vmlStr.push(' <g_vml_:group',
+                ' coordsize="1000,1000"',
+                ' coordorigin="0, 0"' ,
+                ' style="width:100px;height:100px;position:absolute;');
+
+    // If filters are necessary (rotation exists), create them
+    // filters are bog-slow, so only create them if abbsolutely necessary
+    // The following check doesn't account for skews (which don't exist
+    // in the canvas spec (yet) anyway.
+
+    if (this.m_[0][0] != 1 || this.m_[0][1]) {
+      var filter = [];
+
+      // Note the 12/21 reversal
+      filter.push("M11='", this.m_[0][0], "',",
+                  "M12='", this.m_[1][0], "',",
+                  "M21='", this.m_[0][1], "',",
+                  "M22='", this.m_[1][1], "',",
+                  "Dx='", d.x, "',",
+                  "Dy='", d.y, "'");
+
+      // Bounding box calculation (need to minimize displayed area so that
+      // filters don't waste time on unused pixels.
+      var max = d;
+      var c2 = this.getCoords_(dx+dw, dy);
+      var c3 = this.getCoords_(dx, dy+dh);
+      var c4 = this.getCoords_(dx+dw, dy+dh);
+
+      max.x = Math.max(max.x, c2.x, c3.x, c4.x);
+      max.y = Math.max(max.y, c2.y, c3.y, c4.y);
+
+      vmlStr.push(" padding:0 ", mr(max.x), "px ", mr(max.y),
+                  "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
+                  filter.join(""), ", sizingmethod='clip');")
+    } else {
+      vmlStr.push(" top:", d.y, "px;left:", d.x, "px;")
+    }
+
+    vmlStr.push(' ">' ,
+                '<g_vml_:image src="', image.src, '"',
+                ' style="width:', dw, ';',
+                ' height:', dh, ';"',
+                ' cropleft="', sx / w, '"',
+                ' croptop="', sy / h, '"',
+                ' cropright="', (w - sx - sw) / w, '"',
+                ' cropbottom="', (h - sy - sh) / h, '"',
+                ' />',
+                '</g_vml_:group>');
+
+    this.element_.insertAdjacentHTML("BeforeEnd",
+                                    vmlStr.join(""));
+  };
+
+  contextPrototype.stroke = function(aFill) {
+    var lineStr = [];
+    var lineOpen = false;
+    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
+    var color = a[0];
+    var opacity = a[1] * this.globalAlpha;
+
+    lineStr.push('<g_vml_:shape',
+                 ' fillcolor="', color, '"',
+                 ' filled="', Boolean(aFill), '"',
+                 ' style="position:absolute;width:10;height:10;"',
+                 ' coordorigin="0 0" coordsize="100 100"',
+                 ' stroked="', !aFill, '"',
+                 ' strokeweight="', this.lineWidth, '"',
+                 ' strokecolor="', color, '"',
+                 ' path="');
+
+    var newSeq = false;
+    var min = {x: null, y: null};
+    var max = {x: null, y: null};
+
+    for (var i = 0; i < this.currentPath_.length; i++) {
+      var p = this.currentPath_[i];
+
+      if (p.type == "moveTo") {
+        lineStr.push(" m ");
+        var c = this.getCoords_(p.x, p.y);
+        lineStr.push(mr(c.x), ",", mr(c.y));
+      } else if (p.type == "lineTo") {
+        lineStr.push(" l ");
+        var c = this.getCoords_(p.x, p.y);
+        lineStr.push(mr(c.x), ",", mr(c.y));
+      } else if (p.type == "close") {
+        lineStr.push(" x ");
+      } else if (p.type == "bezierCurveTo") {
+        lineStr.push(" c ");
+        var c = this.getCoords_(p.x, p.y);
+        var c1 = this.getCoords_(p.cp1x, p.cp1y);
+        var c2 = this.getCoords_(p.cp2x, p.cp2y);
+        lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
+                     mr(c2.x), ",", mr(c2.y), ",",
+                     mr(c.x), ",", mr(c.y));
+      } else if (p.type == "at" || p.type == "wa") {
+        lineStr.push(" ", p.type, " ");
+        var c  = this.getCoords_(p.x, p.y);
+        var cStart = this.getCoords_(p.xStart, p.yStart);
+        var cEnd = this.getCoords_(p.xEnd, p.yEnd);
+
+        lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
+                     mr(c.y - this.arcScaleY_ * p.radius), " ",
+                     mr(c.x + this.arcScaleX_ * p.radius), ",",
+                     mr(c.y + this.arcScaleY_ * p.radius), " ",
+                     mr(cStart.x), ",", mr(cStart.y), " ",
+                     mr(cEnd.x), ",", mr(cEnd.y));
+      }
+
+
+      // TODO: Following is broken for curves due to
+      //       move to proper paths.
+
+      // Figure out dimensions so we can do gradient fills
+      // properly
+      if(c) {
+        if (min.x == null || c.x < min.x) {
+          min.x = c.x;
+        }
+        if (max.x == null || c.x > max.x) {
+          max.x = c.x;
+        }
+        if (min.y == null || c.y < min.y) {
+          min.y = c.y;
+        }
+        if (max.y == null || c.y > max.y) {
+          max.y = c.y;
+        }
+      }
+    }
+    lineStr.push(' ">');
+
+    if (typeof this.fillStyle == "object") {
+      var focus = {x: "50%", y: "50%"};
+      var width = (max.x - min.x);
+      var height = (max.y - min.y);
+      var dimension = (width > height) ? width : height;
+
+      focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
+      focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
+
+      var colors = [];
+
+      // inside radius (%)
+      if (this.fillStyle.type_ == "gradientradial") {
+        var inside = (this.fillStyle.radius1_ / dimension * 100);
+
+        // percentage that outside radius exceeds inside radius
+        var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
+      } else {
+        var inside = 0;
+        var expansion = 100;
+      }
+
+      var insidecolor = {offset: null, color: null};
+      var outsidecolor = {offset: null, color: null};
+
+      // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
+      // won't interpret it correctly
+      this.fillStyle.colors_.sort(function (cs1, cs2) {
+        return cs1.offset - cs2.offset;
+      });
+
+      for (var i = 0; i < this.fillStyle.colors_.length; i++) {
+        var fs = this.fillStyle.colors_[i];
+
+        colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
+
+        if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
+          insidecolor.offset = fs.offset;
+          insidecolor.color = fs.color;
+        }
+
+        if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
+          outsidecolor.offset = fs.offset;
+          outsidecolor.color = fs.color;
+        }
+      }
+      colors.pop();
+
+      lineStr.push('<g_vml_:fill',
+                   ' color="', outsidecolor.color, '"',
+                   ' color2="', insidecolor.color, '"',
+                   ' type="', this.fillStyle.type_, '"',
+                   ' focusposition="', focus.x, ', ', focus.y, '"',
+                   ' colors="', colors.join(""), '"',
+                   ' opacity="', opacity, '" />');
+    } else if (aFill) {
+      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
+    } else {
+      lineStr.push(
+        '<g_vml_:stroke',
+        ' opacity="', opacity,'"',
+        ' joinstyle="', this.lineJoin, '"',
+        ' miterlimit="', this.miterLimit, '"',
+        ' endcap="', processLineCap(this.lineCap) ,'"',
+        ' weight="', this.lineWidth, 'px"',
+        ' color="', color,'" />'
+      );
+    }
+
+    lineStr.push("</g_vml_:shape>");
+
+    this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
+
+    this.currentPath_ = [];
+  };
+
+  contextPrototype.fill = function() {
+    this.stroke(true);
+  }
+
+  contextPrototype.closePath = function() {
+    this.currentPath_.push({type: "close"});
+  };
+
+  /**
+   * @private
+   */
+  contextPrototype.getCoords_ = function(aX, aY) {
+    return {
+      x: 10 * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - 5,
+      y: 10 * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - 5
+    }
+  };
+
+  contextPrototype.save = function() {
+    var o = {};
+    copyState(this, o);
+    this.aStack_.push(o);
+    this.mStack_.push(this.m_);
+    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+  };
+
+  contextPrototype.restore = function() {
+    copyState(this.aStack_.pop(), this);
+    this.m_ = this.mStack_.pop();
+  };
+
+  contextPrototype.translate = function(aX, aY) {
+    var m1 = [
+      [1,  0,  0],
+      [0,  1,  0],
+      [aX, aY, 1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  contextPrototype.rotate = function(aRot) {
+    var c = mc(aRot);
+    var s = ms(aRot);
+
+    var m1 = [
+      [c,  s, 0],
+      [-s, c, 0],
+      [0,  0, 1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  contextPrototype.scale = function(aX, aY) {
+    this.arcScaleX_ *= aX;
+    this.arcScaleY_ *= aY;
+    var m1 = [
+      [aX, 0,  0],
+      [0,  aY, 0],
+      [0,  0,  1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  /******** STUBS ********/
+  contextPrototype.clip = function() {
+    // TODO: Implement
+  };
+
+  contextPrototype.arcTo = function() {
+    // TODO: Implement
+  };
+
+  contextPrototype.createPattern = function() {
+    return new CanvasPattern_;
+  };
+
+  // Gradient / Pattern Stubs
+  function CanvasGradient_(aType) {
+    this.type_ = aType;
+    this.radius1_ = 0;
+    this.radius2_ = 0;
+    this.colors_ = [];
+    this.focus_ = {x: 0, y: 0};
+  }
+
+  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+    aColor = processStyle(aColor);
+    this.colors_.push({offset: 1-aOffset, color: aColor});
+  };
+
+  function CanvasPattern_() {}
+
+  // set up externs
+  G_vmlCanvasManager = G_vmlCanvasManager_;
+  CanvasRenderingContext2D = CanvasRenderingContext2D_;
+  CanvasGradient = CanvasGradient_;
+  CanvasPattern = CanvasPattern_;
+
+})();
+
+} // if

Added: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/mochikit.noexport.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/Chart/web/static/js/mochikit.noexport.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1 @@
+MochiKit = {__export__: false};

Modified: jifty/branches/virtual-models/share/web/static/js/jifty.js
==============================================================================
--- jifty/branches/virtual-models/share/web/static/js/jifty.js	(original)
+++ jifty/branches/virtual-models/share/web/static/js/jifty.js	Wed Aug  1 12:25:47 2007
@@ -1,6 +1,97 @@
 /* An empty class so we can create things inside it */
 var Jifty = Class.create();
 
+Jifty.Web = Class.create();
+Jifty.Web.current_actions = new Array;
+Jifty.Web.new_action = function() {
+    var args = _get_named_args(arguments);
+    var a;
+    Jifty.Web.current_actions.each(function(x) { if (x.moniker == args.moniker) a = x });
+    if (!a) throw "hate";
+    
+    return a;
+};
+
+Jifty.web = function() { return Jifty.Web };
+
+function _get_named_args(args) {
+    var result = {};
+    for (var i = 0; i < args.length; i+=2) {
+	result[args[i]] = args[i+1];
+    }
+    return result;
+
+}
+
+function _get_onclick(action_hash, name, args, path) {
+    var onclick = 'if(event.ctrlKey||event.metaKey||event.altKey||event.shiftKey) return true; return update('
+    + JSON.stringify({'continuation': {},
+		      'actions': action_hash,
+		      'fragments': [{'mode': 'Replace', 'args': args, 'region': name, 'path': path}]})
+    +', this)';
+    onclick = onclick.replace(/"/g, "'"); //"' )# grr emacs!
+	return onclick;
+}
+// XXX
+var hyperlink  = function() {
+    var args = _get_named_args(arguments);
+    var current_region = Jifty.Web.current_region;
+    var onclick = _get_onclick({}, current_region.name, current_region.args, args.onclick[0].replace_with);
+    outs( a(function() { attr(function()
+			      {return ['onclick', onclick, 'href', '#']});
+	    return args.label
+		}));
+}
+
+var render_param = function(a, field) { outs(a.render_param(field)) };
+var form_return  = function() {
+    var args = _get_named_args(arguments);
+    var action_hash = {};
+    action_hash[args.submit.moniker] = 1;
+    // XXX: fix the fabricated refresh-self
+    // XXX: implicit onclick only for now
+
+    // $self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} });
+    // @args{qw/mode path region/} = ('Replace', Jifty->web->current_region->path, Jifty->web->current_region);
+
+    var current_region = Jifty.Web.current_region;
+    var onclick = _get_onclick(action_hash, current_region.name, current_region.args, current_region.path);
+    outs(
+	 div(function() {
+		 attr(function() { return ['class', 'submit_button'] });
+		 return input(function() { attr(function()
+						{return ['type', 'submit',
+							 'onclick', onclick,
+							 'class', 'widget button',
+							 'id', 'S' + (++SERIAL + SERIAL_postfix),
+							 'value', args.label,
+							 'name', 'J:V-region-__page-signup_widget=_signup|J:ACTIONS=signupnow'] })});
+		     }));
+
+};
+
+function register_action(a) {
+    outs(div(function() {
+		attr(function() { return ['class', 'hidden'] });
+		return input(function() { attr(function() {
+				return ['type', 'hidden',
+					'name', a.register_name(),
+					'id', a.register_name(),
+					'value', a.actionClass] }) } ) } ));
+    /* XXX: fallback values */
+}
+
+function apply_cached_for_action(code, actions) {
+    Jifty.Web.current_actions = actions;
+    this['out_buf'] = '';
+    this['outs'] = function(text) { this.out_buf += text };
+    actions.each(register_action);
+    var foo = code();
+    return foo;
+    alert(foo);
+    throw 'not yet';
+}
+
 /* Actions */
 var Action = Class.create();
 Action.prototype = {
@@ -235,10 +326,145 @@
         var enable = function() { arguments[0].disabled = false; };
         this.fields().each( enable );
         this.buttons().each( enable );
-    }
+    },
+
+
+    /* client side logic extracted from Jifty::Action */
+    _action_spec: function() {
+	if (!this.s_a) {
+	    /* XXX: make REST client accessible */
+	    var Todo = new AsynapseRecord('todo');
+	    this.s_a = $H(Todo.eval_ajax_get('/=/action/'+this.actionClass+'.js'));
+	}
+	
+	return this.s_a
+    },
+    argument_names: function() {
+	return this._action_spec().keys();
+    },
+
+    render_param: function(field) {
+	var a_s = this._action_spec();
+	var type = 'text';
+	var f = new ActionField(field, a_s[field], this);
+	return f.render();
+    },
+    register_name: function() { return this.register.id }
+
 };
 
+var SERIAL_postfix = Math.ceil(10000*Math.random());
+var SERIAL = 0;
+ActionField = Class.create();
+ActionField.prototype = {
+ initialize: function(name, args, action) {
+	this.name = name;
+	this.label = args.label;
+	this.hints = args.hints;
+	this.mandatory = args.mandatory;
+	this.ajax_validates = args.ajax_validates;
+	this.current_value = action.data_structure().fields[name].value;
+        this.error = action.result.field_error[name];
+	this.action = action;
+	if (!this.render_mode) this.render_mode = 'update';
+	this.type = 'text';
+    },
+
+ render: function() {
+	if (this.render_mode == 'read')
+	    return this.render_wrapper
+		(this.render_preamble,
+		 this.render_label,
+		 this.render_value);
+	else
+	    return this.render_wrapper
+	    (this.render_preamble,
+	     this.render_label,
+	     this.render_widget,
+	     this.render_autocomplete_div,
+	     this.render_inline_javascript,
+	     this.render_hints,
+	     this.render_errors,
+	     this.render_warnings,
+	     this.render_canonicalization_notes);
+    },
+ render_wrapper: function () {
+	var classes = ['form_field'];
+	if (this.mandatory) classes.push('mandatory');
+	if (this.name) classes.push('argument-'+this.name);
+	var args = arguments;
+	var tthis = this;
+	return div(function() {
+		attr(function(){return ['class', classes.join(' ')]});
+		var buf = new Array;
+		for (var i = 0; i < args.length; ++i) {
+		    buf.push(typeof(args[i]) == 'function' ? args[i].apply(tthis) : args[i]);
+		}
+		return buf.join('');
+	    });
+    },
+    render_preamble: function() {
+	var tthis = this;
+	return span(function(){attr(function(){return ['class', "preamble"]});
+		return tthis.preamble });
+    },
+
+    render_label: function() {
+	var tthis = this;
+	if(this.render_mode == 'update')
+	    return label(function(){attr(function(){return['class', "label", 'for', tthis.element_id()]});
+		    return tthis.label });
+	else
+	    return span(function(){attr(function(){return['class', "label" ]});
+		    return tthis.label });
+    },
+ input_name: function() {
+	return ['J:A:F', this.name, this.action.moniker].join('-');
+    },
+ render_hints: function() {
+	var tthis = this;
+	return span(function(){attr(function(){return ['class', "hints"]});
+		return tthis.hints });
+    },
+
+ render_errors: function() {
+	if (!this.action) return '';
+	var tthis = this;
+	// XXX: post-request handler needs to extract field error messages
+	return span(function(){attr(function(){return ['class', "error", 'id', 'errors-'+tthis.input_name()]});
+		return tthis.error });
+    },
+
+ render_widget: function () {
+	var tthis = this;
+	return input(function(){
+		    attr(function(){
+			    var fields = ['type', tthis.type];
+			    if (tthis.input_name) fields.push('name', tthis.input_name());
+			    fields.push('id', tthis.element_id());
+			    if (tthis.current_value) fields.push('value', tthis.current_value);
+			    fields.push('class', tthis._widget_class().join(' '));
+			    if (tthis.max_length) fields.push('size', tthis.max_length, 'maxlength', tthis.max_length);
+			    if (tthis.disable_autocomplete) fields.push('autocomplete', "off");
+			    //" " .$self->other_widget_properties;
+			    return fields;
+			})});
+    },
+ _widget_class: function() {
+	var classes = ['form_field'];
+	if (this.mandatory)      classes.push('mandatory');
+	if (this.name)           classes.push('argument-'+this.name);
+	if (this.ajax_validates) classes.push('ajaxvalidation');
+	return classes;
+    },
+
+ element_id: function() { if(!this._element_id) this._element_id = this.input_name() + '-S' + (++SERIAL + SERIAL_postfix);
+			  return this._element_id; },
+ __noSuchMethod__: function(name) {
+	return '<!-- '+name+' not implemented yet -->';
+    }
 
+};
 
 /* Forms */
 Object.extend(Form, {
@@ -249,7 +475,7 @@
 
         for (var i = 0; i < possible.length; i++) {
             if (Form.Element.getType(possible[i]) == "registration")
-                elements.push(new Action(Form.Element.getMoniker(possible[i])));
+                elements.push(Form.Element.getAction(possible[i]));
         }
         
         return elements;
@@ -264,6 +490,7 @@
 });
 
 
+var current_actions = $H();
 
 /* Fields */
 Object.extend(Form.Element, {
@@ -287,9 +514,10 @@
     // Takes an element or an element id
     getAction: function (element) {
         element = $(element);    
-
         var moniker = Form.Element.getMoniker(element);
-        return new Action(moniker);
+	if (!current_actions[moniker])
+	    current_actions[moniker] = new Action(moniker);
+	return current_actions[moniker];
     },
 
     // Returns the name of the field
@@ -652,6 +880,42 @@
 
     return f;    
 }
+
+var CACHE = {};
+
+
+var walk_node = function(node, table) {
+    for (var child = node.firstChild;
+         child != null;
+         child = child.nextSibling) {
+        var name = child.nodeName.toLowerCase();
+        if (table[name])
+	    table[name](child);
+    }
+}
+
+var extract_cacheable = function(fragment, f) {
+    walk_node(fragment,
+    { cacheable: function(fragment_bit) {
+            var c_type = fragment_bit.getAttribute("type");
+            var textContent = '';
+            if (fragment_bit.textContent) {
+                textContent = fragment_bit.textContent;
+            } else if (fragment_bit.firstChild) {
+                textContent = fragment_bit.firstChild.nodeValue;
+            } 
+	    try {
+		var cache_func = eval(textContent);
+		CACHE[f['path']] = { 'type': c_type, 'content': cache_func };
+	    }
+	    catch(e) { 
+		alert(e);
+		alert(textContent);
+	    }
+        }
+    });
+};
+
 // applying updates from a fragment
 //   - fragment: the fragment from the server
 //   - f: fragment spec
@@ -659,11 +923,10 @@
     // We found the right fragment
     var dom_fragment = fragments[f['region']];
     var new_dom_args = $H();
+
     var element = f['element'];
-    for (var fragment_bit = fragment.firstChild;
-	 fragment_bit != null;
-	 fragment_bit = fragment_bit.nextSibling) {
-	if (fragment_bit.nodeName == 'argument') {
+    walk_node(fragment,
+    { argument: function(fragment_bit) {
 	    // First, update the fragment's arguments
 	    // with what the server actually used --
 	    // this is needed in case there was
@@ -675,7 +938,8 @@
 		textContent = fragment_bit.firstChild.nodeValue;
 	    }
 	    new_dom_args[fragment_bit.getAttribute("name")] = textContent;
-	} else if (fragment_bit.nodeName.toLowerCase() == 'content') {
+	},
+      content: function(fragment_bit) {
 	    var textContent = '';
 	    if (fragment_bit.textContent) {
 		textContent = fragment_bit.textContent;
@@ -697,7 +961,7 @@
         });
         Behaviour.apply(element);
 	}
-    }
+    });
     dom_fragment.setArgs(new_dom_args);
 
     // Also, set us up the effect
@@ -733,7 +997,6 @@
         window.event.returnValue = false;
     }
 
-    show_wait_message();
     var named_args = arguments[0];
     var trigger    = arguments[1];
 
@@ -760,15 +1023,17 @@
     if (form && form['J:CALL']) 
 	optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': null}) ];
     // Build actions structure
+    var has_request = 0;
     request['actions'] = $H();
     for (var moniker in named_args['actions']) {
         var disable = named_args['actions'][moniker];
         var a = new Action(moniker, button_args);
+	current_actions[moniker] = a; // XXX: how do i make this bloody singleton?
         // Special case for Redirect, allow optional, implicit __page
         // from the response to be used.
         if (a.actionClass == 'Jifty::Action::Redirect')
             optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': a.fields().last().value}) ];
-
+        a.result = {}; a.result.field_error = {};
         if (a.register) {
             if (a.hasUpload())
                 return true;
@@ -776,15 +1041,71 @@
                 a.disable_input_fields();
             }
             request['actions'][moniker] = a.data_structure();
+            ++has_request;
         }
+
     }
 
     request['fragments'] = $H();
+    var update_from_cache = new Array;
+
     // Build fragments structure
     for (var i = 0; i < named_args['fragments'].length; i++) {
         var f = named_args['fragments'][i];
         f = prepare_element_for_update(f);
         if (!f) continue;
+
+        var cached = CACHE[f['path']];
+        if (cached && cached['type'] == 'static') {
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+	    var cached_result;
+
+	    Jifty.Web.current_region = fragments[f['region']];
+	    try { cached_result = apply_cached_for_action(cached['content'], []) }
+	    catch (e) { alert(e) }
+
+            content_node.textContent = cached_result;
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+
+            update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f);
+ } );
+            continue;
+        }
+	else if (cached && cached['type'] == 'action') {
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+            update_from_cache.push(function(){
+		    var cached_result;
+		    Jifty.Web.current_region = fragments[f['region']];
+		    try {
+			cached_result = apply_cached_for_action(cached['content'], Form.getActions(form));
+		    }
+		    catch (e) { alert(e); throw e }
+		    content_node.textContent = cached_result;
+		    apply_fragment_updates(my_fragment, f);
+ } );
+            continue;
+	}
+        else if (cached && cached['type'] == 'crudview') {
+	    try { // XXX: get model class etc as metadata in cache 
+		// XXX: kill dup code
+	    var Todo = new AsynapseRecord('todo');
+	    var record = Todo.find(f['args']['id']);
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+            content_node.textContent = cached['content'](record);
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+            update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f); } );
+	    } catch (e) { alert(e) };
+	    continue;
+	}
+
         // Update with all new values
         var name = f['region'];
         var fragment_request = fragments[name].data_structure(f['path'], f['args']);
@@ -795,49 +1116,71 @@
 
         // Push it onto the request stack
         request['fragments'][name] = fragment_request;
+        ++has_request;
+    }
+
+    if (!has_request) {
+        for (var i = 0; i < update_from_cache.length; i++)
+            update_from_cache[i]();
+        return false;
     }
 
+    show_wait_message();
+
     // And when we get the result back..
     var onSuccess = function(transport, object) {
         // Grab the XML response
         var response = transport.responseXML.documentElement;
+
+	// Get action results
+        walk_node(response,
+	{ result: function(result) {
+		var moniker = result.getAttribute("moniker");
+		walk_node(result,
+			  { field: function(field) {
+				  var error = field.getElementsByTagName('error')[0];
+				  if (error) {
+				      var text = error.textContent
+					  ? error.textContent
+					  : (error.firstChild ? error.firstChild.nodeValue : '');
+				      var action = current_actions[moniker];
+				      action.result.field_error[field.getAttribute("name")] = text;
+				      }
+			      }});
+	    }});
+	// empty known action. XXX: we should only need to discard actions being submitted
+
         // Loop through the result looking for it
         var expected_fragments = optional_fragments ? optional_fragments : named_args['fragments'];
         for (var response_fragment = response.firstChild;
              response_fragment != null && response_fragment.nodeName == 'fragment';
              response_fragment = response_fragment.nextSibling) {
 
-            var f; 
-            for (var i = 0; i < expected_fragments.length; i++) {
-                f = expected_fragments[i];
-                if (response_fragment.getAttribute("id") == f['region'])
-                    break;
-            }
-            if (response_fragment.getAttribute("id") != f['region'])
+            var exp_id = response_fragment.getAttribute("id");
+            var f = expected_fragments.find(function(f) { return exp_id == f['region'] });
+            if (!f)
                 continue;
 
-	    try {
-            apply_fragment_updates(response_fragment, f);
-	    }catch (e) { alert(e) }
+            try {
+                apply_fragment_updates(response_fragment, f);
+            }catch (e) { alert(e) }
+            extract_cacheable(response_fragment, f);
         }
-        for (var result = response.firstChild;
-             result != null;
-             result = result.nextSibling) {
-            if (result.nodeName == 'result') {
+
+	update_from_cache.each(function(x) { x() });
+
+        walk_node(response,
+	{ result: function(result) {
                 for (var key = result.firstChild;
                      key != null;
                      key = key.nextSibling) {
                     show_action_result(result.getAttribute("moniker"),key);
                 }
-            }
-        }
-        for (var redirect = response.firstChild;
-             redirect != null;
-             redirect = redirect.nextSibling) {
-            if (redirect.nodeName == 'redirect') {
+            },
+	  redirect: function(redirect) {
                 document.location =  redirect.firstChild.firstChild.nodeValue;
-            }
-        }
+	}});
+	current_actions = $H();
     };
     var onFailure = function(transport, object) {
         hide_wait_message_now();

Added: jifty/branches/virtual-models/share/web/static/js/template_declare.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/share/web/static/js/template_declare.js	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,53 @@
+// FIXME: try not to pollute the namespace!
+var tags = ['div', 'h2', 'dl', 'dt', 'dd', 'span', 'label', 'input', 'a'];
+for (var i in tags) {
+    this[tags[i]] = _mk_tag_wrapper(tags[i]);
+}
+this['form'] = _mk_tag_wrapper('form', function(attr) {
+	return '<form method="post" enctype="multipart/form-data" >'; // XXX action: & friends
+    }, null, 1);
+var _ = function(str) { return str };
+var attr = function() {};
+
+function _mk_tag_wrapper(name, pre, post, want_outbuf) {
+    return function() {
+	var buf = new Array;
+	var sp = this['attr'];
+	var attr = {};
+	this['attr'] = function(a) {
+	    var foo;
+	    a = a();
+	    while(foo = a.splice(0, 2)) {
+		if (foo.length == 0)
+		    break;
+		attr[foo[0]] = foo[1];
+	    }
+	};
+
+	var flushed = '';
+	if (this.out_buf) {
+	    flushed = this.out_buf;
+	    this.out_buf = '';
+	}
+
+	for (var i = 0; i < arguments.length; ++i) {
+	    buf.push(typeof(arguments[i]) == 'function' ? arguments[i]() : arguments[i]);
+	}
+	var _mk_attr = function() {
+	    var foo = ' ';
+	    for (var k in attr) {
+		if (k == 'extend') continue;
+		foo += k + '="' + attr[k] + '"';
+	    }
+	    return foo;
+	};
+	var first = buf.splice(0, 1);
+	var _pre = pre ? pre(attr) : '<'+name+_mk_attr(attr)+'>';
+	var _post = post ? post(attr) : '</'+name+'>';
+	if (want_outbuf && this.out_buf) {
+	    first += this.out_buf;
+	    this.out_buf = '';
+	}
+	return flushed + _pre + first + _post + buf.join('');
+    }
+};

Modified: jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml
==============================================================================
--- jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml	(original)
+++ jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml	Wed Aug  1 12:25:47 2007
@@ -52,6 +52,9 @@
     $writer->startTag( "fragment", id => Jifty->web->current_region->qualified_name );
     my %args = %{ Jifty->web->current_region->arguments };
     $writer->dataElement( "argument", $args{$_}, name => $_) for sort keys %args;
+    if (Jifty->config->framework('ClientTemplate') && Jifty->web->current_region->client_cacheable) {
+        $writer->cdataElement( "cacheable", Jifty->web->current_region->client_cache_content, type => Jifty->web->current_region->client_cacheable );
+    }
     $writer->cdataElement( "content", Jifty->web->current_region->as_string );
     $writer->endTag();
 

Added: jifty/branches/virtual-models/t/13-sessions.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/13-sessions.t	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,98 @@
+use strict;
+use warnings;
+
+=head1 DESCRIPTION
+
+Tests Jifty::Web::Session
+
+=cut
+
+use Jifty::Test tests => 31;
+
+my ($first_id, $second_id, $third_id);
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    $first_id = $session->id;
+
+    $session->set(hello => 'world');
+    $session->set(number => '1st');
+
+    is($session->get('hello'),  'world', "immediate 'get' works");
+    is($session->get('number'), '1st',   "immediate 'get' works");
+
+    $session->load_by_kv(user => 'first');
+    is($session->id, $first_id, "same session as before");
+    is($session->get('hello'),  'world', "'get' before destroy works");
+    is($session->get('number'), '1st',   "'get' before destroy works");
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "same session as before");
+
+    is($session->get('hello'),  'world', "'set', destroy, 'get' works");
+    is($session->get('number'), '1st',   "'set', destroy, 'get' works");
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'second');
+    ok($session->id, "got a session");
+    isnt($session->id, $first_id, "NOT same session as before");
+    $second_id = $session->id;
+
+    is($session->get('hello'),  undef, "different value gives different session");
+    is($session->get('number'), undef, "different value gives different session");
+
+    $session->set(hello => 'world');
+    $session->set(number => '2nd');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "first session again");
+
+    is($session->get('hello'), 'world');
+    is($session->get('number'), '1st', "even though the two sessions have some overlapping keys, the one that matters doesn't overlap");
+}
+
+# the three-arg form
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'first', sub { $_[0] =~ /^f/ } );
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "first session again");
+    is($session->get('number'), '1st');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'third', sub { $_[0] =~ /\b(thi|3)rd\b/ } );
+    ok($session->id, "got a session");
+    $third_id = $session->id;
+
+    isnt($session->id, $first_id,  "not first session");
+    isnt($session->id, $second_id, "not second session");
+    is($session->get('number'), undef);
+    $session->set(number => '3rd');
+    is($session->get('number'), '3rd');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'third', sub { $_[0] =~ /\b(thi|3)rd\b/ } );
+    ok($session->id, "got a session");
+    isnt($session->id, $first_id,  "not first session");
+    isnt($session->id, $second_id, "not second session");
+    is($session->id,   $third_id, "third session again");
+    is($session->get('number'), '3rd');
+}
+

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/Makefile.PL	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name        'TestApp::Plugin::Chart';
+version     '0.01';
+requires    'Jifty' => '0.70129';
+
+WriteAll;

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/bin/jifty	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Basename qw(dirname); 
+use UNIVERSAL::require;
+
+use Jifty;
+use Jifty::Script;
+
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/etc/config.yml	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,54 @@
+--- 
+framework: 
+  AdminMode: 1
+  ApplicationClass: TestApp::Plugin::Chart
+  ApplicationName: TestApp-Plugin-Chart
+  ApplicationUUID: D16D885C-3E10-11DC-ABE9-A583E6FF98E1
+  ConfigFileVersion: 2
+  Database: 
+    CheckSchema: 1
+    Database: testapp_plugin_chart
+    Driver: SQLite
+    Host: localhost
+    Password: ''
+    RecordBaseClass: Jifty::DBI::Record::Cachable
+    RecordUUIDs: active
+    User: ''
+    Version: 0.0.1
+  DevelMode: 1
+  L10N: 
+    PoDir: share/po
+  LogLevel: INFO
+  Mailer: Sendmail
+  MailerArgs: []
+
+  Plugins: 
+    - LetMe: {}
+    - SkeletonApp: {}
+    - REST: {}
+    - Halo: {}
+    - ErrorTemplates: {}
+    - OnlineDocs: {}
+    - CompressedCSSandJS: {}
+    - AdminUI: {}
+    - Chart: {}
+
+  PubSub: 
+    Backend: Memcached
+    Enable: ~
+  SkipAccessControl: 0
+  TemplateClass: TestApp::Plugin::Chart::View
+  Web: 
+    BaseURL: http://localhost
+    DataDir: var/mason
+    Globals: []
+
+    MasonConfig: 
+      autoflush: 0
+      default_escape_flags: h
+      error_format: text
+      error_mode: fatal
+    Port: 8888
+    ServeStaticFiles: 1
+    StaticRoot: share/web/static
+    TemplateRoot: share/web/templates

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,21 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::Chart::View;
+use Jifty::View::Declare -base;
+
+template '/graphit' => page {
+    Jifty->web->chart(
+        type   => 'Pie',
+        width  => 400,
+        height => 300,
+        data   => sub {
+            [
+                [ 2004, 2005, 2006, 2007 ],
+                [ 26, 37, 12, 42 ]
+            ];
+        },
+    );
+};
+
+1;

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/chart.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/chart.t	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+# XXX FIXME This is here to prevent a segfault on my machine during testing.
+#   -- sterling
+use GD;
+
+use Jifty::SubTest;
+use Jifty::Test;
+use Jifty::Test::WWW::Mechanize;
+
+eval "use Chart::Pie";
+if ($@) {
+    plan skip_all => 'Chart is not installed.';
+}
+else {
+    plan tests => 9;
+}
+
+use Jifty::Plugin::Chart::Renderer::Chart;
+
+(Jifty->find_plugin('Jifty::Plugin::Chart'))[0]
+    ->renderer('Jifty::Plugin::Chart::Renderer::Chart');
+
+my $server = Jifty::Test->make_server;
+ok($server, 'got a server');
+
+my $url = $server->started_ok;
+
+my $mech = Jifty::Test::WWW::Mechanize->new;
+
+$mech->get_ok($url . '/graphit', 'try getting /graphit');
+my $img_match = qr{<img src="(/chart/chart/S\d+)" width="400" height="300"/>};
+$mech->content_like($img_match, 'has an img tag');
+my ($chart_path) = $mech->content =~ $img_match;
+
+$mech->get_ok($url . $chart_path, 'try getting ' . $chart_path);
+
+my $response = $mech->response;
+is($response->header('Content-type'), 'image/png', 'content type set to png');
+
+SKIP: {
+    eval "use Image::Info qw/ image_info /";
+    skip "Image::Info is not installed", 3 if $@;
+
+    my $imgdata = $mech->content;
+    my $info = image_info(\$imgdata);
+
+    diag($info->{error}) if $info->{error};
+
+    is($info->{file_ext}, 'png', 'it is a png file');
+    is($info->{width}, 400, 'it is 400 pixels wide');
+    is($info->{height}, 300, 'it is 300 pixels tall');
+};
+

Added: jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/gd_graph.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-Chart/t/gd_graph.t	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+# XXX FIXME This is here to prevent a segfault on my machine during testing.
+#   -- sterling
+use GD;
+
+use Jifty::SubTest;
+use Jifty::Test;
+use Jifty::Test::WWW::Mechanize;
+
+eval "use GD::Graph::pie";
+if ($@) {
+    plan skip_all => 'GD::Graph is not installed.';
+}
+else {
+    plan tests => 9;
+}
+
+use Jifty::Plugin::Chart::Renderer::GD::Graph;
+
+(Jifty->find_plugin('Jifty::Plugin::Chart'))[0]
+    ->renderer('Jifty::Plugin::Chart::Renderer::GD::Graph');
+
+my $server = Jifty::Test->make_server;
+ok($server, 'got a server');
+
+my $url = $server->started_ok;
+
+my $mech = Jifty::Test::WWW::Mechanize->new;
+
+$mech->get_ok($url . '/graphit', 'try getting /graphit');
+my $img_match = qr{<img src="(/chart/gd_graph/S\d+)" width="400" height="300"/>};
+$mech->content_like($img_match, 'has an img tag');
+my ($chart_path) = $mech->content =~ $img_match;
+
+$mech->get_ok($url . $chart_path, 'try getting ' . $chart_path);
+
+my $response = $mech->response;
+is($response->header('Content-type'), 'image/png', 'content type set to png');
+
+SKIP: {
+    eval "use Image::Info qw/ image_info /";
+    skip "Image::Info is not installed", 3 if $@;
+
+    my $imgdata = $mech->content;
+    my $info = image_info(\$imgdata);
+
+    diag($info->{error}) if $info->{error};
+
+    is($info->{file_ext}, 'png', 'it is a png file');
+    is($info->{width}, 400, 'it is 400 pixels wide');
+    is($info->{height}, 300, 'it is 300 pixels tall');
+};
+

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm	Wed Aug  1 12:25:47 2007
@@ -34,6 +34,7 @@
   is immutable,
   default is defer { DateTime->now },
   filters are 'Jifty::DBI::Filter::DateTime';
+column 'uuid' => is UUID;
 };
 
 

Modified: jifty/branches/virtual-models/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t	Wed Aug  1 12:25:47 2007
@@ -4,7 +4,7 @@
 
 use lib 't/lib';
 use Jifty::SubTest;
-use Jifty::Test tests => 9;
+use Jifty::Test tests => 8;
 use Jifty::Test::WWW::Mechanize;
 use Test::Log4perl;
 
@@ -16,7 +16,7 @@
 my $mech    = Jifty::Test::WWW::Mechanize->new();
 
 {
-    my $log = Test::Log4perl->expect(['', warn => qr/You can't call a 'show' rule in a 'before' or 'after' block in the dispatcher/ ]);
+#    my $log = Test::Log4perl->expect(['', warn => qr/You can't call a 'show' rule in a 'before' or 'after' block in the dispatcher/ ]);
 $mech->get("$URL/before_stage_show", "Got /before_stage_show");
 $mech->content_lacks("This is content");
 is( $mech->status , '404');

Modified: jifty/branches/virtual-models/t/TestApp/t/05-editactions-Record.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/05-editactions-Record.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/05-editactions-Record.t	Wed Aug  1 12:25:47 2007
@@ -7,12 +7,12 @@
 use Jifty::SubTest;
 BEGIN { $ENV{'JIFTY_CONFIG'} = 't/config-Record' }
 
-use Jifty::Test tests => 10;
+use Jifty::Test tests => 11;
 use Jifty::Test::WWW::Mechanize;
-
 # Make sure we can load the model
 use_ok('TestApp::Model::User');
 
+Jifty->new();
 # Grab a system use
 my $system_user = TestApp::CurrentUser->superuser;
 ok($system_user, "Found a system user");
@@ -38,8 +38,9 @@
 $o->load($id);
 ok($id, "Load returned success");
 
+
 is($o->email, 'newemail at example.com', "Email was updated by form");
 is($o->tasty, 1, "User is still tasty (was not updated since immutable)");
-
+ok($o->uuid,  "The user has a uuid ". $o->uuid);
 1;
 

Modified: jifty/branches/virtual-models/t/TestApp/t/config-Record
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/config-Record	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/config-Record	Wed Aug  1 12:25:47 2007
@@ -2,3 +2,5 @@
 framework:
   Database:
     RecordBaseClass: Jifty::DBI::Record
+  Plugins:
+    - UUID: {}

Added: jifty/branches/virtual-models/t/clientside/td.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/clientside/td.t	Wed Aug  1 12:25:47 2007
@@ -0,0 +1,104 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Jifty;
+
+package Foo;
+use Jifty::View::Declare -base;
+
+template _faq => \&_faq;
+
+sub _faq {
+    div {
+        attr { id => "faq" };
+        h2 { 'Using Yada' }
+        dl {
+            dt { 'Yada Yada Yada!'}
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+template _faq2 => \&_faq2;
+
+sub _faq2 {
+    div {
+        attr { id => "faq" };
+        h2 { 'Using Yada' }
+        dl {
+            dt { 'Yada Yada Yada!'};
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+package main;
+
+use Test::More;
+use IPC::Run3;
+eval 'use Jifty::View::Declare::Compile; 1'
+    or plan skip_all => "Can't load Jifty::View::Declare::Compile";
+
+my $jsbin = can_run('js')
+    or plan skip_all => "Can't find spidermonkey js binary";
+
+Template::Declare->init( roots => ['Foo']);
+
+plan tests => 2;
+
+is_compatible('_faq');
+TODO: {
+local $TODO = 'buf handling (non-katamari version) not yet';
+is_compatible('_faq2');
+
+};
+
+
+
+sub is_compatible {
+    my $template = shift;
+    my $js = js_output( js_code( Foo->can($template) ) );
+    my $td = Template::Declare->show($template);
+    $js =~ s/\s*//g;
+    $td =~ s/\s*//g;
+    unshift @_, $js, $td;
+    goto \&is;
+}
+
+sub js_code {
+    my $code = shift;
+    return '(function() '.Jifty::View::Declare::Compile->new->coderef2text($code) . ')()';
+}
+
+sub js_output {
+    my $code = shift;
+    my ($out, $err);
+    run3 [$jsbin],
+	['load("share/web/static/js/template_declare.js");', "print($code);"],
+	    \$out, \$err;
+    diag $err if $err;
+    return $out;
+
+}
+
+use File::Spec::Functions 'catfile';
+sub can_run {
+    my ($_cmd, @path) = @_;
+
+    return $_cmd if -x $_cmd;
+
+    for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), @path, '.') {
+        my $abs = catfile($dir, $_[0]);
+        return $abs if -x $abs;
+    }
+
+    return;
+}


More information about the Jifty-commit mailing list