[23729] users/jberry/mpwa

source_changes at macosforge.org source_changes at macosforge.org
Sat Apr 7 16:43:11 PDT 2007


Revision: 23729
          http://trac.macosforge.org/projects/macports/changeset/23729
Author:   jberry at macports.org
Date:     2007-04-07 16:43:09 -0700 (Sat, 07 Apr 2007)

Log Message:
-----------
More of mpwa:
 * Model and simple views for ports, portpkgs, people, tags
 * Update to rails 1.2.3
 * Leaving out variants and dependencies for now
 * Leaving out stuff for build logging and comments for now

Modified Paths:
--------------
    users/jberry/mpwa/config/boot.rb
    users/jberry/mpwa/config/database.yml
    users/jberry/mpwa/config/environment.rb
    users/jberry/mpwa/doc/schema-thoughts.sql
    users/jberry/mpwa/public/javascripts/controls.js
    users/jberry/mpwa/public/javascripts/dragdrop.js
    users/jberry/mpwa/public/javascripts/effects.js
    users/jberry/mpwa/public/javascripts/prototype.js

Added Paths:
-----------
    users/jberry/mpwa/app/controllers/person_controller.rb
    users/jberry/mpwa/app/controllers/port_controller.rb
    users/jberry/mpwa/app/controllers/port_pkg_controller.rb
    users/jberry/mpwa/app/controllers/tag_controller.rb
    users/jberry/mpwa/app/helpers/person_helper.rb
    users/jberry/mpwa/app/helpers/port_helper.rb
    users/jberry/mpwa/app/helpers/port_pkg_helper.rb
    users/jberry/mpwa/app/helpers/tag_helper.rb
    users/jberry/mpwa/app/models/person.rb
    users/jberry/mpwa/app/models/port.rb
    users/jberry/mpwa/app/models/port_pkg.rb
    users/jberry/mpwa/app/models/tag.rb
    users/jberry/mpwa/app/views/layouts/person.rhtml
    users/jberry/mpwa/app/views/layouts/port.rhtml
    users/jberry/mpwa/app/views/layouts/port_pkg.rhtml
    users/jberry/mpwa/app/views/layouts/tag.rhtml
    users/jberry/mpwa/app/views/person/
    users/jberry/mpwa/app/views/person/_form.rhtml
    users/jberry/mpwa/app/views/person/edit.rhtml
    users/jberry/mpwa/app/views/person/list.rhtml
    users/jberry/mpwa/app/views/person/new.rhtml
    users/jberry/mpwa/app/views/person/show.rhtml
    users/jberry/mpwa/app/views/port/
    users/jberry/mpwa/app/views/port/_form.rhtml
    users/jberry/mpwa/app/views/port/edit.rhtml
    users/jberry/mpwa/app/views/port/list.rhtml
    users/jberry/mpwa/app/views/port/new.rhtml
    users/jberry/mpwa/app/views/port/show.rhtml
    users/jberry/mpwa/app/views/port_pkg/
    users/jberry/mpwa/app/views/port_pkg/_form.rhtml
    users/jberry/mpwa/app/views/port_pkg/edit.rhtml
    users/jberry/mpwa/app/views/port_pkg/list.rhtml
    users/jberry/mpwa/app/views/port_pkg/new.rhtml
    users/jberry/mpwa/app/views/port_pkg/show.rhtml
    users/jberry/mpwa/app/views/tag/
    users/jberry/mpwa/app/views/tag/_form.rhtml
    users/jberry/mpwa/app/views/tag/edit.rhtml
    users/jberry/mpwa/app/views/tag/list.rhtml
    users/jberry/mpwa/app/views/tag/new.rhtml
    users/jberry/mpwa/app/views/tag/show.rhtml
    users/jberry/mpwa/public/stylesheets/scaffold.css
    users/jberry/mpwa/script/process/inspector
    users/jberry/mpwa/test/fixtures/people.yml
    users/jberry/mpwa/test/fixtures/port_pkgs.yml
    users/jberry/mpwa/test/fixtures/ports.yml
    users/jberry/mpwa/test/fixtures/tags.yml
    users/jberry/mpwa/test/fixtures/users.yml
    users/jberry/mpwa/test/functional/person_controller_test.rb
    users/jberry/mpwa/test/functional/port_controller_test.rb
    users/jberry/mpwa/test/functional/port_pkg_controller_test.rb
    users/jberry/mpwa/test/functional/tag_controller_test.rb
    users/jberry/mpwa/test/unit/person_test.rb
    users/jberry/mpwa/test/unit/port_pkg_test.rb
    users/jberry/mpwa/test/unit/port_test.rb
    users/jberry/mpwa/test/unit/tag_test.rb
    users/jberry/mpwa/test/unit/user_test.rb

Added: users/jberry/mpwa/app/controllers/person_controller.rb
===================================================================
--- users/jberry/mpwa/app/controllers/person_controller.rb	                        (rev 0)
+++ users/jberry/mpwa/app/controllers/person_controller.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,51 @@
+class PersonController < ApplicationController
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+    @person_pages, @people = paginate :people, :per_page => 10
+  end
+
+  def show
+    @person = Person.find(params[:id])
+  end
+
+  def new
+    @person = Person.new
+  end
+
+  def create
+    @person = Person.new(params[:person])
+    if @person.save
+      flash[:notice] = 'Person was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @person = Person.find(params[:id])
+  end
+
+  def update
+    @person = Person.find(params[:id])
+    if @person.update_attributes(params[:person])
+      flash[:notice] = 'Person was successfully updated.'
+      redirect_to :action => 'show', :id => @person
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    Person.find(params[:id]).destroy
+    redirect_to :action => 'list'
+  end
+end

Added: users/jberry/mpwa/app/controllers/port_controller.rb
===================================================================
--- users/jberry/mpwa/app/controllers/port_controller.rb	                        (rev 0)
+++ users/jberry/mpwa/app/controllers/port_controller.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,51 @@
+class PortController < ApplicationController
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+    @port_pages, @ports = paginate :ports, :per_page => 10
+  end
+
+  def show
+    @port = Port.find(params[:id])
+  end
+
+  def new
+    @port = Port.new
+  end
+
+  def create
+    @port = Port.new(params[:port])
+    if @port.save
+      flash[:notice] = 'Port was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @port = Port.find(params[:id])
+  end
+
+  def update
+    @port = Port.find(params[:id])
+    if @port.update_attributes(params[:port])
+      flash[:notice] = 'Port was successfully updated.'
+      redirect_to :action => 'show', :id => @port
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    Port.find(params[:id]).destroy
+    redirect_to :action => 'list'
+  end
+end

Added: users/jberry/mpwa/app/controllers/port_pkg_controller.rb
===================================================================
--- users/jberry/mpwa/app/controllers/port_pkg_controller.rb	                        (rev 0)
+++ users/jberry/mpwa/app/controllers/port_pkg_controller.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,51 @@
+class PortPkgController < ApplicationController
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+    @port_pkg_pages, @port_pkgs = paginate :port_pkgs, :per_page => 10
+  end
+
+  def show
+    @port_pkg = PortPkg.find(params[:id])
+  end
+
+  def new
+    @port_pkg = PortPkg.new
+  end
+
+  def create
+    @port_pkg = PortPkg.new(params[:port_pkg])
+    if @port_pkg.save
+      flash[:notice] = 'PortPkg was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @port_pkg = PortPkg.find(params[:id])
+  end
+
+  def update
+    @port_pkg = PortPkg.find(params[:id])
+    if @port_pkg.update_attributes(params[:port_pkg])
+      flash[:notice] = 'PortPkg was successfully updated.'
+      redirect_to :action => 'show', :id => @port_pkg
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    PortPkg.find(params[:id]).destroy
+    redirect_to :action => 'list'
+  end
+end

Added: users/jberry/mpwa/app/controllers/tag_controller.rb
===================================================================
--- users/jberry/mpwa/app/controllers/tag_controller.rb	                        (rev 0)
+++ users/jberry/mpwa/app/controllers/tag_controller.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,51 @@
+class TagController < ApplicationController
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+    @tag_pages, @tags = paginate :tags, :per_page => 10
+  end
+
+  def show
+    @tag = Tag.find(params[:id])
+  end
+
+  def new
+    @tag = Tag.new
+  end
+
+  def create
+    @tag = Tag.new(params[:tag])
+    if @tag.save
+      flash[:notice] = 'Tag was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @tag = Tag.find(params[:id])
+  end
+
+  def update
+    @tag = Tag.find(params[:id])
+    if @tag.update_attributes(params[:tag])
+      flash[:notice] = 'Tag was successfully updated.'
+      redirect_to :action => 'show', :id => @tag
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    Tag.find(params[:id]).destroy
+    redirect_to :action => 'list'
+  end
+end

Added: users/jberry/mpwa/app/helpers/person_helper.rb
===================================================================
--- users/jberry/mpwa/app/helpers/person_helper.rb	                        (rev 0)
+++ users/jberry/mpwa/app/helpers/person_helper.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,2 @@
+module PersonHelper
+end

Added: users/jberry/mpwa/app/helpers/port_helper.rb
===================================================================
--- users/jberry/mpwa/app/helpers/port_helper.rb	                        (rev 0)
+++ users/jberry/mpwa/app/helpers/port_helper.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,2 @@
+module PortHelper
+end

Added: users/jberry/mpwa/app/helpers/port_pkg_helper.rb
===================================================================
--- users/jberry/mpwa/app/helpers/port_pkg_helper.rb	                        (rev 0)
+++ users/jberry/mpwa/app/helpers/port_pkg_helper.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,2 @@
+module PortPkgHelper
+end

Added: users/jberry/mpwa/app/helpers/tag_helper.rb
===================================================================
--- users/jberry/mpwa/app/helpers/tag_helper.rb	                        (rev 0)
+++ users/jberry/mpwa/app/helpers/tag_helper.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,2 @@
+module TagHelper
+end

Added: users/jberry/mpwa/app/models/person.rb
===================================================================
--- users/jberry/mpwa/app/models/person.rb	                        (rev 0)
+++ users/jberry/mpwa/app/models/person.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,4 @@
+class Person < ActiveRecord::Base
+    has_and_belongs_to_many :ports, :class_name => 'Port', :join_table => 'maintainers_ports'
+    has_many :port_pkgs, :foreign_key => 'submitter_id'
+end

Added: users/jberry/mpwa/app/models/port.rb
===================================================================
--- users/jberry/mpwa/app/models/port.rb	                        (rev 0)
+++ users/jberry/mpwa/app/models/port.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+class Port < ActiveRecord::Base
+    has_many :port_pkgs
+    has_and_belongs_to_many :tags
+    has_and_belongs_to_many :maintainers, :class_name => 'Person', :join_table => 'maintainers_ports'
+end

Added: users/jberry/mpwa/app/models/port_pkg.rb
===================================================================
--- users/jberry/mpwa/app/models/port_pkg.rb	                        (rev 0)
+++ users/jberry/mpwa/app/models/port_pkg.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+class PortPkg < ActiveRecord::Base
+    belongs_to :port
+    belongs_to :submitter, :class_name => 'Person', :foreign_key => 'submitter_id'
+    has_and_belongs_to_many :tags
+end

Added: users/jberry/mpwa/app/models/tag.rb
===================================================================
--- users/jberry/mpwa/app/models/tag.rb	                        (rev 0)
+++ users/jberry/mpwa/app/models/tag.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,4 @@
+class Tag < ActiveRecord::Base
+    has_and_belongs_to_many :ports
+    has_and_belongs_to_many :port_pkgs
+end

Added: users/jberry/mpwa/app/views/layouts/person.rhtml
===================================================================
--- users/jberry/mpwa/app/views/layouts/person.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/layouts/person.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>Person: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%= flash[:notice] %></p>
+
+<%= yield  %>
+
+</body>
+</html>

Added: users/jberry/mpwa/app/views/layouts/port.rhtml
===================================================================
--- users/jberry/mpwa/app/views/layouts/port.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/layouts/port.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>Port: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%= flash[:notice] %></p>
+
+<%= yield  %>
+
+</body>
+</html>

Added: users/jberry/mpwa/app/views/layouts/port_pkg.rhtml
===================================================================
--- users/jberry/mpwa/app/views/layouts/port_pkg.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/layouts/port_pkg.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>PortPkg: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%= flash[:notice] %></p>
+
+<%= yield  %>
+
+</body>
+</html>

Added: users/jberry/mpwa/app/views/layouts/tag.rhtml
===================================================================
--- users/jberry/mpwa/app/views/layouts/tag.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/layouts/tag.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>Tag: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%= flash[:notice] %></p>
+
+<%= yield  %>
+
+</body>
+</html>

Added: users/jberry/mpwa/app/views/person/_form.rhtml
===================================================================
--- users/jberry/mpwa/app/views/person/_form.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/person/_form.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,16 @@
+<%= error_messages_for 'person' %>
+
+<!--[form:person]-->
+<p><label for="person_user_name">User name</label><br/>
+<%= text_field 'person', 'user_name'  %></p>
+
+<p><label for="person_firt_name">First name</label><br/>
+<%= text_field 'person', 'first_name'  %></p>
+
+<p><label for="person_last_name">Last name</label><br/>
+<%= text_field 'person', 'last_name'  %></p>
+
+<p><label for="person_email">Email</label><br/>
+<%= text_field 'person', 'email'  %></p>
+<!--[eoform:person]-->
+

