[Jifty-commit] r1102 - in jifty/trunk: . lib/Jifty lib/Jifty/Web plugins/EditInPlace share/web/static/css share/web/static/images/silk share/web/static/js share/web/static/js/cssquery share/web/static/js/ie7 share/web/static/js/jsan share/web/static/js/jsan/DOM share/web/static/js/jsan/Upgrade share/web/static/js/jsan/Upgrade/Array share/web/static/js/jsan/Upgrade/Function share/web/templates/_elements

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Tue May 23 11:02:09 EDT 2006


Author: jesse
Date: Tue May 23 11:02:05 2006
New Revision: 1102

Added:
   jifty/trunk/share/web/static/css/context-menus.css
   jifty/trunk/share/web/static/images/silk/bullet_arrow_down.png   (contents, props changed)
   jifty/trunk/share/web/static/images/silk/bullet_arrow_up.png   (contents, props changed)
   jifty/trunk/share/web/static/js/context_menu.js
   jifty/trunk/share/web/static/js/cssquery/
   jifty/trunk/share/web/static/js/cssquery/cssQuery-level2.js
   jifty/trunk/share/web/static/js/cssquery/cssQuery-level3.js
   jifty/trunk/share/web/static/js/cssquery/cssQuery-standard.js
   jifty/trunk/share/web/static/js/cssquery/cssQuery.js
   jifty/trunk/share/web/static/js/jifty_utils.js
   jifty/trunk/share/web/static/js/jsan/
   jifty/trunk/share/web/static/js/jsan/DOM/
   jifty/trunk/share/web/static/js/jsan/DOM/Events.js
   jifty/trunk/share/web/static/js/jsan/JSAN.js
   jifty/trunk/share/web/static/js/jsan/Upgrade/
   jifty/trunk/share/web/static/js/jsan/Upgrade.js
   jifty/trunk/share/web/static/js/jsan/Upgrade/Array/
   jifty/trunk/share/web/static/js/jsan/Upgrade/Array/push.js
   jifty/trunk/share/web/static/js/jsan/Upgrade/Function/
   jifty/trunk/share/web/static/js/jsan/Upgrade/Function/apply.js
   jifty/trunk/share/web/templates/_elements/keybindings
Removed:
   jifty/trunk/share/web/static/js/ie7/
Modified:
   jifty/trunk/   (props changed)
   jifty/trunk/Changelog
   jifty/trunk/lib/Jifty/Action.pm
   jifty/trunk/lib/Jifty/Web.pm
   jifty/trunk/lib/Jifty/Web/Form.pm
   jifty/trunk/lib/Jifty/Web/Form/Element.pm
   jifty/trunk/lib/Jifty/Web/Form/Field.pm
   jifty/trunk/lib/Jifty/Web/Form/Link.pm
   jifty/trunk/lib/Jifty/Web/Menu.pm
   jifty/trunk/plugins/EditInPlace/META.yml
   jifty/trunk/share/web/static/css/main.css
   jifty/trunk/share/web/static/js/behaviour.js
   jifty/trunk/share/web/static/js/jifty.js
   jifty/trunk/share/web/static/js/key_bindings.js
   jifty/trunk/share/web/static/js/rico.js
   jifty/trunk/share/web/templates/_elements/javascript
   jifty/trunk/share/web/templates/_elements/menu
   jifty/trunk/share/web/templates/_elements/wrapper

Log:
 r11778 at hualien (orig r892):  trs | 2006-04-22 17:25:16 -0400
 New branch for JSAN integration instead of prototype, etc.
 r11801 at hualien (orig r898):  trs | 2006-04-23 12:15:31 -0400
  r9946 at zot:  tom | 2006-04-23 00:38:34 -0400
  Remove IE7 source from Jifty
 
 r11802 at hualien (orig r899):  trs | 2006-04-23 12:15:37 -0400
  r9947 at zot:  tom | 2006-04-23 00:39:58 -0400
  Reworked Behaviour library to use JSAN and cssQuery
 
 r11803 at hualien (orig r900):  trs | 2006-04-23 12:15:41 -0400
 
 r11858 at hualien (orig r915):  trs | 2006-04-24 15:36:29 -0400
  r9980 at zot:  tom | 2006-04-24 11:49:12 -0400
  We use cssQuery now
 
 r11859 at hualien (orig r916):  trs | 2006-04-24 15:36:37 -0400
  r9981 at zot:  tom | 2006-04-24 12:06:29 -0400
  Turn the key bindings into a Jifty library and fix propagation problems in Firefox
 
 r11873 at hualien (orig r920):  trs | 2006-04-24 17:11:45 -0400
  r10015 at zot:  tom | 2006-04-24 17:10:18 -0400
  Make writing keybinding legend use Behaviour and the DOM
 
 r11877 at hualien (orig r924):  trs | 2006-04-24 22:48:58 -0400
  r10027 at zot:  tom | 2006-04-24 22:48:38 -0400
  IE doesn't like binding to the window, but document works (should have just kept it in the first place, I guess)
 
 r11879 at hualien (orig r926):  trs | 2006-04-24 23:18:35 -0400
  r10035 at zot:  tom | 2006-04-24 23:18:18 -0400
  Support key bindings for Opera
 
 r11962 at hualien (orig r938):  trs | 2006-04-26 21:01:54 -0400
  r10127 at zot:  tom | 2006-04-26 21:01:10 -0400
  Only append the keybindings if there are any
 
 r13101 at hualien (orig r963):  trs | 2006-04-29 17:49:43 -0400
  r10199 at zot:  tom | 2006-04-29 17:48:30 -0400
  Fix Rico's corner implementation to deal with padding better
 
 r13145 at hualien (orig r967):  trs | 2006-04-29 23:15:18 -0400
  r10202 at zot:  tom | 2006-04-29 23:14:57 -0400
  Add a note about the modified Rico and make it a new version
 
 r13405 at hualien (orig r1037):  trs | 2006-05-11 22:42:20 -0400
  r10438 at zot:  tom | 2006-05-11 22:41:10 -0400
  Hardcoding the dependencies instead of letting JSAN handle it was a bad idea (users would have to fiddle then instead of simply "use"ing the libs)
 
 r13567 at hualien (orig r1046):  trs | 2006-05-13 16:43:30 -0400
  r12260 at zot:  tom | 2006-05-13 13:01:09 -0400
  s/'/"/g -- not all browsers like single quotes
 
 r13568 at hualien (orig r1047):  trs | 2006-05-13 16:43:34 -0400
  r12269 at zot:  tom | 2006-05-13 16:40:32 -0400
  Put JSAN.js first and take care of its config _before_ loading anything that uses it.  Plus, be a little smarter about the includePath and errorLevel.
 
 r13569 at hualien (orig r1048):  trs | 2006-05-13 16:43:41 -0400
 
 r13570 at hualien (orig r1049):  trs | 2006-05-13 16:43:47 -0400
  r12275 at zot:  tom | 2006-05-13 16:43:21 -0400
  Fix merge bug
 
 r13572 at hualien (orig r1051):  trs | 2006-05-13 17:46:54 -0400
  r12281 at zot:  tom | 2006-05-13 17:30:28 -0400
  Compat with Konq
 
 r13891 at hualien (orig r1079):  trs | 2006-05-18 18:49:52 -0400
  r12387 at zot:  tom | 2006-05-18 18:49:43 -0400
  Not really as desirable or as useful as I first thought.
 
 r13965 at hualien (orig r1086):  alexmv | 2006-05-22 16:43:57 -0400
  r13204 at zoq-fot-pik:  chmrr | 2006-05-22 16:43:48 -0400
   * Register actions of buttons in the buttons, not in the form
 
 r13966 at hualien (orig r1087):  alexmv | 2006-05-22 19:13:23 -0400
  r13212 at zoq-fot-pik:  chmrr | 2006-05-22 19:12:06 -0400
   * "" is false in javascript
 
 r13967 at hualien (orig r1088):  jesse | 2006-05-22 21:43:15 -0400
 * Added support for context menus (at the code level. next up, at the CSS level)
 
 r13977 at hualien (orig r1093):  jesse | 2006-05-22 22:01:58 -0400
 * Added back missing loc
 r13978 at hualien (orig r1094):  trs | 2006-05-22 22:23:34 -0400
  r12461 at zot:  tom | 2006-05-22 22:23:22 -0400
  Oops, we don't want these
 
 r13981 at hualien (orig r1096):  jesse | 2006-05-22 22:39:32 -0400
 * Pull in backcompat breakage notes from trs
 r13986 at hualien (orig r1097):  trs | 2006-05-22 23:32:14 -0400
  r12463 at zot:  tom | 2006-05-22 23:24:39 -0400
  Contextual menu support
 
 r13987 at hualien (orig r1098):  trs | 2006-05-22 23:32:19 -0400
 
 r13989 at hualien (orig r1099):  trs | 2006-05-22 23:57:37 -0400
  r12469 at zot:  tom | 2006-05-22 23:57:21 -0400
  * Use JS to place the context menu above everything else
  * Better styling
 


Modified: jifty/trunk/Changelog
==============================================================================
--- jifty/trunk/Changelog	(original)
+++ jifty/trunk/Changelog	Tue May 23 11:02:05 2006
@@ -1,6 +1,24 @@
   * Removed the ActionBasePath and CurrentUserClass configuration
     variables.  This breaks backwards compatibility.
 
+  * If you've overridden /_elements/javascript in your app, you'll want to 
+    take a look at the new stock version to see what has changed.
+
+  * The JS method document.getElementsBySelector() no longer exists.  Use 
+    cssQuery() instead.
+
+* The keybinding JS code is now a more proper library 
+    (Jifty.KeyBindings) and less intrusive.  If you've overridden the 
+    default /_elements/wrapper in your app, you'll want to make sure you get 
+    rid of the keybinding javascript (see the stock wrapper).  Just include 
+    <& /_elements/keybindings &> wherever you want the keybindings for a 
+    page to appear.
+
+* This shouldn't be the cause of breakage, but the included rico.js is 
+    no longer a stock Rico build.  See the comments at the top of the file 
+    if you're interested.
+
+
 0.60507 (7 May 2006)
 
   * Pod fixes from Eric Wilhelm

Modified: jifty/trunk/lib/Jifty/Action.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action.pm	(original)
+++ jifty/trunk/lib/Jifty/Action.pm	Tue May 23 11:02:05 2006
@@ -423,8 +423,14 @@
                  submit => $self,
                  @_);
 
-    Jifty->web->form->register_action( $self );
-    Jifty->web->form->print_action_registration($self->moniker);
+    # Unless we've printed a moniker for the action, we embed the
+    # action registration into the button
+    unless ( Jifty->web->form->printed_actions->{ $self->moniker } ) {
+        $args{parameters}{ $self->register_name } = ref $self;
+        $args{parameters}{ $self->double_fallback_form_field_name($_) }
+            = $self->argument_value($_) || $self->arguments->{$_}->{'default_value'}
+            for grep { $self->arguments->{$_}{constructor} } keys %{ $self->arguments };
+    }
     $args{parameters}{$self->form_field_name($_)} = $args{arguments}{$_}
       for keys %{$args{arguments}};
 