Added: users/jberry/mpwa/app/views/person/edit.rhtml
===================================================================
--- users/jberry/mpwa/app/views/person/edit.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/person/edit.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,9 @@
+<h1>Editing person</h1>
+
+<% form_tag :action => 'update', :id => @person do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag 'Edit' %>
+<% end %>
+
+<%= link_to 'Show', :action => 'show', :id => @person %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/person/list.rhtml
===================================================================
--- users/jberry/mpwa/app/views/person/list.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/person/list.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,27 @@
+<h1>Listing people</h1>
+
+<table>
+  <tr>
+  <% for column in Person.content_columns %>
+    <th><%= column.human_name %></th>
+  <% end %>
+  </tr>
+  
+<% for person in @people %>
+  <tr>
+  <% for column in Person.content_columns %>
+    <td><%=h person.send(column.name) %></td>
+  <% end %>
+    <td><%= link_to 'Show', :action => 'show', :id => person %></td>
+    <td><%= link_to 'Edit', :action => 'edit', :id => person %></td>
+    <td><%= link_to 'Destroy', { :action => 'destroy', :id => person }, :confirm => 'Are you sure?', :method => :post %></td>
+  </tr>
+<% end %>
+</table>
+
+<%= link_to 'Previous page', { :page => @person_pages.current.previous } if @person_pages.current.previous %>
+<%= link_to 'Next page', { :page => @person_pages.current.next } if @person_pages.current.next %> 
+
+<br />
+
+<%= link_to 'New person', :action => 'new' %>

Added: users/jberry/mpwa/app/views/person/new.rhtml
===================================================================
--- users/jberry/mpwa/app/views/person/new.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/person/new.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,8 @@
+<h1>New person</h1>
+
+<% form_tag :action => 'create' do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag "Create" %>
+<% end %>
+
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/person/show.rhtml
===================================================================
--- users/jberry/mpwa/app/views/person/show.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/person/show.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,22 @@
+<% for column in Person.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @person.send(column.name) %>
+</p>
+<% end %>
+
+<h4>Ports</h4>
+<p>
+	<% for port in @person.ports %>
+		<%= link_to port.name, :controller => 'port', :action => 'show', :id => port %><br />
+	<% end %>
+</p>
+
+<h4>Packages</h4>
+<p>
+	<% for pkg in @person.port_pkgs %>
+		<%= link_to pkg.id, :controller => 'port_pkg', :action => 'show', :id => pkg %><br />
+	<% end %>
+</p>
+
+<%= link_to 'Edit', :action => 'edit', :id => @person %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port/_form.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port/_form.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port/_form.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,16 @@
+<%= error_messages_for 'port' %>
+
+<!--[form:port]-->
+<p><label for="name">Name</label><br/>
+<%= text_field 'port', 'name'  %></p>
+
+<p><label for="port_home_page">HomePage</label><br/>
+<%= text_field 'port', 'home_page'  %></p>
+
+<p><label for="port_short_desc">ShortDesc</label><br/>
+<%= text_field 'port', 'short_desc'  %></p>
+
+<p><label for="port_long_desc">LongDesc</label><br/>
+<%= text_area 'port', 'long_desc'  %></p>
+<!--[eoform:port]-->
+

Added: users/jberry/mpwa/app/views/port/edit.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port/edit.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port/edit.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,9 @@
+<h1>Editing port</h1>
+
+<% form_tag :action => 'update', :id => @port do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag 'Edit' %>
+<% end %>
+
+<%= link_to 'Show', :action => 'show', :id => @port %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port/list.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port/list.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port/list.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,27 @@
+<h1>Listing ports</h1>
+
+<table>
+  <tr>
+  <% for column in Port.content_columns %>
+    <th><%= column.human_name %></th>
+  <% end %>
+  </tr>
+  
+<% for port in @ports %>
+  <tr>
+  <% for column in Port.content_columns %>
+    <td><%=h port.send(column.name) %></td>
+  <% end %>
+    <td><%= link_to 'Show', :action => 'show', :id => port %></td>
+    <td><%= link_to 'Edit', :action => 'edit', :id => port %></td>
+    <td><%= link_to 'Destroy', { :action => 'destroy', :id => port }, :confirm => 'Are you sure?', :method => :post %></td>
+  </tr>
+<% end %>
+</table>
+
+<%= link_to 'Previous page', { :page => @port_pages.current.previous } if @port_pages.current.previous %>
+<%= link_to 'Next page', { :page => @port_pages.current.next } if @port_pages.current.next %> 
+
+<br />
+
+<%= link_to 'New port', :action => 'new' %>

Added: users/jberry/mpwa/app/views/port/new.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port/new.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port/new.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,8 @@
+<h1>New port</h1>
+
+<% form_tag :action => 'create' do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag "Create" %>
+<% end %>
+
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port/show.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port/show.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port/show.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,29 @@
+<% for column in Port.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @port.send(column.name) %>
+</p>
+<% end %>
+
+<h4>Maintainers</h4>
+<p>
+	<% for maintainer in @port.maintainers %>
+		<%= link_to maintainer.user_name, :controller => 'person', :action => 'show', :id => maintainer %><br />
+	<% end %>
+</p>
+
+<h4>Tags</h4>
+<p>
+	<% for tag in @port.tags %>
+		<%= link_to tag.name, :controller => 'tag', :action => 'show', :id => tag %>
+	<% end %>
+</p>
+
+<h4>Port Packages</h4>
+<p>
+	<% for pkg in @port.port_pkgs %>
+		<%= link_to pkg.id, :controller => 'port_pkg', :action => 'show', :id => pkg %><br />
+	<% end %>
+</p>
+
+<%= link_to 'Edit', :action => 'edit', :id => @port %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port_pkg/_form.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port_pkg/_form.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port_pkg/_form.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,19 @@
+<%= error_messages_for 'port_pkg' %>
+
+<!--[form:port_pkg]-->
+<p><label for="port_pkg_submitted_at">Submitted at</label><br/>
+<%= datetime_select 'port_pkg', 'submitted_at'  %></p>
+
+<p><label for="port_pkg_epoch">Epoch</label><br/>
+<%= text_field 'port_pkg', 'epoch'  %></p>
+
+<p><label for="port_pkg_version">Version</label><br/>
+<%= text_field 'port_pkg', 'version'  %></p>
+
+<p><label for="port_pkg_revision">Revision</label><br/>
+<%= text_field 'port_pkg', 'revision'  %></p>
+
+<p><label for="port_pkg_url">Url</label><br/>
+<%= text_field 'port_pkg', 'url'  %></p>
+<!--[eoform:port_pkg]-->
+

Added: users/jberry/mpwa/app/views/port_pkg/edit.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port_pkg/edit.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port_pkg/edit.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,9 @@
+<h1>Editing port_pkg</h1>
+
+<% form_tag :action => 'update', :id => @port_pkg do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag 'Edit' %>
+<% end %>
+
+<%= link_to 'Show', :action => 'show', :id => @port_pkg %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port_pkg/list.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port_pkg/list.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port_pkg/list.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,27 @@
+<h1>Listing port_pkgs</h1>
+
+<table>
+  <tr>
+  <% for column in PortPkg.content_columns %>
+    <th><%= column.human_name %></th>
+  <% end %>
+  </tr>
+  
+<% for port_pkg in @port_pkgs %>
+  <tr>
+  <% for column in PortPkg.content_columns %>
+    <td><%=h port_pkg.send(column.name) %></td>
+  <% end %>
+    <td><%= link_to 'Show', :action => 'show', :id => port_pkg %></td>
+    <td><%= link_to 'Edit', :action => 'edit', :id => port_pkg %></td>
+    <td><%= link_to 'Destroy', { :action => 'destroy', :id => port_pkg }, :confirm => 'Are you sure?', :method => :post %></td>
+  </tr>
+<% end %>
+</table>
+
+<%= link_to 'Previous page', { :page => @port_pkg_pages.current.previous } if @port_pkg_pages.current.previous %>
+<%= link_to 'Next page', { :page => @port_pkg_pages.current.next } if @port_pkg_pages.current.next %> 
+
+<br />
+
+<%= link_to 'New port_pkg', :action => 'new' %>

Added: users/jberry/mpwa/app/views/port_pkg/new.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port_pkg/new.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port_pkg/new.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,8 @@
+<h1>New port_pkg</h1>
+
+<% form_tag :action => 'create' do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag "Create" %>
+<% end %>
+
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/port_pkg/show.rhtml
===================================================================
--- users/jberry/mpwa/app/views/port_pkg/show.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/port_pkg/show.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,21 @@
+<% for column in PortPkg.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @port_pkg.send(column.name) %>
+</p>
+<% end %>
+
+<h4>Port</h4>
+<p><%= link_to @port_pkg.port.name, :controller => 'port', :action => 'show', :id => @port_pkg.port %></p>
+
+<h4>Submitter</h4>
+<p><%= link_to @port_pkg.submitter.user_name, :controller => 'person', :action => 'show', :id => @port_pkg.submitter %></p>
+
+<h4>Tags</h4>
+<p>
+	<% for tag in @port_pkg.tags %>
+		<%= link_to tag.name, :controller => 'tag', :action => 'show', :id => tag %>
+	<% end %>
+</p>
+
+<%= link_to 'Edit', :action => 'edit', :id => @port_pkg %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/tag/_form.rhtml
===================================================================
--- users/jberry/mpwa/app/views/tag/_form.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/tag/_form.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,7 @@
+<%= error_messages_for 'tag' %>
+
+<!--[form:tag]-->
+<p><label for="tag_name">Name</label><br/>
+<%= text_field 'tag', 'name'  %></p>
+<!--[eoform:tag]-->
+

Added: users/jberry/mpwa/app/views/tag/edit.rhtml
===================================================================
--- users/jberry/mpwa/app/views/tag/edit.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/tag/edit.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,9 @@
+<h1>Editing tag</h1>
+
+<% form_tag :action => 'update', :id => @tag do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag 'Edit' %>
+<% end %>
+
+<%= link_to 'Show', :action => 'show', :id => @tag %> |
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/tag/list.rhtml
===================================================================
--- users/jberry/mpwa/app/views/tag/list.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/tag/list.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,27 @@
+<h1>Listing tags</h1>
+
+<table>
+  <tr>
+  <% for column in Tag.content_columns %>
+    <th><%= column.human_name %></th>
+  <% end %>
+  </tr>
+  
+<% for tag in @tags %>
+  <tr>
+  <% for column in Tag.content_columns %>
+    <td><%=h tag.send(column.name) %></td>
+  <% end %>
+    <td><%= link_to 'Show', :action => 'show', :id => tag %></td>
+    <td><%= link_to 'Edit', :action => 'edit', :id => tag %></td>
+    <td><%= link_to 'Destroy', { :action => 'destroy', :id => tag }, :confirm => 'Are you sure?', :method => :post %></td>
+  </tr>
+<% end %>
+</table>
+
+<%= link_to 'Previous page', { :page => @tag_pages.current.previous } if @tag_pages.current.previous %>
+<%= link_to 'Next page', { :page => @tag_pages.current.next } if @tag_pages.current.next %> 
+
+<br />
+
+<%= link_to 'New tag', :action => 'new' %>

Added: users/jberry/mpwa/app/views/tag/new.rhtml
===================================================================
--- users/jberry/mpwa/app/views/tag/new.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/tag/new.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,8 @@
+<h1>New tag</h1>
+
+<% form_tag :action => 'create' do %>
+  <%= render :partial => 'form' %>
+  <%= submit_tag "Create" %>
+<% end %>
+
+<%= link_to 'Back', :action => 'list' %>

Added: users/jberry/mpwa/app/views/tag/show.rhtml
===================================================================
--- users/jberry/mpwa/app/views/tag/show.rhtml	                        (rev 0)
+++ users/jberry/mpwa/app/views/tag/show.rhtml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,22 @@
+<% for column in Tag.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @tag.send(column.name) %>
+</p>
+<% end %>
+
+<h4>Ports with this tag</h4>
+<p>
+	<% for port in @tag.ports %>
+		<%= link_to port.name, :controller => 'port', :action => 'show', :id => port %><br />
+	<% end %>
+</p>
+
+<h4>Port Packages with this tag</h4>
+<p>
+	<% for pkg in @tag.port_pkgs %>
+		<%= link_to pkg.id, :controller => 'port_pkg', :action => 'show', :id => pkg %><br />
+	<% end %>
+</p>
+
+<%= link_to 'Edit', :action => 'edit', :id => @tag %> |
+<%= link_to 'Back', :action => 'list' %>

Modified: users/jberry/mpwa/config/boot.rb
===================================================================
--- users/jberry/mpwa/config/boot.rb	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/config/boot.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -3,7 +3,7 @@
 unless defined?(RAILS_ROOT)
   root_path = File.join(File.dirname(__FILE__), '..')
 
-  unless RUBY_PLATFORM =~ /mswin32/
+  unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
     require 'pathname'
     root_path = Pathname.new(root_path).cleanpath(true).to_s
   end
@@ -22,13 +22,14 @@
     rails_gem_version = $1
 
     if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version
-      rails_gem = Gem.cache.search('rails', "=#{version}").first
+      # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems
+      rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last
 
       if rails_gem
-        gem "rails", "=#{version}"
+        gem "rails", "=#{rails_gem.version.version}"
         require rails_gem.full_gem_path + '/lib/initializer'
       else
-        STDERR.puts %(Cannot find gem for Rails =#{version}:
+        STDERR.puts %(Cannot find gem for Rails ~>#{version}.0:
     Install the missing gem with 'gem install -v=#{version} rails', or
     change environment.rb to define RAILS_GEM_VERSION with your desired version.
   )
@@ -41,4 +42,4 @@
   end
 
   Rails::Initializer.run(:set_load_path)
-end
\ No newline at end of file
+end

Modified: users/jberry/mpwa/config/database.yml
===================================================================
--- users/jberry/mpwa/config/database.yml	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/config/database.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -12,10 +12,11 @@
 #   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
 development:
   adapter: mysql
-  database: mpwebapp_development
-  username: mpwebapp
+  database: mpwa_development
+  username: mpwa
   password: macports
   host: localhost
+  socket: /opt/local/var/run/mysql5/mysqld.sock
 
 # Warning: The database defined as 'test' will be erased and
 # re-generated from your development database when you run 'rake'.
@@ -23,13 +24,13 @@
 test:
   adapter: mysql
   database: mpwa_test
-  username: root
+  username: mpwa
   password:
   host: localhost
 
 production:
   adapter: mysql
   database: mpwa_production
-  username: root
+  username: mpwa
   password: 
   host: localhost

Modified: users/jberry/mpwa/config/environment.rb
===================================================================
--- users/jberry/mpwa/config/environment.rb	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/config/environment.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -5,7 +5,7 @@
 # ENV['RAILS_ENV'] ||= 'production'
 
 # Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '1.1.6'
+RAILS_GEM_VERSION = '1.2.3'
 
 # Bootstrap the Rails environment, frameworks, and default configuration
 require File.join(File.dirname(__FILE__), 'boot')

Modified: users/jberry/mpwa/doc/schema-thoughts.sql
===================================================================
--- users/jberry/mpwa/doc/schema-thoughts.sql	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/doc/schema-thoughts.sql	2007-04-07 23:43:09 UTC (rev 23729)
@@ -1,123 +1,123 @@
--- Person represents a person, potentially with a login/password
+-- People represents a person, potentially with a login/password
 -- persons are used for maintainer, submitter, commenter?
-drop table if exists Person;
-create table Person (
-	id			bigint not null primary key auto_increment,
-	
-	userName	text,
-	firtName	text,
-	lastName	text,
-	email		text
-	
-	-- authMethod
-	-- authToken
-	);
-	
--- Port represents a port: a piece of software
-drop table if exists Port;
-create table Port (
-	id			bigint not null primary key auto_increment,
-	
-	portName	text,
-	shortDesc	text,
-	longDesc	text,
-	homePage	text
-	
-	-- many-many association for tags through PortTagAssoc
-	-- many-many association for maintainers through PortMaintainerAssoc
-	);
-	
--- PortMaintainerAssoc: many-many association between Port and Maintainer
-drop table if exists PortMaintainerAssoc;
-create table PortMaintainerAssoc (
-	port		bigint not null,
-	person		bigint not null
-	);
-	
--- PortPkg is an instance of build/install rules for a port.
--- There may be many PortPkgs for each port
-drop table if exists PortPkg;
-create table PortPkg (
-	id			bigint not null primary key auto_increment,
+drop table if exists People;
+create table People (
+    id              bigint not null primary key auto_increment,
+    
+    user_name       varchar(63),
+    first_name      varchar(63),
+    last_name       varchar(63),
+    email           varchar(63)
+    
+    -- auth_method
+    -- auth_token
+);
+    
+-- Ports represents a port: a piece of software
+drop table if exists Ports;
+create table Ports (
+    id              bigint not null primary key auto_increment,
+    
+    name            varchar(63),
+    short_desc      text,
+    long_desc       text,
+    home_page       varchar(255)
+    
+    -- many-many association for tags through ports_tags
+    -- many-many association for maintainers through ports_maintainers
+);
+    
+-- ports_maintainers: many-many association between Maintainers and Ports
+drop table if exists Maintainers_Ports;
+create table Maintainers_Ports (
+    person_id       bigint not null,
+    port_id         bigint not null
+);
+    
+-- A PortPkg is an instance of build/install rules for a port.
+-- There may be many PortPkg for each Port
+drop table if exists Port_Pkgs;
+create table Port_Pkgs (
+    id              bigint not null primary key auto_increment,
 
-	port		bigint not null,
-	
-	submitter	bigint not null, -- one-one: Person
-	submitDate	datetime not null,
-	
-	epoch		text,
-	version		text,
-	revision	text,
-	
-	pkgurl		text 				-- uniquely identify a portpkg to outside world
-									-- this might also also indexing of externally served portpkgs
-									
-	-- many-many association for tags through PkgTagAssoc
-	-- many-many association for variants through VariantAssoc
-	-- many-many association for dependencies through PkgDependencyAssoc
-	
-	-- (note that both pkgs and variants, have dependency relations)
-	);
+    port_id         bigint not null,
+    
+    submitter_id    bigint not null, -- one-one: Person
+    submitted_at    datetime not null,
+    
+    epoch           varchar(32),
+    version         varchar(32),
+    revision        varchar(32),
+    
+    url             varchar(127)    -- uniquely identify a portpkg to outside world
+                                    -- this might also also indexing of externally served portpkgs
+                                    
+    -- one-many association for variants
+    -- many-many association for tags through PkgTagAssoc
+    -- many-many association for dependencies through PkgDependencyAssoc
+    
+    -- (note that both pkgs and variants, have dependency relations)
+);
 
 -- A tag which may be attached to various items through PkgTagAssoc, PortTagAssoc
-drop table if exists Tag;
-create table Tag (
-	id			bigint not null primary key auto_increment,
-	name		text
-	);
-	
+drop table if exists Tags;
+create table Tags (
+    id              bigint not null primary key auto_increment,
+    name            varchar(31)
+);
+    
 -- many-many relationship between PortPkg and Tag
-drop table if exists PkgTagAssoc;
-create table PkgTagAssoc (
-	portPkg		bigint not null,
-	tag			bigint not null
-	);
+drop table if exists Port_Pkgs_Tags;
+create table Port_Pkgs_Tags (
+    port_pkg_id     bigint not null,
+    tag_id          bigint not null
+);
 
 -- many-many relationship between Port and Tag
-drop table if exists PortTagAssoc;
-create table PortTagAssoc (
-	port		bigint not null,
-	tag			bigint not null
-	);
+drop table if exists Ports_Tags;
+create table Ports_Tags (
+    port_id         bigint not null,
+    tag_id          bigint not null
+);
 
 -- A dependency onto onther port
-drop table if exists Dependency;
-create table Dependency (
-	id			bigint not null primary key auto_increment,
-	depExpr		text				-- textual? dependency expression
-	
-	-- can we point directly to the target port (or portpkg, or porturl???)
-	-- that would make it much easier to determine reverse dependencies.
-	-- maybe we have nullable fields for port, portpkg, porturl, version, revision, etc...
-	);
+drop table if exists Dependencies;
+create table Dependencies (
+    id              bigint not null primary key auto_increment,
+    expression      text                -- textual? dependency expression
+    
+    -- can we point directly to the target port (or portpkg, or porturl???)
+    -- that would make it much easier to determine reverse dependencies.
+    -- maybe we have nullable fields for port, portpkg, porturl, version, revision, etc...
+);
 
 -- many-many relationship between PortPkg and Dependency
-drop table if exists PkgDependencyAssoc;
-create table PkgDependencyAssoc (
-	portPkg		bigint not null,
-	dependency	bigint not null
-	);
+drop table if exists Dependencies_Port_Pkgs;
+create table Dependencies_Port_Pkgs (
+    package_id      bigint not null,
+    dependency_id   bigint not null
+);
 
 -- Variant available to a PortPkg
-drop table if exists Variant;
-create table Variant (
-	id			bigint not null primary key auto_increment,
-	portPkg		bigint not null,
-	
-	name		text,
-	description	text
-	
-	-- many-many association for dependencies through VariantDependencyAssoc
-	
-	-- conflicts expr?
-	);
+drop table if exists Variants;
+create table Variants (
+    id              bigint not null primary key auto_increment,
+    port_pkg_id     bigint not null,
+    
+    name            varchar(63),
+    description     text
+    
+    -- many-many association for dependencies through Dependencies_Variants
+    
+    -- conflicts expr?
+);
 
 -- many-many relationship between a Variant and Dependency
-drop table if exists VariantDependencyAssoc;
-create table VariantDependencyAssoc (
-	variant	bigint not null,
-	dependency	bigint not null
-	);
+drop table if exists Dependencies_Variants;
+create table Dependencies_Variants (
+    variant_id      bigint not null,
+    dependency_id   bigint not null
+);
 
 
 -- Missing components:

Modified: users/jberry/mpwa/public/javascripts/controls.js
===================================================================
--- users/jberry/mpwa/public/javascripts/controls.js	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/public/javascripts/controls.js	2007-04-07 23:43:09 UTC (rev 23729)
@@ -1,12 +1,13 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
 // Contributors:
 //  Richard Livsey
 //  Rahul Bhargava
 //  Rob Wills
 // 
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
 // Autocompleter.Base handles all the autocompletion functionality 
 // that's independent of the data source for autocompletion. This
@@ -33,6 +34,9 @@
 // useful when one of the tokens is \n (a newline), as it 
 // allows smart autocompletion after linebreaks.
 
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
 var Autocompleter = {}
 Autocompleter.Base = function() {};
 Autocompleter.Base.prototype = {
@@ -45,7 +49,7 @@
     this.index       = 0;     
     this.entryCount  = 0;
 
-    if (this.setOptions)
+    if(this.setOptions)
       this.setOptions(options);
     else
       this.options = options || {};
@@ -55,17 +59,20 @@
     this.options.frequency    = this.options.frequency || 0.4;
     this.options.minChars     = this.options.minChars || 1;
     this.options.onShow       = this.options.onShow || 
-    function(element, update){ 
-      if(!update.style.position || update.style.position=='absolute') {
-        update.style.position = 'absolute';
-        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
-      }
-      Effect.Appear(update,{duration:0.15});
-    };
+      function(element, update){ 
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
     this.options.onHide = this.options.onHide || 
-    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
 
-    if (typeof(this.options.tokens) == 'string') 
+    if(typeof(this.options.tokens) == 'string') 
       this.options.tokens = new Array(this.options.tokens);
 
     this.observer = null;
@@ -94,7 +101,7 @@
   },
   
   fixIEOverlapping: function() {
-    Position.clone(this.update, this.iefix);
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
     this.iefix.style.zIndex = 1;
     this.update.style.zIndex = 2;
     Element.show(this.iefix);
@@ -202,11 +209,13 @@
   markPrevious: function() {
     if(this.index > 0) this.index--
       else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
   },
   
   markNext: function() {
     if(this.index < this.entryCount-1) this.index++
       else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
   },
   
   getEntry: function(index) {
@@ -254,11 +263,11 @@
     if(!this.changed && this.hasFocus) {
       this.update.innerHTML = choices;
       Element.cleanWhitespace(this.update);
-      Element.cleanWhitespace(this.update.firstChild);
+      Element.cleanWhitespace(this.update.down());
 
-      if(this.update.firstChild && this.update.firstChild.childNodes) {
+      if(this.update.firstChild && this.update.down().childNodes) {
         this.entryCount = 
-          this.update.firstChild.childNodes.length;
+          this.update.down().childNodes.length;
         for (var i = 0; i < this.entryCount; i++) {
           var entry = this.getEntry(i);
           entry.autocompleteIndex = i;
@@ -269,9 +278,14 @@
       }
 
       this.stopIndicator();
-
       this.index = 0;
-      this.render();
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
     }
   },
 
@@ -459,6 +473,7 @@
     this.element = $(element);
 
     this.options = Object.extend({
+      paramName: "value",
       okButton: true,
       okText: "ok",
       cancelLink: true,
@@ -531,7 +546,7 @@
     Element.hide(this.element);
     this.createForm();
     this.element.parentNode.insertBefore(this.form, this.element);
-    Field.scrollFreeActivate(this.editField);
+    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
     // stop the event to avoid a page refresh in Safari
     if (evt) {
       Event.stop(evt);
@@ -590,7 +605,7 @@
       var textField = document.createElement("input");
       textField.obj = this;
       textField.type = "text";
-      textField.name = "value";
+      textField.name = this.options.paramName;
       textField.value = text;
       textField.style.backgroundColor = this.options.highlightcolor;
       textField.className = 'editor_field';
@@ -603,7 +618,7 @@
       this.options.textarea = true;
       var textArea = document.createElement("textarea");
       textArea.obj = this;
-      textArea.name = "value";
+      textArea.name = this.options.paramName;
       textArea.value = this.convertHTMLLineBreaks(text);
       textArea.rows = this.options.rows;
       textArea.cols = this.options.cols || 40;
@@ -636,6 +651,7 @@
     Element.removeClassName(this.form, this.options.loadingClassName);
     this.editField.disabled = false;
     this.editField.value = transport.responseText.stripTags();
+    Field.scrollFreeActivate(this.editField);
   },
   onclickCancel: function() {
     this.onComplete();
@@ -772,6 +788,8 @@
       collection.each(function(e,i) {
         optionTag = document.createElement("option");
         optionTag.value = (e instanceof Array) ? e[0] : e;
+        if((typeof this.options.value == 'undefined') && 
+          ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
         if(this.options.value==optionTag.value) optionTag.selected = true;
         optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
         selectTag.appendChild(optionTag);

Modified: users/jberry/mpwa/public/javascripts/dragdrop.js
===================================================================
--- users/jberry/mpwa/public/javascripts/dragdrop.js	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/public/javascripts/dragdrop.js	2007-04-07 23:43:09 UTC (rev 23729)
@@ -1,9 +1,11 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
 // 
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
-/*--------------------------------------------------------------------------*/
+if(typeof Effect == 'undefined')
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
 
 var Droppables = {
   drops: [],
@@ -145,8 +147,16 @@
   },
   
   activate: function(draggable) {
-    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
-    this.activeDraggable = draggable;
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
   },
   
   deactivate: function() {
@@ -160,10 +170,15 @@
     // the same coordinates, prevent needless redrawing (moz bug?)
     if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
     this._lastPointer = pointer;
+    
     this.activeDraggable.updateDrag(event, pointer);
   },
   
   endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
     if(!this.activeDraggable) return;
     this._lastPointer = null;
     this.activeDraggable.endDrag(event);
@@ -190,6 +205,7 @@
       this.observers.each( function(o) {
         if(o[eventName]) o[eventName](eventName, draggable, event);
       });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
   },
   
   _cacheObserverCallbacks: function() {
@@ -204,39 +220,59 @@
 /*--------------------------------------------------------------------------*/
 
 var Draggable = Class.create();
+Draggable._dragging    = {};
+
 Draggable.prototype = {
   initialize: function(element) {
-    var options = Object.extend({
+    var defaults = {
       handle: false,
-      starteffect: function(element) { 
-        new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); 
-      },
       reverteffect: function(element, top_offset, left_offset) {
         var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
-        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
       },
-      endeffect: function(element) { 
-        new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); 
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
       },
       zindex: 1000,
       revert: false,
       scroll: false,
       scrollSensitivity: 20,
       scrollSpeed: 15,
-      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
-    }, arguments[1] || {});
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || {});
 
     this.element = $(element);
     
-    if(options.handle && (typeof options.handle == 'string')) {
-      var h = Element.childrenWithClassName(this.element, options.handle, true);
-      if(h.length>0) this.handle = h[0];
-    }
+    if(options.handle && (typeof options.handle == 'string'))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
     if(!this.handle) this.handle = $(options.handle);
     if(!this.handle) this.handle = this.element;
     
-    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
       options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
 
     Element.makePositioned(this.element); // fix IE    
 
@@ -262,6 +298,8 @@
   },
   
   initDrag: function(event) {
+    if(typeof Draggable._dragging[this.element] != 'undefined' &&
+      Draggable._dragging[this.element]) return;
     if(Event.isLeftClick(event)) {    
       // abort on form elements, fixes a Firefox issue
       var src = Event.element(event);
@@ -272,11 +310,6 @@
         src.tagName=='BUTTON' ||
         src.tagName=='TEXTAREA')) return;
         
-      if(this.element._revert) {
-        this.element._revert.cancel();
-        this.element._revert = null;
-      }
-      
       var pointer = [Event.pointerX(event), Event.pointerY(event)];
       var pos     = Position.cumulativeOffset(this.element);
       this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
@@ -312,6 +345,7 @@
     }
     
     Draggables.notify('onStart', this, event);
+        
     if(this.options.starteffect) this.options.starteffect(this.element);
   },
   
@@ -320,6 +354,7 @@
     Position.prepare();
     Droppables.show(pointer, this.element);
     Draggables.notify('onDrag', this, event);
+    
     this.draw(pointer);
     if(this.options.change) this.options.change(this);
     
@@ -331,8 +366,8 @@
         with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
       } else {
         p = Position.page(this.options.scroll);
-        p[0] += this.options.scroll.scrollLeft;
-        p[1] += this.options.scroll.scrollTop;
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
         p.push(p[0]+this.options.scroll.offsetWidth);
         p.push(p[1]+this.options.scroll.offsetHeight);
       }
@@ -378,7 +413,7 @@
 
     if(this.options.endeffect) 
       this.options.endeffect(this.element);
-
+      
     Draggables.deactivate(this);
     Droppables.reset();
   },
@@ -398,10 +433,15 @@
   
   draw: function(point) {
     var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
     var d = this.currentDelta();
     pos[0] -= d[0]; pos[1] -= d[1];
     
-    if(this.options.scroll && (this.options.scroll != window)) {
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
       pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
       pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
     }
@@ -412,7 +452,7 @@
     
     if(this.options.snap) {
       if(typeof this.options.snap == 'function') {
-        p = this.options.snap(p[0],p[1]);
+        p = this.options.snap(p[0],p[1],this);
       } else {
       if(this.options.snap instanceof Array) {
         p = p.map( function(v, i) {
@@ -428,6 +468,7 @@
       style.left = p[0] + "px";
     if((!this.options.constraint) || (this.options.constraint=='vertical'))
       style.top  = p[1] + "px";
+    
     if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
   },
   
@@ -440,6 +481,7 @@
   },
   
   startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
     this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
     this.lastScrolled = new Date();
     this.scrollInterval = setInterval(this.scroll.bind(this), 10);
@@ -464,14 +506,16 @@
     Position.prepare();
     Droppables.show(Draggables._lastPointer, this.element);
     Draggables.notify('onDrag', this);
-    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
-    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
-    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
-    if (Draggables._lastScrollPointer[0] < 0)
-      Draggables._lastScrollPointer[0] = 0;
-    if (Draggables._lastScrollPointer[1] < 0)
-      Draggables._lastScrollPointer[1] = 0;
-    this.draw(Draggables._lastScrollPointer);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
     
     if(this.options.change) this.options.change(this);
   },
@@ -523,6 +567,8 @@
 }
 
 var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
   sortables: {},
   
   _findRootElement: function(element) {
@@ -563,12 +609,13 @@
       containment: element,    // also takes array of elements (or id's); or false
       handle:      false,      // or a CSS class
       only:        false,
+      delay:       0,
       hoverclass:  null,
       ghosting:    false,
       scroll:      false,
       scrollSensitivity: 20,
       scrollSpeed: 15,
-      format:      /^[^_]*_(.*)$/,
+      format:      this.SERIALIZE_RULE,
       onChange:    Prototype.emptyFunction,
       onUpdate:    Prototype.emptyFunction
     }, arguments[1] || {});
@@ -582,6 +629,7 @@
       scroll:      options.scroll,
       scrollSpeed: options.scrollSpeed,
       scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
       ghosting:    options.ghosting,
       constraint:  options.constraint,
       handle:      options.handle };
@@ -610,7 +658,6 @@
       tree:        options.tree,
       hoverclass:  options.hoverclass,
       onHover:     Sortable.onHover
-      //greedy:      !options.dropOnEmpty
     }
     
     var options_for_tree = {
@@ -635,7 +682,7 @@
     (this.findElements(element, options) || []).each( function(e) {
       // handles are per-draggable
       var handle = options.handle ? 
-        Element.childrenWithClassName(e, options.handle)[0] : e;    
+        $(e).down('.'+options.handle,0) : e;    
       options.draggables.push(
         new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
       Droppables.add(e, options_for_droppable);
@@ -706,7 +753,7 @@
     if(!Element.isParent(dropon, element)) {
       var index;
       
-      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
       var child = null;
             
       if(children) {
@@ -733,7 +780,7 @@
   },
 
   unmark: function() {
-    if(Sortable._marker) Element.hide(Sortable._marker);
+    if(Sortable._marker) Sortable._marker.hide();
   },
 
   mark: function(dropon, position) {
@@ -742,23 +789,21 @@
     if(sortable && !sortable.ghosting) return; 
 
     if(!Sortable._marker) {
-      Sortable._marker = $('dropmarker') || document.createElement('DIV');
-      Element.hide(Sortable._marker);
-      Element.addClassName(Sortable._marker, 'dropmarker');
-      Sortable._marker.style.position = 'absolute';
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
       document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
     }    
     var offsets = Position.cumulativeOffset(dropon);
-    Sortable._marker.style.left = offsets[0] + 'px';
-    Sortable._marker.style.top = offsets[1] + 'px';
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
     
     if(position=='after')
       if(sortable.overlap == 'horizontal') 
-        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
       else
-        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
     
-    Element.show(Sortable._marker);
+    Sortable._marker.show();
   },
   
   _tree: function(element, options, parent) {
@@ -773,9 +818,9 @@
         id: encodeURIComponent(match ? match[1] : null),
         element: element,
         parent: parent,
-        children: new Array,
+        children: [],
         position: parent.children.length,
-        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+        container: $(children[i]).down(options.treeTag)
       }
       
       /* Get the element containing the children and recurse over it */
@@ -788,17 +833,6 @@
     return parent; 
   },
 
-  /* Finds the first element of the given tag type within a parent element.
-    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
-  _findChildrenElement: function (element, containerTag) {
-    if (element && element.hasChildNodes)
-      for (var i = 0; i < element.childNodes.length; ++i)
-        if (element.childNodes[i].tagName == containerTag)
-          return element.childNodes[i];
-  
-    return null;
-  },
-
   tree: function(element) {
     element = $(element);
     var sortableOptions = this.options(element);
@@ -813,12 +847,12 @@
     var root = {
       id: null,
       parent: null,
-      children: new Array,
+      children: [],
       container: element,
       position: 0
     }
     
-    return Sortable._tree (element, options, root);
+    return Sortable._tree(element, options, root);
   },
 
   /* Construct a [i] index for a particular node */
@@ -867,7 +901,7 @@
     
     if (options.tree) {
       return Sortable.tree(element, arguments[1]).children.map( function (item) {
-        return [name + Sortable._constructIndex(item) + "=" + 
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
                 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
       }).flatten().join('&');
     } else {
@@ -878,12 +912,10 @@
   }
 }
 
-/* Returns true if child is contained within element */
+// Returns true if child is contained within element
 Element.isParent = function(child, element) {
   if (!child.parentNode || child == element) return false;
-
   if (child.parentNode == element) return true;
-
   return Element.isParent(child.parentNode, element);
 }
 
@@ -906,8 +938,5 @@
 }
 
 Element.offsetSize = function (element, type) {
-  if (type == 'vertical' || type == 'height')
-    return element.offsetHeight;
-  else
-    return element.offsetWidth;
-}
\ No newline at end of file
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}

Modified: users/jberry/mpwa/public/javascripts/effects.js
===================================================================
--- users/jberry/mpwa/public/javascripts/effects.js	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/public/javascripts/effects.js	2007-04-07 23:43:09 UTC (rev 23729)
@@ -1,15 +1,16 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
 // Contributors:
 //  Justin Palmer (http://encytemedia.com/)
 //  Mark Pilgrim (http://diveintomark.org/)
 //  Martin Bialasinki
 // 
-// See scriptaculous.js for full license.  
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
 
 // converts rgb() and #xxx to #xxxxxx format,  
 // returns self (or first argument) if not convertable  
 String.prototype.parseColor = function() {  
-  var color = '#';  
+  var color = '#';
   if(this.slice(0,4) == 'rgb(') {  
     var cols = this.slice(4,this.length-1).split(',');  
     var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
@@ -41,15 +42,17 @@
 
 Element.setContentZoom = function(element, percent) {
   element = $(element);  
-  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  element.setStyle({fontSize: (percent/100) + 'em'});   
   if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+  return element;
 }
 
-Element.getOpacity = function(element){  
+Element.getOpacity = function(element){
+  element = $(element);
   var opacity;
-  if (opacity = Element.getStyle(element, 'opacity'))  
+  if (opacity = element.getStyle('opacity'))  
     return parseFloat(opacity);  
-  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+  if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))  
     if(opacity[1]) return parseFloat(opacity[1]) / 100;  
   return 1.0;  
 }
@@ -57,34 +60,26 @@
 Element.setOpacity = function(element, value){  
   element= $(element);  
   if (value == 1){
-    Element.setStyle(element, { opacity: 
+    element.setStyle({ opacity: 
       (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
-      0.999999 : null });
-    if(/MSIE/.test(navigator.userAgent))  
-      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+      0.999999 : 1.0 });
+    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
+      element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
   } else {  
     if(value < 0.00001) value = 0;  
-    Element.setStyle(element, {opacity: value});
-    if(/MSIE/.test(navigator.userAgent))  
-     Element.setStyle(element, 
-       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
-                 'alpha(opacity='+value*100+')' });  
+    element.setStyle({opacity: value});
+    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
+      element.setStyle(
+        { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
+            'alpha(opacity='+value*100+')' });  
   }
+  return element;
 }  
  
 Element.getInlineOpacity = function(element){  
   return $(element).style.opacity || '';
 }  
 
-Element.childrenWithClassName = function(element, className, findFirst) {
-  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
-  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
-    return (c.className && c.className.match(classNameRegExp));
-  });
-  if(!results) results = [];
-  return results;
-}
-
 Element.forceRerendering = function(element) {
   try {
     element = $(element);
@@ -104,9 +99,17 @@
 /*--------------------------------------------------------------------------*/
 
 var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
   tagifyText: function(element) {
+    if(typeof Builder == 'undefined')
+      throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
+      
     var tagifyStyle = 'position:relative';
-    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
+    
     element = $(element);
     $A(element.childNodes).each( function(child) {
       if(child.nodeType==3) {
@@ -159,34 +162,36 @@
 
 /* ------------- transitions ------------- */
 
-Effect.Transitions = {}
+Effect.Transitions = {
+  linear: Prototype.K,
+  sinoidal: function(pos) {
+    return (-Math.cos(pos*Math.PI)/2) + 0.5;
+  },
+  reverse: function(pos) {
+    return 1-pos;
+  },
+  flicker: function(pos) {
+    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+  },
+  wobble: function(pos) {
+    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+  },
+  pulse: function(pos, pulses) { 
+    pulses = pulses || 5; 
+    return (
+      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
+            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
+        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+      );
+  },
+  none: function(pos) {
+    return 0;
+  },
+  full: function(pos) {
+    return 1;
+  }
+};
 
-Effect.Transitions.linear = function(pos) {
-  return pos;
-}
-Effect.Transitions.sinoidal = function(pos) {
-  return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse  = function(pos) {
-  return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
-  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
-  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
-  return (Math.floor(pos*10) % 2 == 0 ? 
-    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
-  return 0;
-}
-Effect.Transitions.full = function(pos) {
-  return 1;
-}
-
 /* ------------- core effects ------------- */
 
 Effect.ScopedQueue = Class.create();
@@ -212,6 +217,9 @@
             e.finishOn += effect.finishOn;
           });
         break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
       case 'end':
         // start effect after last queued effect has finished
         timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -348,12 +356,24 @@
   }
 });
 
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+  initialize: function() {
+    var options = Object.extend({
+      duration: 0
+    }, arguments[0] || {});
+    this.start(options);
+  },
+  update: Prototype.emptyFunction
+});
+
 Effect.Opacity = Class.create();
 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     // make this work on IE on elements without 'layout'
-    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
       this.element.setStyle({zoom: 1});
     var options = Object.extend({
       from: this.element.getOpacity() || 0.0,
@@ -370,6 +390,7 @@
 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       x:    0,
       y:    0,
@@ -393,8 +414,8 @@
   },
   update: function(position) {
     this.element.setStyle({
-      left: this.options.x  * position + this.originalLeft + 'px',
-      top:  this.options.y  * position + this.originalTop  + 'px'
+      left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
+      top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
     });
   }
 });
@@ -408,7 +429,8 @@
 Effect.Scale = Class.create();
 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
   initialize: function(element, percent) {
-    this.element = $(element)
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       scaleX: true,
       scaleY: true,
@@ -433,7 +455,7 @@
     this.originalLeft = this.element.offsetLeft;
     
     var fontSize = this.element.getStyle('font-size') || '100%';
-    ['em','px','%'].each( function(fontSizeType) {
+    ['em','px','%','pt'].each( function(fontSizeType) {
       if(fontSize.indexOf(fontSizeType)>0) {
         this.fontSize     = parseFloat(fontSize);
         this.fontSizeType = fontSizeType;
@@ -458,12 +480,12 @@
     this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
   },
   finish: function(position) {
-    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+    if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
   },
   setDimensions: function(height, width) {
     var d = {};
-    if(this.options.scaleX) d.width = width + 'px';
-    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleX) d.width = Math.round(width) + 'px';
+    if(this.options.scaleY) d.height = Math.round(height) + 'px';
     if(this.options.scaleFromCenter) {
       var topd  = (height - this.dims[0])/2;
       var leftd = (width  - this.dims[1])/2;
@@ -483,6 +505,7 @@
 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
     this.start(options);
   },
@@ -547,8 +570,7 @@
   to:   0.0,
   afterFinishInternal: function(effect) { 
     if(effect.options.to!=0) return;
-    effect.element.hide();
-    effect.element.setStyle({opacity: oldOpacity}); 
+    effect.element.hide().setStyle({opacity: oldOpacity}); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
@@ -563,25 +585,31 @@
     effect.element.forceRerendering();
   },
   beforeSetup: function(effect) {
-    effect.element.setOpacity(effect.options.from);
-    effect.element.show(); 
+    effect.element.setOpacity(effect.options.from).show(); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
 
 Effect.Puff = function(element) {
   element = $(element);
-  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
   return new Effect.Parallel(
    [ new Effect.Scale(element, 200, 
       { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
      Object.extend({ duration: 1.0, 
       beforeSetupInternal: function(effect) {
-        effect.effects[0].element.setStyle({position: 'absolute'}); },
+        Position.absolutize(effect.effects[0].element)
+      },
       afterFinishInternal: function(effect) {
-         effect.effects[0].element.hide();
-         effect.effects[0].element.setStyle(oldStyle); }
+         effect.effects[0].element.hide().setStyle(oldStyle); }
      }, arguments[1] || {})
    );
 }
@@ -589,13 +617,12 @@
 Effect.BlindUp = function(element) {
   element = $(element);
   element.makeClipping();
-  return new Effect.Scale(element, 0, 
+  return new Effect.Scale(element, 0,
     Object.extend({ scaleContent: false, 
       scaleX: false, 
       restoreAfterFinish: true,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping();
+        effect.element.hide().undoClipping();
       } 
     }, arguments[1] || {})
   );
@@ -604,28 +631,25 @@
 Effect.BlindDown = function(element) {
   element = $(element);
   var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, 
-    Object.extend({ scaleContent: false, 
-      scaleX: false,
-      scaleFrom: 0,
-      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
-      restoreAfterFinish: true,
-      afterSetup: function(effect) {
-        effect.element.makeClipping();
-        effect.element.setStyle({height: '0px'});
-        effect.element.show(); 
-      },  
-      afterFinishInternal: function(effect) {
-        effect.element.undoClipping();
-      }
-    }, arguments[1] || {})
-  );
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || {}));
 }
 
 Effect.SwitchOff = function(element) {
   element = $(element);
   var oldOpacity = element.getInlineOpacity();
-  return new Effect.Appear(element, { 
+  return new Effect.Appear(element, Object.extend({
     duration: 0.4,
     from: 0,
     transition: Effect.Transitions.flicker,
@@ -634,18 +658,14 @@
         duration: 0.3, scaleFromCenter: true,
         scaleX: false, scaleContent: false, restoreAfterFinish: true,
         beforeSetup: function(effect) { 
-          effect.element.makePositioned();
-          effect.element.makeClipping();
+          effect.element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
-          effect.element.hide();
-          effect.element.undoClipping();
-          effect.element.undoPositioned();
-          effect.element.setStyle({opacity: oldOpacity});
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
         }
       })
     }
-  });
+  }, arguments[1] || {}));
 }
 
 Effect.DropOut = function(element) {
@@ -663,9 +683,7 @@
           effect.effects[0].element.makePositioned(); 
         },
         afterFinishInternal: function(effect) {
-          effect.effects[0].element.hide();
-          effect.effects[0].element.undoPositioned();
-          effect.effects[0].element.setStyle(oldStyle);
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
         } 
       }, arguments[1] || {}));
 }
@@ -687,54 +705,42 @@
       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     new Effect.Move(effect.element,
       { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
-        effect.element.undoPositioned();
-        effect.element.setStyle(oldStyle);
+        effect.element.undoPositioned().setStyle(oldStyle);
   }}) }}) }}) }}) }}) }});
 }
 
 Effect.SlideDown = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