Modified: jifty/trunk/lib/Jifty/Web.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web.pm	(original)
+++ jifty/trunk/lib/Jifty/Web.pm	Tue May 23 11:02:05 2006
@@ -707,7 +707,7 @@
 
 sub link {
     my $self = shift;
-    return Jifty::Web::Form::Clickable->new(@_)->generate->render;
+    return Jifty::Web::Form::Clickable->new(@_)->generate;
 }
 
 =head3 return PARAMHASH

Modified: jifty/trunk/lib/Jifty/Web/Form.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/Form.pm	(original)
+++ jifty/trunk/lib/Jifty/Web/Form.pm	Tue May 23 11:02:05 2006
@@ -230,7 +230,7 @@
     my $action = $self->has_action($moniker);
     return unless ($action);
     return if exists $self->printed_actions->{$moniker};
-     $self->printed_actions->{$moniker} = 1;
+    $self->printed_actions->{$moniker} = 1;
 
     $action->register();
 

Modified: jifty/trunk/lib/Jifty/Web/Form/Element.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/Form/Element.pm	(original)
+++ jifty/trunk/lib/Jifty/Web/Form/Element.pm	Tue May 23 11:02:05 2006
@@ -258,7 +258,7 @@
     my $self = shift;
     my $key  = $self->key_binding;
     if ($key) {
-        Jifty->web->out( "<script><!--\naddKeyBinding(" . "'"
+        Jifty->web->out( "<script><!--\nJifty.KeyBindings.add(" . "'"
                 . uc($key) . "', "
                 . "'click', " . "'"
                 . $self->id . "'," . "'"

Modified: jifty/trunk/lib/Jifty/Web/Form/Field.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/Form/Field.pm	(original)
+++ jifty/trunk/lib/Jifty/Web/Form/Field.pm	Tue May 23 11:02:05 2006
@@ -45,7 +45,7 @@
 
 use Scalar::Util;
 use HTML::Entities;
-use overload '""' => sub {shift->render};
+use overload '""' => sub { shift->render};
 
 =head2 new
 

Modified: jifty/trunk/lib/Jifty/Web/Form/Link.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/Form/Link.pm	(original)
+++ jifty/trunk/lib/Jifty/Web/Form/Link.pm	Tue May 23 11:02:05 2006
@@ -17,6 +17,9 @@
 
 use base qw/Jifty::Web::Form::Element Class::Accessor::Fast/;
 
+# Since we don't inherit from Form::Field, we don't otherwise stringify
+use overload '""' => sub { shift->render};
+
 =head2 accessors
 
 Link adds C<url> and C<escape_label> to the list of possible accessors

Modified: jifty/trunk/lib/Jifty/Web/Menu.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/Menu.pm	(original)
+++ jifty/trunk/lib/Jifty/Web/Menu.pm	Tue May 23 11:02:05 2006
@@ -3,7 +3,7 @@
 use base qw/Class::Accessor::Fast/;
 use URI;
 
-__PACKAGE__->mk_accessors(qw(label parent sort_order));
+__PACKAGE__->mk_accessors(qw(label parent sort_order link));
 
 =head2 new PARAMHASH
 
@@ -14,6 +14,14 @@
 
 =cut
 
+sub new {
+    my $package = shift;
+    # Class::Accessor only wants a hashref;
+    $package->SUPER::new( ref($_[0]) eq 'HASH' ? @_ : {@_} );
+
+}
+
+
 =head2 label [STRING]
 
 Sets or returns the string that the menu item will be displayed as.
@@ -28,6 +36,14 @@
 Gets or sets the sort order of the item, as it will be displayed under
 the parent.  This defaults to adding onto the end.
 
+
+=head2 link
+
+Gets or set a Jifty::Web::Link object that represents this menu item. If
+you're looking to do complex ajaxy things with menus, this is likely
+the option you want.
+
+
 =head2 url
 
 Gets or sets the URL that the menu's link goes to.  If the link
@@ -146,4 +162,42 @@
     return wantarray ? @kids : \@kids;
 }
 
+
+sub render_as_context_menu {
+    my $self = shift;
+    my @kids = $self->children;
+    my $id = Jifty->web->serial;
+    Jifty->web->out(
+        qq{<ul class="context_menu">} .qq{<li class="closed toplevel">}.  qq{<span class="title">} . $self->label() . qq{</span>}
+            . (
+            @kids
+            ? qq{<span class="expand"><a href="#" onClick="Jifty.ContextMenu.hideshow('}.$id.qq{'); return false;">+</a></span>}
+            : ''
+            )
+            . qq{<ul id="}.$id.  qq{">}
+    );
+    for (@kids) {
+        Jifty->web->out("<li>");
+
+        # We should be able to get this as a string.
+        $_->as_link;
+        Jifty->web->out("</li>");
+    }
+
+    Jifty->web->out(qq{</ul></li></ul>});
+    '';
+
+}
+
+=head2 as_link
+
+Return this menu item as a C<Jifty::Web::Link>, either the one we were initialized with or a new one made from the C</label> and c</url>
+
+=cut
+
+sub as_link {
+     my $self = shift;
+     ($self->link ? $self->link : Jifty->web->link(label => _($self->label), url => $self->url)); 
+}
+
 1;

Modified: jifty/trunk/plugins/EditInPlace/META.yml
==============================================================================
--- jifty/trunk/plugins/EditInPlace/META.yml	(original)
+++ jifty/trunk/plugins/EditInPlace/META.yml	Tue May 23 11:02:05 2006
@@ -1,5 +1,7 @@
+build_requires: 
+  ExtUtils::MakeMaker: 6.11
 distribution_type: module
-generated_by: Module::Install version 0.610
+generated_by: Module::Install version 0.620
 license: unknown
 name: Jifty-Plugin-EditInPlace
 no_index: 

Added: jifty/trunk/share/web/static/css/context-menus.css
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/css/context-menus.css	Tue May 23 11:02:05 2006
@@ -0,0 +1,101 @@
+ul.context_menu {
+    clear: none;
+    float: left;
+}
+
+ul.context_menu, ul.context_menu ul {
+    list-style: none;
+    margin-left: 0;
+    padding-left: 0;
+}
+
+.context_menu li.toplevel {
+    float: left;
+    margin: 0 0.5em 0 0;
+    padding: 0.2em;
+
+    border: 1px solid #ccc;
+    border-top-color: white;
+    border-left-color: white;
+
+    color: #793300;
+}
+
+.context_menu li.toplevel ul li {
+    color: gray;
+}
+
+.context_menu li.toplevel .title {
+    font-weight: bold;
+}
+
+.context_menu li.toplevel ul {
+    font-size: 0.9em;
+    display: none;
+
+    padding: 0.1em 0.2em 0.5em 0.1em;
+    background: white;
+    border-top: 1px dotted lightgrey;
+    border-left: 1px solid lightgrey;
+    border-right: 1px solid grey;
+    border-bottom: 1px solid grey;
+}
+
+.context_menu li.toplevel ul a {
+    width: 100%;
+    display: block;
+}
+
+.context_menu .open span.expand a {
+    background-image: url(/static/images/silk/bullet_arrow_up.png);
+}
+
+.context_menu span.expand a {
+    background: url(/static/images/silk/bullet_arrow_down.png) no-repeat center center;
+    padding-left: 16px;
+    border: 1px solid transparent;
+    color: white;
+    margin-left: 0.3em;
+}
+
+.context_menu li.toplevel:hover .expand a {
+    border-top: 1px solid grey;
+    border-left: 1px solid grey;
+    border-right: 1px solid lightgrey;
+    border-bottom: 1px solid lightgrey;
+}
+
+.context_menu li.open a {
+    border-bottom-color: transparent;
+}
+
+.context_menu li.open, .context_menu li.closed:hover {
+    border-top: 1px solid lightgrey;
+    border-left: 1px solid lightgrey;
+    border-right: 1px solid grey;
+    border-bottom-color: transparent;
+}
+
+.context_menu li.closed:hover {
+    border-bottom-color: grey;
+}
+
+.context_menu li.toplevel span.expand a:active {
+    border-top: 1px solid white;
+    border-left: 1px solid white;
+    border-right: 1px solid grey;
+    border-bottom: 1px solid grey;
+
+    position: relative;
+    top: 1px;
+    left: 1px;
+}
+
+.context_menu li ul li {
+    padding: 0.2em;
+}
+
+.context_menu li.open ul li:hover {
+    background: #eee;
+}
+

Modified: jifty/trunk/share/web/static/css/main.css
==============================================================================
--- jifty/trunk/share/web/static/css/main.css	(original)
+++ jifty/trunk/share/web/static/css/main.css	Tue May 23 11:02:05 2006
@@ -1,6 +1,7 @@
 @import "app-base.css";
 @import "base.css";
 @import "nav.css";
+ at import "context-menus.css";
 @import "calendar.css";
 @import "combobox.css";
 @import "keybindings.css";

Added: jifty/trunk/share/web/static/images/silk/bullet_arrow_down.png
==============================================================================
Binary file. No diff available.

Added: jifty/trunk/share/web/static/images/silk/bullet_arrow_up.png
==============================================================================
Binary file. No diff available.

Modified: jifty/trunk/share/web/static/js/behaviour.js
==============================================================================
--- jifty/trunk/share/web/static/js/behaviour.js	(original)
+++ jifty/trunk/share/web/static/js/behaviour.js	Tue May 23 11:02:05 2006
@@ -1,257 +1,55 @@
 /*
-   Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
-   of Simon Willison (see comments by Simon below).
-
-   Description:
-   	
-   	Uses css selectors to apply javascript behaviours to enable
-   	unobtrusive javascript in html documents.
-   	
+   Modified to fix some bugs, use a different css query engine, and to
+   to use JSAN classes.
+   
+   Based on Behaviour v1.1 by Ben Nolan, June 2005, which was based
+   largely on the work of Simon Willison.
+ 
    Usage:   
    
-	var myrules = {
-		'b.someclass' : function(element){
-			element.onclick = function(){
-				alert(this.innerHTML);
-			}
-		},
-		'#someid u' : function(element){
-			element.onmouseover = function(){
-				this.innerHTML = "BLAH!";
-			}
-		}
-	};
-	
-	Behaviour.register(myrules);
-	
-	// Call Behaviour.apply() to re-apply the rules (if you
-	// update the dom, etc).
+    var myrules = {
+        'b.someclass' : function(element){
+            element.onclick = function(){
+                alert(this.innerHTML);
+            }
+        },
+        '#someid u' : function(element){
+            element.onmouseover = function(){
+                this.innerHTML = "BLAH!";
+            }
+        }
+    };
+    
+    Behaviour.register(myrules);
+    
+    // Call Behaviour.apply() to re-apply the rules (if you
+    // update the dom, etc).
 
-   License:
-   
-   	My stuff is BSD licensed. Not sure about Simon's.
-   	
-   More information:
-   	
-   	http://ripcord.co.nz/behaviour/
-   
 */   
 
-var Behaviour = {
-	list : new Array,
-	
-	register : function(sheet){
-		Behaviour.list.push(sheet);
-	},
-	
-	start : function(){
-		Behaviour.addLoadEvent(function(){
-			Behaviour.apply();
-		});
-	},
-	
-	apply : function(){
-		for (h=0;sheet=Behaviour.list[h];h++){
-			for (selector in sheet){
-				list = document.getElementsBySelector(selector);
-				
-				if (!list){
-					continue;
-				}
-
-				for (i=0;element=list[i];i++){
-					sheet[selector](element);
-				}
-			}
-		}
-	},
-	
-	addLoadEvent : function(func){
-		var oldonload = window.onload;
-		
-		if (typeof window.onload != 'function') {
-			window.onload = func;
-		} else {
-			window.onload = function() {
-				oldonload();
-				func();
-			}
-		}
-	}
-}
-
-Behaviour.start();
-
-/*
-   The following code is Copyright (C) Simon Willison 2004.
-
-   document.getElementsBySelector(selector)
-   - returns an array of element objects from the current document
-     matching the CSS selector. Selectors can contain element names, 
-     class names and ids and can be nested. For example:
-     
-       elements = document.getElementsBySelect('div#main p a.external')
-     
-     Will return an array of all 'a' elements with 'external' in their 
-     class attribute that are contained inside 'p' elements that are 
-     contained inside the 'div' element which has id="main"
-
-   New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
-   See http://www.w3.org/TR/css3-selectors/#attribute-selectors
-
-   Version 0.4 - Simon Willison, March 25th 2003
-   -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
-   -- Opera 7 fails 
-*/
+JSAN.use("DOM.Events");
+JSAN.use("Upgrade.Array.push");
 
-function getAllChildren(e) {
-  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
-  return e.all ? e.all : e.getElementsByTagName('*');
-}
-
-document.getElementsBySelector = function(selector) {
-  // Attempt to fail gracefully in lesser browsers
-  if (!document.getElementsByTagName) {
-    return new Array();
-  }
-  // Split selector in to tokens
-  var tokens = selector.split(' ');
-  var currentContext = new Array(document);
-  for (var i = 0; i < tokens.length; i++) {
-    token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
-    if (token.indexOf('#') > -1) {
-      // Token is an ID selector
-      var bits = token.split('#');
-      var tagName = bits[0];
-      var id = bits[1];
-      var element = document.getElementById(id);
-      if (!element) {
-        // id doesn't exist on the page; no use continuing
-        return new Array();
-      } else if (tagName && element.nodeName.toLowerCase() != tagName) {
-        // tag with that ID not found, return false
-        return new Array();
-      }
-      // Set currentContext to contain just this element
-      currentContext = new Array(element);
-      continue; // Skip to next token
-    }
-    if (token.indexOf('.') > -1) {
-      // Token contains a class selector
-      var bits = token.split('.');
-      var tagName = bits[0];
-      var className = bits[1];
-      if (!tagName) {
-        tagName = '*';
-      }
-      // Get elements matching tag, filter them for class selector
-      var found = new Array;
-      var foundCount = 0;
-      for (var h = 0; h < currentContext.length; h++) {
-        var elements;
-        if (tagName == '*') {
-            elements = getAllChildren(currentContext[h]);
-        } else {
-            elements = currentContext[h].getElementsByTagName(tagName);
-        }
-        for (var j = 0; j < elements.length; j++) {
-          found[foundCount++] = elements[j];
-        }
-      }
-      currentContext = new Array;
-      var currentContextIndex = 0;
-      for (var k = 0; k < found.length; k++) {
-        if (found[k].className && found[k].className.match(new RegExp('(^|\\s)'+className+'(\\s|$)'))) {
-          currentContext[currentContextIndex++] = found[k];
-        }
-      }
-      continue; // Skip to next token
-    }
-    // Code to deal with attribute selectors
-    if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
-      var tagName = RegExp.$1;
-      var attrName = RegExp.$2;
-      var attrOperator = RegExp.$3;
-      var attrValue = RegExp.$4;
-      if (!tagName) {
-        tagName = '*';
-      }
-      // Grab all of the tagName elements within current context
-      var found = new Array;
-      var foundCount = 0;
-      for (var h = 0; h < currentContext.length; h++) {
-        var elements;
-        if (tagName == '*') {
-            elements = getAllChildren(currentContext[h]);
-        } else {
-            elements = currentContext[h].getElementsByTagName(tagName);
-        }
-        for (var j = 0; j < elements.length; j++) {
-          found[foundCount++] = elements[j];
-        }
-      }
-      currentContext = new Array;
-      var currentContextIndex = 0;
-      var checkFunction; // This function will be used to filter the elements
-      switch (attrOperator) {
-        case '=': // Equality
-          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
-          break;
-        case '~': // Match one of space seperated words 
-          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
-          break;
-        case '|': // Match start with value followed by optional hyphen
-          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
-          break;
-        case '^': // Match starts with value
-          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
-          break;
-        case '$': // Match ends with value - fails with "Warning" in Opera 7
-          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
-          break;
-        case '*': // Match ends with value
-          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
-          break;
-        default :
-          // Just test for existence of attribute
-          checkFunction = function(e) { return e.getAttribute(attrName); };
-      }
-      currentContext = new Array;
-      var currentContextIndex = 0;
-      for (var k = 0; k < found.length; k++) {
-        if (checkFunction(found[k])) {
-          currentContext[currentContextIndex++] = found[k];
-        }
-      }
-      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
-      continue; // Skip to next token
-    }
+var Behaviour = {
+    list: new Array(),
     
-    if (!currentContext[0]){
-    	return;
-    }
+    register: function(sheet) {
+        Behaviour.list.push(sheet);
+    },
     
-    // If we get here, token is JUST an element (not a class or ID selector)
-    tagName = token;
-    var found = new Array;
-    var foundCount = 0;
-    for (var h = 0; h < currentContext.length; h++) {
-      var elements = currentContext[h].getElementsByTagName(tagName);
-      for (var j = 0; j < elements.length; j++) {
-        found[foundCount++] = elements[j];
-      }
+    apply: function() {
+        for (var h = 0; sheet = Behaviour.list[h]; h++) {
+            for (var selector in sheet) {
+                var elements = cssQuery(selector);
+                
+                if ( !elements ) continue;
+
+                for (var i = 0; element = elements[i]; i++)
+                    sheet[selector](element);
+            }
+        }
     }
-    currentContext = found;
-  }
-  return currentContext;
-}
+}    
+
+DOM.Events.addListener( window, "load", Behaviour.apply );
 
-/* That revolting regular expression explained 
-/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
-  \---/  \---/\-------------/    \-------/
-    |      |         |               |
-    |      |         |           The value
-    |      |    ~,|,^,$,* or =
-    |   Attribute 
-   Tag
-*/

Added: jifty/trunk/share/web/static/js/context_menu.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/context_menu.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,86 @@
+JSAN.use("DOM.Events");
+
+if (typeof Jifty == "undefined") Jifty = { };
+
+Jifty.ContextMenu = {
+    currently_open:  "",
+    prevent_stutter: "",
+
+    getParentListItem: function(ul) {
+        /* XXX TODO: Put this in the onclick handler? */
+        return ul.parentNode;
+    },
+
+    hideshow: function(id) {
+        var ul = document.getElementById(id);
+
+        Jifty.ContextMenu.prevent_stutter = id;
+
+        if ( ul.style.display == "block" )
+            Jifty.ContextMenu.hide(id);
+        else
+            Jifty.ContextMenu.show(id);
+    },
+
+    hide: function(id) {
+        var ul = document.getElementById(id);
+
+        var li = Jifty.ContextMenu.getParentListItem(ul);
+        Element.removeClassName(li, "open");
+        Element.addClassName(li, "closed");
+                             
+        ul.style.display = "none";
+        Jifty.ContextMenu.currently_open = "";
+    },
+
+    show: function(id) {
+        var ul = document.getElementById(id);
+        
+        if (   Jifty.ContextMenu.currently_open
+            && Jifty.ContextMenu.currently_open != ul.id )
+        {
+            Jifty.ContextMenu.hide(Jifty.ContextMenu.currently_open);
+        }
+    
+        var li = Jifty.ContextMenu.getParentListItem(ul);
+
+        if ( ul.style.position == "" ) {
+            var x = Jifty.Utils.findPosX( li );
+            var y = Jifty.Utils.findPosY( li ) + li.offsetHeight;
+
+            ul.style.position = "absolute";
+            ul.style.left = x + "px";
+            ul.style.top = y + "px";
+            ul.style.width = li.offsetWidth * 2 + "px";
+        }
+
+        Element.removeClassName(li, "closed");
+        Element.addClassName(li, "open");
+         
+        ul.style.display = "block";
+        Jifty.ContextMenu.currently_open = ul.id;
+    },
+
+    hideOpenMenu: function(event) {
+        /* This is a bloody hack to deal with the Document based listener
+           firing just before our listener on the link.
+           If both fire, the window closes and then opens again.
+         */
+        if (   Jifty.ContextMenu.prevent_stutter
+            && Jifty.ContextMenu.prevent_stutter == Jifty.ContextMenu.currently_open)
+        {
+            Jifty.ContextMenu.prevent_stutter = "";
+            return;
+        }
+        else {
+            Jifty.ContextMenu.prevent_stutter = "";
+        }
+        
+        if (Jifty.ContextMenu.currently_open) {
+            Jifty.ContextMenu.hide(Jifty.ContextMenu.currently_open);
+        }
+    }
+};
+
+DOM.Events.addListener( window, "click", Jifty.ContextMenu.hideOpenMenu );
+

Added: jifty/trunk/share/web/static/js/cssquery/cssQuery-level2.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/cssquery/cssQuery-level2.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,142 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+cssQuery.addModule("css-level2", function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// child selector
+selectors[">"] = function($results, $from, $tagName, $namespace) {
+	var $element, i, j;
+	for (i = 0; i < $from.length; i++) {
+		var $subset = childElements($from[i]);
+		for (j = 0; ($element = $subset[j]); j++)
+			if (compareTagName($element, $tagName, $namespace))
+				$results.push($element);
+	}
+};
+
+// sibling selector
+selectors["+"] = function($results, $from, $tagName, $namespace) {
+	for (var i = 0; i < $from.length; i++) {
+		var $element = nextElementSibling($from[i]);
+		if ($element && compareTagName($element, $tagName, $namespace))
+			$results.push($element);
+	}
+};
+
+// attribute selector
+selectors["@"] = function($results, $from, $attributeSelectorID) {
+	var $test = attributeSelectors[$attributeSelectorID].test;
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++)
+		if ($test($element)) $results.push($element);
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+pseudoClasses["first-child"] = function($element) {
+	return !previousElementSibling($element);
+};
+
+pseudoClasses["lang"] = function($element, $code) {
+	$code = new RegExp("^" + $code, "i");
+	while ($element && !$element.getAttribute("lang")) $element = $element.parentNode;
+	return $element && $code.test($element.getAttribute("lang"));
+};
+
+// -----------------------------------------------------------------------
+//  attribute selectors
+// -----------------------------------------------------------------------
+
+// constants
+AttributeSelector.NS_IE = /\\:/g;
+AttributeSelector.PREFIX = "@";
+// properties
+AttributeSelector.tests = {};
+// methods
+AttributeSelector.replace = function($match, $attribute, $namespace, $compare, $value) {
+	var $key = this.PREFIX + $match;
+	if (!attributeSelectors[$key]) {
+		$attribute = this.create($attribute, $compare || "", $value || "");
+		// store the selector
+		attributeSelectors[$key] = $attribute;
+		attributeSelectors.push($attribute);
+	}
+	return attributeSelectors[$key].id;
+};
+AttributeSelector.parse = function($selector) {
+	$selector = $selector.replace(this.NS_IE, "|");
+	var $match;
+	while ($match = $selector.match(this.match)) {
+		var $replace = this.replace($match[0], $match[1], $match[2], $match[3], $match[4]);
+		$selector = $selector.replace(this.match, $replace);
+	}
+	return $selector;
+};
+AttributeSelector.create = function($propertyName, $test, $value) {
+	var $attributeSelector = {};
+	$attributeSelector.id = this.PREFIX + attributeSelectors.length;
+	$attributeSelector.name = $propertyName;
+	$test = this.tests[$test];
+	$test = $test ? $test(this.getAttribute($propertyName), getText($value)) : false;
+	$attributeSelector.test = new Function("e", "return " + $test);
+	return $attributeSelector;
+};
+AttributeSelector.getAttribute = function($name) {
+	switch ($name.toLowerCase()) {
+		case "id":
+			return "e.id";
+		case "class":
+			return "e.className";
+		case "for":
+			return "e.htmlFor";
+		case "href":
+			if (isMSIE) {
+				// IE always returns the full path not the fragment in the href attribute
+				//  so we RegExp it out of outerHTML. Opera does the same thing but there
+				//  is no way to get the original attribute.
+				return "String((e.outerHTML.match(/href=\\x22?([^\\s\\x22]*)\\x22?/)||[])[1]||'')";
+			}
+	}
+	return "e.getAttribute('" + $name.replace($NAMESPACE, ":") + "')";
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests[""] = function($attribute) {
+	return $attribute;
+};
+
+AttributeSelector.tests["="] = function($attribute, $value) {
+	return $attribute + "==" + Quote.add($value);
+};
+
+AttributeSelector.tests["~="] = function($attribute, $value) {
+	return "/(^| )" + regEscape($value) + "( |$)/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["|="] = function($attribute, $value) {
+	return "/^" + regEscape($value) + "(-|$)/.test(" + $attribute + ")";
+};
+
+// -----------------------------------------------------------------------
+//  parsing
+// -----------------------------------------------------------------------
+
+// override parseSelector to parse out attribute selectors
+var _parseSelector = parseSelector;
+parseSelector = function($selector) {
+	return _parseSelector(AttributeSelector.parse($selector));
+};
+
+}); // addModule

Added: jifty/trunk/share/web/static/js/cssquery/cssQuery-level3.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/cssquery/cssQuery-level3.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,150 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+/* Thanks to Bill Edney */
+
+cssQuery.addModule("css-level3", function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// indirect sibling selector
+selectors["~"] = function($results, $from, $tagName, $namespace) {
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++) {
+		while ($element = nextElementSibling($element)) {
+			if (compareTagName($element, $tagName, $namespace))
+				$results.push($element);
+		}
+	}
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+// I'm hoping these pseudo-classes are pretty readable. Let me know if
+//  any need explanation.
+
+pseudoClasses["contains"] = function($element, $text) {
+	$text = new RegExp(regEscape(getText($text)));
+	return $text.test(getTextContent($element));
+};
+
+pseudoClasses["root"] = function($element) {
+	return $element == getDocument($element).documentElement;
+};
+
+pseudoClasses["empty"] = function($element) {
+	var $node, i;
+	for (i = 0; ($node = $element.childNodes[i]); i++) {
+		if (thisElement($node) || $node.nodeType == 3) return false;
+	}
+	return true;
+};
+
+pseudoClasses["last-child"] = function($element) {
+	return !nextElementSibling($element);
+};
+
+pseudoClasses["only-child"] = function($element) {
+	$element = $element.parentNode;
+	return firstElementChild($element) == lastElementChild($element);
+};
+
+pseudoClasses["not"] = function($element, $selector) {
+	var $negated = cssQuery($selector, getDocument($element));
+	for (var i = 0; i < $negated.length; i++) {
+		if ($negated[i] == $element) return false;
+	}
+	return true;
+};
+
+pseudoClasses["nth-child"] = function($element, $arguments) {
+	return nthChild($element, $arguments, previousElementSibling);
+};
+
+pseudoClasses["nth-last-child"] = function($element, $arguments) {
+	return nthChild($element, $arguments, nextElementSibling);
+};
+
+pseudoClasses["target"] = function($element) {
+	return $element.id == location.hash.slice(1);
+};
+
+// UI element states
+
+pseudoClasses["checked"] = function($element) {
+	return $element.checked;
+};
+
+pseudoClasses["enabled"] = function($element) {
+	return $element.disabled === false;
+};
+
+pseudoClasses["disabled"] = function($element) {
+	return $element.disabled;
+};
+
+pseudoClasses["indeterminate"] = function($element) {
+	return $element.indeterminate;
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests["^="] = function($attribute, $value) {
+	return "/^" + regEscape($value) + "/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["$="] = function($attribute, $value) {
+	return "/" + regEscape($value) + "$/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["*="] = function($attribute, $value) {
+	return "/" + regEscape($value) + "/.test(" + $attribute + ")";
+};
+
+// -----------------------------------------------------------------------
+//  nth child support (Bill Edney)
+// -----------------------------------------------------------------------
+
+function nthChild($element, $arguments, $traverse) {
+	switch ($arguments) {
+		case "n": return true;
+		case "even": $arguments = "2n"; break;
+		case "odd": $arguments = "2n+1";
+	}
+
+	var $$children = childElements($element.parentNode);
+	function _checkIndex($index) {
+		var $index = ($traverse == nextElementSibling) ? $$children.length - $index : $index - 1;
+		return $$children[$index] == $element;
+	};
+
+	//	it was just a number (no "n")
+	if (!isNaN($arguments)) return _checkIndex($arguments);
+
+	$arguments = $arguments.split("n");
+	var $multiplier = parseInt($arguments[0]);
+	var $step = parseInt($arguments[1]);
+
+	if ((isNaN($multiplier) || $multiplier == 1) && $step == 0) return true;
+	if ($multiplier == 0 && !isNaN($step)) return _checkIndex($step);
+	if (isNaN($step)) $step = 0;
+
+	var $count = 1;
+	while ($element = $traverse($element)) $count++;
+
+	if (isNaN($multiplier) || $multiplier == 1)
+		return ($traverse == nextElementSibling) ? ($count <= $step) : ($step >= $count);
+
+	return ($count % $multiplier) == $step;
+};
+
+}); // addModule

Added: jifty/trunk/share/web/static/js/cssquery/cssQuery-standard.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/cssquery/cssQuery-standard.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,53 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+cssQuery.addModule("css-standard", function() { // override IE optimisation
+
+// cssQuery was originally written as the CSS engine for IE7. It is
+//  optimised (in terms of size not speed) for IE so this module is
+//  provided separately to provide cross-browser support.
+
+// -----------------------------------------------------------------------
+// browser compatibility
+// -----------------------------------------------------------------------
+
+// sniff for Win32 Explorer
+isMSIE = eval("false;/*@cc_on at if(@\x5fwin32)isMSIE=true at end@*/");
+
+if (!isMSIE) {
+	getElementsByTagName = function($element, $tagName, $namespace) {
+		return $namespace ? $element.getElementsByTagNameNS("*", $tagName) :
+			$element.getElementsByTagName($tagName);
+	};
+
+	compareNamespace = function($element, $namespace) {
+		return !$namespace || ($namespace == "*") || ($element.prefix == $namespace);
+	};
+
+	isXML = document.contentType ? function($element) {
+		return /xml/i.test(getDocument($element).contentType);
+	} : function($element) {
+		return getDocument($element).documentElement.tagName != "HTML";
+	};
+
+	getTextContent = function($element) {
+		// mozilla || opera || other
+		return $element.textContent || $element.innerText || _getTextContent($element);
+	};
+
+	function _getTextContent($element) {
+		var $textContent = "", $node, i;
+		for (i = 0; ($node = $element.childNodes[i]); i++) {
+			switch ($node.nodeType) {
+				case 11: // document fragment
+				case 1: $textContent += _getTextContent($node); break;
+				case 3: $textContent += $node.nodeValue; break;
+			}
+		}
+		return $textContent;
+	};
+}
+}); // addModule

Added: jifty/trunk/share/web/static/js/cssquery/cssQuery.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/cssquery/cssQuery.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,356 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+// the following functions allow querying of the DOM using CSS selectors
+var cssQuery = function() {
+var version = "2.0.2";
+
+// -----------------------------------------------------------------------
+// main query function
+// -----------------------------------------------------------------------
+
+var $COMMA = /\s*,\s*/;
+var cssQuery = function($selector, $$from) {
+try {
+	var $match = [];
+	var $useCache = arguments.callee.caching && !$$from;
+	var $base = ($$from) ? ($$from.constructor == Array) ? $$from : [$$from] : [document];
+	// process comma separated selectors
+	var $$selectors = parseSelector($selector).split($COMMA), i;
+	for (i = 0; i < $$selectors.length; i++) {
+		// convert the selector to a stream
+		$selector = _toStream($$selectors[i]);
+		// faster chop if it starts with id (MSIE only)
+		if (isMSIE && $selector.slice(0, 3).join("") == " *#") {
+			$selector = $selector.slice(2);
+			$$from = _msie_selectById([], $base, $selector[1]);
+		} else $$from = $base;
+		// process the stream
+		var j = 0, $token, $filter, $arguments, $cacheSelector = "";
+		while (j < $selector.length) {
+			$token = $selector[j++];
+			$filter = $selector[j++];
+			$cacheSelector += $token + $filter;
+			// some pseudo-classes allow arguments to be passed
+			//  e.g. nth-child(even)
+			$arguments = "";
+			if ($selector[j] == "(") {
+				while ($selector[j++] != ")" && j < $selector.length) {
+					$arguments += $selector[j];
+				}
+				$arguments = $arguments.slice(0, -1);
+				$cacheSelector += "(" + $arguments + ")";
+			}
+			// process a token/filter pair use cached results if possible
+			$$from = ($useCache && cache[$cacheSelector]) ?
+				cache[$cacheSelector] : select($$from, $token, $filter, $arguments);
+			if ($useCache) cache[$cacheSelector] = $$from;
+		}
+		$match = $match.concat($$from);
+	}
+	delete cssQuery.error;
+	return $match;
+} catch ($error) {
+	cssQuery.error = $error;
+	return [];
+}};
+
+// -----------------------------------------------------------------------
+// public interface
+// -----------------------------------------------------------------------
+
+cssQuery.toString = function() {
+	return "function cssQuery() {\n  [version " + version + "]\n}";
+};
+
+// caching
+var cache = {};
+cssQuery.caching = false;
+cssQuery.clearCache = function($selector) {
+	if ($selector) {
+		$selector = _toStream($selector).join("");
+		delete cache[$selector];
+	} else cache = {};
+};
+
+// allow extensions
+var modules = {};
+var loaded = false;
+cssQuery.addModule = function($name, $script) {
+	if (loaded) eval("$script=" + String($script));
+	modules[$name] = new $script();;
+};
+
+// hackery
+cssQuery.valueOf = function($code) {
+	return $code ? eval($code) : this;
+};
+
+// -----------------------------------------------------------------------
+// declarations
+// -----------------------------------------------------------------------
+
+var selectors = {};
+var pseudoClasses = {};
+// a safari bug means that these have to be declared here
+var AttributeSelector = {match: /\[([\w-]+(\|[\w-]+)?)\s*(\W?=)?\s*([^\]]*)\]/};
+var attributeSelectors = [];
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// descendant selector
+selectors[" "] = function($results, $from, $tagName, $namespace) {
+	// loop through current selection
+	var $element, i, j;
+	for (i = 0; i < $from.length; i++) {
+		// get descendants
+		var $subset = getElementsByTagName($from[i], $tagName, $namespace);
+		// loop through descendants and add to results selection
+		for (j = 0; ($element = $subset[j]); j++) {
+			if (thisElement($element) && compareNamespace($element, $namespace))
+				$results.push($element);
+		}
+	}
+};
+
+// ID selector
+selectors["#"] = function($results, $from, $id) {
+	// loop through current selection and check ID
+	var $element, j;
+	for (j = 0; ($element = $from[j]); j++) if ($element.id == $id) $results.push($element);
+};
+
+// class selector
+selectors["."] = function($results, $from, $className) {
+	// create a RegExp version of the class
+	$className = new RegExp("(^|\\s)" + $className + "(\\s|$)");
+	// loop through current selection and check class
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++)
+		if ($className.test($element.className)) $results.push($element);
+};
+
+// pseudo-class selector
+selectors[":"] = function($results, $from, $pseudoClass, $arguments) {
+	// retrieve the cssQuery pseudo-class function
+	var $test = pseudoClasses[$pseudoClass], $element, i;
+	// loop through current selection and apply pseudo-class filter
+	if ($test) for (i = 0; ($element = $from[i]); i++)
+		// if the cssQuery pseudo-class function returns "true" add the element
+		if ($test($element, $arguments)) $results.push($element);
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+pseudoClasses["link"] = function($element) {
+	var $document = getDocument($element);
+	if ($document.links) for (var i = 0; i < $document.links.length; i++) {
+		if ($document.links[i] == $element) return true;
+	}
+};
+
+pseudoClasses["visited"] = function($element) {
+	// can't do this without jiggery-pokery
+};
+
+// -----------------------------------------------------------------------
+// DOM traversal
+// -----------------------------------------------------------------------
+
+// IE5/6 includes comments (LOL) in it's elements collections.
+// so we have to check for this. the test is tagName != "!". LOL (again).
+var thisElement = function($element) {
+	return ($element && $element.nodeType == 1 && $element.tagName != "!") ? $element : null;
+};
+
+// return the previous element to the supplied element
+//  previousSibling is not good enough as it might return a text or comment node
+var previousElementSibling = function($element) {
+	while ($element && ($element = $element.previousSibling) && !thisElement($element)) continue;
+	return $element;
+};
+
+// return the next element to the supplied element
+var nextElementSibling = function($element) {
+	while ($element && ($element = $element.nextSibling) && !thisElement($element)) continue;
+	return $element;
+};
+
+// return the first child ELEMENT of an element
+//  NOT the first child node (though they may be the same thing)
+var firstElementChild = function($element) {
+	return thisElement($element.firstChild) || nextElementSibling($element.firstChild);
+};
+
+var lastElementChild = function($element) {
+	return thisElement($element.lastChild) || previousElementSibling($element.lastChild);
+};
+
+// return child elements of an element (not child nodes)
+var childElements = function($element) {
+	var $childElements = [];
+	$element = firstElementChild($element);
+	while ($element) {
+		$childElements.push($element);
+		$element = nextElementSibling($element);
+	}
+	return $childElements;
+};
+
+// -----------------------------------------------------------------------
+// browser compatibility
+// -----------------------------------------------------------------------
+
+// all of the functions in this section can be overwritten. the default
+//  configuration is for IE. The functions below reflect this. standard
+//  methods are included in a separate module. It would probably be better
+//  the other way round of course but this makes it easier to keep IE7 trim.
+
+var isMSIE = true;
+
+var isXML = function($element) {
+	var $document = getDocument($element);
+	return (typeof $document.mimeType == "unknown") ?
+		/\.xml$/i.test($document.URL) :
+		Boolean($document.mimeType == "XML Document");
+};
+
+// return the element's containing document
+var getDocument = function($element) {
+	return $element.ownerDocument || $element.document;
+};
+
+var getElementsByTagName = function($element, $tagName) {
+	return ($tagName == "*" && $element.all) ? $element.all : $element.getElementsByTagName($tagName);
+};
+
+var compareTagName = function($element, $tagName, $namespace) {
+	if ($tagName == "*") return thisElement($element);
+	if (!compareNamespace($element, $namespace)) return false;
+	if (!isXML($element)) $tagName = $tagName.toUpperCase();
+	return $element.tagName == $tagName;
+};
+
+var compareNamespace = function($element, $namespace) {
+	return !$namespace || ($namespace == "*") || ($element.scopeName == $namespace);
+};
+
+var getTextContent = function($element) {
+	return $element.innerText;
+};
+
+function _msie_selectById($results, $from, id) {
+	var $match, i, j;
+	for (i = 0; i < $from.length; i++) {
+		if ($match = $from[i].all.item(id)) {
+			if ($match.id == id) $results.push($match);
+			else if ($match.length != null) {
+				for (j = 0; j < $match.length; j++) {
+					if ($match[j].id == id) $results.push($match[j]);
+				}
+			}
+		}
+	}
+	return $results;
+};
+
+// for IE5.0
+if (![].push) Array.prototype.push = function() {
+	for (var i = 0; i < arguments.length; i++) {
+		this[this.length] = arguments[i];
+	}
+	return this.length;
+};
+
+// -----------------------------------------------------------------------
+// query support
+// -----------------------------------------------------------------------
+
+// select a set of matching elements.
+// "from" is an array of elements.
+// "token" is a character representing the type of filter
+//  e.g. ">" means child selector
+// "filter" represents the tag name, id or class name that is being selected
+// the function returns an array of matching elements
+var $NAMESPACE = /\|/;
+function select($$from, $token, $filter, $arguments) {
+	if ($NAMESPACE.test($filter)) {
+		$filter = $filter.split($NAMESPACE);
+		$arguments = $filter[0];
+		$filter = $filter[1];
+	}
+	var $results = [];
+	if (selectors[$token]) {
+		selectors[$token]($results, $$from, $filter, $arguments);
+	}
+	return $results;
+};
+
+// -----------------------------------------------------------------------
+// parsing
+// -----------------------------------------------------------------------
+
+// convert css selectors to a stream of tokens and filters
+//  it's not a real stream. it's just an array of strings.
+var $STANDARD_SELECT = /^[^\s>+~]/;
+var $$STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
+function _toStream($selector) {
+	if ($STANDARD_SELECT.test($selector)) $selector = " " + $selector;
+	return $selector.match($$STREAM) || [];
+};
+
+var $WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
+var $IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
+var parseSelector = function($selector) {
+	return $selector
+	// trim whitespace
+	.replace($WHITESPACE, "$1")
+	// e.g. ".class1" --> "*.class1"
+	.replace($IMPLIED_ALL, "$1*$2");
+};
+
+var Quote = {
+	toString: function() {return "'"},
+	match: /^('[^']*')|("[^"]*")$/,
+	test: function($string) {
+		return this.match.test($string);
+	},
+	add: function($string) {
+		return this.test($string) ? $string : this + $string + this;
+	},
+	remove: function($string) {
+		return this.test($string) ? $string.slice(1, -1) : $string;
+	}
+};
+
+var getText = function($text) {
+	return Quote.remove($text);
+};
+
+var $ESCAPE = /([\/()[\]?{}|*+-])/g;
+function regEscape($string) {
+	return $string.replace($ESCAPE, "\\$1");
+};
+
+// -----------------------------------------------------------------------
+// modules
+// -----------------------------------------------------------------------
+
+// -------- >>      insert modules here for packaging       << -------- \\
+
+loaded = true;
+
+// -----------------------------------------------------------------------
+// return the query function
+// -----------------------------------------------------------------------
+
+return cssQuery;
+
+}(); // cssQuery

Modified: jifty/trunk/share/web/static/js/jifty.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty.js	(original)
+++ jifty/trunk/share/web/static/js/jifty.js	Tue May 23 11:02:05 2006
@@ -80,7 +80,7 @@
         for (var i = 0; i < fields.length; i++) {
             var f = fields[i];
 
-            if ((Form.Element.getType(f) != "registration") && Form.Element.getValue(f)) {
+            if ((Form.Element.getType(f) != "registration") && (Form.Element.getValue(f) != null)) {
                 if (! a['fields'][Form.Element.getField(f)])
                     a['fields'][Form.Element.getField(f)] = {};
                 a['fields'][Form.Element.getField(f)][Form.Element.getType(f)] = Form.Element.getValue(f);
@@ -417,7 +417,7 @@
         // Find where we are going to go
         var element = $('region-' + f['region']);
         if (f['element']) {
-            var possible = document.getElementsBySelector(f['element']);
+            var possible = cssQuery(f['element']);
             if (possible.length == 0)
                 element = null;
             else

Added: jifty/trunk/share/web/static/js/jifty_utils.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jifty_utils.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,36 @@
+
+if (typeof Jifty == "undefined") Jifty = { };
+
+Jifty.Utils = {
+    /* From 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;
+    }
+};
+

Added: jifty/trunk/share/web/static/js/jsan/DOM/Events.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/DOM/Events.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,262 @@
+/**
+
+=head1 NAME
+
+DOM.Events - Event registration abstraction layer
+
+=head1 SYNOPSIS
+
+  JSAN.use("DOM.Events");
+
+  function handleClick(e) {
+      e.currentTarget.style.backgroundColor = "#68b";
+  }
+
+  DOM.Events.addListener(window, "load", function () {
+      alert("The page is loaded.");
+  });
+
+  DOM.Events.addListener(window, "load", function () {
+      // this listener won't interfere with the first one
+      var divs = document.getElementsByTagName("div");
+      for(var i=0; i<divs.length; i++) {
+          DOM.Events.addListener(divs[i], "click", handleClick);
+      }
+  });
+
+=head1 DESCRIPTION
+
+This library lets you use a single interface to listen for and handle all DOM events
+to reduce browser-specific code branching.  It also helps in dealing with Internet
+Explorer's memory leak problem by automatically unsetting all event listeners when
+the page is unloaded (for IE only).
+
+=cut
+
+*/
+
+(function () {
+	if(typeof DOM == "undefined") DOM = {};
+	DOM.Events = {};
+	
+    DOM.Events.VERSION = "0.02";
+	DOM.Events.EXPORT = [];
+	DOM.Events.EXPORT_OK = ["addListener", "removeListener"];
+	DOM.Events.EXPORT_TAGS = {
+		":common": DOM.Events.EXPORT,
+		":all": [].concat(DOM.Events.EXPORT, DOM.Events.EXPORT_OK)
+	};
+	
+	// list of event listeners set by addListener
+	// offset 0 is null to prevent 0 from being used as a listener identifier
+	var listenerList = [null];
+	
+/**
+
+=head2 Functions
+
+All functions are kept inside the namespace C<DOM.Events> and aren't exported
+automatically.
+
+=head3 addListener( S<I<HTMLElement> element,> S<I<string> eventType,>
+S<I<Function> handler> S<[, I<boolean> makeCompatible = true] )>
+
+Registers an event listener/handler on an element.  The C<eventType> string should
+I<not> be prefixed with "on" (e.g. "mouseover" not "onmouseover"). If C<makeCompatible>
+is C<true> (the default), the handler is put inside a wrapper that lets you handle the
+events using parts of the DOM Level 2 Events model, even in Internet Explorer (and
+behave-alikes). Specifically:
+
+=over
+
+=item *
+
+The event object is passed as the first argument to the event handler, so you don't
+have to access it through C<window.event>.
+
+=item *
+
+The event object has the properties C<target>, C<currentTarget>, and C<relatedTarget>
+and the methods C<preventDefault()> and C<stopPropagation()> that behave as described
+in the DOM Level 2 Events specification (for the most part).
+
+=item *
+
+If possible, the event object for mouse events will have the properties C<pageX> and
+C<pageY> that contain the mouse's position relative to the document at the time the
+event occurred.
+
+=item *
+
+If you attempt to set a duplicate event handler on an element, the duplicate will
+still be added (this is different from the DOM2 Events model, where duplicates are
+discarded).
+
+=back
+
+If C<makeCompatible> is C<false>, the arguments are simply passed to the browser's
+native event registering facilities, which means you'll have to deal with event
+incompatibilities yourself. However, if you don't need to access the event information,
+doing it this way can be slightly faster and it gives you the option of unsetting the
+handler with a different syntax (see below).
+
+The return value is a positive integer identifier for the listener that can be used to
+unregister it later on in your script.
+
+=cut
+
+*/
+    
+	DOM.Events.addListener = function(elt, ev, func, makeCompatible) {
+		var usedFunc = func;
+        var id = listenerList.length;
+		if(makeCompatible == true || makeCompatible == undefined) {
+			usedFunc = makeCompatibilityWrapper(elt, ev, func);
+		}
+		if(elt.addEventListener) {
+			elt.addEventListener(ev, usedFunc, false);
+			listenerList[id] = [elt, ev, usedFunc];
+			return id;
+		}
+		else if(elt.attachEvent) {
+			elt.attachEvent("on" + ev, usedFunc);
+			listenerList[id] = [elt, ev, usedFunc];
+			return id;
+		}
+		else return false;
+	};
+	
+/**
+
+=head3 removeListener( S<I<integer> identifier> )
+
+Unregisters the event listener associated with the given identifier so that it will
+no longer be called when the event fires.
+
+  var listener = DOM.Events.addListener(myElement, "mousedown", myHandler);
+  // later on ...
+  DOM.Events.removeListener(listener);
+
+=head3 removeListener( S<I<HTMLElement> element,> S<I<string> eventType,> S<I<Function> handler )>
+
+This alternative syntax can be also be used to unset an event listener, but it can only
+be used if C<makeCompatible> was C<false> when it was set.
+
+=cut
+
+*/
+
+	DOM.Events.removeListener = function() {
+		var elt, ev, func;
+		if(arguments.length == 1 && listenerList[arguments[0]]) {
+			elt  = listenerList[arguments[0]][0];
+			ev   = listenerList[arguments[0]][1];
+			func = listenerList[arguments[0]][2];
+			delete listenerList[arguments[0]];
+		}
+		else if(arguments.length == 3) {
+			elt  = arguments[0];
+			ev   = arguments[1];
+			func = arguments[2];
+		}
+		else return;
+		
+		if(elt.removeEventListener) {
+			elt.removeEventListener(ev, func, false);
+		}
+		else if(elt.detachEvent) {
+			elt.detachEvent("on" + ev, func);
+		}
+	};
+	
+    var rval;
+    
+    function makeCompatibilityWrapper(elt, ev, func) {
+        return function (e) {
+            rval = true;
+            if(e == undefined && window.event != undefined)
+                e = window.event;
+            if(e.target == undefined && e.srcElement != undefined)
+                e.target = e.srcElement;
+            if(e.currentTarget == undefined)
+                e.currentTarget = elt;
+            if(e.relatedTarget == undefined) {
+                if(ev == "mouseover" && e.fromElement != undefined)
+                    e.relatedTarget = e.fromElement;
+                else if(ev == "mouseout" && e.toElement != undefined)
+                    e.relatedTarget = e.toElement;
+            }
+            if(e.pageX == undefined) {
+                if(document.body.scrollTop != undefined) {
+                    e.pageX = e.clientX + document.body.scrollLeft;
+                    e.pageY = e.clientY + document.body.scrollTop;
+                }
+                if(document.documentElement != undefined
+                && document.documentElement.scrollTop != undefined) {
+                    if(document.documentElement.scrollTop > 0
+                    || document.documentElement.scrollLeft > 0) {
+                        e.pageX = e.clientX + document.documentElement.scrollLeft;
+                        e.pageY = e.clientY + document.documentElement.scrollTop;
+                    }
+                }
+            }
+            if(e.stopPropagation == undefined)
+                e.stopPropagation = IEStopPropagation;
+            if(e.preventDefault == undefined)
+                e.preventDefault = IEPreventDefault;
+            if(e.cancelable == undefined) e.cancelable = true;
+            func(e);
+            return rval;
+        };
+    }
+    
+    function IEStopPropagation() {
+        if(window.event) window.event.cancelBubble = true;
+    }
+    
+    function IEPreventDefault() {
+        rval = false;
+    }
+
+	function cleanUpIE () {
+		for(var i=0; i<listenerList.length; i++) {
+			var listener = listenerList[i];
+			if(listener) {
+				var elt = listener[0];
+                var ev = listener[1];
+                var func = listener[2];
+				elt.detachEvent("on" + ev, func);
+			}
+		}
+        listenerList = null;
+	}
+
+	if(!window.addEventListener && window.attachEvent) {
+		window.attachEvent("onunload", cleanUpIE);
+	}
+
+})();
+
+/**
+
+=head1 SEE ALSO
+
+DOM Level 2 Events Specification,
+L<http://www.w3.org/TR/DOM-Level-2-Events/>
+
+Understanding and Solving Internet Explorer Leak Patterns,
+L<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp>
+
+=head1 AUTHOR
+
+Justin Constantino, <F<goflyapig at gmail.com>>.
+
+=head1 COPYRIGHT
+
+  Copyright (c) 2005 Justin Constantino.  All rights reserved.
+  This module is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public Licence.
+
+=cut
+
+*/
\ No newline at end of file

Added: jifty/trunk/share/web/static/js/jsan/JSAN.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/JSAN.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,303 @@
+/*
+ * This is not a stock JSAN.js.  To increase browser compatibility,
+ * s/'/"/g has been applied to the file and the range of valid response
+ * codes for JSAN.Request has been expanded (lifted from Prototype, in
+ * fact).
+ */
+
+/*
+
+*/
+
+var JSAN = function () { JSAN.addRepository(arguments) };
+
+JSAN.VERSION = "0.10-jifty2";
+
+/*
+
+*/
+
+JSAN.globalScope   = self;
+JSAN.includePath   = [".", "lib"];
+JSAN.errorLevel    = "none";
+JSAN.errorMessage  = "";
+JSAN.loaded        = {};
+
+/*
+
+*/
+
+JSAN.use = function () {
+    var classdef = JSAN.require(arguments[0]);
+    if (!classdef) return null;
+
+    var importList = JSAN._parseUseArgs.apply(JSAN, arguments).importList;
+    JSAN.exporter(classdef, importList);
+
+    return classdef;
+}
+
+/*
+
+*/
+
+JSAN.require = function (pkg) {
+    var path = JSAN._convertPackageToPath(pkg);
+    if (JSAN.loaded[path]) {
+        return JSAN.loaded[path];
+    }
+
+    try {
+        var classdef = eval(pkg);
+        if (typeof classdef != "undefined") return classdef;
+    } catch (e) { /* nice try, eh? */ }
+
+
+    for (var i = 0; i < JSAN.includePath.length; i++) {
+        var js;
+        try{
+            var url = JSAN._convertPathToUrl(path, JSAN.includePath[i]);
+                js  = JSAN._loadJSFromUrl(url);
+        } catch (e) {
+            if (i == JSAN.includePath.length - 1) throw e;
+        }
+        if (js != null) {
+            var classdef = JSAN._createScript(js, pkg);
+            JSAN.loaded[path] = classdef;
+            return classdef;
+        }
+    }
+    return false;
+
+}
+
+/*
+
+*/
+
+JSAN.exporter = function () {
+    JSAN._exportItems.apply(JSAN, arguments);
+}
+
+/*
+
+*/
+
+JSAN.addRepository = function () {
+    var temp = JSAN._flatten( arguments );
+    // Need to go in reverse to do something as simple as unshift( @foo, @_ );
+    for ( var i = temp.length - 1; i >= 0; i-- )
+        JSAN.includePath.unshift(temp[i]);
+    return JSAN;
+}
+
+JSAN._flatten = function( list1 ) {
+    var list2 = new Array();
+    for ( var i = 0; i < list1.length; i++ ) {
+        if ( typeof list1[i] == "object" ) {
+            list2 = JSAN._flatten( list1[i], list2 );
+        }
+        else {
+            list2.push( list1[i] );
+        }
+    }
+    return list2;
+};
+
+JSAN._findMyPath = function () {
+    if (document) {
+        var scripts = document.getElementsByTagName("script");
+        for ( var i = 0; i < scripts.length; i++ ) {
+            var src = scripts[i].getAttribute("src");
+            if (src) {
+                var inc = src.match(/^(.*?)\/?JSAN.js/);
+                if (inc && inc[1]) {
+                    var repo = inc[1];
+                    for (var j = 0; j < JSAN.includePath.length; j++) {
+                        if (JSAN.includePath[j] == repo) {
+                            return;
+                        }
+                    }
+                    JSAN.addRepository(repo);
+                }
+            }
+        }
+    }
+}
+JSAN._findMyPath();
+
+JSAN._convertPathToUrl = function (path, repository) {
+    return repository.concat("/" + path);
+};
+    
+
+JSAN._convertPackageToPath = function (pkg) {
+    var path = pkg.replace(/\./g, "/");
+        path = path.concat(".js");
+    return path;
+}
+
+JSAN._parseUseArgs = function () {
+    var pkg        = arguments[0];
+    var importList = [];
+
+    for (var i = 1; i < arguments.length; i++)
+        importList.push(arguments[i]);
+
+    return {
+        pkg:        pkg,
+        importList: importList
+    }
+}
+
+JSAN._loadJSFromUrl = function (url) {
+    return new JSAN.Request().getText(url);
+}
+
+JSAN._findExportInList = function (list, request) {
+    if (list == null) return false;
+    for (var i = 0; i < list.length; i++)
+        if (list[i] == request)
+            return true;
+    return false;
+}
+
+JSAN._findExportInTag = function (tags, request) {
+    if (tags == null) return [];
+    for (var i in tags)
+        if (i == request)
+            return tags[i];
+    return [];
+}
+
+JSAN._exportItems = function (classdef, importList) {
+    var exportList  = new Array();
+    var EXPORT      = classdef.EXPORT;
+    var EXPORT_OK   = classdef.EXPORT_OK;
+    var EXPORT_TAGS = classdef.EXPORT_TAGS;
+    
+    if (importList.length > 0) {
+       importList = JSAN._flatten( importList );
+
+       for (var i = 0; i < importList.length; i++) {
+            var request = importList[i];
+            if (   JSAN._findExportInList(EXPORT,    request)
+                || JSAN._findExportInList(EXPORT_OK, request)) {
+                exportList.push(request);
+                continue;
+            }
+            var list = JSAN._findExportInTag(EXPORT_TAGS, request);
+            for (var i = 0; i < list.length; i++) {
+                exportList.push(list[i]);
+            }
+        }
+    } else {
+        exportList = EXPORT;
+    }
+    JSAN._exportList(classdef, exportList);
+}
+
+JSAN._exportList = function (classdef, exportList) {
+    if (typeof(exportList) != "object") return null;
+    for (var i = 0; i < exportList.length; i++) {
+        var name = exportList[i];
+
+        if (JSAN.globalScope[name] == null)
+            JSAN.globalScope[name] = classdef[name];
+    }
+}
+
+JSAN._makeNamespace = function(js, pkg) {
+    var spaces = pkg.split(".");
+    var parent = JSAN.globalScope;
+    eval(js);
+    var classdef = eval(pkg);
+    for (var i = 0; i < spaces.length; i++) {
+        var name = spaces[i];
+        if (i == spaces.length - 1) {
+            if (typeof parent[name] == "undefined") {
+                parent[name] = classdef;
+                if ( typeof classdef["prototype"] != "undefined" ) {
+                    parent[name].prototype = classdef.prototype;
+                }
+            }
+        } else {
+            if (parent[name] == undefined) {
+                parent[name] = {};
+            }
+        }
+
+        parent = parent[name];
+    }
+    return classdef;
+}
+
+JSAN._handleError = function (msg, level) {
+    if (!level) level = JSAN.errorLevel;
+    JSAN.errorMessage = msg;
+
+    switch (level) {
+        case "none":
+            break;
+        case "warn":
+            alert(msg);
+            break;
+        case "die":
+        default:
+            throw new Error(msg);
+            break;
+    }
+}
+
+JSAN._createScript = function (js, pkg) {
+    try {
+        return JSAN._makeNamespace(js, pkg);
+    } catch (e) {
+        JSAN._handleError("Could not create namespace[" + pkg + "]: " + e);
+    }
+    return null;
+}
+
+
+JSAN.prototype = {
+    use: function () { JSAN.use.apply(JSAN, arguments) }
+};
+
+
+// Low-Level HTTP Request
+JSAN.Request = function (jsan) {
+    if (JSAN.globalScope.XMLHttpRequest) {
+        this._req = new XMLHttpRequest();
+    } else {
+        this._req = new ActiveXObject("Microsoft.XMLHTTP");
+    }
+}
+
+JSAN.Request.prototype = {
+    _req:  null,
+    
+    getText: function (url) {
+        this._req.open("GET", url, false);
+        try {
+            this._req.send(null);
+            if ( this.responseIsSuccess() )
+                return this._req.responseText;
+        } catch (e) {
+            JSAN._handleError("File not found: " + url);
+            return null;
+        };
+
+        JSAN._handleError("File not found: " + url);
+        return null;
+    },
+
+    responseIsSuccess: function() {
+        return this._req.status == undefined
+            || this._req.status == 0
+            || (this._req.status >= 200 && this._req.status < 300);
+    }
+};
+
+/*
+
+*/

Added: jifty/trunk/share/web/static/js/jsan/Upgrade.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/Upgrade.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,121 @@
+/*
+Keep package scanners happy
+Upgrade.VERSION = 0.04;
+*/
+
+/*
+
+=head1 NAME
+
+Upgrade - Upgrade older JavaScript implementations to support newer features
+
+=head1 SYNOPSIS
+
+  // Upgrade Array.push if the client does not have it
+  JSAN.use("Upgrade.Array.push");
+
+=head1 DESCRIPTION
+
+Many many different JavaScript toolkits start with something like the following:
+
+  // Provides Array.push for implementations that don't have it
+  if ( ! Array.prototype.push ) {
+      Array.prototype.push = function () {
+          var l = this.length;
+          for ( var i = 0; i < arguments.length; i++ ) {
+              this[l+i] = arguments[i];
+          }
+          return this.length;
+      }
+  }
+
+These provide implementations of expected or required functions/classes
+for older JavaScript implementations that do not provide them natively,
+in effect "upgrading" the client at run-time.
+
+In fact, due to its flexibility JavaScript is a language ideally suited
+to this sort of behaviour.
+
+C<Upgrade> is a JSAN package that provides standard implementations for
+many of these standard functions. If your code relies on a particular
+function that you later find to be not as common as you might have
+initially thought, you can simply add a dependency on that function within
+the C<Upgrade> namespace, and if an implementation exists the standard code
+to implement it will be added it the current environment (when it doesn't
+already have it).
+
+Rather than one huge file that provides a "compatibility layer" and upgrades
+verything all at once, C<Upgrade> is broken down into a large number of
+maller .js files, each implementing one function or class.
+
+Generally these functions are ones defined in the ECMA standard, and those
+that aren't, such as C<HTMLHttpRequest> are not provided by Upgrade (as much
+as we would like to) :)
+
+=head1 USING UPGRADE
+
+The C<Upgrade> namespace acts as a parallel root to the global namespace.
+For any function you want to upgrade, you can then simply prepend
+C<"Upgrade"> to it.
+
+For example, to do the very common upgrade for the C<Array.push> function,
+you simply add C<JSAN.use("Upgrade.Array.push") to your module (or manually
+load the C<Upgrade/Array/push.js> file).
+
+One advantage of using these standard implementations rather than your own
+is that when a number of modules with C<Upgrade> depedencies are merged
+together by C<JSAN::Concat> or another package merger it results in only
+a single copy of the upgrading code at the appropriate place in the code.
+
+=head1 UPGRADABLE FUNCTIONS
+
+While implementations are provided seperately, rather than document them
+this way we will instead defined all functions available in the C<Upgrade>
+package here.
+
+=head2 Upgrade.Array.push
+
+This provides the same standard implementation of the instance method
+C<Array.push> (located at Array.prototype.push) as used by all of the
+major frameworks.
+
+=head2 Upgrade.Function.apply
+
+This provides a version of the instance method C<Function.apply>
+(located at C<Function.prototype.apply>) adapted from an implementation
+found in the L<Prototype> framework, which was itself adapted from an
+implementation found on L<http://www.youngpup.net/>.
+
+=head1 METHODS
+
+The C<Upgrade> module itself does not at this time provide any
+functionality, and only acts as a source of documentation.
+
+Likewise, nothing is ever actually created at or beneath the C<Upgrade>
+namespace, but serves as an address mechanism for determining which
+.js files to load. Each of these files only inserts functions into the
+core tree and do not create any additional useless namespace variables.
+
+=head1 SUPPORT
+
+Until the JSAN RT gains package-specific queues, bugs or new functions
+to add to Upgrade should be reported to the jsan-authors mailing list.
+
+For B<non-support> issues or questions, contact the author.
+
+=head1 AUTHOR
+
+Adam Kennedy <jsan at ali.as>, L<http://ali.as/>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2005 Adam Kennedy. All rights reserved.
+This program is free software; you can redistribute it and/or modify
+it under the the terms of the Perl dual GPL/Artistic license.
+
+The full text of the license can be found in the
+LICENSE file included with this package
+
+=cut
+
+*/

Added: jifty/trunk/share/web/static/js/jsan/Upgrade/Array/push.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/Upgrade/Array/push.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,14 @@
+/*
+Upgrade.Array.push.VERSION = 0.04;
+*/
+
+// Provides Array.push for implementations that don't have it
+if ( ! Array.prototype.push ) {
+	Array.prototype.push = function () {
+		var l = this.length;
+		for ( var i = 0; i < arguments.length; i++ ) {
+			this[l+i] = arguments[i];
+		}
+		return this.length;
+	}
+}

Added: jifty/trunk/share/web/static/js/jsan/Upgrade/Function/apply.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/Upgrade/Function/apply.js	Tue May 23 11:02:05 2006
@@ -0,0 +1,19 @@
+/*
+Upgrade.Function.Apply.VERSION = 0.04;
+*/
+// Adapted from a Prototype adaptation of code
+// originally from http://www.youngpup.net/
+if ( ! Function.prototype.apply ) {
+	Function.prototype.apply = function(o, p) {
+		var pstr = new Array();
+		if ( ! o ) o = window;
+		if ( ! p ) p = new Array();
+		for ( var i = 0; i < p.length; i++ ) {
+			pstr[i] = 'p[' + i + ']';
+		}
+    		o.__apply__ = this;
+		var rv = eval('o.__apply__(' + pstr[i].join(', ') + ')' );
+		o.__apply__ = null;
+		return rv;
+	}
+}

Modified: jifty/trunk/share/web/static/js/key_bindings.js
==============================================================================
--- jifty/trunk/share/web/static/js/key_bindings.js	(original)
+++ jifty/trunk/share/web/static/js/key_bindings.js	Tue May 23 11:02:05 2006
@@ -1,69 +1,112 @@
-// Copyright 2004-2005, Best Practical Solutions, LLC
+// Copyright 2004-2006, Best Practical Solutions, LLC
 // This Library is licensed to you under the same terms as Perl 5.x
 
-var bindings = Array;
-
-document.onkeydown = doClick;
-function doClick(e) {
-    var targ;
-        if (!e) var e = window.event;
-            if (e.target) targ = e.target;
-            else if (e.srcElement) targ = e.srcElement;
-        if (targ.nodeType == 3) // defeat Safari bug
-                targ = targ.parentNode;
-   
-   // safari or mozilla
-   if ( ( ! e.metaKey && ! e.altKey &&  ! e.ctrlKey )
-        && (
-        (targ == document.body) || 
-       (targ ==  document.getElementsByTagName('html')[0])  
-        ) ){
-	var code = String.fromCharCode(e.keyCode);
-    var binding = getKeyBinding(code);
-   if (binding) {
-   if (binding["action"] == "goto") {
-        document.location = (binding["data"]);
-    } 
-   else if (binding["action"] == "focus") {
-      var elements = document.getElementsByName(binding["data"]);
-       elements[0].focus();
-    }
-   else if (binding["action"] == "click") {
-      var elements = document.getElementsByName(binding["data"]);
-       elements[0].click();
-    }
-
- }     
-
-}
-}
-
-function addKeyBinding(key, action, data, label) {
-    var binding = new Array;
-    binding["action"] = action;
-    binding["data"] = data;
-    binding["label"] = label;
-    bindings[key] = binding;
-}
+JSAN.use("DOM.Events");
 
+if ( typeof Jifty == "undefined" ) Jifty = {};
 
-function getKeyBinding(key) {
-    return(bindings[key]);
+Jifty.KeyBindings = {
+    bindings: new Array(),
+    listener: null,
+
+    activate: function() {
+        if ( Jifty.KeyBindings.listener )
+            return;
+        
+        Jifty.KeyBindings.listener = DOM.Events.addListener(
+                                        document,
+                                        "keydown",
+                                        Jifty.KeyBindings.doClick
+                                     );
+    },
+
+    deactivate: function() {
+        DOM.Events.removeListener(Jifty.KeyBindings.listener);
+    },
+
+    doClick: function(e) {
+        if (e.target.nodeType == 3) // defeat Safari bug
+            e.target = e.target.parentNode;
+       
+        /* XXX TODO: Is there a better way to do this and still support
+                     opera?
+         */            
+        if (    !e.metaKey && !e.altKey && !e.ctrlKey
+             && !e.target.nodeName.match(/^(INPUT|TEXTAREA)$/) )
+        {
+            var code    = String.fromCharCode(e.keyCode);
+            var binding = Jifty.KeyBindings.get(code);
+            
+            if (binding) {
+                e.preventDefault();
+                
+                if (binding["action"] == "goto") {
+                    document.location = (binding["data"]);
+                }
+                else if (binding["action"] == "focus") {
+                    var elements = document.getElementsByName(binding["data"]);
+                    elements[0].focus();
+                }
+                else if (binding["action"] == "click") {
+                    var elements = document.getElementsByName(binding["data"]);
+                    elements[0].click();
+                }
+            }
+        }
+    },
+
+    add: function(key, action, data, label) {
+        var binding = new Array();
+        binding["action"]  = action;
+        binding["data"]    = data;
+        binding["label"]   = label;
+        Jifty.KeyBindings.bindings[key] = binding;
+    },
+
+    get: function(key) {
+        return Jifty.KeyBindings.bindings[key];
+    },
+
+    writeLegend: function(e) {
+        if (    !document.createElement
+             || !document.createTextNode
+             || Element.hasClassName(e, 'keybindings-written') )
+            return;
+        
+        
+        /* definition list */
+        var dl = document.createElement("dl");
+        dl.setAttribute("class", "keybindings");
+
+    
+        /* terms of the list */
+        
+        for (var key in Jifty.KeyBindings.bindings) {
+            if ( Jifty.KeyBindings.get(key)["label"] ) {
+                var div = document.createElement("div");
+                div.setAttribute("class", "keybinding");
+                
+                var dt = document.createElement("dt");
+                dt.appendChild( document.createTextNode( key ) );
+
+                var dd = document.createElement("dd");
+                dd.appendChild( document.createTextNode( Jifty.KeyBindings.get(key)["label"] ) );
+                
+                div.appendChild( dt );
+                div.appendChild( dd );
+                dl.appendChild( div );
+            }
+        }
+        
+        if ( dl.hasChildNodes() ) {
+            e.appendChild( dl );
+            Element.addClassName(e, 'keybindings-written');
+        
+            /* since we wrote the legend, now obey it */
+            Jifty.KeyBindings.activate();
+        }
+    }
 }
 
+Behaviour.register({ "div.keybindings": Jifty.KeyBindings.writeLegend });
 
-function writeKeyBindingLegend() {
-    var content = '';
-    for  (var key in bindings) {
-    if ( bindings[key]['label']) {
-    content = content + '<div class="keybinding"><dt>'+key + '</dt>' +'<dd>'+bindings[key]['label'] +'</dd></div>'; 
-    }
-    }
-    if (content) {
-    document.write('<div class="keybindings">');
-    document.write('<dl class="keybindings">');
-    document.write(content);
-    document.write('</dl>');
-    document.write('</div>');
-    }
-}

Modified: jifty/trunk/share/web/static/js/rico.js
==============================================================================
--- jifty/trunk/share/web/static/js/rico.js	(original)
+++ jifty/trunk/share/web/static/js/rico.js	Tue May 23 11:02:05 2006
@@ -1,3 +1,12 @@
+/*
+ * This is a slightly modified version of Rico which has improved
+ * corner rounding methods that try to take care of preserving padding.
+ * 
+ * Replacing it with a stock Rico probably isn't a good idea unless you
+ * repatch it or don't plan on using any of the corner rounding.
+ *
+ */
+
 /**
   *
   *  Copyright 2005 Sabre Airline Solutions
@@ -16,7 +25,7 @@
 
 //-------------------- rico.js
 var Rico = {
-  Version: '1.1.2',
+  Version: '1.1.2.jifty.r963',
   prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
 }
 
@@ -785,7 +794,7 @@
    },
 
    _roundTopCorners: function(el, color, bgColor) {
-      var corner = this._createCorner(bgColor);
+      var corner = this._createCorner(el, bgColor);
       for(var i=0 ; i < this.options.numSlices ; i++ )
          corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
       el.style.paddingTop = 0;
@@ -793,16 +802,22 @@
    },
 
    _roundBottomCorners: function(el, color, bgColor) {
-      var corner = this._createCorner(bgColor);
+      var corner = this._createCorner(el, bgColor);
       for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
          corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
       el.style.paddingBottom = 0;
       el.appendChild(corner);
    },
 
-   _createCorner: function(bgColor) {
+   _createCorner: function(el, bgColor) {
       var corner = document.createElement("div");
       corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      var paddingLeft = "-" + RicoUtil.getElementsComputedStyle(el, "paddingLeft", "padding-left");
+      var paddingRight = "-" + RicoUtil.getElementsComputedStyle(el, "paddingRight", "padding-right");
+      
+      corner.style.marginRight = paddingRight;
+      corner.style.marginLeft  = paddingLeft;
+
       return corner;
    },
 

Modified: jifty/trunk/share/web/templates/_elements/javascript
==============================================================================
--- jifty/trunk/share/web/templates/_elements/javascript	(original)
+++ jifty/trunk/share/web/templates/_elements/javascript	Tue May 23 11:02:05 2006
@@ -4,19 +4,35 @@
 my @libs = qw(
   /static/js/json.js
   /static/js/prototype.js
+  /static/js/cssquery/cssQuery.js
+  /static/js/cssquery/cssQuery-level2.js
+  /static/js/cssquery/cssQuery-level3.js
+  /static/js/cssquery/cssQuery-standard.js
   /static/js/behaviour.js
-  /static/js/scriptaculous/scriptaculous.js
+  /static/js/scriptaculous/builder.js
+  /static/js/scriptaculous/effects.js
+  /static/js/scriptaculous/controls.js
   /static/js/jifty.js
   /static/js/dom-drag.js
   /static/js/halo.js
   /static/js/combobox.js
+  /static/js/jifty_utils.js
   /static/js/key_bindings.js
+  /static/js/context_menu.js
   /static/js/bps_util.js
   /static/js/rico.js
   /static/js/app_behaviour.js
 );
 
 </%init>
+   <script type="text/javascript" src="/static/js/jsan/JSAN.js"></script>
+   <script type="text/javascript">
+     JSAN.includePath = [ "/static/js/jsan" ];
+     // You might want to change this to "warn" for development
+     JSAN.errorLevel = "none";
+   </script>
+
 % for (@libs) {
    <script type="text/javascript" src="<%$_%>"></script>
 % }
+

Added: jifty/trunk/share/web/templates/_elements/keybindings
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/templates/_elements/keybindings	Tue May 23 11:02:05 2006
@@ -0,0 +1 @@
+<div class="keybindings"></div>

Modified: jifty/trunk/share/web/templates/_elements/menu
==============================================================================
--- jifty/trunk/share/web/templates/_elements/menu	(original)
+++ jifty/trunk/share/web/templates/_elements/menu	Tue May 23 11:02:05 2006
@@ -20,11 +20,7 @@
   <%init>
     my @kids = $item->children;
   </%init>
-  <li <%  $item->active ? 'class="active"' : '' |n %>><%
-    Jifty->web->link(
-        url   => $item->url,
-        label => _($item->label),
-    ) %><% @kids ? '' : '</li>' |n %>
+  <li <%  $item->active ? 'class="active"' : '' |n %>><% $item->as_link |n %><% @kids ? '' : '</li>' |n %>
 % if (@kids) {
     <ul class="submenu">
 % $m->comp(".menu", item => $_) for @kids;

Modified: jifty/trunk/share/web/templates/_elements/wrapper
==============================================================================
--- jifty/trunk/share/web/templates/_elements/wrapper	(original)
+++ jifty/trunk/share/web/templates/_elements/wrapper	Tue May 23 11:02:05 2006
@@ -13,12 +13,8 @@
 % }
   <% Jifty->web->render_messages %>
   <% $m->content |n%>
-  <script type="text/javascript"><!--
-  document.write('<div id="keybindings">');
-  writeKeyBindingLegend();
-  document.write('<\/div>');
-  document.write('<div id="jifty-wait-message" style="display: none"><%_('Loading...')%><\/div>');
-  --></script>
+  <& /_elements/keybindings &>
+  <div id="jifty-wait-message" style="display: none"><%_('Loading...')%></div>
 % Jifty::Mason::Halo->render_component_tree() if (Jifty->config->framework('DevelMode') );
 </body>
 </html>


More information about the Jifty-commit mailing list