+  element = $(element).cleanWhitespace();
   // SlideDown need to have the content of the element wrapped in a container element with fixed height!
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var oldInnerBottom = element.down().getStyle('bottom');
   var elementDimensions = element.getDimensions();
   return new Effect.Scale(element, 100, Object.extend({ 
     scaleContent: false, 
     scaleX: false, 
-    scaleFrom: 0,
+    scaleFrom: window.opera ? 0 : 1,
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
     restoreAfterFinish: true,
     afterSetup: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.setStyle({height: '0px'});
-      effect.element.show(); },
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
+      effect.element.down().setStyle({bottom:
         (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
     },
     afterFinishInternal: function(effect) {
-      effect.element.undoClipping(); 
-      // IE will crash if child is undoPositioned first
-      if(/MSIE/.test(navigator.userAgent)){
-        effect.element.undoPositioned();
-        effect.element.firstChild.undoPositioned();
-      }else{
-        effect.element.firstChild.undoPositioned();
-        effect.element.undoPositioned();
-      }
-      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
     }, arguments[1] || {})
   );
 }
-  
+
 Effect.SlideUp = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
-  return new Effect.Scale(element, 0, 
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
    Object.extend({ scaleContent: false, 
     scaleX: false, 
     scaleMode: 'box',
@@ -742,32 +748,32 @@
     restoreAfterFinish: true,
     beforeStartInternal: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.show(); },  
+      effect.element.makeClipping().show();
+    },  
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
     afterFinishInternal: function(effect) {
-      effect.element.hide();
-      effect.element.undoClipping();
-      effect.element.firstChild.undoPositioned();
-      effect.element.undoPositioned();
-      effect.element.setStyle({bottom: oldInnerBottom}); }
+      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+      effect.element.down().undoPositioned();
+    }
    }, arguments[1] || {})
   );
 }
 
 // Bug in opera makes the TD containing this element expand for a instance after finish 
 Effect.Squish = function(element) {
-  return new Effect.Scale(element, window.opera ? 1 : 0, 
-    { restoreAfterFinish: true,
-      beforeSetup: function(effect) {
-        effect.element.makeClipping(effect.element); },  
-      afterFinishInternal: function(effect) {
-        effect.element.hide(effect.element); 
-        effect.element.undoClipping(effect.element); }
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
   });
 }
 
@@ -823,9 +829,7 @@
     y: initialMoveY,
     duration: 0.01, 
     beforeSetup: function(effect) {
-      effect.element.hide();
-      effect.element.makeClipping();
-      effect.element.makePositioned();
+      effect.element.hide().makeClipping().makePositioned();
     },
     afterFinishInternal: function(effect) {
       new Effect.Parallel(
@@ -836,13 +840,10 @@
             sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
         ], Object.extend({
              beforeSetup: function(effect) {
-               effect.effects[0].element.setStyle({height: '0px'});
-               effect.effects[0].element.show(); 
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
              },
              afterFinishInternal: function(effect) {
-               effect.effects[0].element.undoClipping();
-               effect.effects[0].element.undoPositioned();
-               effect.effects[0].element.setStyle(oldStyle); 
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
              }
            }, options)
       )
@@ -896,13 +897,10 @@
       new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
     ], Object.extend({            
          beforeStartInternal: function(effect) {
-           effect.effects[0].element.makePositioned();
-           effect.effects[0].element.makeClipping(); },
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
          afterFinishInternal: function(effect) {
-           effect.effects[0].element.hide();
-           effect.effects[0].element.undoClipping();
-           effect.effects[0].element.undoPositioned();
-           effect.effects[0].element.setStyle(oldStyle); }
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
        }, options)
   );
 }
@@ -912,10 +910,10 @@
   var options    = arguments[1] || {};
   var oldOpacity = element.getInlineOpacity();
   var transition = options.transition || Effect.Transitions.sinoidal;
-  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
   reverser.bind(transition);
   return new Effect.Opacity(element, 
-    Object.extend(Object.extend({  duration: 3.0, from: 0,
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
       afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
     }, options), {transition: reverser}));
 }
@@ -927,7 +925,7 @@
     left: element.style.left,
     width: element.style.width,
     height: element.style.height };
-  Element.makeClipping(element);
+  element.makeClipping();
   return new Effect.Scale(element, 5, Object.extend({   
     scaleContent: false,
     scaleX: false,
@@ -936,15 +934,147 @@
       scaleContent: false, 
       scaleY: false,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping(); 
-        effect.element.setStyle(oldStyle);
+        effect.element.hide().undoClipping().setStyle(oldStyle);
       } });
   }}, arguments[1] || {}));
 };
 
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: ''
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function(){
+    function parseColor(color){
+      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.options.style.parseStyle().map(function(property){
+      var originalValue = this.element.getStyle(property[0]);
+      return $H({ 
+        style: property[0], 
+        originalValue: property[1].unit=='color' ? 
+          parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: property[1].unit=='color' ? 
+          parseColor(property[1].value) : property[1].value,
+        unit: property[1].unit
+      });
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = $H(), value = null;
+    this.transforms.each(function(transform){
+      value = transform.unit=='color' ?
+        $R(0,2).inject('#',function(m,v,i){
+          return m+(Math.round(transform.originalValue[i]+
+            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
+        transform.originalValue + Math.round(
+          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+      style[transform.style] = value;
+    });
+    this.element.setStyle(style);
+  }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || {};
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      var data = $H(track).values().first();
+      this.tracks.push($H({
+        ids:     $H(track).keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var elements = [$(track.ids) || $$(track.ids)].flatten();
+        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', 
+  'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', 
+  'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
+  'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
+  'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
+  'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
+  'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
+  'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
+  'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
+  'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
+  'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
+  'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
+  'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
+  'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
+  'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
+  'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
+  'width', 'wordSpacing', 'zIndex'];
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+  var element = Element.extend(document.createElement('div'));
+  element.innerHTML = '<div style="' + this + '"></div>';
+  var style = element.down().style, styleRules = $H();
+  
+  Element.CSS_PROPERTIES.each(function(property){
+   if(style[property]) styleRules[property] = style[property]; 
+  });
+  
+  var result = $H();
+  
+  styleRules.each(function(pair){
+    var property = pair[0], value = pair[1], unit = null;
+    
+    if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+      value = value.parseColor();
+      unit  = 'color';
+    } else if(Element.CSS_LENGTH.test(value)) 
+      var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+    
+    result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
+  }.bind(this));
+  
+  return result;
+};
+
+Element.morph = function(element, style) {
+  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+  return element;
+};
+
 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
   function(f) { Element.Methods[f] = Element[f]; }
 );
 

Modified: users/jberry/mpwa/public/javascripts/prototype.js
===================================================================
--- users/jberry/mpwa/public/javascripts/prototype.js	2007-04-07 21:27:37 UTC (rev 23728)
+++ users/jberry/mpwa/public/javascripts/prototype.js	2007-04-07 23:43:09 UTC (rev 23729)
@@ -1,5 +1,5 @@
-/*  Prototype JavaScript framework, version 1.5.0_rc0
- *  (c) 2005 Sam Stephenson <sam at conio.net>
+/*  Prototype JavaScript framework, version 1.5.0
+ *  (c) 2005-2007 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://prototype.conio.net/
@@ -7,11 +7,14 @@
 /*--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.5.0_rc0',
+  Version: '1.5.0',
+  BrowserFeatures: {
+    XPath: !!document.evaluate
+  },
+
   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
-
   emptyFunction: function() {},
-  K: function(x) {return x}
+  K: function(x) { return x }
 }
 
 var Class = {
@@ -31,16 +34,36 @@
   return destination;
 }
 
-Object.inspect = function(object) {
-  try {
-    if (object == undefined) return 'undefined';
-    if (object == null) return 'null';
-    return object.inspect ? object.inspect() : object.toString();
-  } catch (e) {
-    if (e instanceof RangeError) return '...';
-    throw e;
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
   }
-}
+});
 
 Function.prototype.bind = function() {
   var __method = this, args = $A(arguments), object = args.shift();
@@ -50,9 +73,9 @@
 }
 
 Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
+  var __method = this, args = $A(arguments), object = args.shift();
   return function(event) {
-    return __method.call(object, event || window.event);
+    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
   }
 }
 
@@ -77,7 +100,7 @@
   these: function() {
     var returnValue;
 
-    for (var i = 0; i < arguments.length; i++) {
+    for (var i = 0, length = arguments.length; i < length; i++) {
       var lambda = arguments[i];
       try {
         returnValue = lambda();
@@ -102,20 +125,30 @@
   },
 
   registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
   },
 
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
   onTimerEvent: function() {
     if (!this.currentlyExecuting) {
       try {
         this.currentlyExecuting = true;
-        this.callback();
+        this.callback(this);
       } finally {
         this.currentlyExecuting = false;
       }
     }
   }
 }
+String.interpret = function(value){
+  return value == null ? '' : String(value);
+}
+
 Object.extend(String.prototype, {
   gsub: function(pattern, replacement) {
     var result = '', source = this, match;
@@ -124,7 +157,7 @@
     while (source.length > 0) {
       if (match = source.match(pattern)) {
         result += source.slice(0, match.index);
-        result += (replacement(match) || '').toString();
+        result += String.interpret(replacement(match));
         source  = source.slice(match.index + match[0].length);
       } else {
         result += source, source = '';
@@ -189,15 +222,28 @@
   unescapeHTML: function() {
     var div = document.createElement('div');
     div.innerHTML = this.stripTags();
-    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
   },
 
-  toQueryParams: function() {
-    var pairs = this.match(/^\??(.*)$/)[1].split('&');
-    return pairs.inject({}, function(params, pairString) {
-      var pair = pairString.split('=');
-      params[pair[0]] = pair[1];
-      return params;
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var name = decodeURIComponent(pair[0]);
+        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
+
+        if (hash[name] !== undefined) {
+          if (hash[name].constructor != Array)
+            hash[name] = [hash[name]];
+          if (value) hash[name].push(value);
+        }
+        else hash[name] = value;
+      }
+      return hash;
     });
   },
 
@@ -205,24 +251,43 @@
     return this.split('');
   },
 
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
   camelize: function() {
-    var oStringList = this.split('-');
-    if (oStringList.length == 1) return oStringList[0];
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
 
-    var camelizedString = this.indexOf('-') == 0
-      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
-      : oStringList[0];
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
 
-    for (var i = 1, len = oStringList.length; i < len; i++) {
-      var s = oStringList[i];
-      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
-    }
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 
-    return camelizedString;
+    return camelized;
   },
 
-  inspect: function() {
-    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  capitalize: function(){
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.replace(/\\/g, '\\\\');
+    if (useDoubleQuotes)
+      return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    else
+      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
   }
 });
 
@@ -246,7 +311,7 @@
     return this.template.gsub(this.pattern, function(match) {
       var before = match[1];
       if (before == '\\') return match[2];
-      return before + (object[match[3]] || '').toString();
+      return before + String.interpret(object[match[3]]);
     });
   }
 }
@@ -268,8 +333,16 @@
     } catch (e) {
       if (e != $break) throw e;
     }
+    return this;
   },
 
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
+  },
+
   all: function(iterator) {
     var result = true;
     this.each(function(value, index) {
@@ -280,7 +353,7 @@
   },
 
   any: function(iterator) {
-    var result = true;
+    var result = false;
     this.each(function(value, index) {
       if (result = !!(iterator || Prototype.K)(value, index))
         throw $break;
@@ -291,12 +364,12 @@
   collect: function(iterator) {
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push((iterator || Prototype.K)(value, index));
     });
     return results;
   },
 
-  detect: function (iterator) {
+  detect: function(iterator) {
     var result;
     this.each(function(value, index) {
       if (iterator(value, index)) {
@@ -337,6 +410,14 @@
     return found;
   },
 
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
   inject: function(memo, iterator) {
     this.each(function(value, index) {
       memo = iterator(memo, value, index);
@@ -346,7 +427,7 @@
 
   invoke: function(method) {
     var args = $A(arguments).slice(1);
-    return this.collect(function(value) {
+    return this.map(function(value) {
       return value[method].apply(value, args);
     });
   },
@@ -398,7 +479,7 @@
   },
 
   sortBy: function(iterator) {
-    return this.collect(function(value, index) {
+    return this.map(function(value, index) {
       return {value: value, criteria: iterator(value, index)};
     }).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
@@ -407,7 +488,7 @@
   },
 
   toArray: function() {
-    return this.collect(Prototype.K);
+    return this.map();
   },
 
   zip: function() {
@@ -421,6 +502,10 @@
     });
   },
 
+  size: function() {
+    return this.toArray().length;
+  },
+
   inspect: function() {
     return '#<Enumerable:' + this.toArray().inspect() + '>';
   }
@@ -439,7 +524,7 @@
     return iterable.toArray();
   } else {
     var results = [];
-    for (var i = 0; i < iterable.length; i++)
+    for (var i = 0, length = iterable.length; i < length; i++)
       results.push(iterable[i]);
     return results;
   }
@@ -452,7 +537,7 @@
 
 Object.extend(Array.prototype, {
   _each: function(iterator) {
-    for (var i = 0; i < this.length; i++)
+    for (var i = 0, length = this.length; i < length; i++)
       iterator(this[i]);
   },
 
@@ -471,7 +556,7 @@
 
   compact: function() {
     return this.select(function(value) {
-      return value != undefined || value != null;
+      return value != null;
     });
   },
 
@@ -490,7 +575,7 @@
   },
 
   indexOf: function(object) {
-    for (var i = 0; i < this.length; i++)
+    for (var i = 0, length = this.length; i < length; i++)
       if (this[i] == object) return i;
     return -1;
   },
@@ -499,15 +584,88 @@
     return (inline !== false ? this : this.toArray())._reverse();
   },
 
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function() {
+    return this.inject([], function(array, value) {
+      return array.include(value) ? array : array.concat([value]);
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
   inspect: function() {
     return '[' + this.map(Object.inspect).join(', ') + ']';
   }
 });
-var Hash = {
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string){
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if(window.opera){
+  Array.prototype.concat = function(){
+    var array = [];
+    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      if(arguments[i].constructor == Array) {
+        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(obj) {
+  Object.extend(this, obj || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+
+	  this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+
+      if (pair.value && pair.value.constructor == Array) {
+        var values = pair.value.compact();
+        if (values.length < 2) pair.value = values.reduce();
+        else {
+        	key = encodeURIComponent(pair.key);
+          values.each(function(value) {
+            value = value != undefined ? encodeURIComponent(value) : '';
+            parts.push(key + '=' + encodeURIComponent(value));
+          });
+          return;
+        }
+      }
+      if (pair.value == undefined) pair[1] = '';
+      parts.push(pair.map(encodeURIComponent).join('='));
+	  });
+
+    return parts.join('&');
+  }
+});
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
   _each: function(iterator) {
     for (var key in this) {
       var value = this[key];
-      if (typeof value == 'function') continue;
+      if (value && value == Hash.prototype[key]) continue;
 
       var pair = [key, value];
       pair.key = key;
@@ -525,16 +683,30 @@
   },
 
   merge: function(hash) {
-    return $H(hash).inject($H(this), function(mergedHash, pair) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
       mergedHash[pair.key] = pair.value;
       return mergedHash;
     });
   },
 
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
   toQueryString: function() {
-    return this.map(function(pair) {
-      return pair.map(encodeURIComponent).join('=');
-    }).join('&');
+    return Hash.toQueryString(this);
   },
 
   inspect: function() {
@@ -542,14 +714,12 @@
       return pair.map(Object.inspect).join(': ');
     }).join(', ') + '}>';
   }
-}
+});
 
 function $H(object) {
-  var hash = Object.extend({}, object || {});
-  Object.extend(hash, Enumerable);
-  Object.extend(hash, Hash);
-  return hash;
-}
+  if (object && object.constructor == Hash) return object;
+  return new Hash(object);
+};
 ObjectRange = Class.create();
 Object.extend(ObjectRange.prototype, Enumerable);
 Object.extend(ObjectRange.prototype, {
@@ -561,10 +731,10 @@
 
   _each: function(iterator) {
     var value = this.start;
-    do {
+    while (this.include(value)) {
       iterator(value);
       value = value.succ();
-    } while (this.include(value));
+    }
   },
 
   include: function(value) {
@@ -599,18 +769,18 @@
     this.responders._each(iterator);
   },
 
-  register: function(responderToAdd) {
-    if (!this.include(responderToAdd))
-      this.responders.push(responderToAdd);
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
   },
 
-  unregister: function(responderToRemove) {
-    this.responders = this.responders.without(responderToRemove);
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
   },
 
   dispatch: function(callback, request, transport, json) {
     this.each(function(responder) {
-      if (responder[callback] && typeof responder[callback] == 'function') {
+      if (typeof responder[callback] == 'function') {
         try {
           responder[callback].apply(responder, [request, transport, json]);
         } catch (e) {}
@@ -625,7 +795,6 @@
   onCreate: function() {
     Ajax.activeRequestCount++;
   },
-
   onComplete: function() {
     Ajax.activeRequestCount--;
   }
@@ -638,19 +807,14 @@
       method:       'post',
       asynchronous: true,
       contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
       parameters:   ''
     }
     Object.extend(this.options, options || {});
-  },
 
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0
-        || (this.transport.status >= 200 && this.transport.status < 300);
-  },
-
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
   }
 }
 
@@ -659,6 +823,8 @@
   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 
 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
   initialize: function(url, options) {
     this.transport = Ajax.getTransport();
     this.setOptions(options);
@@ -666,113 +832,148 @@
   },
 
   request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length > 0) parameters += '&_=';
+    this.url = url;
+    this.method = this.options.method;
+    var params = this.options.parameters;
 
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    params = Hash.toQueryString(params);
+    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
+
+    // when GET, append parameters to URL
+    if (this.method == 'get' && params)
+      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
+
     try {
-      this.url = url;
-      if (this.options.method == 'get' && parameters.length > 0)
-        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
-
       Ajax.Responders.dispatch('onCreate', this, this.transport);
 
-      this.transport.open(this.options.method, this.url,
+      this.transport.open(this.method.toUpperCase(), this.url,
         this.options.asynchronous);
 
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
 
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
       this.setRequestHeaders();
 
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
+      var body = this.method == 'post' ? (this.options.postBody || params) : null;
 
-    } catch (e) {
+      this.transport.send(body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
       this.dispatchException(e);
     }
   },
 
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
   setRequestHeaders: function() {
-    var requestHeaders =
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version,
-       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
 
-    if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', this.options.contentType);
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
 
-      /* Force "Connection: close" for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651.
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
        */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
     }
 
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
 
-    for (var i = 0; i < requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
-  },
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
 
-  onStateChange: function() {
-    var readyState = this.transport.readyState;
-    if (readyState != 1)
-      this.respondToReadyState(this.transport.readyState);
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
   },
 
-  header: function(name) {
-    try {
-      return this.transport.getResponseHeader(name);
-    } catch (e) {}
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
   },
 
-  evalJSON: function() {
-    try {
-      return eval('(' + this.header('X-JSON') + ')');
-    } catch (e) {}
-  },
-
-  evalResponse: function() {
-    try {
-      return eval(this.transport.responseText);
-    } catch (e) {
-      this.dispatchException(e);
-    }
-  },
-
   respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
+    var state = Ajax.Request.Events[readyState];
     var transport = this.transport, json = this.evalJSON();
 
-    if (event == 'Complete') {
+    if (state == 'Complete') {
       try {
+        this._complete = true;
         (this.options['on' + this.transport.status]
-         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
          || Prototype.emptyFunction)(transport, json);
       } catch (e) {
         this.dispatchException(e);
       }
 
-      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
-        this.evalResponse();
+      if ((this.getHeader('Content-type') || 'text/javascript').strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
     }
 
     try {
-      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
-      Ajax.Responders.dispatch('on' + event, this, transport, json);
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
     } catch (e) {
       this.dispatchException(e);
     }
 
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
       this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
   },
 
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? eval('(' + json + ')') : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
   dispatchException: function(exception) {
     (this.options.onException || Prototype.emptyFunction)(this, exception);
     Ajax.Responders.dispatch('onException', this, exception);
@@ -783,41 +984,37 @@
 
 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
   initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $(container.success) : $(container),
-      failure: container.failure ? $(container.failure) :
-        (container.success ? null : $(container))
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
     }
 
     this.transport = Ajax.getTransport();
     this.setOptions(options);
 
     var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function(transport, object) {
+    this.options.onComplete = (function(transport, param) {
       this.updateContent();
-      onComplete(transport, object);
+      onComplete(transport, param);
     }).bind(this);
 
     this.request(url);
   },
 
   updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
     var response = this.transport.responseText;
 
-    if (!this.options.evalScripts)
-      response = response.stripScripts();
+    if (!this.options.evalScripts) response = response.stripScripts();
 
-    if (receiver) {
-      if (this.options.insertion) {
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
         new this.options.insertion(receiver, response);
-      } else {
-        Element.update(receiver, response);
-      }
+      else
+        receiver.update(response);
     }
 
-    if (this.responseIsSuccess()) {
+    if (this.success()) {
       if (this.onComplete)
         setTimeout(this.onComplete.bind(this), 10);
     }
@@ -846,7 +1043,7 @@
   },
 
   stop: function() {
-    this.updater.onComplete = undefined;
+    this.updater.options.onComplete = undefined;
     clearTimeout(this.timer);
     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   },
@@ -866,25 +1063,43 @@
     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   }
 });
-function $() {
-  var results = [], element;
-  for (var i = 0; i < arguments.length; i++) {
-    element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-    results.push(Element.extend(element));
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
   }
-  return results.length < 2 ? results[0] : results;
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
 }
 
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+}
+
 document.getElementsByClassName = function(className, parentElement) {
-  var children = ($(parentElement) || document.body).getElementsByTagName('*');
-  return $A(children).inject([], function(elements, child) {
-    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
-      elements.push(Element.extend(child));
+  if (Prototype.BrowserFeatures.XPath) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  } else {
+    var children = ($(parentElement) || document.body).getElementsByTagName('*');
+    var elements = [], child;
+    for (var i = 0, length = children.length; i < length; i++) {
+      child = children[i];
+      if (Element.hasClassName(child, className))
+        elements.push(Element.extend(child));
+    }
     return elements;
-  });
-}
+  }
+};
 
 /*--------------------------------------------------------------------------*/
 
@@ -892,21 +1107,28 @@
   var Element = new Object();
 
 Element.extend = function(element) {
-  if (!element) return;
-  if (_nativeExtensions) return element;
+  if (!element || _nativeExtensions || element.nodeType == 3) return element;
 
   if (!element._extended && element.tagName && element != window) {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
+    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
+
+    if (element.tagName == 'FORM')
+      Object.extend(methods, Form.Methods);
+    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
+      Object.extend(methods, Form.Element.Methods);
+
+    Object.extend(methods, Element.Methods.Simulated);
+
+    for (var property in methods) {
       var value = methods[property];
-      if (typeof value == 'function')
+      if (typeof value == 'function' && !(property in element))
         element[property] = cache.findOrStore(value);
     }
   }
 
   element._extended = true;
   return element;
-}
+};
 
 Element.extend.cache = {
   findOrStore: function(value) {
@@ -914,46 +1136,45 @@
       return value.apply(null, [this].concat($A(arguments)));
     }
   }
-}
+};
 
 Element.Methods = {
   visible: function(element) {
     return $(element).style.display != 'none';
   },
 
-  toggle: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      Element[Element.visible(element) ? 'hide' : 'show'](element);
-    }
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
   },
 
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
-    }
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
   },
 
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
-    }
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
   },
 
   remove: function(element) {
     element = $(element);
     element.parentNode.removeChild(element);
+    return element;
   },
 
   update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
     $(element).innerHTML = html.stripScripts();
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
 
   replace: function(element, html) {
     element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
     if (element.outerHTML) {
       element.outerHTML = html.stripScripts();
     } else {
@@ -963,47 +1184,168 @@
         range.createContextualFragment(html.stripScripts()), element);
     }
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
 
-  getHeight: function(element) {
+  inspect: function(element) {
     element = $(element);
-    return element.offsetHeight;
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
   },
 
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*'));
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    return Selector.findElement($(element).ancestors(), expression, index);
+  },
+
+  down: function(element, expression, index) {
+    return Selector.findElement($(element).descendants(), expression, index);
+  },
+
+  previous: function(element, expression, index) {
+    return Selector.findElement($(element).previousSiblings(), expression, index);
+  },
+
+  next: function(element, expression, index) {
+    return Selector.findElement($(element).nextSiblings(), expression, index);
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (document.all && !window.opera) {
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      if(attribute) return attribute.nodeValue;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
   classNames: function(element) {
     return new Element.ClassNames(element);
   },
 
   hasClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).include(className);
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
   },
 
   addClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).add(className);
+    Element.classNames(element).add(className);
+    return element;
   },
 
   removeClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).remove(className);
+    Element.classNames(element).remove(className);
+    return element;
   },
 
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
   // removes whitespace-only text node children
   cleanWhitespace: function(element) {
     element = $(element);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
-        Element.remove(node);
+        element.removeChild(node);
+      node = nextNode;
     }
+    return element;
   },
 
   empty: function(element) {
     return $(element).innerHTML.match(/^\s*$/);
   },
 
-  childOf: function(element, ancestor) {
+  descendantOf: function(element, ancestor) {
     element = $(element), ancestor = $(ancestor);
     while (element = element.parentNode)
       if (element == ancestor) return true;
@@ -1012,38 +1354,69 @@
 
   scrollTo: function(element) {
     element = $(element);
-    var x = element.x ? element.x : element.offsetLeft,
-        y = element.y ? element.y : element.offsetTop;
-    window.scrollTo(x, y);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
   },
 
   getStyle: function(element, style) {
     element = $(element);
-    var value = element.style[style.camelize()];
+    if (['float','cssFloat'].include(style))
+      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
+    style = style.camelize();
+    var value = element.style[style];
     if (!value) {
       if (document.defaultView && document.defaultView.getComputedStyle) {
         var css = document.defaultView.getComputedStyle(element, null);
-        value = css ? css.getPropertyValue(style) : null;
+        value = css ? css[style] : null;
       } else if (element.currentStyle) {
-        value = element.currentStyle[style.camelize()];
+        value = element.currentStyle[style];
       }
     }
 
+    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
+      value = element['offset'+style.capitalize()] + 'px';
+
     if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
       if (Element.getStyle(element, 'position') == 'static') value = 'auto';
-
+    if(style == 'opacity') {
+      if(value) return parseFloat(value);
+      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if(value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
     return value == 'auto' ? null : value;
   },
 
   setStyle: function(element, style) {
     element = $(element);
-    for (var name in style)
-      element.style[name.camelize()] = style[name];
+    for (var name in style) {
+      var value = style[name];
+      if(name == 'opacity') {
+        if (value == 1) {
+          value = (/Gecko/.test(navigator.userAgent) &&
+            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
+          if(/MSIE/.test(navigator.userAgent) && !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
+        } else if(value == '') {
+          if(/MSIE/.test(navigator.userAgent) && !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
+        } else {
+          if(value < 0.00001) value = 0;
+          if(/MSIE/.test(navigator.userAgent) && !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
+              'alpha(opacity='+value*100+')';
+        }
+      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
+      element.style[name.camelize()] = value;
+    }
+    return element;
   },
 
   getDimensions: function(element) {
     element = $(element);
-    if (Element.getStyle(element, 'display') != 'none')
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
 
     // All *Width and *Height properties give 0 on elements with display none,
@@ -1051,12 +1424,13 @@
     var els = element.style;
     var originalVisibility = els.visibility;
     var originalPosition = els.position;
+    var originalDisplay = els.display;
     els.visibility = 'hidden';
     els.position = 'absolute';
-    els.display = '';
+    els.display = 'block';
     var originalWidth = element.clientWidth;
     var originalHeight = element.clientHeight;
-    els.display = 'none';
+    els.display = originalDisplay;
     els.position = originalPosition;
     els.visibility = originalVisibility;
     return {width: originalWidth, height: originalHeight};
@@ -1075,6 +1449,7 @@
         element.style.left = 0;
       }
     }
+    return element;
   },
 
   undoPositioned: function(element) {
@@ -1087,49 +1462,153 @@
         element.style.bottom =
         element.style.right = '';
     }
+    return element;
   },
 
   makeClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element._overflow = element.style.overflow;
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
       element.style.overflow = 'hidden';
+    return element;
   },
 
   undoClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element.style.overflow = element._overflow;
-    element._overflow = undefined;
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
   }
-}
+};
 
+Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
+
+Element._attributeTranslations = {};
+
+Element._attributeTranslations.names = {
+  colspan:   "colSpan",
+  rowspan:   "rowSpan",
+  valign:    "vAlign",
+  datetime:  "dateTime",
+  accesskey: "accessKey",
+  tabindex:  "tabIndex",
+  enctype:   "encType",
+  maxlength: "maxLength",
+  readonly:  "readOnly",
+  longdesc:  "longDesc"
+};
+
+Element._attributeTranslations.values = {
+  _getAttr: function(element, attribute) {
+    return element.getAttribute(attribute, 2);
+  },
+
+  _flag: function(element, attribute) {
+    return $(element).hasAttribute(attribute) ? attribute : null;
+  },
+
+  style: function(element) {
+    return element.style.cssText.toLowerCase();
+  },
+
+  title: function(element) {
+    var node = element.getAttributeNode('title');
+    return node.specified ? node.nodeValue : null;
+  }
+};
+
+Object.extend(Element._attributeTranslations.values, {
+  href: Element._attributeTranslations.values._getAttr,
+  src:  Element._attributeTranslations.values._getAttr,
+  disabled: Element._attributeTranslations.values._flag,
+  checked:  Element._attributeTranslations.values._flag,
+  readonly: Element._attributeTranslations.values._flag,
+  multiple: Element._attributeTranslations.values._flag
+});
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations;
+    attribute = t.names[attribute] || attribute;
+    return $(element).getAttributeNode(attribute).specified;
+  }
+};
+
+// IE is missing .innerHTML support for TABLE-related elements
+if (document.all && !window.opera){
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node){
+        element.removeChild(node)
+      });
+      depth.times(function(){ div = div.firstChild });
+
+      $A(div.childNodes).each(
+        function(node){ element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  }
+};
+
 Object.extend(Element, Element.Methods);
 
 var _nativeExtensions = false;
 
-if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
-  var HTMLElement = {}
-  HTMLElement.prototype = document.createElement('div').__proto__;
-}
+if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
+    var className = 'HTML' + tag + 'Element';
+    if(window[className]) return;
+    var klass = window[className] = {};
+    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
+  });
 
 Element.addMethods = function(methods) {
   Object.extend(Element.Methods, methods || {});
 
-  if(typeof HTMLElement != 'undefined') {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
       var value = methods[property];
-      if (typeof value == 'function')
-        HTMLElement.prototype[property] = cache.findOrStore(value);
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
     }
+  }
+
+  if (typeof HTMLElement != 'undefined') {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+    copy(Form.Methods, HTMLFormElement.prototype);
+    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
+      copy(Form.Element.Methods, klass.prototype);
+    });
     _nativeExtensions = true;
   }
 }
 
-Element.addMethods();
-
 var Toggle = new Object();
 Toggle.display = Element.toggle;
 
@@ -1148,8 +1627,8 @@
       try {
         this.element.insertAdjacentHTML(this.adjacency, this.content);
       } catch (e) {
-        var tagName = this.element.tagName.toLowerCase();
-        if (tagName == 'tbody' || tagName == 'tr') {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
           this.insertContent(this.contentFromAnonymousTable());
         } else {
           throw e;
@@ -1248,20 +1727,18 @@
 
   add: function(classNameToAdd) {
     if (this.include(classNameToAdd)) return;
-    this.set(this.toArray().concat(classNameToAdd).join(' '));
+    this.set($A(this).concat(classNameToAdd).join(' '));
   },
 
   remove: function(classNameToRemove) {
     if (!this.include(classNameToRemove)) return;
-    this.set(this.select(function(className) {
-      return className != classNameToRemove;
-    }).join(' '));
+    this.set($A(this).without(classNameToRemove).join(' '));
   },
 
   toString: function() {
-    return this.toArray().join(' ');
+    return $A(this).join(' ');
   }
-}
+};
 
 Object.extend(Element.ClassNames.prototype, Enumerable);
 var Selector = Class.create();
@@ -1308,15 +1785,15 @@
     if (params.wildcard)
       conditions.push('true');
     if (clause = params.id)
-      conditions.push('element.id == ' + clause.inspect());
+      conditions.push('element.readAttribute("id") == ' + clause.inspect());
     if (clause = params.tagName)
       conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
     if ((clause = params.classNames).length > 0)
-      for (var i = 0; i < clause.length; i++)
-        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+      for (var i = 0, length = clause.length; i < length; i++)
+        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
     if (clause = params.attributes) {
       clause.each(function(attribute) {
-        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
         var splitValueBy = function(delimiter) {
           return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
         }
@@ -1329,7 +1806,7 @@
                           ); break;
           case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
           case '':
-          case undefined: conditions.push(value + ' != null'); break;
+          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
           default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
         }
       });
@@ -1340,6 +1817,7 @@
 
   compileMatcher: function() {
     this.match = new Function('element', 'if (!element.tagName) return false; \
+      element = $(element); \
       return ' + this.buildMatchExpression());
   },
 
@@ -1354,7 +1832,7 @@
     scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
 
     var results = [];
-    for (var i = 0; i < scope.length; i++)
+    for (var i = 0, length = scope.length; i < length; i++)
       if (this.match(element = scope[i]))
         results.push(Element.extend(element));
 
@@ -1366,206 +1844,241 @@
   }
 }
 
-function $$() {
-  return $A(arguments).map(function(expression) {
-    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
-      var selector = new Selector(expr);
-      return results.map(selector.findElements.bind(selector)).flatten();
-    });
-  }).flatten();
-}
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $(arguments[i]).value = '';
+Object.extend(Selector, {
+  matchElements: function(elements, expression) {
+    var selector = new Selector(expression);
+    return elements.select(selector.match.bind(selector)).map(Element.extend);
   },
 
-  focus: function(element) {
-    $(element).focus();
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') index = expression, expression = false;
+    return Selector.matchElements(elements, expression || '*')[index || 0];
   },
 
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
-    return true;
-  },
+  findChildElements: function(element, expressions) {
+    return expressions.map(function(expression) {
+      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
+        var selector = new Selector(expr);
+        return results.inject([], function(elements, result) {
+          return elements.concat(selector.findElements(result || element));
+        });
+      });
+    }).flatten();
+  }
+});
 
-  select: function(element) {
-    $(element).select();
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
   },
 
-  activate: function(element) {
-    element = $(element);
-    element.focus();
-    if (element.select)
-      element.select();
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != undefined) {
+          if (result[key]) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
   }
-}
+};
 
-/*--------------------------------------------------------------------------*/
-
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
-
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
-    }
-
-    return queryComponents.join('&');
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
   },
 
   getElements: function(form) {
-    form = $(form);
-    var elements = new Array();
-
-    for (var tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
-    }
-    return elements;
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
   },
 
   getInputs: function(form, typeName, name) {
     form = $(form);
     var inputs = form.getElementsByTagName('input');
 
-    if (!typeName && !name)
-      return inputs;
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
 
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
       var input = inputs[i];
-      if ((typeName && input.type != typeName) ||
-          (name && input.name != name))
+      if ((typeName && input.type != typeName) || (name && input.name != name))
         continue;
-      matchingInputs.push(input);
+      matchingInputs.push(Element.extend(input));
     }
 
     return matchingInputs;
   },
 
   disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.blur();
       element.disabled = 'true';
-    }
+    });
+    return form;
   },
 
   enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.disabled = '';
-    }
+    });
+    return form;
   },
 
   findFirstElement: function(form) {
-    return Form.getElements(form).find(function(element) {
+    return $(form).getElements().find(function(element) {
       return element.type != 'hidden' && !element.disabled &&
         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     });
   },
 
   focusFirstElement: function(form) {
-    Field.activate(Form.findFirstElement(form));
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
   },
 
-  reset: function(form) {
-    $(form).reset();
+  select: function(element) {
+    $(element).select();
+    return element;
   }
 }
 
-Form.Element = {
+Form.Element.Methods = {
   serialize: function(element) {
     element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
     var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    return Form.Element.Serializers[method](element);
+  },
 
-    if (parameter) {
-      var key = encodeURIComponent(parameter[0]);
-      if (key.length == 0) return;
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
 
-      if (parameter[1].constructor != Array)
-        parameter[1] = [parameter[1]];
+  present: function(element) {
+    return $(element).value != '';
+  },
 
-      return parameter[1].map(function(value) {
-        return key + '=' + encodeURIComponent(value);
-      }).join('&');
-    }
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
+      !['button', 'reset', 'submit'].include(element.type) ) )
+      element.select();
+    return element;
   },
 
-  getValue: function(element) {
+  disable: function(element) {
     element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    element.disabled = true;
+    return element;
+  },
 
-    if (parameter)
-      return parameter[1];
+  enable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = false;
+    return element;
   }
 }
 
+Object.extend(Form.Element, Form.Element.Methods);
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
 Form.Element.Serializers = {
   input: function(element) {
     switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
       case 'checkbox':
       case 'radio':
         return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
     }
-    return false;
   },
 
   inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
+    return element.checked ? element.value : null;
   },
 
   textarea: function(element) {
-    return [element.name, element.value];
+    return element.value;
   },
 
   select: function(element) {
-    return Form.Element.Serializers[element.type == 'select-one' ?
+    return this[element.type == 'select-one' ?
       'selectOne' : 'selectMany'](element);
   },
 
   selectOne: function(element) {
-    var value = '', opt, index = element.selectedIndex;
-    if (index >= 0) {
-      opt = element.options[index];
-      value = opt.value || opt.text;
-    }
-    return [element.name, value];
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
   },
 
   selectMany: function(element) {
-    var value = [];
-    for (var i = 0; i < element.length; i++) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
       var opt = element.options[i];
-      if (opt.selected)
-        value.push(opt.value || opt.text);
+      if (opt.selected) values.push(this.optionValue(opt));
     }
-    return [element.name, value];
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
   }
 }
 
 /*--------------------------------------------------------------------------*/
 
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
 Abstract.TimedObserver = function() {}
 Abstract.TimedObserver.prototype = {
   initialize: function(element, frequency, callback) {
@@ -1583,7 +2096,9 @@
 
   onTimerEvent: function() {
     var value = this.getValue();
-    if (this.lastValue != value) {
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
       this.callback(this.element, value);
       this.lastValue = value;
     }
@@ -1628,9 +2143,7 @@
   },
 
   registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
   },
 
   registerCallback: function(element) {
@@ -1640,11 +2153,7 @@
         case 'radio':
           Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
+        default:
           Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
@@ -1679,6 +2188,10 @@
   KEY_RIGHT:    39,
   KEY_DOWN:     40,
   KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
 
   element: function(event) {
     return event.target || event.srcElement;
@@ -1734,7 +2247,7 @@
 
   unloadCache: function() {
     if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
       Event.stopObserving.apply(this, Event.observers[i]);
       Event.observers[i][0] = null;
     }
@@ -1742,7 +2255,7 @@
   },
 
   observe: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &&
@@ -1750,11 +2263,11 @@
         || element.attachEvent))
       name = 'keydown';
 
-    this._observeAndCache(element, name, observer, useCapture);
+    Event._observeAndCache(element, name, observer, useCapture);
   },
 
   stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &&
@@ -1765,7 +2278,9 @@
     if (element.removeEventListener) {
       element.removeEventListener(name, observer, useCapture);
     } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
     }
   }
 });
@@ -1819,7 +2334,8 @@
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        p = Element.getStyle(element, 'position');
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
         if (p == 'relative' || p == 'absolute') break;
       }
     } while (element);
@@ -1875,17 +2391,6 @@
         element.offsetWidth;
   },
 
-  clone: function(source, target) {
-    source = $(source);
-    target = $(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
-  },
-
   page: function(forElement) {
     var valueT = 0, valueL = 0;
 
@@ -1902,8 +2407,10 @@
 
     element = forElement;
     do {
-      valueT -= element.scrollTop  || 0;
-      valueL -= element.scrollLeft || 0;
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
     } while (element = element.parentNode);
 
     return [valueL, valueT];
@@ -1964,10 +2471,10 @@
     element._originalHeight = element.style.height;
 
     element.style.position = 'absolute';
-    element.style.top    = top + 'px';;
-    element.style.left   = left + 'px';;
-    element.style.width  = width + 'px';;
-    element.style.height = height + 'px';;
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
   },
 
   relativize: function(element) {
@@ -2003,4 +2510,6 @@
 
     return [valueL, valueT];
   }
-}
\ No newline at end of file
+}
+
+Element.addMethods();
\ No newline at end of file

Added: users/jberry/mpwa/public/stylesheets/scaffold.css
===================================================================
--- users/jberry/mpwa/public/stylesheets/scaffold.css	                        (rev 0)
+++ users/jberry/mpwa/public/stylesheets/scaffold.css	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,74 @@
+body { background-color: #fff; color: #333; }
+
+body, p, ol, ul, td {
+  font-family: verdana, arial, helvetica, sans-serif;
+  font-size:   13px;
+  line-height: 18px;
+}
+
+pre {
+  background-color: #eee;
+  padding: 10px;
+  font-size: 11px;
+}
+
+a { color: #000; }
+a:visited { color: #666; }
+a:hover { color: #fff; background-color:#000; }
+
+.fieldWithErrors {
+  padding: 2px;
+  background-color: red;
+  display: table;
+}
+
+#errorExplanation {
+  width: 400px;
+  border: 2px solid red;
+  padding: 7px;
+  padding-bottom: 12px;
+  margin-bottom: 20px;
+  background-color: #f0f0f0;
+}
+
+#errorExplanation h2 {
+  text-align: left;
+  font-weight: bold;
+  padding: 5px 5px 5px 15px;
+  font-size: 12px;
+  margin: -7px;
+  background-color: #c00;
+  color: #fff;
+}
+
+#errorExplanation p {
+  color: #333;
+  margin-bottom: 0;
+  padding: 5px;
+}
+
+#errorExplanation ul li {
+  font-size: 12px;
+  list-style: square;
+}
+
+div.uploadStatus {
+  margin: 5px;
+}
+
+div.progressBar {
+  margin: 5px;
+}
+
+div.progressBar div.border {
+  background-color: #fff;
+  border: 1px solid grey;
+  width: 100%;
+}
+
+div.progressBar div.background {
+  background-color: #333;
+  height: 18px;
+  width: 0%;
+}
+

Added: users/jberry/mpwa/script/process/inspector
===================================================================
--- users/jberry/mpwa/script/process/inspector	                        (rev 0)
+++ users/jberry/mpwa/script/process/inspector	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/inspector'


Property changes on: users/jberry/mpwa/script/process/inspector
___________________________________________________________________
Name: svn:executable
   + *

Added: users/jberry/mpwa/test/fixtures/people.yml
===================================================================
--- users/jberry/mpwa/test/fixtures/people.yml	                        (rev 0)
+++ users/jberry/mpwa/test/fixtures/people.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2

Added: users/jberry/mpwa/test/fixtures/port_pkgs.yml
===================================================================
--- users/jberry/mpwa/test/fixtures/port_pkgs.yml	                        (rev 0)
+++ users/jberry/mpwa/test/fixtures/port_pkgs.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2

Added: users/jberry/mpwa/test/fixtures/ports.yml
===================================================================
--- users/jberry/mpwa/test/fixtures/ports.yml	                        (rev 0)
+++ users/jberry/mpwa/test/fixtures/ports.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2

Added: users/jberry/mpwa/test/fixtures/tags.yml
===================================================================
--- users/jberry/mpwa/test/fixtures/tags.yml	                        (rev 0)
+++ users/jberry/mpwa/test/fixtures/tags.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2

Added: users/jberry/mpwa/test/fixtures/users.yml
===================================================================
--- users/jberry/mpwa/test/fixtures/users.yml	                        (rev 0)
+++ users/jberry/mpwa/test/fixtures/users.yml	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2

Added: users/jberry/mpwa/test/functional/person_controller_test.rb
===================================================================
--- users/jberry/mpwa/test/functional/person_controller_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/functional/person_controller_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'person_controller'
+
+# Re-raise errors caught by the controller.
+class PersonController; def rescue_action(e) raise e end; end
+
+class PersonControllerTest < Test::Unit::TestCase
+  fixtures :people
+
+  def setup
+    @controller = PersonController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @first_id = people(:first).id
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:people)
+  end
+
+  def test_show
+    get :show, :id => @first_id
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:person)
+    assert assigns(:person).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:person)
+  end
+
+  def test_create
+    num_people = Person.count
+
+    post :create, :person => {}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_people + 1, Person.count
+  end
+
+  def test_edit
+    get :edit, :id => @first_id
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:person)
+    assert assigns(:person).valid?
+  end
+
+  def test_update
+    post :update, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'show', :id => @first_id
+  end
+
+  def test_destroy
+    assert_nothing_raised {
+      Person.find(@first_id)
+    }
+
+    post :destroy, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_raise(ActiveRecord::RecordNotFound) {
+      Person.find(@first_id)
+    }
+  end
+end

Added: users/jberry/mpwa/test/functional/port_controller_test.rb
===================================================================
--- users/jberry/mpwa/test/functional/port_controller_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/functional/port_controller_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'port_controller'
+
+# Re-raise errors caught by the controller.
+class PortController; def rescue_action(e) raise e end; end
+
+class PortControllerTest < Test::Unit::TestCase
+  fixtures :ports
+
+  def setup
+    @controller = PortController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @first_id = ports(:first).id
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:ports)
+  end
+
+  def test_show
+    get :show, :id => @first_id
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:port)
+    assert assigns(:port).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:port)
+  end
+
+  def test_create
+    num_ports = Port.count
+
+    post :create, :port => {}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_ports + 1, Port.count
+  end
+
+  def test_edit
+    get :edit, :id => @first_id
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:port)
+    assert assigns(:port).valid?
+  end
+
+  def test_update
+    post :update, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'show', :id => @first_id
+  end
+
+  def test_destroy
+    assert_nothing_raised {
+      Port.find(@first_id)
+    }
+
+    post :destroy, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_raise(ActiveRecord::RecordNotFound) {
+      Port.find(@first_id)
+    }
+  end
+end

Added: users/jberry/mpwa/test/functional/port_pkg_controller_test.rb
===================================================================
--- users/jberry/mpwa/test/functional/port_pkg_controller_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/functional/port_pkg_controller_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'port_pkg_controller'
+
+# Re-raise errors caught by the controller.
+class PortPkgController; def rescue_action(e) raise e end; end
+
+class PortPkgControllerTest < Test::Unit::TestCase
+  fixtures :port_pkgs
+
+  def setup
+    @controller = PortPkgController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @first_id = port_pkgs(:first).id
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:port_pkgs)
+  end
+
+  def test_show
+    get :show, :id => @first_id
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:port_pkg)
+    assert assigns(:port_pkg).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:port_pkg)
+  end
+
+  def test_create
+    num_port_pkgs = PortPkg.count
+
+    post :create, :port_pkg => {}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_port_pkgs + 1, PortPkg.count
+  end
+
+  def test_edit
+    get :edit, :id => @first_id
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:port_pkg)
+    assert assigns(:port_pkg).valid?
+  end
+
+  def test_update
+    post :update, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'show', :id => @first_id
+  end
+
+  def test_destroy
+    assert_nothing_raised {
+      PortPkg.find(@first_id)
+    }
+
+    post :destroy, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_raise(ActiveRecord::RecordNotFound) {
+      PortPkg.find(@first_id)
+    }
+  end
+end

Added: users/jberry/mpwa/test/functional/tag_controller_test.rb
===================================================================
--- users/jberry/mpwa/test/functional/tag_controller_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/functional/tag_controller_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'tag_controller'
+
+# Re-raise errors caught by the controller.
+class TagController; def rescue_action(e) raise e end; end
+
+class TagControllerTest < Test::Unit::TestCase
+  fixtures :tags
+
+  def setup
+    @controller = TagController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @first_id = tags(:first).id
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:tags)
+  end
+
+  def test_show
+    get :show, :id => @first_id
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:tag)
+    assert assigns(:tag).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:tag)
+  end
+
+  def test_create
+    num_tags = Tag.count
+
+    post :create, :tag => {}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_tags + 1, Tag.count
+  end
+
+  def test_edit
+    get :edit, :id => @first_id
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:tag)
+    assert assigns(:tag).valid?
+  end
+
+  def test_update
+    post :update, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'show', :id => @first_id
+  end
+
+  def test_destroy
+    assert_nothing_raised {
+      Tag.find(@first_id)
+    }
+
+    post :destroy, :id => @first_id
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_raise(ActiveRecord::RecordNotFound) {
+      Tag.find(@first_id)
+    }
+  end
+end

Added: users/jberry/mpwa/test/unit/person_test.rb
===================================================================
--- users/jberry/mpwa/test/unit/person_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/unit/person_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PersonTest < Test::Unit::TestCase
+  fixtures :people
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end

Added: users/jberry/mpwa/test/unit/port_pkg_test.rb
===================================================================
--- users/jberry/mpwa/test/unit/port_pkg_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/unit/port_pkg_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PortPkgTest < Test::Unit::TestCase
+  fixtures :port_pkgs
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end

Added: users/jberry/mpwa/test/unit/port_test.rb
===================================================================
--- users/jberry/mpwa/test/unit/port_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/unit/port_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PortTest < Test::Unit::TestCase
+  fixtures :ports
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end

Added: users/jberry/mpwa/test/unit/tag_test.rb
===================================================================
--- users/jberry/mpwa/test/unit/tag_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/unit/tag_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TagTest < Test::Unit::TestCase
+  fixtures :tags
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end

Added: users/jberry/mpwa/test/unit/user_test.rb
===================================================================
--- users/jberry/mpwa/test/unit/user_test.rb	                        (rev 0)
+++ users/jberry/mpwa/test/unit/user_test.rb	2007-04-07 23:43:09 UTC (rev 23729)
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UserTest < Test::Unit::TestCase
+  fixtures :users
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20070407/75073880/attachment.html


More information about the macports-changes mailing list