Index: /trunk/.classpath
===================================================================
--- /trunk/.classpath	(revision 104)
+++ /trunk/.classpath	(revision 104)
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+<classpathentry kind="src" path="src/java"/>
+<classpathentry kind="src" path="src/groovy"/>
+<classpathentry kind="src" path="grails-app/conf"/>
+<classpathentry kind="src" path="grails-app/controllers"/>
+<classpathentry kind="src" path="grails-app/domain"/>
+<classpathentry kind="src" path="grails-app/services"/>
+<classpathentry kind="src" path="grails-app/taglib"/>
+<classpathentry kind="src" path="test/integration"/>
+<classpathentry kind="src" path="test/unit"/>
+<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+<classpathentry kind="output" path="web-app/WEB-INF/classes"/>
+<classpathentry kind="var" path="GRAILS_HOME/ant/lib/ant.jar"/>
+<classpathentry kind="var" path="GRAILS_HOME/lib/cglib-nodep-2.1_3.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ant-junit-1.7.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.webflow-2.0.3.RELEASE.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/junit-3.8.2.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/spring-test-2.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-io-1.4.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.4.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-collections-3.2.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jcl-over-slf4j-1.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/spring-webmvc-2.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-validator-1.3.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-util-6.1.14.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jta-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/antlr-2.7.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-cli-1.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/servlet-api-2.5-6.1.14.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/oscache-2.4.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-api-1.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.3.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ivy-2.0.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ant-1.7.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/start.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ant-nodeps-1.7.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ehcache-1.5.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/hsqldb-1.8.0.5.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-el-1.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/groovy-all-1.6.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.3.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/serializer.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jsr107cache-1.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-pool-1.2.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-lang-2.4.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.4.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.js-2.0.3.RELEASE.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-beanutils-1.7.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-plus-6.1.14.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/oro-2.0.8.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-log4j12-1.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/backport-util-concurrent-3.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jline-0.9.91.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-runtime-5.5.15.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ant-trax.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/log4j-1.2.15.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/xpp3_min-1.1.3.4.O.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-fileupload-1.1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-5.5.15.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/gant_groovy1.6-1.6.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-naming-6.1.14.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-dbcp-1.2.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-jdt-5.5.15.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/commons-codec-1.3.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.binding-2.0.3.RELEASE.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/spring-2.5.6.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ognl-2.6.9.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/svnkit-1.2.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/sitemesh-2.4.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-6.1.14.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ant-launcher-1.7.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/lib/ejb3-persistence-3.3.0.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-spring-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-crud-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-web-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-bootstrap-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-webflow-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-resources-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-gorm-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-test-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-scripts-1.1.jar" />
+<classpathentry kind="var" path="GRAILS_HOME/dist/grails-core-1.1.jar" />
+</classpath>
Index: /trunk/.project
===================================================================
--- /trunk/.project	(revision 104)
+++ /trunk/.project	(revision 104)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>gnuMims</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.codehaus.groovy.eclipse.groovyBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.codehaus.groovy.eclipse.groovyNature</nature>
+	</natures>
+</projectDescription>
Index: /trunk/COPYING
===================================================================
--- /trunk/COPYING	(revision 104)
+++ /trunk/COPYING	(revision 104)
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
Index: /trunk/README
===================================================================
--- /trunk/README	(revision 104)
+++ /trunk/README	(revision 104)
@@ -0,0 +1,12 @@
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
Index: /trunk/application.properties
===================================================================
--- /trunk/application.properties	(revision 104)
+++ /trunk/application.properties	(revision 104)
@@ -0,0 +1,9 @@
+#utf-8
+#Fri Apr 03 23:02:58 EST 2009
+plugins.help-balloons=1.2
+app.version=0.1
+plugins.acegi=0.5.1
+app.servlet.version=2.4
+app.grails.version=1.1
+plugins.hibernate=1.1
+app.name=gnuMims
Index: unk/appsrc/.classpath
===================================================================
--- /trunk/appsrc/.classpath	(revision 103)
+++ 	(revision )
@@ -1,86 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-<classpathentry kind="src" path="src/java"/>
-<classpathentry kind="src" path="src/groovy"/>
-<classpathentry kind="src" path="grails-app/conf"/>
-<classpathentry kind="src" path="grails-app/controllers"/>
-<classpathentry kind="src" path="grails-app/domain"/>
-<classpathentry kind="src" path="grails-app/services"/>
-<classpathentry kind="src" path="grails-app/taglib"/>
-<classpathentry kind="src" path="test/integration"/>
-<classpathentry kind="src" path="test/unit"/>
-<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-<classpathentry kind="output" path="web-app/WEB-INF/classes"/>
-<classpathentry kind="var" path="GRAILS_HOME/ant/lib/ant.jar"/>
-<classpathentry kind="var" path="GRAILS_HOME/lib/cglib-nodep-2.1_3.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ant-junit-1.7.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.webflow-2.0.3.RELEASE.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/junit-3.8.2.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/spring-test-2.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-io-1.4.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.4.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-collections-3.2.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jcl-over-slf4j-1.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/spring-webmvc-2.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-validator-1.3.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-util-6.1.14.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jta-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/antlr-2.7.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-cli-1.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/servlet-api-2.5-6.1.14.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/oscache-2.4.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-api-1.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.3.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ivy-2.0.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ant-1.7.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/start.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ant-nodeps-1.7.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ehcache-1.5.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/hsqldb-1.8.0.5.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-el-1.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/groovy-all-1.6.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.3.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/serializer.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jsr107cache-1.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-pool-1.2.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-lang-2.4.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.4.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.js-2.0.3.RELEASE.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-beanutils-1.7.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-plus-6.1.14.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/oro-2.0.8.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-log4j12-1.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/backport-util-concurrent-3.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jline-0.9.91.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-runtime-5.5.15.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ant-trax.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/log4j-1.2.15.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/xpp3_min-1.1.3.4.O.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-fileupload-1.1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-5.5.15.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/gant_groovy1.6-1.6.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-naming-6.1.14.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-dbcp-1.2.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-jdt-5.5.15.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/commons-codec-1.3.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.binding-2.0.3.RELEASE.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/spring-2.5.6.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ognl-2.6.9.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/svnkit-1.2.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/sitemesh-2.4.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-6.1.14.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ant-launcher-1.7.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/lib/ejb3-persistence-3.3.0.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-spring-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-crud-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-web-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-bootstrap-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-webflow-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-resources-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-gorm-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-test-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-scripts-1.1.jar" />
-<classpathentry kind="var" path="GRAILS_HOME/dist/grails-core-1.1.jar" />
-</classpath>
Index: unk/appsrc/.project
===================================================================
--- /trunk/appsrc/.project	(revision 103)
+++ 	(revision )
@@ -1,23 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>gnuMims</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.codehaus.groovy.eclipse.groovyBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>org.codehaus.groovy.eclipse.groovyNature</nature>
-	</natures>
-</projectDescription>
Index: unk/appsrc/COPYING
===================================================================
--- /trunk/appsrc/COPYING	(revision 103)
+++ 	(revision )
@@ -1,661 +1,0 @@
-                    GNU AFFERO GENERAL PUBLIC LICENSE
-                       Version 3, 19 November 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-our General Public Licenses are intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
-  A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate.  Many developers of free software are heartened and
-encouraged by the resulting cooperation.  However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
-  The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community.  It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server.  Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
-  An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals.  This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU Affero General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Remote Network Interaction; Use with the GNU General Public License.
-
-  Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software.  This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time.  Such new versions
-will be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU Affero General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
-
-    You should have received a copy of the GNU Affero General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source.  For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code.  There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-<http://www.gnu.org/licenses/>.
Index: unk/appsrc/README
===================================================================
--- /trunk/appsrc/README	(revision 103)
+++ 	(revision )
@@ -1,12 +1,0 @@
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
-
-    You should have received a copy of the GNU Affero General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
Index: unk/appsrc/application.properties
===================================================================
--- /trunk/appsrc/application.properties	(revision 103)
+++ 	(revision )
@@ -1,9 +1,0 @@
-#utf-8
-#Fri Apr 03 23:02:58 EST 2009
-plugins.help-balloons=1.2
-app.version=0.1
-plugins.acegi=0.5.1
-app.servlet.version=2.4
-app.grails.version=1.1
-plugins.hibernate=1.1
-app.name=gnuMims
Index: unk/appsrc/build.xml
===================================================================
--- /trunk/appsrc/build.xml	(revision 103)
+++ 	(revision )
@@ -1,131 +1,0 @@
-<project xmlns:ivy="antlib:org.apache.ivy.ant" name="gnumims" default="test">
-    <property environment="env"/>
-  	<property name="ivy.install.version" value="2.0.0" />
-    <condition property="ivy.home" value="${env.IVY_HOME}">
-      <isset property="env.IVY_HOME" />
-    </condition>
-    <property name="ivy.home" value="${user.home}/.ant" />
-    <property name="ivy.jar.dir" value="${ivy.home}/lib" />
-    <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-${ivy.install.version}.jar" />
-
-    <target name="download-ivy" unless="offline">
-		<available file="${ivy.jar.file}" property="ivy.available"/>
-		<antcall target="-download-ivy" />
-    </target>
-
-	<target name="-download-ivy" unless="ivy.available">
-        <mkdir dir="${ivy.jar.dir}"/>
-        <!-- download Ivy from web site so that it can be used even without any special installation -->
-        <get src="http://www.apache.org/dist/ant/ivy/${ivy.install.version}/apache-ivy-${ivy.install.version}-bin.zip"
-            dest="${ivy.home}/ivy.zip" usetimestamp="true" verbose="true"/>
-	    <unzip src="${ivy.home}/ivy.zip" dest="${ivy.jar.dir}">
-		   <patternset>
-		        <include name="**/*.jar"/>
-		    </patternset>
-			<mapper type="flatten"/>
-		</unzip>
-	</target>
-
-    <target name="init-ivy" depends="download-ivy" unless="ivy.lib.path">
-      <!-- try to load ivy here from ivy home, in case the user has not already dropped
-              it into ant's lib dir (note that the latter copy will always take precedence).
-              We will not fail as long as local lib dir exists (it may be empty) and
-              ivy is in at least one of ant's lib dir or the local lib dir. -->
-        <path id="ivy.lib.path">
-            <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
-        </path>
-        <taskdef resource="org/apache/ivy/ant/antlib.xml"
-                 uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
-    </target>
-
-
-    <property name="lib.dir" value="${basedir}/lib"/>
-
-    <macrodef name="grails">
-        <attribute name="script"/>
-        <attribute name="args" default="" />
-        <sequential>
-            <grailsTask script="@{script}" args="@{args}" classpathref="grails.classpath">
-                <compileClasspath refid="compile.classpath"/>
-                <testClasspath refid="test.classpath"/>
-                <runtimeClasspath refid="app.classpath"/>
-            </grailsTask>
-        </sequential>
-    </macrodef>
-
-    <!-- =================================
-          target: resolve
-         ================================= -->
-    <target name="-resolve" description="--> Retrieve dependencies with ivy" depends="init-ivy">
-        <ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[revision].[ext]"/>
-    </target>
-
-    <target name="-init-grails" depends="-resolve">
-        <path id="grails.classpath">
-            <fileset dir="${lib.dir}/build"/>
-        </path>
-
-        <path id="compile.classpath">
-            <fileset dir="${lib.dir}/compile"/>
-        </path>
-
-        <path id="test.classpath">
-            <fileset dir="${lib.dir}/test"/>
-        </path>
-
-        <path id="app.classpath">
-            <fileset dir="${lib.dir}/runtime"/>
-        </path>
-
-        <taskdef name="grailsTask"
-                 classname="grails.ant.GrailsTask"
-                 classpathref="grails.classpath"/>
-    </target>
-
-    <target name="deps-report" depends="-resolve" description="--> Generate report of module dependencies.">
-        <ivy:report conf="*"/>
-    </target>
-
-    <!-- =================================
-          target: clean
-         ================================= -->
-    <target name="clean" depends="-init-grails" description="--> Cleans a Grails application">
-        <grails script="Clean"/>
-        <delete dir="${lib.dir}" includes="**/*"/>
-    </target>
-
-    <!-- =================================
-          target: compile
-         ================================= -->
-    <target name="compile" depends="-init-grails" description="--> Compiles a Grails application">
-        <grails script="Compile"/>
-    </target>
-
-    <!-- =================================
-          target: war
-         ================================= -->
-    <target name="war" depends="-init-grails" description="--> Creates a WAR of a Grails application">
-        <grails script="War"/>
-    </target>
-
-    <!-- =================================
-          target: test
-         ================================= -->
-    <target name="test" depends="-init-grails" description="--> Run a Grails applications unit tests">
-        <grails script="TestApp"/>
-    </target>
-
-    <!-- =================================
-          target: run
-         ================================= -->
-    <target name="run" depends="-init-grails" description="--> Runs a Grails application using embedded Jetty">
-        <grails script="RunApp"/>
-    </target>
-
-    <!-- =================================
-          target: deploy
-         ================================= -->
-    <target name="deploy" depends="war" description="--> The deploy target (initially empty)">
-        <!-- TODO -->
-    </target>
-</project>
Index: unk/appsrc/gnuMims-test.launch
===================================================================
--- /trunk/appsrc/gnuMims-test.launch	(revision 103)
+++ 	(revision )
@@ -1,11 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="grails.test.GrailsAwareGroovyTestSuite"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gnuMims"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dtest=${resource_loc}"/>
-</launchConfiguration>
Index: unk/appsrc/gnuMims.launch
===================================================================
--- /trunk/appsrc/gnuMims.launch	(revision 103)
+++ 	(revision )
@@ -1,19 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/gnuMims"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;gnuMims&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;GRAILS_HOME/dist/grails-bootstrap-1.1.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;GRAILS_HOME/lib/groovy-all-1.6.0.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt;&#10;"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="grails.util.GrailsMain"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gnuMims"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dbase.dir=&quot;${project_loc:gnuMims}&quot; -Dserver.port=8080 -Dgrails.env=development"/>
-</launchConfiguration>
Index: unk/appsrc/gnuMims.tmproj
===================================================================
--- /trunk/appsrc/gnuMims.tmproj	(revision 103)
+++ 	(revision )
@@ -1,73 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>documents</key>
-	<array>
-		<dict>
-			<key>filename</key>
-			<string>gnuMims.launch</string>
-		</dict>
-		<dict>
-			<key>filename</key>
-			<string>build.xml</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>grails-app</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>grails-app</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>test</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>test</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>lib</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>lib</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>scripts</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>scripts</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>src</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>src</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>web-app</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
-			<key>sourceDirectory</key>
-			<string>web-app</string>
-		</dict>
-	</array>
-	<key>fileHierarchyDrawerWidth</key>
-	<integer>200</integer>
-	<key>metaData</key>
-	<dict/>
-	<key>showFileHierarchyDrawer</key>
-	<true/>
-	<key>windowFrame</key>
-	<string>{{237, 127}, {742, 553}}</string>
-</dict>
-</plist>
Index: unk/appsrc/ivy.xml
===================================================================
--- /trunk/appsrc/ivy.xml	(revision 103)
+++ 	(revision )
@@ -1,27 +1,0 @@
-<ivy-module version="2.0">
-    <info organisation="org.example" module="gnuMims"/>
-    <configurations defaultconfmapping="build->default;compile->compile(*),master(*);test,runtime->runtime(*),master(*)">
-        <conf name="build"/>
-        <conf name="compile"/>
-        <conf name="test" extends="compile"/>
-        <conf name="runtime" extends="compile"/>
-    </configurations>
-    <dependencies>
-        <dependency org="org.grails" name="grails-bootstrap" rev="1.1" conf="build"/>
-        <dependency org="org.grails" name="grails-scripts" rev="1.1" conf="build"/>
-        <dependency org="org.grails" name="grails-gorm" rev="1.1" conf="compile"/>
-        <dependency org="org.grails" name="grails-web" rev="1.1" conf="compile"/>
-        <dependency org="org.grails" name="grails-test" rev="1.1" conf="test"/>
-        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.5" conf="runtime"/>
-        <dependency org="opensymphony" name="oscache" rev="2.4" conf="runtime">
-            <exclude org="javax.jms" module="jms" name="*" type="*" ext="*" conf="" matcher="exact"/>
-            <exclude org="commons-logging" module="commons-logging" name="*" type="*" ext="*" conf="" matcher="exact"/>
-            <exclude org="javax.servlet" module="servlet-api" name="*" type="*" ext="*" conf="" matcher="exact"/>
-        </dependency>
-        <dependency org="hsqldb" name="hsqldb" rev="1.8.0.5" conf="runtime"/>
-        <!--
-        <dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="runtime"/>
-        <dependency org="postgresql" name="postgresql" rev="8.3-603.jdbc3" conf="runtime"/>
-        -->
-    </dependencies>
-</ivy-module>
Index: unk/appsrc/ivysettings.xml
===================================================================
--- /trunk/appsrc/ivysettings.xml	(revision 103)
+++ 	(revision )
@@ -1,15 +1,0 @@
-<ivysettings>
-  <settings defaultResolver="codehaus-plus"/>
-  <include url="${ivy.default.settings.dir}/ivysettings-public.xml" />
-  <include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
-  <include url="${ivy.default.settings.dir}/ivysettings-local.xml" />
-  <include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
-  <resolvers>
-    <chain name="codehaus-plus" dual="true">
-      <ibiblio name="codehaus-snapshots" root="http://snapshots.repository.codehaus.org" m2compatible="true" changingPattern=".*SNAPSHOT"/>
-      <ibiblio name="codehaus" root="http://repository.codehaus.org" m2compatible="true"/>
-      <ibiblio name="javanet" root="http://download.java.net/maven/2/" m2compatible="true"/>
-      <resolver ref="public"/>
-    </chain>
-  </resolvers>
-</ivysettings>
Index: /trunk/build.xml
===================================================================
--- /trunk/build.xml	(revision 104)
+++ /trunk/build.xml	(revision 104)
@@ -0,0 +1,131 @@
+<project xmlns:ivy="antlib:org.apache.ivy.ant" name="gnumims" default="test">
+    <property environment="env"/>
+  	<property name="ivy.install.version" value="2.0.0" />
+    <condition property="ivy.home" value="${env.IVY_HOME}">
+      <isset property="env.IVY_HOME" />
+    </condition>
+    <property name="ivy.home" value="${user.home}/.ant" />
+    <property name="ivy.jar.dir" value="${ivy.home}/lib" />
+    <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-${ivy.install.version}.jar" />
+
+    <target name="download-ivy" unless="offline">
+		<available file="${ivy.jar.file}" property="ivy.available"/>
+		<antcall target="-download-ivy" />
+    </target>
+
+	<target name="-download-ivy" unless="ivy.available">
+        <mkdir dir="${ivy.jar.dir}"/>
+        <!-- download Ivy from web site so that it can be used even without any special installation -->
+        <get src="http://www.apache.org/dist/ant/ivy/${ivy.install.version}/apache-ivy-${ivy.install.version}-bin.zip"
+            dest="${ivy.home}/ivy.zip" usetimestamp="true" verbose="true"/>
+	    <unzip src="${ivy.home}/ivy.zip" dest="${ivy.jar.dir}">
+		   <patternset>
+		        <include name="**/*.jar"/>
+		    </patternset>
+			<mapper type="flatten"/>
+		</unzip>
+	</target>
+
+    <target name="init-ivy" depends="download-ivy" unless="ivy.lib.path">
+      <!-- try to load ivy here from ivy home, in case the user has not already dropped
+              it into ant's lib dir (note that the latter copy will always take precedence).
+              We will not fail as long as local lib dir exists (it may be empty) and
+              ivy is in at least one of ant's lib dir or the local lib dir. -->
+        <path id="ivy.lib.path">
+            <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
+        </path>
+        <taskdef resource="org/apache/ivy/ant/antlib.xml"
+                 uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
+    </target>
+
+
+    <property name="lib.dir" value="${basedir}/lib"/>
+
+    <macrodef name="grails">
+        <attribute name="script"/>
+        <attribute name="args" default="" />
+        <sequential>
+            <grailsTask script="@{script}" args="@{args}" classpathref="grails.classpath">
+                <compileClasspath refid="compile.classpath"/>
+                <testClasspath refid="test.classpath"/>
+                <runtimeClasspath refid="app.classpath"/>
+            </grailsTask>
+        </sequential>
+    </macrodef>
+
+    <!-- =================================
+          target: resolve
+         ================================= -->
+    <target name="-resolve" description="--> Retrieve dependencies with ivy" depends="init-ivy">
+        <ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[revision].[ext]"/>
+    </target>
+
+    <target name="-init-grails" depends="-resolve">
+        <path id="grails.classpath">
+            <fileset dir="${lib.dir}/build"/>
+        </path>
+
+        <path id="compile.classpath">
+            <fileset dir="${lib.dir}/compile"/>
+        </path>
+
+        <path id="test.classpath">
+            <fileset dir="${lib.dir}/test"/>
+        </path>
+
+        <path id="app.classpath">
+            <fileset dir="${lib.dir}/runtime"/>
+        </path>
+
+        <taskdef name="grailsTask"
+                 classname="grails.ant.GrailsTask"
+                 classpathref="grails.classpath"/>
+    </target>
+
+    <target name="deps-report" depends="-resolve" description="--> Generate report of module dependencies.">
+        <ivy:report conf="*"/>
+    </target>
+
+    <!-- =================================
+          target: clean
+         ================================= -->
+    <target name="clean" depends="-init-grails" description="--> Cleans a Grails application">
+        <grails script="Clean"/>
+        <delete dir="${lib.dir}" includes="**/*"/>
+    </target>
+
+    <!-- =================================
+          target: compile
+         ================================= -->
+    <target name="compile" depends="-init-grails" description="--> Compiles a Grails application">
+        <grails script="Compile"/>
+    </target>
+
+    <!-- =================================
+          target: war
+         ================================= -->
+    <target name="war" depends="-init-grails" description="--> Creates a WAR of a Grails application">
+        <grails script="War"/>
+    </target>
+
+    <!-- =================================
+          target: test
+         ================================= -->
+    <target name="test" depends="-init-grails" description="--> Run a Grails applications unit tests">
+        <grails script="TestApp"/>
+    </target>
+
+    <!-- =================================
+          target: run
+         ================================= -->
+    <target name="run" depends="-init-grails" description="--> Runs a Grails application using embedded Jetty">
+        <grails script="RunApp"/>
+    </target>
+
+    <!-- =================================
+          target: deploy
+         ================================= -->
+    <target name="deploy" depends="war" description="--> The deploy target (initially empty)">
+        <!-- TODO -->
+    </target>
+</project>
Index: /trunk/gnuMims-test.launch
===================================================================
--- /trunk/gnuMims-test.launch	(revision 104)
+++ /trunk/gnuMims-test.launch	(revision 104)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="grails.test.GrailsAwareGroovyTestSuite"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gnuMims"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dtest=${resource_loc}"/>
+</launchConfiguration>
Index: /trunk/gnuMims.launch
===================================================================
--- /trunk/gnuMims.launch	(revision 104)
+++ /trunk/gnuMims.launch	(revision 104)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/gnuMims"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;gnuMims&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;GRAILS_HOME/dist/grails-bootstrap-1.1.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;GRAILS_HOME/lib/groovy-all-1.6.0.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="grails.util.GrailsMain"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gnuMims"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dbase.dir=&quot;${project_loc:gnuMims}&quot; -Dserver.port=8080 -Dgrails.env=development"/>
+</launchConfiguration>
Index: /trunk/gnuMims.tmproj
===================================================================
--- /trunk/gnuMims.tmproj	(revision 104)
+++ /trunk/gnuMims.tmproj	(revision 104)
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>documents</key>
+	<array>
+		<dict>
+			<key>filename</key>
+			<string>gnuMims.launch</string>
+		</dict>
+		<dict>
+			<key>filename</key>
+			<string>build.xml</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>grails-app</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>grails-app</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>test</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>test</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>lib</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>lib</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>scripts</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>scripts</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>src</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>src</string>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>web-app</string>
+			<key>regexFolderFilter</key>
+			<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+			<key>sourceDirectory</key>
+			<string>web-app</string>
+		</dict>
+	</array>
+	<key>fileHierarchyDrawerWidth</key>
+	<integer>200</integer>
+	<key>metaData</key>
+	<dict/>
+	<key>showFileHierarchyDrawer</key>
+	<true/>
+	<key>windowFrame</key>
+	<string>{{237, 127}, {742, 553}}</string>
+</dict>
+</plist>
Index: /trunk/grails-app/conf/BootStrap.groovy
===================================================================
--- /trunk/grails-app/conf/BootStrap.groovy	(revision 104)
+++ /trunk/grails-app/conf/BootStrap.groovy	(revision 104)
@@ -0,0 +1,350 @@
+import grails.util.GrailsUtil
+
+class BootStrap 
+{
+    //Required to be right here for Acegi plugin.
+    def authenticateService
+    Boolean BootStrapDemoDataSuccessful = true
+
+    def init = { servletContext ->
+
+    println "**** BootStrap GrailsUtil.environment = ${GrailsUtil.environment}"
+    
+        switch (GrailsUtil.environment)
+        {
+            case "development":
+                        bootStrapDemoData()
+                        break
+            case "test":
+                        break
+            case "production":
+                        bootStrapDemoData()
+                        break 
+        }
+    
+    }
+
+    def destroy = {
+    }
+
+    //Insert some demo/startup data.
+    void bootStrapDemoData()
+    {
+        println "BootStrapping demo data..."
+    
+        //TypeOfPersonGroup
+	def personGroupTypeInstance
+        personGroupTypeInstance = new PersonGroupType(name:"Department")
+	BootStrapSaveAndTest(personGroupTypeInstance)
+        personGroupTypeInstance = new PersonGroupType(name:"Contractor")
+	BootStrapSaveAndTest(personGroupTypeInstance)
+        personGroupTypeInstance = new PersonGroupType(name:"ProjectTeam")
+	BootStrapSaveAndTest(personGroupTypeInstance)
+    
+        //PersonGroup
+	def personGroupInstance
+        personGroupInstance = new PersonGroup(personGroupType:PersonGroupType.findByName("Department"),
+                        name:"Electrical")
+	BootStrapSaveAndTest(personGroupInstance)
+        personGroupInstance = new PersonGroup(personGroupType:PersonGroupType.findByName("Department"),
+                        name:"Mechanical")
+	BootStrapSaveAndTest(personGroupInstance)
+        personGroupInstance = new PersonGroup(personGroupType:PersonGroupType.findByName("Department"),
+                        name:"Production")
+	BootStrapSaveAndTest(personGroupInstance)
+        personGroupInstance = new PersonGroup(personGroupType:PersonGroupType.get(2),
+                        name:"Kewl AirCon Guys")
+	BootStrapSaveAndTest(personGroupInstance)
+        personGroupInstance = new PersonGroup(personGroupType:PersonGroupType.get(3),
+                        name:"gnuMims")
+	BootStrapSaveAndTest(personGroupInstance)
+
+        //Authority
+        def authInstance
+
+        authInstance = new Authority(description:"Application Admin, not required for daily use! Grants full admin access to the application.",
+                                        authority:"ROLE_AppAdmin")
+        BootStrapSaveAndTest(authInstance)
+
+        authInstance = new Authority(description:"Business manager, grants full management access.",
+                                        authority:"ROLE_Manager")
+        BootStrapSaveAndTest(authInstance)
+
+        authInstance = new Authority(description:"Application User, all application users need this base role to allow login.",
+                                        authority:"ROLE_AppUser")
+        BootStrapSaveAndTest(authInstance)
+            
+        //Person
+        def passClearText = "pass"
+        def passwordEncoded = authenticateService.encodePassword(passClearText)
+        def personInstance
+
+        //Person #1
+        personInstance = new Person(loginName:"admin",
+                                    firstName:"Admin",
+                                    lastName:"Powers",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"admin@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(1))
+        personInstance.addToAuthorities(Authority.get(2))
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("gnuMims"))
+
+        //Person #2
+        personInstance = new Person(loginName:"manager",
+                                    firstName:"Meca",
+                                    lastName:"Manager",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"manager@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(2))
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("gnuMims"))
+
+        //Person #3
+        personInstance = new Person(loginName:"user",
+                                    firstName:"Demo",
+                                    lastName:"User",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"user@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("Electrical"))
+
+        //Person #4
+        personInstance = new Person(loginName:"craig",
+                                    firstName:"Craig",
+                                    lastName:"SuperSparky",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"user@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("Electrical"))
+
+        //Person #5
+        personInstance = new Person(loginName:"john",
+                                    firstName:"John",
+                                    lastName:"SuperFitter",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"user@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("Mechanical"))
+
+        //Person #6
+        personInstance = new Person(loginName:"mann",
+                                    firstName:"Production",
+                                    lastName:"Mann",
+                                    pass:passClearText,
+                                    password:passwordEncoded,
+                                    email:"user@example.com")
+        BootStrapSaveAndTest(personInstance)
+        personInstance.addToAuthorities(Authority.get(3))
+        personInstance.addToPersonGroups(PersonGroup.findByName("Production"))
+
+        //TaskGroup
+        def taskGroupInstance
+
+        taskGroupInstance = new TaskGroup(name:"Engineering Activites",
+                      description:"Engineering daily activities")
+        BootStrapSaveAndTest(taskGroupInstance)
+
+        taskGroupInstance = new TaskGroup(name:"Production Activites",
+                      description:"Production daily activities")
+        BootStrapSaveAndTest(taskGroupInstance)
+
+        taskGroupInstance = new TaskGroup(name:"New Projects",
+                      description:" ")
+        BootStrapSaveAndTest(taskGroupInstance)
+
+        //TaskStatus
+        def taskStatusInstance
+    
+        taskStatusInstance = new TaskStatus(name:"Not Started")
+        BootStrapSaveAndTest(taskStatusInstance)
+
+        taskStatusInstance = new TaskStatus(name:"In Progress")
+        BootStrapSaveAndTest(taskStatusInstance)
+
+        taskStatusInstance = new TaskStatus(name:"Completed")
+        BootStrapSaveAndTest(taskStatusInstance)
+
+        //TaskPriority
+        def taskPriorityInstance
+
+        taskPriorityInstance = new TaskPriority(name:"Low")
+        BootStrapSaveAndTest(taskPriorityInstance)
+
+        taskPriorityInstance = new TaskPriority(name:"Normal")
+        BootStrapSaveAndTest(taskPriorityInstance)
+
+        taskPriorityInstance = new TaskPriority(name:"High")
+        BootStrapSaveAndTest(taskPriorityInstance)
+
+        taskPriorityInstance = new TaskPriority(name:"Immediate")
+        BootStrapSaveAndTest(taskPriorityInstance)
+
+        //TaskType
+        def taskTypeInstance
+
+        taskTypeInstance = new TaskType(name:"Unscheduled Breakin")
+        BootStrapSaveAndTest(taskTypeInstance)
+
+        taskTypeInstance = new TaskType(name:"Planned Maintenance")
+        BootStrapSaveAndTest(taskTypeInstance)
+
+        taskTypeInstance = new TaskType(name:"Project")
+        BootStrapSaveAndTest(taskTypeInstance)
+
+        taskTypeInstance = new TaskType(name:"Turnaround")
+        BootStrapSaveAndTest(taskTypeInstance)
+
+        taskTypeInstance = new TaskType(name:"Production Run")
+        BootStrapSaveAndTest(taskTypeInstance)
+
+        //Task
+        def taskInstance
+        def subTaskInstance
+
+        taskInstance = new Task(taskGroup:TaskGroup.findByName("Engineering Activites"),
+                 taskStatus:TaskStatus.findByName("Not Started"),
+                 taskPriority:TaskPriority.get(2),
+                 taskType:TaskType.get(1),
+                 leadPerson:Person.get(3),
+                 description:"Check specific level sensor",
+                 comment:"Has been noted as problematic, try recallibrating")
+        BootStrapSaveAndTest(taskInstance)
+
+        subTaskInstance = new Task(taskGroup:TaskGroup.findByName("Engineering Activites"),
+                 taskStatus:TaskStatus.findByName("Not Started"),
+                 taskPriority:TaskPriority.get(2),
+                 taskType:TaskType.get(1),
+                 leadPerson:Person.get(5),
+                 description:"Some follow-up work",
+                 comment:"Some help required")
+        BootStrapSaveAndTest(subTaskInstance)
+        
+        //Add task 2 as a subTask of task 1.
+        taskInstance.addToSubTasks(Task.get(2))
+        subTaskInstance.parentTask = Task.get(1)
+
+        subTaskInstance = new Task(taskGroup:TaskGroup.findByName("Engineering Activites"),
+                 taskStatus:TaskStatus.findByName("Not Started"),
+                 taskPriority:TaskPriority.get(2),
+                 taskType:TaskType.get(1),
+                 leadPerson:Person.get(4),
+                 description:"Replace sensor at next opportunity.",
+                 comment:"Nothing else has worked.")
+        BootStrapSaveAndTest(subTaskInstance)
+
+        //Add task 3 as a subTask of task 1.
+        taskInstance.addToSubTasks(Task.get(3))
+        subTaskInstance.parentTask = Task.get(1)
+
+        taskInstance = new Task(taskGroup:TaskGroup.findByName("Production Activites"),
+                 taskStatus:TaskStatus.findByName("Not Started"),
+                 taskPriority:TaskPriority.get(2),
+                 taskType:TaskType.get(5),
+                 leadPerson:Person.get(6),
+                 description:"Production Report",
+                 comment:"Production report for specific production run or shift")
+        BootStrapSaveAndTest(taskInstance)
+
+        taskInstance = new Task(taskGroup:TaskGroup.findByName("New Projects"),
+                 taskStatus:TaskStatus.findByName("Not Started"),
+                 taskPriority:TaskPriority.get(2),
+                 taskType:TaskType.get(3),
+                 leadPerson:Person.get(1),
+                 description:"Make killer CMMS app",
+                 comment:"Use Grails and get a move on!")
+        BootStrapSaveAndTest(taskInstance)
+
+        //EntryType
+        def entryTypeInstance
+
+        entryTypeInstance = new EntryType(name:"Fault")
+        BootStrapSaveAndTest(entryTypeInstance)
+
+        entryTypeInstance = new EntryType(name:"WorkDone")
+        BootStrapSaveAndTest(entryTypeInstance)
+
+        entryTypeInstance = new EntryType(name:"Production Note")
+        BootStrapSaveAndTest(entryTypeInstance)
+
+        entryTypeInstance = new EntryType(name:"Work Request")
+        BootStrapSaveAndTest(entryTypeInstance)
+
+        //Entry
+        def entryInstance
+
+        entryInstance = new Entry(enteredBy: Person.get(6),
+                                                    task: Task.get(1),
+                                                    entryType: EntryType.findByName("Fault"),
+                                                    comment: "This level sensor is causing us trouble.",
+                                                    durationMinute: 20)
+        BootStrapSaveAndTest(entryInstance)
+
+        entryInstance = new Entry(enteredBy: Person.get(4),
+                                                    task: Task.get(1),
+                                                    entryType: EntryType.findByName("WorkDone"),
+                                                    comment: "Cleaned sensor, see how it goes.",
+                                                    durationMinute: 30)
+        BootStrapSaveAndTest(entryInstance)
+
+        entryInstance = new Entry(enteredBy: Person.get(4),
+                                                    task: Task.get(1),
+                                                    entryType: EntryType.findByName("WorkDone"),
+                                                    comment: "Checked up on it later and sensor is dropping out intermittently, created subTask to replace sensor.",
+                                                    durationMinute: 20)
+        BootStrapSaveAndTest(entryInstance)
+
+        //ModificationType
+        def taskModificationTypeInstance
+        taskModificationTypeInstance = new TaskModificationType(name:"Created").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"Completed").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"Closed").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"Altered").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"TargetDateModified").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"ScheduledDateModified").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"DescriptionModified").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"AssignedToModified").save()
+        taskModificationTypeInstance = new TaskModificationType(name:"NameModified").save()
+    
+        //AssignedPerson
+        def assignedPersonInstance
+
+        assignedPersonInstance = new AssignedPerson(person: Person.get(4),
+                                                                                        task: Task.get(1),
+                                                                                        estimatedHour: 1,
+                                                                                        estimatedMinute: 20)
+        BootStrapSaveAndTest(assignedPersonInstance)
+
+        assignedPersonInstance = new AssignedPerson(person: Person.get(5),
+                                                                                        task: Task.get(1),
+                                                                                        estimatedHour: 3,
+                                                                                        estimatedMinute: 30)
+        BootStrapSaveAndTest(assignedPersonInstance)
+
+
+        //Finally did it all work.        
+        if(BootStrapDemoDataSuccessful) {
+            println "BootStrapping demo data...successful."
+        }
+        else println "BootStrapping demo data...failed."
+    }
+    
+    void BootStrapSaveAndTest(object) {
+        if(!object.save()) {
+            BootStrapDemoDataSuccessful = false
+            println "'${object}' failed to save!"
+            println object.errors
+
+        }
+    } 
+}
Index: /trunk/grails-app/conf/Config.groovy
===================================================================
--- /trunk/grails-app/conf/Config.groovy	(revision 104)
+++ /trunk/grails-app/conf/Config.groovy	(revision 104)
@@ -0,0 +1,73 @@
+// locations to search for config files that get merged into the main config
+// config files can either be Java properties files or ConfigSlurper scripts
+
+// grails.config.locations = [ "classpath:${appName}-config.properties",
+//                             "classpath:${appName}-config.groovy",
+//                             "file:${userHome}/.grails/${appName}-config.properties",
+//                             "file:${userHome}/.grails/${appName}-config.groovy"]
+
+// if(System.properties["${appName}.config.location"]) {
+//    grails.config.locations << "file:" + System.properties["${appName}.config.location"]
+// }
+grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
+grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
+                      xml: ['text/xml', 'application/xml'],
+                      text: 'text-plain',
+                      js: 'text/javascript',
+                      rss: 'application/rss+xml',
+                      atom: 'application/atom+xml',
+                      css: 'text/css',
+                      csv: 'text/csv',
+                      all: '*/*',
+                      json: ['application/json','text/json'],
+                      form: 'application/x-www-form-urlencoded',
+                      multipartForm: 'multipart/form-data'
+                    ]
+// The default codec used to encode data with ${}
+grails.views.default.codec="none" // none, html, base64
+grails.views.gsp.encoding="UTF-8"
+grails.converters.encoding="UTF-8"
+
+// enabled native2ascii conversion of i18n properties files
+grails.enable.native2ascii = true
+
+// set per-environment serverURL stem for creating absolute links
+environments {
+    production {
+        grails.serverURL = "http://www.changeme.com"
+    }
+}
+
+// log4j configuration
+log4j {
+    appender.stdout = "org.apache.log4j.ConsoleAppender"
+    appender.'stdout.layout'="org.apache.log4j.PatternLayout"
+    appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'
+    appender.stacktraceLog = "org.apache.log4j.FileAppender"
+    appender.'stacktraceLog.layout'="org.apache.log4j.PatternLayout"
+    appender.'stacktraceLog.layout.ConversionPattern'='[%r] %c{2} %m%n'
+    appender.'stacktraceLog.File'="stacktrace.log"
+    rootLogger="error,stdout"
+    logger {
+        grails="error"
+        StackTrace="error,stacktraceLog"
+        org {
+            codehaus.groovy.grails.web.servlet="error"  //  controllers
+            codehaus.groovy.grails.web.pages="error" //  GSP
+            codehaus.groovy.grails.web.sitemesh="error" //  layouts
+            codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
+            codehaus.groovy.grails."web.mapping"="error" // URL mapping
+            codehaus.groovy.grails.commons="info" // core / classloading
+            codehaus.groovy.grails.plugins="error" // plugins
+            codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
+            springframework="off"
+            hibernate="off"
+        }
+    }
+    additivity.StackTrace=false
+}
+
+
+
+
+//log4j.logger.org.springframework.security='off,stdout'
Index: /trunk/grails-app/conf/DataSource.groovy
===================================================================
--- /trunk/grails-app/conf/DataSource.groovy	(revision 104)
+++ /trunk/grails-app/conf/DataSource.groovy	(revision 104)
@@ -0,0 +1,38 @@
+dataSource {
+	pooled = true
+	driverClassName = "org.hsqldb.jdbcDriver"
+	username = "sa"
+	password = ""
+}
+hibernate {
+    cache.use_second_level_cache=true
+    cache.use_query_cache=true
+    cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
+}
+
+//environment specific settings
+//create-drop:  drop tables and data between sessions, then insert from Bootstrap.
+//create:       drop data between sessions, can't explain the rest.
+//update:       keep data, update tables as required, keeps inserting from BootStrap.
+environments {
+	development {
+		dataSource {
+			dbCreate = "create-drop" // one of 'create', 'create-drop','update'
+			url = "jdbc:hsqldb:mem:devDB"
+		}
+	}
+	test {
+		dataSource {
+			dbCreate = "update"
+			url = "jdbc:hsqldb:mem:testDb"
+		}
+	}
+	production {
+		dataSource {
+			//Delete dbCreate line after setup!
+            dbCreate = "create-drop"
+			//url = "jdbc:hsqldb:file:prodDb;shutdown=true"
+            url = "jdbc:hsqldb:mem:devDB"
+		}
+	}
+}
Index: /trunk/grails-app/conf/NoCacheFilters.groovy
===================================================================
--- /trunk/grails-app/conf/NoCacheFilters.groovy	(revision 104)
+++ /trunk/grails-app/conf/NoCacheFilters.groovy	(revision 104)
@@ -0,0 +1,26 @@
+class NoCacheFilters {
+
+    def filters = {
+        all(controller:'*', action:'*') {
+            before = {
+                
+                //Turn off as much caching as we can.
+                response.setHeader("Cache-Control", "no-cache,no-store,must-revalidate")// HTTP 1.1
+                response.addDateHeader('Expires', 0)
+                response.setDateHeader('max-age', 0)
+                response.setIntHeader ('Expires', -1) //prevents caching at the proxy server
+                response.addHeader('cache-Control', 'private') //IE5.x only 
+                response.addHeader('Pragma', 'no-cache') //HTTP 1.0
+
+            }
+
+            after = {
+                
+            }
+            afterView = {
+                
+            }
+        }
+    }
+    
+}
Index: /trunk/grails-app/conf/SecurityConfig.groovy
===================================================================
--- /trunk/grails-app/conf/SecurityConfig.groovy	(revision 104)
+++ /trunk/grails-app/conf/SecurityConfig.groovy	(revision 104)
@@ -0,0 +1,62 @@
+security {
+
+	// see DefaultSecurityConfig.groovy for all settable/overridable properties
+
+	active = true
+
+	loginUserDomainClass = "Person"
+    userName = 'loginName'
+    password = 'password'
+    enabled = 'isActive'
+
+	authorityDomainClass = "Authority"
+
+    //Required if we want to run "grails generate-manager"
+    //Which recreates the controller and views, so save the views!
+//     requestMapClass = 'Requestmap'
+
+    useRequestMapDomainClass = false
+    useControllerAnnotations = true
+
+    //Set true especially if used across the internet.
+    forceHttps = 'false'
+
+    //Pessimistic locking, deny access to all URLs that don't 
+    //have an applicable URL-Role configuration.
+    //This forces us to set an annotation, static rule or
+    //extend BaseController and prevents accidentally leaving pages open.
+    controllerAnnotationsRejectIfNoRule = true
+    
+    //Static rules for controllers, actions and urls.
+    //Since we are using pessimistic locking we have to set some things
+    //here but most security should be set in the controllers.
+    controllerAnnotationStaticRules = [
+    '/': ['IS_AUTHENTICATED_FULLY'],
+    '/index.gsp': ['IS_AUTHENTICATED_FULLY'],
+    '/css/*': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/plugins/help-balloons-1.2/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/login*': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
+    '/logout*': ['IS_AUTHENTICATED_FULLY'],
+    '/logout/**': ['IS_AUTHENTICATED_FULLY']
+    ]
+
+    //We always want to go to the home page so that bookmarks are not used.
+    defaultTargetUrl = '/appCore/home'
+    alwaysUseDefaultTargetUrl = true
+
+    //User caching, turned this off so that password changes take effect.
+    //It would appear that user is still in the session as logout/login
+    // is still required for role changes to take effect.
+    //If this option causes high database load try:
+    //  import org.acegisecurity.providers.dao.DaoAuthenticationProvider
+    //  import org.acegisecurity.context.SecurityContextHolder
+    //  DaoAuthenticationProvider daoAuthenticationProvider 
+    //  def user = SecurityContextHolder.context.authentication.principal.username 
+    //  daoAuthenticationProvider.userCache.removeUserFromCache(user)
+    //  in logout controller and perhaps on password change and role change.
+    cacheUsers = false
+
+}
Index: /trunk/grails-app/conf/UrlMappings.groovy
===================================================================
--- /trunk/grails-app/conf/UrlMappings.groovy	(revision 104)
+++ /trunk/grails-app/conf/UrlMappings.groovy	(revision 104)
@@ -0,0 +1,11 @@
+class UrlMappings {
+    static mappings = {
+      "/$controller/$action?/$id?"{
+	      constraints {
+			 // apply constraints here
+		  }
+	  }
+      "/"(view:"/index")
+	  "500"(view:'/error')
+	}
+}
Index: /trunk/grails-app/conf/spring/resources.groovy
===================================================================
--- /trunk/grails-app/conf/spring/resources.groovy	(revision 104)
+++ /trunk/grails-app/conf/spring/resources.groovy	(revision 104)
@@ -0,0 +1,4 @@
+// Place your Spring DSL code here
+beans = {
+    
+}
Index: /trunk/grails-app/controllers/AppCoreController.groovy
===================================================================
--- /trunk/grails-app/controllers/AppCoreController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/AppCoreController.groovy	(revision 104)
@@ -0,0 +1,61 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class AppCoreController extends BaseController {
+
+    def authenticateService
+
+    def index = { redirect(action:home,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    //def allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def home = {
+    }
+
+    def options = {
+    }
+
+    def changePassword = {
+        //def principal = authenticateService.principal()
+        //println principal.getAuthorities()
+
+        if (request.method == 'GET') {
+            def personInstance = Person.get(authenticateService.userDomain().id)
+            return [ personInstance : personInstance ]       
+        }  
+
+        if (request.method == 'POST') {
+            def personInstance = Person.get(authenticateService.userDomain().id)
+
+            if(params.confirmPass == params.pass) {
+                personInstance.pass = params.pass
+                personInstance.password = authenticateService.encodePassword(personInstance.pass)
+
+                if (!personInstance.hasErrors() && personInstance.save()) {
+                    //userCache.removeUserFromCache(personInstance.loginName)
+                    flash.message = "Password changed successfully."
+                    redirect(action:options)
+                }
+                else {
+                    render(view:'changePassword',model:[personInstance:personInstance])
+                }
+            }
+            else {
+                personInstance.errors.reject('person.pass.doesNotMatch',            // Error code, see grails-app/i18n/message.properties
+                                                                ['pass', 'class Person'].toArray(),      // Groovy ListArray cast to Object[]
+                                                                 '[NothingUseMessageProperites]')  // Default mapping string.
+                render(view:'changePassword',model:[personInstance:personInstance])
+            }
+                
+        }  
+    }
+
+    @Secured(['ROLE_Manager'])   
+    def manager = {
+    }
+
+    @Secured(['ROLE_AppAdmin'])   
+    def admin = {
+    }
+
+}
Index: /trunk/grails-app/controllers/AssignedPersonController.groovy
===================================================================
--- /trunk/grails-app/controllers/AssignedPersonController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/AssignedPersonController.groovy	(revision 104)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class AssignedPersonController extends BaseAppAdminController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ assignedPersonInstanceList: AssignedPerson.list( params ), assignedPersonInstanceTotal: AssignedPerson.count() ]
+    }
+
+    def show = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+
+        if(!assignedPersonInstance) {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ assignedPersonInstance : assignedPersonInstance ] }
+    }
+
+    def delete = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+        if(assignedPersonInstance) {
+            try {
+                assignedPersonInstance.delete()
+                flash.message = "AssignedPerson ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "AssignedPerson ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+
+        if(!assignedPersonInstance) {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ assignedPersonInstance : assignedPersonInstance ]
+        }
+    }
+
+    def update = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+        if(assignedPersonInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(assignedPersonInstance.version > version) {
+                    
+                    assignedPersonInstance.errors.rejectValue("version", "assignedPerson.optimistic.locking.failure", "Another user has updated this AssignedPerson while you were editing.")
+                    render(view:'edit',model:[assignedPersonInstance:assignedPersonInstance])
+                    return
+                }
+            }
+            assignedPersonInstance.properties = params
+            if(!assignedPersonInstance.hasErrors() && assignedPersonInstance.save()) {
+                flash.message = "AssignedPerson ${params.id} updated"
+                redirect(action:show,id:assignedPersonInstance.id)
+            }
+            else {
+                render(view:'edit',model:[assignedPersonInstance:assignedPersonInstance])
+            }
+        }
+        else {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def assignedPersonInstance = new AssignedPerson()
+        assignedPersonInstance.properties = params
+        return ['assignedPersonInstance':assignedPersonInstance]
+    }
+
+    def save = {
+        def assignedPersonInstance = new AssignedPerson(params)
+        if(!assignedPersonInstance.hasErrors() && assignedPersonInstance.save()) {
+            flash.message = "AssignedPerson ${assignedPersonInstance.id} created"
+            redirect(action:show,id:assignedPersonInstance.id)
+        }
+        else {
+            render(view:'create',model:[assignedPersonInstance:assignedPersonInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/AssignedPersonDetailedController.groovy
===================================================================
--- /trunk/grails-app/controllers/AssignedPersonDetailedController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/AssignedPersonDetailedController.groovy	(revision 104)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class AssignedPersonDetailedController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ assignedPersonInstanceList: AssignedPerson.list( params ), assignedPersonInstanceTotal: AssignedPerson.count() ]
+    }
+
+    def show = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+
+        if(!assignedPersonInstance) {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ assignedPersonInstance : assignedPersonInstance ] }
+    }
+
+    def delete = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+        if(assignedPersonInstance) {
+            try {
+                assignedPersonInstance.delete()
+                flash.message = "AssignedPerson ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "AssignedPerson ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+
+        if(!assignedPersonInstance) {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ assignedPersonInstance : assignedPersonInstance ]
+        }
+    }
+
+    def update = {
+        def assignedPersonInstance = AssignedPerson.get( params.id )
+        if(assignedPersonInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(assignedPersonInstance.version > version) {
+                    
+                    assignedPersonInstance.errors.rejectValue("version", "assignedPerson.optimistic.locking.failure", "Another user has updated this AssignedPerson while you were editing.")
+                    render(view:'edit',model:[assignedPersonInstance:assignedPersonInstance])
+                    return
+                }
+            }
+            assignedPersonInstance.properties = params
+            if(!assignedPersonInstance.hasErrors() && assignedPersonInstance.save()) {
+                flash.message = "AssignedPerson ${params.id} updated"
+                redirect(action:show,id:assignedPersonInstance.id)
+            }
+            else {
+                render(view:'edit',model:[assignedPersonInstance:assignedPersonInstance])
+            }
+        }
+        else {
+            flash.message = "AssignedPerson not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def assignedPersonInstance = new AssignedPerson()
+        assignedPersonInstance.properties = params
+        return ['assignedPersonInstance':assignedPersonInstance]
+    }
+
+    def save = {
+        def assignedPersonInstance = new AssignedPerson(params)
+        if(!assignedPersonInstance.hasErrors() && assignedPersonInstance.save()) {
+            flash.message = "AssignedPerson ${assignedPersonInstance.id} created"
+            redirect(action:show,id:assignedPersonInstance.id)
+        }
+        else {
+            render(view:'create',model:[assignedPersonInstance:assignedPersonInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/AuthorityController.groovy
===================================================================
--- /trunk/grails-app/controllers/AuthorityController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/AuthorityController.groovy	(revision 104)
@@ -0,0 +1,120 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin'])
+class AuthorityController {
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def authenticateService
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	/**
+	 * Display the list authority page.
+	 */
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[authorityList: Authority.list(params)]
+	}
+
+	/**
+	 * Display the show authority page.
+	 */
+	def show = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Delete an authority.
+	 */
+	def delete = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		authenticateService.deleteRole(authority)
+
+		flash.message = "Authority $params.id deleted."
+		redirect action: list
+	}
+
+	/**
+	 * Display the edit authority page.
+	 */
+	def edit = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Authority update action.
+	 */
+	def update = {
+
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (authority.version > version) {
+			authority.errors.rejectValue 'version', 'authority.optimistic.locking.failure',
+				'Another user has updated this Authority while you were editing.'
+			render view: 'edit', model: [authority: authority]
+			return
+		}
+
+		if (authenticateService.updateRole(authority, params)) {
+			authenticateService.clearCachedRequestmaps()
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'edit', model: [authority: authority]
+		}
+	}
+
+	/**
+	 * Display the create new authority page.
+	 */
+	def create = {
+		[authority: new Authority()]
+	}
+
+	/**
+	 * Save a new authority.
+	 */
+	def save = {
+
+		def authority = new Authority()
+		authority.properties = params
+		if (authority.save()) {
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'create', model: [authority: authority]
+		}
+	}
+}
Index: /trunk/grails-app/controllers/BaseAppAdminController.groovy
===================================================================
--- /trunk/grails-app/controllers/BaseAppAdminController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/BaseAppAdminController.groovy	(revision 104)
@@ -0,0 +1,9 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin'])
+abstract class BaseAppAdminController {
+    def whatsit() {
+    }
+
+}
+
Index: /trunk/grails-app/controllers/BaseController.groovy
===================================================================
--- /trunk/grails-app/controllers/BaseController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/BaseController.groovy	(revision 104)
@@ -0,0 +1,9 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppUser'])
+abstract class BaseController {
+    def whatsit() {
+    }
+
+}
+
Index: /trunk/grails-app/controllers/EntryController.groovy
===================================================================
--- /trunk/grails-app/controllers/EntryController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/EntryController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class EntryController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ entryInstanceList: Entry.list( params ) ]
+    }
+
+    def show = {
+        def entryInstance = Entry.get( params.id )
+
+        if(!entryInstance) {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ entryInstance : entryInstance ] }
+    }
+
+    def delete = {
+        def entryInstance = Entry.get( params.id )
+        if(entryInstance) {
+            entryInstance.delete()
+            flash.message = "Entry ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def entryInstance = Entry.get( params.id )
+
+        if(!entryInstance) {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ entryInstance : entryInstance ]
+        }
+    }
+
+    def update = {
+        def entryInstance = Entry.get( params.id )
+        if(entryInstance) {
+            entryInstance.properties = params
+            if(!entryInstance.hasErrors() && entryInstance.save()) {
+                flash.message = "Entry ${params.id} updated"
+                redirect(action:show,id:entryInstance.id)
+            }
+            else {
+                render(view:'edit',model:[entryInstance:entryInstance])
+            }
+        }
+        else {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def entryInstance = new Entry()
+        entryInstance.properties = params
+        return ['entryInstance':entryInstance]
+    }
+
+    def save = {
+        def entryInstance = new Entry(params)
+        if(!entryInstance.hasErrors() && entryInstance.save()) {
+            flash.message = "Entry ${entryInstance.id} created"
+            redirect(action:show,id:entryInstance.id)
+        }
+        else {
+            render(view:'create',model:[entryInstance:entryInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/EntryDetailedController.groovy
===================================================================
--- /trunk/grails-app/controllers/EntryDetailedController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/EntryDetailedController.groovy	(revision 104)
@@ -0,0 +1,109 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class EntryDetailedController extends BaseController {
+
+    def authenticateService
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ entryInstanceList: Entry.list( params ) ]
+    }
+
+    def show = {
+        def entryInstance = Entry.get( params.id )
+
+        if(!entryInstance) {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ entryInstance : entryInstance ] }
+    }
+
+    def delete = {
+        def entryInstance = Entry.get( params.id )
+        if(entryInstance) {
+            if(entryInstance.enteredBy.loginName == authenticateService.userDomain().loginName) {
+                entryInstance.delete()
+                flash.message = "Entry ${params.id} deleted"
+                redirect(action:list)
+            }
+            else {
+                flash.message = "You may only delete your own entries."
+                redirect(action:show,id:entryInstance.id)
+            }
+
+        }
+        else {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def entryInstance = Entry.get( params.id )
+        if(!entryInstance) {
+                flash.message = "Entry not found with id ${params.id}"
+                redirect(action:list)
+        }
+        else {
+
+            if(entryInstance.enteredBy.loginName == authenticateService.userDomain().loginName) {
+                return [ entryInstance : entryInstance ]
+            }
+            else {
+                flash.message = "You may only edit your own entries."
+                redirect(action:show,id:entryInstance.id)
+            }
+
+        }
+    }
+
+    def update = {
+        def entryInstance = Entry.get( params.id )
+        if(entryInstance) {
+            entryInstance.properties = params
+            if(!entryInstance.hasErrors() && entryInstance.save()) {
+                flash.message = "Entry ${params.id} updated"
+                redirect(action:show,id:entryInstance.id)
+            }
+            else {
+                render(view:'edit',model:[entryInstance:entryInstance])
+            }
+        }
+        else {
+            flash.message = "Entry not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        try {
+            def taskInstance = Task.get(params.taskInstance.id)
+            def entryInstance = new Entry()
+            entryInstance.task = taskInstance
+            return ['entryInstance':entryInstance]
+        }
+        catch(Exception e) {
+            flash.message = "Please select a task, then 'Add Entry'"
+            redirect(controller:"taskDetailed", action:"list")
+        }
+    }
+
+    def save = {
+        def entryInstance = new Entry(params)
+
+        entryInstance.enteredBy = Person.get(authenticateService.userDomain().id)
+        if(!entryInstance.hasErrors() && entryInstance.save()) {
+            flash.message = "Entry ${entryInstance.id} created"
+            redirect(controller:"taskDetailed", action:"show", id: params.task.id)
+        }
+        else {
+            render(view:'create',model:[entryInstance:entryInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/EntryTypeController.groovy
===================================================================
--- /trunk/grails-app/controllers/EntryTypeController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/EntryTypeController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class EntryTypeController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ entryTypeInstanceList: EntryType.list( params ) ]
+    }
+
+    def show = {
+        def entryTypeInstance = EntryType.get( params.id )
+
+        if(!entryTypeInstance) {
+            flash.message = "EntryType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ entryTypeInstance : entryTypeInstance ] }
+    }
+
+    def delete = {
+        def entryTypeInstance = EntryType.get( params.id )
+        if(entryTypeInstance) {
+            entryTypeInstance.delete()
+            flash.message = "EntryType ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "EntryType not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def entryTypeInstance = EntryType.get( params.id )
+
+        if(!entryTypeInstance) {
+            flash.message = "EntryType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ entryTypeInstance : entryTypeInstance ]
+        }
+    }
+
+    def update = {
+        def entryTypeInstance = EntryType.get( params.id )
+        if(entryTypeInstance) {
+            entryTypeInstance.properties = params
+            if(!entryTypeInstance.hasErrors() && entryTypeInstance.save()) {
+                flash.message = "EntryType ${params.id} updated"
+                redirect(action:show,id:entryTypeInstance.id)
+            }
+            else {
+                render(view:'edit',model:[entryTypeInstance:entryTypeInstance])
+            }
+        }
+        else {
+            flash.message = "EntryType not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def entryTypeInstance = new EntryType()
+        entryTypeInstance.properties = params
+        return ['entryTypeInstance':entryTypeInstance]
+    }
+
+    def save = {
+        def entryTypeInstance = new EntryType(params)
+        if(!entryTypeInstance.hasErrors() && entryTypeInstance.save()) {
+            flash.message = "EntryType ${entryTypeInstance.id} created"
+            redirect(action:show,id:entryTypeInstance.id)
+        }
+        else {
+            render(view:'create',model:[entryTypeInstance:entryTypeInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/LoginController.groovy
===================================================================
--- /trunk/grails-app/controllers/LoginController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/LoginController.groovy	(revision 104)
@@ -0,0 +1,184 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.RedirectUtils
+import org.grails.plugins.springsecurity.service.AuthenticateService
+
+import org.springframework.security.AuthenticationTrustResolverImpl
+import org.springframework.security.DisabledException
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.security.ui.AbstractProcessingFilter
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
+
+/**
+ * Login Controller (Example).
+ */
+class LoginController {
+
+	/**
+	 * Dependency injection for the authentication service.
+	 */
+	def authenticateService
+
+	/**
+	 * Dependency injection for OpenIDConsumer.
+	 */
+	def openIDConsumer
+
+	/**
+	 * Dependency injection for OpenIDAuthenticationProcessingFilter.
+	 */
+	def openIDAuthenticationProcessingFilter
+
+	private final authenticationTrustResolver = new AuthenticationTrustResolverImpl()
+
+	def index = {
+		if (isLoggedIn()) {
+			redirect uri: '/'
+		}
+		else {
+			redirect action: auth, params: params
+		}
+	}
+
+    def loggedOut = {
+        flash['message'] = 'Successfully logged out'
+        auth()
+    }
+
+	/**
+	 * Show the login page.
+	 */
+	def auth = {
+
+		nocache(response)
+
+		if (isLoggedIn()) {
+			redirect uri: '/'
+			return
+		}
+
+		String view
+		String postUrl
+		def config = authenticateService.securityConfig.security
+		if (config.useOpenId) {
+			view = 'openIdAuth'
+			postUrl = "${request.contextPath}/login/openIdAuthenticate"
+		}
+		else if (config.useFacebook) {
+			view = 'facebookAuth'
+			postUrl = "${request.contextPath}${config.facebook.filterProcessesUrl}"
+		}
+		else {
+			view = 'auth'
+			postUrl = "${request.contextPath}${config.filterProcessesUrl}"
+		}
+
+		render view: view, model: [postUrl: postUrl]
+	}
+
+	/**
+	 * Form submit action to start an OpenID authentication.
+	 */
+	def openIdAuthenticate = {
+		String openID = params['j_username']
+		try {
+			String returnToURL = RedirectUtils.buildRedirectUrl(
+					request, response, openIDAuthenticationProcessingFilter.filterProcessesUrl)
+			String redirectUrl = openIDConsumer.beginConsumption(request, openID, returnToURL)
+			redirect url: redirectUrl
+		}
+		catch (org.springframework.security.ui.openid.OpenIDConsumerException e) {
+			log.error "Consumer error: $e.message", e
+			redirect url: openIDAuthenticationProcessingFilter.authenticationFailureUrl
+		}
+	}
+
+	// Login page (function|json) for Ajax access.
+	def authAjax = {
+		nocache(response)
+		//this is example:
+		render """
+		<script type='text/javascript'>
+		(function() {
+			loginForm();
+		})();
+		</script>
+		"""
+	}
+
+	/**
+	 * The Ajax success redirect url.
+	 */
+	def ajaxSuccess = {
+		nocache(response)
+		render '{success: true}'
+	}
+
+	/**
+	 * Show denied page.
+	 */
+	def denied = {
+		if (isLoggedIn() && authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) {
+			// have cookie but the page is guarded with IS_AUTHENTICATED_FULLY
+			redirect action: full, params: params
+		}
+	}
+
+	/**
+	 * Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page.
+	 */
+	def full = {
+		render view: 'auth', params: params,
+			model: [hasCookie: authenticationTrustResolver.isRememberMe(SCH.context?.authentication)]
+	}
+
+	// Denial page (data|view|json) for Ajax access.
+	def deniedAjax = {
+		//this is example:
+		render "{error: 'access denied'}"
+	}
+
+	/**
+	 * login failed
+	 */
+	def authfail = {
+
+		def username = session[AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
+		def msg = ''
+		def exception = session[AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY]
+		if (exception) {
+			if (exception instanceof DisabledException) {
+				msg = "[$username] is disabled."
+			}
+			else {
+				msg = "[$username] wrong username/password."
+			}
+		}
+
+		if (isAjax()) {
+			render "{error: '${msg}'}"
+		}
+		else {
+			flash.message = msg
+			redirect action: auth, params: params
+		}
+	}
+
+	/**
+	 * Check if logged in.
+	 */
+	private boolean isLoggedIn() {
+		return authenticateService.isLoggedIn()
+	}
+
+	private boolean isAjax() {
+		return authenticateService.isAjax(request)
+	}
+
+	/** cache controls */
+	private void nocache(response) {
+		response.setHeader('Cache-Control', 'no-cache') // HTTP 1.1
+		response.addDateHeader('Expires', 0)
+		response.setDateHeader('max-age', 0)
+		response.setIntHeader ('Expires', -1) //prevents caching at the proxy server
+		response.addHeader('cache-Control', 'private') //IE5.x only
+	}
+}
Index: /trunk/grails-app/controllers/LogoutController.groovy
===================================================================
--- /trunk/grails-app/controllers/LogoutController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/LogoutController.groovy	(revision 104)
@@ -0,0 +1,14 @@
+/**
+ * Logout Controller (Example).
+ */
+class LogoutController {
+
+	/**
+	 * Index action. Redirects to the Spring security logout uri.
+	 */
+	def index = {
+		// TODO  put any pre-logout code here
+        
+		redirect(uri: '/j_spring_security_logout?logoutSuccessUrl=/login/loggedOut')
+	}
+}
Index: /trunk/grails-app/controllers/PersonController.groovy
===================================================================
--- /trunk/grails-app/controllers/PersonController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/PersonController.groovy	(revision 104)
@@ -0,0 +1,173 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_Manager'])
+class PersonController extends BaseAppAdminController {
+
+	def authenticateService
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[personList: Person.list(params)]
+	}
+
+	def show = {
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: list
+			return
+		}
+		List roleNames = []
+		for (role in person.authorities) {
+			roleNames << role.authority
+		}
+		roleNames.sort { n1, n2 ->
+			n1 <=> n2
+		}
+		[person: person, roleNames: roleNames]
+	}
+
+	/**
+	 * Person delete action. Before removing an existing person,
+	 * he should be removed from those authorities which he is involved.
+	 */
+	def delete = {
+
+		def person = Person.get(params.id)
+		if (person) {
+			def authPrincipal = authenticateService.principal()
+			//avoid self-delete if the logged-in user is an admin
+			if (!(authPrincipal instanceof String) && authPrincipal.username == person.loginName) {
+				flash.message = "You can not delete yourself, please login as another admin and try again"
+			}
+			else {
+				//first, delete this person from Persons_Authorities table.
+				Authority.findAll().each { it.removeFromPersons(person) }
+                
+                try {
+                    person.delete()
+                    flash.message = "Person $params.id deleted."
+                    redirect(action:list)
+                }
+                catch(org.springframework.dao.DataIntegrityViolationException e) {
+                    flash.message = "Could not delete '$person.loginName' due to database constraints, but all authorities have been removed."
+                    redirect(action:show,id:params.id)
+                }
+			}
+		}
+		else {
+			flash.message = "Person not found with id $params.id"
+		}
+	}
+
+	def edit = {
+
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		return buildPersonModel(person)
+	}
+
+	/**
+	 * Person update action.
+	 */
+	def update = {
+
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (person.version > version) {
+			person.errors.rejectValue 'version', "person.optimistic.locking.failure",
+				"Another user has updated this Person while you were editing."
+            render view: 'edit', model: buildPersonModel(person)
+			return
+		}
+
+		person.properties = params
+
+        if(params.pass == "") {
+            person.pass = "InsertNothingToClearValidation"
+        }
+        else {
+            if (person.validate()) {
+                person.password = authenticateService.encodePassword(params.pass)
+            }
+        }
+
+        if (!person.hasErrors() && person.save()) {
+            Authority.findAll().each { it.removeFromPersons(person) }
+            addRoles(person)
+            flash.message = "Person '$params.id - $params.loginName' updated."
+            redirect action: show, id: person.id
+        }
+        else {
+            render view: 'edit', model: buildPersonModel(person)
+        }
+
+	}
+
+	def create = {
+		[person: new Person(params), authorityList: Authority.list()]
+	}
+
+	/**
+	 * Person save action.
+	 */
+	def save = {
+
+		def person = new Person()
+		person.properties = params
+		person.password = authenticateService.encodePassword(params.pass)
+		if (person.save()) {
+			addRoles(person)
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'create', model: [authorityList: Authority.list(), person: person]
+		}
+	}
+
+	private void addRoles(person) {
+		for (String key in params.keySet()) {
+			if (key.contains('ROLE') && 'on' == params.get(key)) {
+				Authority.findByAuthority(key).addToPersons(person)
+			}
+		}
+	}
+
+	private Map buildPersonModel(person) {
+
+		List roles = Authority.list()
+		roles.sort { r1, r2 ->
+			r1.authority <=> r2.authority
+		}
+		Set userRoleNames = []
+		for (role in person.authorities) {
+			userRoleNames << role.authority
+		}
+		LinkedHashMap<Authority, Boolean> roleMap = [:]
+		for (role in roles) {
+			roleMap[(role)] = userRoleNames.contains(role.authority)
+		}
+
+		return [person: person, roleMap: roleMap]
+	}
+}
Index: /trunk/grails-app/controllers/PersonGroupController.groovy
===================================================================
--- /trunk/grails-app/controllers/PersonGroupController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/PersonGroupController.groovy	(revision 104)
@@ -0,0 +1,85 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin'])
+class PersonGroupController extends BaseController {
+
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ personGroupInstanceList: PersonGroup.list( params ) ]
+    }
+
+    def show = {
+        def personGroupInstance = PersonGroup.get( params.id )
+
+        if(!personGroupInstance) {
+            flash.message = "PersonGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ personGroupInstance : personGroupInstance ] }
+    }
+
+    def delete = {
+        def personGroupInstance = PersonGroup.get( params.id )
+        if(personGroupInstance) {
+            personGroupInstance.delete()
+            flash.message = "PersonGroup ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "PersonGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def personGroupInstance = PersonGroup.get( params.id )
+
+        if(!personGroupInstance) {
+            flash.message = "PersonGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ personGroupInstance : personGroupInstance ]
+        }
+    }
+
+    def update = {
+        def personGroupInstance = PersonGroup.get( params.id )
+        if(personGroupInstance) {
+            personGroupInstance.properties = params
+            if(!personGroupInstance.hasErrors() && personGroupInstance.save()) {
+                flash.message = "PersonGroup ${params.id} updated"
+                redirect(action:show,id:personGroupInstance.id)
+            }
+            else {
+                render(view:'edit',model:[personGroupInstance:personGroupInstance])
+            }
+        }
+        else {
+            flash.message = "PersonGroup not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def personGroupInstance = new PersonGroup()
+        personGroupInstance.properties = params
+        return ['personGroupInstance':personGroupInstance]
+    }
+
+    def save = {
+        def personGroupInstance = new PersonGroup(params)
+        if(!personGroupInstance.hasErrors() && personGroupInstance.save()) {
+            flash.message = "PersonGroup ${personGroupInstance.id} created"
+            redirect(action:show,id:personGroupInstance.id)
+        }
+        else {
+            render(view:'create',model:[personGroupInstance:personGroupInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/PersonGroupTypeController.groovy
===================================================================
--- /trunk/grails-app/controllers/PersonGroupTypeController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/PersonGroupTypeController.groovy	(revision 104)
@@ -0,0 +1,85 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+@Secured(['ROLE_AppAdmin'])
+class PersonGroupTypeController extends BaseController {
+
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ personGroupTypeInstanceList: PersonGroupType.list( params ) ]
+    }
+
+    def show = {
+        def personGroupTypeInstance = PersonGroupType.get( params.id )
+
+        if(!personGroupTypeInstance) {
+            flash.message = "PersonGroupType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ personGroupTypeInstance : personGroupTypeInstance ] }
+    }
+
+    def delete = {
+        def personGroupTypeInstance = PersonGroupType.get( params.id )
+        if(personGroupTypeInstance) {
+            personGroupTypeInstance.delete()
+            flash.message = "PersonGroupType ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "PersonGroupType not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def personGroupTypeInstance = PersonGroupType.get( params.id )
+
+        if(!personGroupTypeInstance) {
+            flash.message = "PersonGroupType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ personGroupTypeInstance : personGroupTypeInstance ]
+        }
+    }
+
+    def update = {
+        def personGroupTypeInstance = PersonGroupType.get( params.id )
+        if(personGroupTypeInstance) {
+            personGroupTypeInstance.properties = params
+            if(!personGroupTypeInstance.hasErrors() && personGroupTypeInstance.save()) {
+                flash.message = "PersonGroupType ${params.id} updated"
+                redirect(action:show,id:personGroupTypeInstance.id)
+            }
+            else {
+                render(view:'edit',model:[personGroupTypeInstance:personGroupTypeInstance])
+            }
+        }
+        else {
+            flash.message = "PersonGroupType not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def personGroupTypeInstance = new PersonGroupType()
+        personGroupTypeInstance.properties = params
+        return ['personGroupTypeInstance':personGroupTypeInstance]
+    }
+
+    def save = {
+        def personGroupTypeInstance = new PersonGroupType(params)
+        if(!personGroupTypeInstance.hasErrors() && personGroupTypeInstance.save()) {
+            flash.message = "PersonGroupType ${personGroupTypeInstance.id} created"
+            redirect(action:show,id:personGroupTypeInstance.id)
+        }
+        else {
+            render(view:'create',model:[personGroupTypeInstance:personGroupTypeInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskController.groovy	(revision 104)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskController extends BaseAppAdminController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ taskInstanceList: Task.list( params ), taskInstanceTotal: Task.count() ]
+    }
+
+    def show = {
+        def taskInstance = Task.get( params.id )
+
+        if(!taskInstance) {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskInstance : taskInstance ] }
+    }
+
+    def delete = {
+        def taskInstance = Task.get( params.id )
+        if(taskInstance) {
+            try {
+                taskInstance.delete()
+                flash.message = "Task ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "Task ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskInstance = Task.get( params.id )
+
+        if(!taskInstance) {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskInstance : taskInstance ]
+        }
+    }
+
+    def update = {
+        def taskInstance = Task.get( params.id )
+        if(taskInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(taskInstance.version > version) {
+                    
+                    taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
+                    render(view:'edit',model:[taskInstance:taskInstance])
+                    return
+                }
+            }
+            taskInstance.properties = params
+            if(!taskInstance.hasErrors() && taskInstance.save()) {
+                flash.message = "Task ${params.id} updated"
+                redirect(action:show,id:taskInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskInstance:taskInstance])
+            }
+        }
+        else {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskInstance = new Task()
+        taskInstance.properties = params
+        return ['taskInstance':taskInstance]
+    }
+
+    def save = {
+        def taskInstance = new Task(params)
+        if(!taskInstance.hasErrors() && taskInstance.save()) {
+            flash.message = "Task ${taskInstance.id} created"
+            redirect(action:show,id:taskInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskInstance:taskInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskDetailedController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskDetailedController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskDetailedController.groovy	(revision 104)
@@ -0,0 +1,105 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskDetailedController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ taskInstanceList: Task.list( params ), taskInstanceTotal: Task.count() ]
+    }
+
+    def show = {
+        def taskInstance = Task.get( params.id )
+
+        if(!taskInstance) {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskInstance : taskInstance ] }
+    }
+
+    def delete = {
+        def taskInstance = Task.get( params.id )
+        if(taskInstance) {
+            try {
+                taskInstance.delete()
+                flash.message = "Task ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "Task ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskInstance = Task.get( params.id )
+
+        if(!taskInstance) {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            def criteria = taskInstance.createCriteria()
+            def results = criteria {
+                and {
+                    notEqual('id', taskInstance.id)
+                    }
+            }
+            return [ taskInstance : taskInstance, possibleParentList: results ]
+        }
+    }
+
+    def update = {
+        def taskInstance = Task.get( params.id )
+        if(taskInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(taskInstance.version > version) {
+                    
+                    taskInstance.errors.rejectValue("version", "task.optimistic.locking.failure", "Another user has updated this Task while you were editing.")
+                    render(view:'edit',model:[taskInstance:taskInstance])
+                    return
+                }
+            }
+            taskInstance.properties = params
+            if(!taskInstance.hasErrors() && taskInstance.save()) {
+                flash.message = "Task ${params.id} updated"
+                redirect(action:show,id:taskInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskInstance:taskInstance])
+            }
+        }
+        else {
+            flash.message = "Task not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskInstance = new Task()
+        taskInstance.properties = params
+        return ['taskInstance':taskInstance]
+    }
+
+    def save = {
+        def taskInstance = new Task(params)
+        if(!taskInstance.hasErrors() && taskInstance.save()) {
+            flash.message = "Task ${taskInstance.id} created"
+            redirect(action:show,id:taskInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskInstance:taskInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskGroupController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskGroupController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskGroupController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskGroupController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ taskGroupInstanceList: TaskGroup.list( params ) ]
+    }
+
+    def show = {
+        def taskGroupInstance = TaskGroup.get( params.id )
+
+        if(!taskGroupInstance) {
+            flash.message = "TaskGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskGroupInstance : taskGroupInstance ] }
+    }
+
+    def delete = {
+        def taskGroupInstance = TaskGroup.get( params.id )
+        if(taskGroupInstance) {
+            taskGroupInstance.delete()
+            flash.message = "TaskGroup ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "TaskGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskGroupInstance = TaskGroup.get( params.id )
+
+        if(!taskGroupInstance) {
+            flash.message = "TaskGroup not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskGroupInstance : taskGroupInstance ]
+        }
+    }
+
+    def update = {
+        def taskGroupInstance = TaskGroup.get( params.id )
+        if(taskGroupInstance) {
+            taskGroupInstance.properties = params
+            if(!taskGroupInstance.hasErrors() && taskGroupInstance.save()) {
+                flash.message = "TaskGroup ${params.id} updated"
+                redirect(action:show,id:taskGroupInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskGroupInstance:taskGroupInstance])
+            }
+        }
+        else {
+            flash.message = "TaskGroup not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskGroupInstance = new TaskGroup()
+        taskGroupInstance.properties = params
+        return ['taskGroupInstance':taskGroupInstance]
+    }
+
+    def save = {
+        def taskGroupInstance = new TaskGroup(params)
+        if(!taskGroupInstance.hasErrors() && taskGroupInstance.save()) {
+            flash.message = "TaskGroup ${taskGroupInstance.id} created"
+            redirect(action:show,id:taskGroupInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskGroupInstance:taskGroupInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskModificationController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskModificationController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskModificationController.groovy	(revision 104)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskModificationController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ taskModificationInstanceList: TaskModification.list( params ), taskModificationInstanceTotal: TaskModification.count() ]
+    }
+
+    def show = {
+        def taskModificationInstance = TaskModification.get( params.id )
+
+        if(!taskModificationInstance) {
+            flash.message = "TaskModification not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskModificationInstance : taskModificationInstance ] }
+    }
+
+    def delete = {
+        def taskModificationInstance = TaskModification.get( params.id )
+        if(taskModificationInstance) {
+            try {
+                taskModificationInstance.delete()
+                flash.message = "TaskModification ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "TaskModification ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "TaskModification not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskModificationInstance = TaskModification.get( params.id )
+
+        if(!taskModificationInstance) {
+            flash.message = "TaskModification not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskModificationInstance : taskModificationInstance ]
+        }
+    }
+
+    def update = {
+        def taskModificationInstance = TaskModification.get( params.id )
+        if(taskModificationInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(taskModificationInstance.version > version) {
+                    
+                    taskModificationInstance.errors.rejectValue("version", "taskModification.optimistic.locking.failure", "Another user has updated this TaskModification while you were editing.")
+                    render(view:'edit',model:[taskModificationInstance:taskModificationInstance])
+                    return
+                }
+            }
+            taskModificationInstance.properties = params
+            if(!taskModificationInstance.hasErrors() && taskModificationInstance.save()) {
+                flash.message = "TaskModification ${params.id} updated"
+                redirect(action:show,id:taskModificationInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskModificationInstance:taskModificationInstance])
+            }
+        }
+        else {
+            flash.message = "TaskModification not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskModificationInstance = new TaskModification()
+        taskModificationInstance.properties = params
+        return ['taskModificationInstance':taskModificationInstance]
+    }
+
+    def save = {
+        def taskModificationInstance = new TaskModification(params)
+        if(!taskModificationInstance.hasErrors() && taskModificationInstance.save()) {
+            flash.message = "TaskModification ${taskModificationInstance.id} created"
+            redirect(action:show,id:taskModificationInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskModificationInstance:taskModificationInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskModificationTypeController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskModificationTypeController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskModificationTypeController.groovy	(revision 104)
@@ -0,0 +1,99 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskModificationTypeController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)
+        [ taskModificationTypeInstanceList: TaskModificationType.list( params ), taskModificationTypeInstanceTotal: TaskModificationType.count() ]
+    }
+
+    def show = {
+        def taskModificationTypeInstance = TaskModificationType.get( params.id )
+
+        if(!taskModificationTypeInstance) {
+            flash.message = "TaskModificationType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskModificationTypeInstance : taskModificationTypeInstance ] }
+    }
+
+    def delete = {
+        def taskModificationTypeInstance = TaskModificationType.get( params.id )
+        if(taskModificationTypeInstance) {
+            try {
+                taskModificationTypeInstance.delete()
+                flash.message = "TaskModificationType ${params.id} deleted"
+                redirect(action:list)
+            }
+            catch(org.springframework.dao.DataIntegrityViolationException e) {
+                flash.message = "TaskModificationType ${params.id} could not be deleted"
+                redirect(action:show,id:params.id)
+            }
+        }
+        else {
+            flash.message = "TaskModificationType not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskModificationTypeInstance = TaskModificationType.get( params.id )
+
+        if(!taskModificationTypeInstance) {
+            flash.message = "TaskModificationType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskModificationTypeInstance : taskModificationTypeInstance ]
+        }
+    }
+
+    def update = {
+        def taskModificationTypeInstance = TaskModificationType.get( params.id )
+        if(taskModificationTypeInstance) {
+            if(params.version) {
+                def version = params.version.toLong()
+                if(taskModificationTypeInstance.version > version) {
+                    
+                    taskModificationTypeInstance.errors.rejectValue("version", "taskModificationType.optimistic.locking.failure", "Another user has updated this TaskModificationType while you were editing.")
+                    render(view:'edit',model:[taskModificationTypeInstance:taskModificationTypeInstance])
+                    return
+                }
+            }
+            taskModificationTypeInstance.properties = params
+            if(!taskModificationTypeInstance.hasErrors() && taskModificationTypeInstance.save()) {
+                flash.message = "TaskModificationType ${params.id} updated"
+                redirect(action:show,id:taskModificationTypeInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskModificationTypeInstance:taskModificationTypeInstance])
+            }
+        }
+        else {
+            flash.message = "TaskModificationType not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskModificationTypeInstance = new TaskModificationType()
+        taskModificationTypeInstance.properties = params
+        return ['taskModificationTypeInstance':taskModificationTypeInstance]
+    }
+
+    def save = {
+        def taskModificationTypeInstance = new TaskModificationType(params)
+        if(!taskModificationTypeInstance.hasErrors() && taskModificationTypeInstance.save()) {
+            flash.message = "TaskModificationType ${taskModificationTypeInstance.id} created"
+            redirect(action:show,id:taskModificationTypeInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskModificationTypeInstance:taskModificationTypeInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskPriorityController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskPriorityController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskPriorityController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskPriorityController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ taskPriorityInstanceList: TaskPriority.list( params ) ]
+    }
+
+    def show = {
+        def taskPriorityInstance = TaskPriority.get( params.id )
+
+        if(!taskPriorityInstance) {
+            flash.message = "TaskPriority not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskPriorityInstance : taskPriorityInstance ] }
+    }
+
+    def delete = {
+        def taskPriorityInstance = TaskPriority.get( params.id )
+        if(taskPriorityInstance) {
+            taskPriorityInstance.delete()
+            flash.message = "TaskPriority ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "TaskPriority not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskPriorityInstance = TaskPriority.get( params.id )
+
+        if(!taskPriorityInstance) {
+            flash.message = "TaskPriority not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskPriorityInstance : taskPriorityInstance ]
+        }
+    }
+
+    def update = {
+        def taskPriorityInstance = TaskPriority.get( params.id )
+        if(taskPriorityInstance) {
+            taskPriorityInstance.properties = params
+            if(!taskPriorityInstance.hasErrors() && taskPriorityInstance.save()) {
+                flash.message = "TaskPriority ${params.id} updated"
+                redirect(action:show,id:taskPriorityInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskPriorityInstance:taskPriorityInstance])
+            }
+        }
+        else {
+            flash.message = "TaskPriority not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskPriorityInstance = new TaskPriority()
+        taskPriorityInstance.properties = params
+        return ['taskPriorityInstance':taskPriorityInstance]
+    }
+
+    def save = {
+        def taskPriorityInstance = new TaskPriority(params)
+        if(!taskPriorityInstance.hasErrors() && taskPriorityInstance.save()) {
+            flash.message = "TaskPriority ${taskPriorityInstance.id} created"
+            redirect(action:show,id:taskPriorityInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskPriorityInstance:taskPriorityInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskStatusController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskStatusController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskStatusController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskStatusController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ taskStatusInstanceList: TaskStatus.list( params ) ]
+    }
+
+    def show = {
+        def taskStatusInstance = TaskStatus.get( params.id )
+
+        if(!taskStatusInstance) {
+            flash.message = "TaskStatus not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskStatusInstance : taskStatusInstance ] }
+    }
+
+    def delete = {
+        def taskStatusInstance = TaskStatus.get( params.id )
+        if(taskStatusInstance) {
+            taskStatusInstance.delete()
+            flash.message = "TaskStatus ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "TaskStatus not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskStatusInstance = TaskStatus.get( params.id )
+
+        if(!taskStatusInstance) {
+            flash.message = "TaskStatus not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskStatusInstance : taskStatusInstance ]
+        }
+    }
+
+    def update = {
+        def taskStatusInstance = TaskStatus.get( params.id )
+        if(taskStatusInstance) {
+            taskStatusInstance.properties = params
+            if(!taskStatusInstance.hasErrors() && taskStatusInstance.save()) {
+                flash.message = "TaskStatus ${params.id} updated"
+                redirect(action:show,id:taskStatusInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskStatusInstance:taskStatusInstance])
+            }
+        }
+        else {
+            flash.message = "TaskStatus not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskStatusInstance = new TaskStatus()
+        taskStatusInstance.properties = params
+        return ['taskStatusInstance':taskStatusInstance]
+    }
+
+    def save = {
+        def taskStatusInstance = new TaskStatus(params)
+        if(!taskStatusInstance.hasErrors() && taskStatusInstance.save()) {
+            flash.message = "TaskStatus ${taskStatusInstance.id} created"
+            redirect(action:show,id:taskStatusInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskStatusInstance:taskStatusInstance])
+        }
+    }
+}
Index: /trunk/grails-app/controllers/TaskTypeController.groovy
===================================================================
--- /trunk/grails-app/controllers/TaskTypeController.groovy	(revision 104)
+++ /trunk/grails-app/controllers/TaskTypeController.groovy	(revision 104)
@@ -0,0 +1,84 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured
+
+class TaskTypeController extends BaseController {
+    
+    def index = { redirect(action:list,params:params) }
+
+    // the delete, save and update actions only accept POST requests
+    static allowedMethods = [delete:'POST', save:'POST', update:'POST']
+
+    def list = {
+        if(!params.max) params.max = 10
+        [ taskTypeInstanceList: TaskType.list( params ) ]
+    }
+
+    def show = {
+        def taskTypeInstance = TaskType.get( params.id )
+
+        if(!taskTypeInstance) {
+            flash.message = "TaskType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else { return [ taskTypeInstance : taskTypeInstance ] }
+    }
+
+    def delete = {
+        def taskTypeInstance = TaskType.get( params.id )
+        if(taskTypeInstance) {
+            taskTypeInstance.delete()
+            flash.message = "TaskType ${params.id} deleted"
+            redirect(action:list)
+        }
+        else {
+            flash.message = "TaskType not found with id ${params.id}"
+            redirect(action:list)
+        }
+    }
+
+    def edit = {
+        def taskTypeInstance = TaskType.get( params.id )
+
+        if(!taskTypeInstance) {
+            flash.message = "TaskType not found with id ${params.id}"
+            redirect(action:list)
+        }
+        else {
+            return [ taskTypeInstance : taskTypeInstance ]
+        }
+    }
+
+    def update = {
+        def taskTypeInstance = TaskType.get( params.id )
+        if(taskTypeInstance) {
+            taskTypeInstance.properties = params
+            if(!taskTypeInstance.hasErrors() && taskTypeInstance.save()) {
+                flash.message = "TaskType ${params.id} updated"
+                redirect(action:show,id:taskTypeInstance.id)
+            }
+            else {
+                render(view:'edit',model:[taskTypeInstance:taskTypeInstance])
+            }
+        }
+        else {
+            flash.message = "TaskType not found with id ${params.id}"
+            redirect(action:edit,id:params.id)
+        }
+    }
+
+    def create = {
+        def taskTypeInstance = new TaskType()
+        taskTypeInstance.properties = params
+        return ['taskTypeInstance':taskTypeInstance]
+    }
+
+    def save = {
+        def taskTypeInstance = new TaskType(params)
+        if(!taskTypeInstance.hasErrors() && taskTypeInstance.save()) {
+            flash.message = "TaskType ${taskTypeInstance.id} created"
+            redirect(action:show,id:taskTypeInstance.id)
+        }
+        else {
+            render(view:'create',model:[taskTypeInstance:taskTypeInstance])
+        }
+    }
+}
Index: /trunk/grails-app/domain/AssignedPerson.groovy
===================================================================
--- /trunk/grails-app/domain/AssignedPerson.groovy	(revision 104)
+++ /trunk/grails-app/domain/AssignedPerson.groovy	(revision 104)
@@ -0,0 +1,18 @@
+class AssignedPerson {
+
+    Person person
+    Task task
+    Integer estimatedHour = 0
+    Integer estimatedMinute = 0
+
+    static constraints = {
+        task()
+        person()
+        estimatedHour(min:0)
+        estimatedMinute(min:0,max:59)
+    }
+
+    String toString() {
+        "${person.firstName} ${person.lastName} - ${estimatedHour}h : ${estimatedMinute}min"
+    }
+}
Index: /trunk/grails-app/domain/Authority.groovy
===================================================================
--- /trunk/grails-app/domain/Authority.groovy	(revision 104)
+++ /trunk/grails-app/domain/Authority.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class Authority {
+
+	static hasMany = [persons: Person]
+
+	/** description */
+	String description
+	/** ROLE String */
+	String authority
+
+	static constraints = {
+		authority(blank: false, unique: true)
+		description()
+	}
+}
Index: /trunk/grails-app/domain/Entry.groovy
===================================================================
--- /trunk/grails-app/domain/Entry.groovy	(revision 104)
+++ /trunk/grails-app/domain/Entry.groovy	(revision 104)
@@ -0,0 +1,26 @@
+class Entry {
+    Person enteredBy
+    Task task
+    EntryType entryType
+    String comment
+    Date dateDone = new Date()
+    Date dateEntered = new Date()
+    Integer durationHour = 0
+    Integer durationMinute = 0
+
+    static belongsTo = [EntryType, Task, Person]
+
+    static constraints = {
+        task()
+        comment(blank:false,maxSize:500)
+        dateDone()
+        durationHour(min:0,max:16)
+        durationMinute(min:0,max:59)
+        
+    }
+
+    String toString() {
+        "${this.comment} - ${this.enteredBy}, ${this.dateDone}"
+    }
+}
+
Index: /trunk/grails-app/domain/EntryType.groovy
===================================================================
--- /trunk/grails-app/domain/EntryType.groovy	(revision 104)
+++ /trunk/grails-app/domain/EntryType.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class EntryType {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [entries: Entry]
+    
+    static contstraints = {
+    }
+
+    String toString() {
+        "${this.name}"
+    }
+}
Index: /trunk/grails-app/domain/Person.groovy
===================================================================
--- /trunk/grails-app/domain/Person.groovy	(revision 104)
+++ /trunk/grails-app/domain/Person.groovy	(revision 104)
@@ -0,0 +1,49 @@
+class Person {
+	static transients = ['pass']
+    static hasMany = [authorities: Authority,
+                        personGroups: PersonGroup,
+                        taskModifications: TaskModification,
+                        entries: Entry,
+                        tasks: Task]
+
+    static belongsTo = [Authority, PersonGroup]
+
+	String loginName
+	String firstName
+    String lastName
+    String employeeID
+
+	/** MD5 Password */
+	String password
+
+	/** enabled */
+	boolean isActive = true
+
+	String email
+	boolean emailShow = true
+
+	/** description */
+	String description = ''
+
+	/** plain password to create a MD5 password */
+	String pass
+
+	static constraints = {
+		loginName(blank: false, unique: true, minSize:4)//minSize:7
+		firstName(blank: false)
+        lastName(blank: false)
+        employeeID(blank: true, nullable:true)
+        description()
+        email()
+        emailShow()
+        isActive()
+        //Enforcing minSize on password does not work since "" gets encoded to a string.
+		password(blank: false)
+        //So we need to use pass for validation then encode it for above.
+        pass(blank: false, minSize:4)
+
+	}
+
+    //Overriding the default toString method
+    String toString() {"${this.firstName} ${this.lastName}"}
+}
Index: /trunk/grails-app/domain/PersonGroup.groovy
===================================================================
--- /trunk/grails-app/domain/PersonGroup.groovy	(revision 104)
+++ /trunk/grails-app/domain/PersonGroup.groovy	(revision 104)
@@ -0,0 +1,17 @@
+class PersonGroup {
+    PersonGroupType personGroupType
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [persons : Person]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {
+        "${this.name}"
+    }
+}
Index: /trunk/grails-app/domain/PersonGroupType.groovy
===================================================================
--- /trunk/grails-app/domain/PersonGroupType.groovy	(revision 104)
+++ /trunk/grails-app/domain/PersonGroupType.groovy	(revision 104)
@@ -0,0 +1,16 @@
+class PersonGroupType {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [personGroups : PersonGroup]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {
+        "${this.name}"
+    }
+}
Index: /trunk/grails-app/domain/Task.groovy
===================================================================
--- /trunk/grails-app/domain/Task.groovy	(revision 104)
+++ /trunk/grails-app/domain/Task.groovy	(revision 104)
@@ -0,0 +1,35 @@
+class Task {
+    TaskGroup taskGroup
+    TaskStatus taskStatus
+    TaskPriority taskPriority
+    TaskType taskType
+    Task    parentTask
+    Person leadPerson
+    String description
+    String comment = ""
+    Date targetStartDate = new Date()
+    Date targetCompletionDate = new Date()
+    boolean isScheduled = false
+    boolean isApproved = false
+    boolean isActive = true
+
+    static hasMany = [entries: Entry, 
+                        taskModifications: TaskModification, 
+                        assignedPersons: AssignedPerson, 
+                        subTasks: Task]
+
+    static belongsTo = [TaskGroup, TaskStatus, Task, Person]
+
+    static constraints = {
+        targetStartDate()
+        description(blank:false,maxSize:75)
+        leadPerson()
+        taskPriority()
+        taskStatus()
+        parentTask(blank: true, nullable:true)
+        comment(maxSize:255)
+        
+    }
+
+    String toString() {"${this.description}"}
+}
Index: /trunk/grails-app/domain/TaskGroup.groovy
===================================================================
--- /trunk/grails-app/domain/TaskGroup.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskGroup.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class TaskGroup {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [tasks : Task]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {"${this.name}"}
+}
Index: /trunk/grails-app/domain/TaskModification.groovy
===================================================================
--- /trunk/grails-app/domain/TaskModification.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskModification.groovy	(revision 104)
@@ -0,0 +1,19 @@
+class TaskModification {
+    Person person
+    TaskModificationType taskModificationType
+    Task task
+    Date date = new Date()
+    String comment
+
+    static belongsTo = [Person, TaskModificationType, Task]
+
+    static constraints = {
+        person()
+        taskModificationType()
+        task()
+        date()
+        comment()
+    }
+
+    static optionals = ["comment"]
+}
Index: /trunk/grails-app/domain/TaskModificationType.groovy
===================================================================
--- /trunk/grails-app/domain/TaskModificationType.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskModificationType.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class TaskModificationType {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [taskModifications : TaskModification]
+
+    static constraints = {
+    }
+
+    String toString() {
+        "${this.name}"
+    }
+}
Index: /trunk/grails-app/domain/TaskPriority.groovy
===================================================================
--- /trunk/grails-app/domain/TaskPriority.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskPriority.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class TaskPriority {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [tasks : Task]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {"${this.name}"}
+}
Index: /trunk/grails-app/domain/TaskStatus.groovy
===================================================================
--- /trunk/grails-app/domain/TaskStatus.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskStatus.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class TaskStatus {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [tasks : Task]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {"${this.name}"}
+}
Index: /trunk/grails-app/domain/TaskType.groovy
===================================================================
--- /trunk/grails-app/domain/TaskType.groovy	(revision 104)
+++ /trunk/grails-app/domain/TaskType.groovy	(revision 104)
@@ -0,0 +1,14 @@
+class TaskType {
+    String name
+    String description = ""
+    boolean isActive = true
+
+    static hasMany = [tasks : Task]
+
+    static constraints = {
+        name(maxSize:50,unique:true,blank:false)
+        description(maxSize:100)
+    }
+
+    String toString() {"${this.name}"}
+}
Index: /trunk/grails-app/i18n/messages.properties
===================================================================
--- /trunk/grails-app/i18n/messages.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages.properties	(revision 104)
@@ -0,0 +1,39 @@
+person.pass.minSize.notmet=Password is less than the minimum size of [{3}]
+person.pass.blank=Password cannot be blank
+person.pass.doesNotMatch=Passwords must match
+
+entry.duration=Duration
+entry.duration.help=The time (hh:mm) booked against this entry for date done.
+
+default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
+default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
+default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
+default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
+default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
+default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
+default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
+default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
+default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
+default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
+default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
+default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
+default.blank.message=Property [{0}] of class [{1}] cannot be blank
+default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
+default.null.message=Property [{0}] of class [{1}] cannot be null
+default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
+
+default.paginate.prev=Previous
+default.paginate.next=Next
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Property {0} must be a valid URL
+typeMismatch.java.net.URI=Property {0} must be a valid URI
+typeMismatch.java.util.Date=Property {0} must be a valid Date
+typeMismatch.java.lang.Double=Property {0} must be a valid number
+typeMismatch.java.lang.Integer=Property {0} must be a valid number
+typeMismatch.java.lang.Long=Property {0} must be a valid number
+typeMismatch.java.lang.Short=Property {0} must be a valid number
+typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
+typeMismatch.java.math.BigInteger=Property {0} must be a valid number
+
+
Index: /trunk/grails-app/i18n/messages_de.properties
===================================================================
--- /trunk/grails-app/i18n/messages_de.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_de.properties	(revision 104)
@@ -0,0 +1,30 @@
+default.doesnt.match.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] entspricht nicht dem vorgegebenen Muster [{3}]
+default.invalid.url.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gÃŒltige URL
+default.invalid.creditCard.message=Das Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gÃŒltige Kreditkartennummer
+default.invalid.email.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gÃŒltige E-Mail Adresse
+default.invalid.range.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
+default.invalid.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
+default.invalid.max.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist grÃ¶Ãer als der HÃ¶chstwert von [{3}]
+default.invalid.min.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist kleiner als der Mindestwert von [{3}]
+default.invalid.max.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ÃŒbersteigt den HÃ¶chstwert von [{3}]
+default.invalid.min.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] unterschreitet den Mindestwert von [{3}]
+default.invalid.validator.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist ungÃŒltig
+default.not.inlist.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht in der Liste [{3}] enthalten.
+default.blank.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht leer sein
+default.not.equal.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nicht gleich [{3}] sein
+default.null.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht null sein
+default.not.unique.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nur einmal vorkommen
+
+default.paginate.prev=Vorherige
+default.paginate.next=NÃ€chste
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Die Eigenschaft {0} muss eine gÃŒltige URL sein
+typeMismatch.java.net.URI=Die Eigenschaft {0} muss eine gÃŒltige URI sein
+typeMismatch.java.util.Date=Die Eigenschaft {0} muss ein gÃŒltiges Datum sein
+typeMismatch.java.lang.Double=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
+typeMismatch.java.lang.Integer=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
+typeMismatch.java.lang.Long=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
+typeMismatch.java.lang.Short=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
+typeMismatch.java.math.BigDecimal=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
+typeMismatch.java.math.BigInteger=Die Eigenschaft {0} muss eine gÃŒltige Zahl sein
Index: /trunk/grails-app/i18n/messages_es.properties
===================================================================
--- /trunk/grails-app/i18n/messages_es.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_es.properties	(revision 104)
@@ -0,0 +1,30 @@
+default.doesnt.match.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no corresponde al patrÃ³n [{3}]
+default.invalid.url.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una URL vÃ¡lida
+default.invalid.creditCard.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es un nÃºmero de tarjeta de crÃ©dito vÃ¡lida
+default.invalid.email.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una direcciÃ³n de correo electrÃ³nico vÃ¡lida
+default.invalid.range.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el rango vÃ¡lido de [{3}] a [{4}]
+default.invalid.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el tamaÃ±o vÃ¡lido de [{3}] a [{4}]
+default.invalid.max.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el valor mÃ¡ximo [{3}]
+default.invalid.min.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menos que el valor mÃ­nimo [{3}]
+default.invalid.max.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el tamaÃ±o mÃ¡ximo de [{3}]
+default.invalid.min.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menor que el tamaÃ±o mÃ­nimo de [{3}]
+default.invalid.validator.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es vÃ¡lido
+default.not.inlist.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no esta contenido dentro de la lista [{3}]
+default.blank.message=La propiedad [{0}] de la clase [{1}] no puede ser vacÃ­a
+default.not.equal.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no puede igualar a [{3}]
+default.null.message=La propiedad [{0}] de la clase [{1}] no puede ser nulo
+default.not.unique.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] debe ser Ãºnica
+
+default.paginate.prev=Anterior
+default.paginate.next=Siguiente
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=La propiedad {0} debe ser una URL vÃ¡lida
+typeMismatch.java.net.URI=La propiedad {0} debe ser una URI vÃ¡lida
+typeMismatch.java.util.Date=La propiedad {0} debe ser una fecha vÃ¡lida
+typeMismatch.java.lang.Double=La propiedad {0} debe ser un nÃºmero vÃ¡lido
+typeMismatch.java.lang.Integer=La propiedad {0} debe ser un nÃºmero vÃ¡lido
+typeMismatch.java.lang.Long=La propiedad {0} debe ser un nÃºmero vÃ¡lido
+typeMismatch.java.lang.Short=La propiedad {0} debe ser un nÃºmero vÃ¡lido
+typeMismatch.java.math.BigDecimal=La propiedad {0} debe ser un nÃºmero vÃ¡lido
+typeMismatch.java.math.BigInteger=La propiedad {0} debe ser un nÃºmero vÃ¡lido
Index: /trunk/grails-app/i18n/messages_fr.properties
===================================================================
--- /trunk/grails-app/i18n/messages_fr.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_fr.properties	(revision 104)
@@ -0,0 +1,19 @@
+default.doesnt.match.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] ne correspond pas au pattern [{3}]
+default.invalid.url.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une URL valide
+default.invalid.creditCard.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas un numÃ©ro de carte de crÃ©dit valide
+default.invalid.email.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une adresse e-mail valide
+default.invalid.range.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] Ã  [{4}]
+default.invalid.size.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] Ã  [{4}]
+default.invalid.max.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] est supÃ©rieure Ã  la valeur maximum [{3}]
+default.invalid.min.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] est infÃ©rieure Ã  la valeur minimum [{3}]
+default.invalid.max.size.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] est supÃ©rieure Ã  la valeur maximum [{3}]
+default.invalid.min.size.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] est infÃ©rieure Ã  la valeur minimum [{3}]
+default.invalid.validator.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas valide
+default.not.inlist.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] ne fait pas partie de la liste [{3}]
+default.blank.message=La propriÃ©tÃ© [{0}] de la classe [{1}] ne peut pas Ãªtre vide
+default.not.equal.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] ne peut pas Ãªtre Ã©gale Ã  [{3}]
+default.null.message=La propriÃ©tÃ© [{0}] de la classe [{1}] ne peut pas Ãªtre nulle
+default.not.unique.message=La propriÃ©tÃ© [{0}] de la classe [{1}] avec la valeur [{2}] doit Ãªtre unique
+
+default.paginate.prev=PrÃ©cÃ©dent
+default.paginate.next=Suivant
Index: /trunk/grails-app/i18n/messages_it.properties
===================================================================
--- /trunk/grails-app/i18n/messages_it.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_it.properties	(revision 104)
@@ -0,0 +1,19 @@
+default.doesnt.match.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non corrisponde al pattern [{3}]
+default.invalid.url.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non Ãš un URL valido
+default.invalid.creditCard.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non Ãš un numero di carta di credito valido
+default.invalid.email.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non Ãš un indirizzo email valido
+default.invalid.range.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo valido da [{3}] a [{4}]
+default.invalid.size.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo di dimensioni valide da [{3}] a [{4}]
+default.invalid.max.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] Ãš maggiore di [{3}]
+default.invalid.min.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] Ãš minore di [{3}]
+default.invalid.max.size.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] Ãš maggiore di [{3}]
+default.invalid.min.size.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] Ãš minore di [{3}]
+default.invalid.validator.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non Ãš valida
+default.not.inlist.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non Ãš contenuta nella lista [{3}]
+default.blank.message=La proprietÃ  [{0}] della classe [{1}] non puÃ² essere vuota
+default.not.equal.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] non puÃ² essere uguale a [{3}]
+default.null.message=La proprietÃ  [{0}] della classe [{1}] non puÃ² essere null
+default.not.unique.message=La proprietÃ  [{0}] della classe [{1}] con valore [{2}] deve essere unica
+
+default.paginate.prev=Precedente
+default.paginate.next=Successivo
Index: /trunk/grails-app/i18n/messages_ja.properties
===================================================================
--- /trunk/grails-app/i18n/messages_ja.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_ja.properties	(revision 104)
@@ -0,0 +1,22 @@
+default.doesnt.match.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ã[{3}]ãã¿ãŒã³ãšäžèŽããŠããŸããã
+default.invalid.url.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãURLã§ã¯ãããŸããã
+default.invalid.creditCard.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãæ­£åœãªã¯ã¬ãžããã«ãŒãçªå·ã§ã¯ãããŸããã
+default.invalid.email.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãã¡ãŒã«ã¢ãã¬ã¹ã§ã¯ãããŸããã
+default.invalid.range.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ã[{3}]ãã[{4}]ç¯å²å
+ãæå®ããŠãã ããã
+default.invalid.size.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ã[{3}]ãã[{4}]ä»¥å
+ãæå®ããŠãã ããã
+default.invalid.max.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãæå€§å€[{3}]ããå€§ããã§ãã
+default.invalid.min.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãæå°å€[{3}]ããå°ããã§ãã
+default.invalid.max.size.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãæå€§å€[{3}]ããå€§ããã§ãã
+default.invalid.min.size.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãæå°å€[{3}]ããå°ããã§ãã
+default.invalid.validator.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ãã«ã¹ã¿ã ããªããŒã·ã§ã³ãééã§ããŸããã
+default.not.inlist.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ã[{3}]ãªã¹ãå
+ã«å­åšããŸããã
+default.blank.message=[{1}]ã¯ã©ã¹ã®ãã­ããã£[{0}]ã®ç©ºçœã¯èš±å¯ãããŸããã
+default.not.equal.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯ã[{3}]ãšåç­ã§ã¯ãããŸããã
+default.null.message=[{1}]ã¯ã©ã¹ã®ãã­ããã£[{0}]ã«nullã¯èš±å¯ãããŸããã
+default.not.unique.message=ã¯ã©ã¹[{1}]ãã­ããã£[{0}]ã®å€[{2}]ã¯æ¢ã«äœ¿çšãããŠããŸãã
+
+default.paginate.prev=æ»ã
+default.paginate.next=æ¬¡ãž
Index: /trunk/grails-app/i18n/messages_nl.properties
===================================================================
--- /trunk/grails-app/i18n/messages_nl.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_nl.properties	(revision 104)
@@ -0,0 +1,30 @@
+default.doesnt.match.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet overeen met het vereiste patroon [{3}]
+default.invalid.url.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldige URL
+default.invalid.creditCard.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig credit card nummer
+default.invalid.email.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig e-mailadres
+default.invalid.range.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige waardenreeks van [{3}] tot [{4}]
+default.invalid.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige grootte van [{3}] tot [{4}]
+default.invalid.max.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumwaarde [{3}]
+default.invalid.min.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan de minimumwaarde [{3}]
+default.invalid.max.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumgrootte van [{3}]
+default.invalid.min.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan mainimumgrootte van [{3}]
+default.invalid.validator.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is niet geldig
+default.not.inlist.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet voor in de lijst [{3}]
+default.blank.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
+default.not.equal.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] mag niet gelijk zijn aan [{3}]
+default.null.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
+default.not.unique.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] moet uniek zijn
+
+default.paginate.prev=Vorige
+default.paginate.next=Volgende
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Attribuut {0} is geen geldige URL
+typeMismatch.java.net.URI=Attribuut {0} is geen geldige URI
+typeMismatch.java.util.Date=Attribuut {0} is geen geldige datum
+typeMismatch.java.lang.Double=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Integer=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Long=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Short=Attribuut {0} is geen geldig nummer
+typeMismatch.java.math.BigDecimal=Attribuut {0} is geen geldig nummer
+typeMismatch.java.math.BigInteger=Attribuut {0} is geen geldig nummer
Index: /trunk/grails-app/i18n/messages_pt_BR.properties
===================================================================
--- /trunk/grails-app/i18n/messages_pt_BR.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_pt_BR.properties	(revision 104)
@@ -0,0 +1,34 @@
+#
+# Translated by Lucas Teixeira - lucastex@gmail.com
+#
+
+default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o atende ao padrÃ£o definido [{3}]
+default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o Ã© uma URL vÃ¡lida
+default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o Ã© um nÃºmero vÃ¡lido de cartÃ£o de crÃ©dito
+default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o Ã© um endereÃ§o de email vÃ¡lido.
+default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o estÃ¡ entre a faixa de valores vÃ¡lida de [{3}] atÃ© [{4}]
+default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o estÃ¡ na faixa de tamanho vÃ¡lida de [{3}] atÃ© [{4}]
+default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapass o valor mÃ¡ximo [{3}]
+default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o atinge o valor mÃ­nimo [{3}]
+default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho mÃ¡ximo de [{3}]
+default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o atinge o tamanho mÃ­nimo de [{3}]
+default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o passou na validaÃ§Ã£o
+default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o Ã© um valor dentre os permitidos na lista [{3}]
+default.blank.message=O campo [{0}] da classe [{1}] nÃ£o pode ficar em branco
+default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] nÃ£o pode ser igual a [{3}]
+default.null.message=O campo [{0}] da classe [{1}] nÃ£o pode ser vazia
+default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser Ãºnico
+
+default.paginate.prev=Anterior
+default.paginate.next=PrÃ³ximo
+
+# Mensagens de erro em atribuiÃ§Ã£o de valores. Use "typeMismatch.$className.$propertyName" para customizar (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=O campo {0} deve ser uma URL vÃ¡lida.
+typeMismatch.java.net.URI=O campo {0} deve ser uma URI vÃ¡lida.
+typeMismatch.java.util.Date=O campo {0} deve ser uma data vÃ¡lida
+typeMismatch.java.lang.Double=O campo {0} deve ser um nÃºmero vÃ¡lido.
+typeMismatch.java.lang.Integer=O campo {0} deve ser um nÃºmero vÃ¡lido.
+typeMismatch.java.lang.Long=O campo {0} deve ser um nÃºmero vÃ¡lido.
+typeMismatch.java.lang.Short=O campo {0} deve ser um nÃºmero vÃ¡lido.
+typeMismatch.java.math.BigDecimal=O campo {0} deve ser um nÃºmero vÃ¡lido.
+typeMismatch.java.math.BigInteger=O campo {0} deve ser um nÃºmero vÃ¡lido.
Index: /trunk/grails-app/i18n/messages_ru.properties
===================================================================
--- /trunk/grails-app/i18n/messages_ru.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_ru.properties	(revision 104)
@@ -0,0 +1,33 @@
+default.doesnt.match.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÑÐŸÐŸÑÐ²ÐµÑÑÑÐ²ÑÐµÑ ÐŸÐ±ÑÐ°Ð·ÑÑ [{3}]
+default.invalid.url.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ URL-Ð°ÐŽÑÐµÑÐŸÐŒ
+default.invalid.creditCard.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÐœÐŸÐŒÐµÑÐŸÐŒ ÐºÑÐµÐŽÐžÑÐœÐŸÐ¹ ÐºÐ°ÑÑÑ
+default.invalid.email.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ e-mail Ð°ÐŽÑÐµÑÐŸÐŒ
+default.invalid.range.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ Ð¿ÐŸÐ¿Ð°ÐŽÐ°ÐµÑ Ð² ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐ¹ ÐžÐœÑÐµÑÐ²Ð°Ð» ÐŸÑ [{3}] ÐŽÐŸ [{4}]
+default.invalid.size.message=Ð Ð°Ð·ÐŒÐµÑ Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] (Ð·ÐœÐ°ÑÐµÐœÐžÐµ: [{2}]) ÐœÐµ Ð¿ÐŸÐ¿Ð°ÐŽÐ°ÐµÑ Ð² ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐ¹ ÐžÐœÑÐµÑÐ²Ð°Ð» ÐŸÑ [{3}] ÐŽÐŸ [{4}]
+default.invalid.max.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] Ð±ÐŸÐ»ÑÑÐµ ÑÐµÐŒ ÐŒÐ°ÐºÑÐžÐŒÐ°Ð»ÑÐœÐŸ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÐŸÐµ Ð·ÐœÐ°ÑÐµÐœÐžÐµ [{3}]
+default.invalid.min.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐŒÐµÐœÑÑÐµ ÑÐµÐŒ ÐŒÐžÐœÐžÐŒÐ°Ð»ÑÐœÐŸ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÐŸÐµ Ð·ÐœÐ°ÑÐµÐœÐžÐµ [{3}]
+default.invalid.max.size.message=Ð Ð°Ð·ÐŒÐµÑ Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] (Ð·ÐœÐ°ÑÐµÐœÐžÐµ: [{2}]) Ð±ÐŸÐ»ÑÑÐµ ÑÐµÐŒ ÐŒÐ°ÐºÑÐžÐŒÐ°Ð»ÑÐœÐŸ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐ¹ ÑÐ°Ð·ÐŒÐµÑ [{3}]
+default.invalid.min.size.message=Ð Ð°Ð·ÐŒÐµÑ Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] (Ð·ÐœÐ°ÑÐµÐœÐžÐµ: [{2}]) ÐŒÐµÐœÑÑÐµ ÑÐµÐŒ ÐŒÐžÐœÐžÐŒÐ°Ð»ÑÐœÐŸ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐ¹ ÑÐ°Ð·ÐŒÐµÑ [{3}]
+default.invalid.validator.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÐŸ
+default.not.inlist.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ Ð¿ÐŸÐ¿Ð°ÐŽÐ°ÐµÑ Ð² ÑÐ¿ÐžÑÐŸÐº ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÑ
+ Ð·ÐœÐ°ÑÐµÐœÐžÐ¹ [{3}]
+default.blank.message=ÐÐŸÐ»Ðµ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÐŒÐŸÐ¶ÐµÑ Ð±ÑÑÑ Ð¿ÑÑÑÑÐŒ
+default.not.equal.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÐŒÐŸÐ¶ÐµÑ Ð±ÑÑÑ ÑÐ°Ð²ÐœÐŸ [{3}]
+default.null.message=ÐÐŸÐ»Ðµ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐœÐµ ÐŒÐŸÐ¶ÐµÑ ÐžÐŒÐµÑÑ Ð·ÐœÐ°ÑÐµÐœÐžÐµ null
+default.not.unique.message=ÐÐœÐ°ÑÐµÐœÐžÐµ [{2}] Ð¿ÐŸÐ»Ñ [{0}] ÐºÐ»Ð°ÑÑÐ° [{1}] ÐŽÐŸÐ»Ð¶ÐœÐŸ Ð±ÑÑÑ ÑÐœÐžÐºÐ°Ð»ÑÐœÑÐŒ
+
+default.paginate.prev=ÐÑÐµÐŽÑÐŽÑÑÐ°Ñ ÑÑÑÐ°ÐœÐžÑÐ°
+default.paginate.next=Ð¡Ð»ÐµÐŽÑÑÑÐ°Ñ ÑÑÑÐ°ÐœÐžÑÐ°
+
+# ÐÑÐžÐ±ÐºÐž Ð¿ÑÐž Ð¿ÑÐžÑÐ²ÐŸÐµÐœÐžÐž ÐŽÐ°ÐœÐœÑÑ
+. ÐÐ»Ñ ÑÐŸÑÐœÐŸÐ¹ ÐœÐ°ÑÑÑÐŸÐ¹ÐºÐž ÐŽÐ»Ñ Ð¿ÐŸÐ»ÐµÐ¹ ÐºÐ»Ð°ÑÑÐŸÐ² ÐžÑÐ¿ÐŸÐ»ÑÐ·ÑÐ¹ÑÐµ
+# ÑÐŸÑÐŒÐ°Ñ "typeMismatch.$className.$propertyName" (ÐœÐ°Ð¿ÑÐžÐŒÐµÑ, typeMismatch.Book.author)
+typeMismatch.java.net.URL=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ URL
+typeMismatch.java.net.URI=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ URI
+typeMismatch.java.util.Date=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÐŸÐ¹ ÐŽÐ°ÑÐŸÐ¹
+typeMismatch.java.lang.Double=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
+typeMismatch.java.lang.Integer=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
+typeMismatch.java.lang.Long=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
+typeMismatch.java.lang.Short=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
+typeMismatch.java.math.BigDecimal=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
+typeMismatch.java.math.BigInteger=ÐÐœÐ°ÑÐµÐœÐžÐµ Ð¿ÐŸÐ»Ñ {0} ÐœÐµ ÑÐ²Ð»ÑÐµÑÑÑ ÐŽÐŸÐ¿ÑÑÑÐžÐŒÑÐŒ ÑÐžÑÐ»ÐŸÐŒ
Index: /trunk/grails-app/i18n/messages_th.properties
===================================================================
--- /trunk/grails-app/i18n/messages_th.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_th.properties	(revision 104)
@@ -0,0 +1,30 @@
+default.doesnt.match.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžàž¹àžàžà¹àž­àžàžàž²àž¡àž£àž¹àžà¹àžàžàžàžµà¹àžàž³àž«àžàžà¹àž§à¹à¹àž [{3}]
+default.invalid.url.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžàž¹àžàžà¹àž­àžàžàž²àž¡àž£àž¹àžà¹àžàž URL
+default.invalid.creditCard.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžàž¹àžàžà¹àž­àžàžàž²àž¡àž£àž¹àžà¹àžàžàž«àž¡àž²àž¢à¹àž¥àžàžàž±àžàž£à¹àžàž£àžàžŽàž
+default.invalid.email.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžàž¹àžàžà¹àž­àžàžàž²àž¡àž£àž¹àžà¹àžàžàž­àžµà¹àž¡àž¥à¹
+default.invalid.range.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹à¹àžà¹àž¡àžµàžà¹àž²àžàžµà¹àžàž¹àžàžà¹àž­àžà¹àžàžà¹àž§àžàžàž²àž [{3}] àžàž¶àž [{4}]
+default.invalid.size.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹à¹àžà¹àž¡àžµàžàžàž²àžàžàžµà¹àžàž¹àžàžà¹àž­àžà¹àžàžà¹àž§àžàžàž²àž [{3}] àžàž¶àž [{4}]
+default.invalid.max.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] àž¡àžµàžà¹àž²à¹àžàžŽàžàžàž§à¹àž²àžà¹àž²àž¡àž²àžàžªàžžàž [{3}]
+default.invalid.min.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] àž¡àžµàžà¹àž²àžà¹àž­àž¢àžàž§à¹àž²àžà¹àž²àžà¹àž³àžªàžžàž  [{3}]
+default.invalid.max.size.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] àž¡àžµàžàžàž²àžà¹àžàžŽàžàžàž§à¹àž²àžàžàž²àžàž¡àž²àžàžªàžžàžàžàž­àž [{3}]
+default.invalid.min.size.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] àž¡àžµàžàžàž²àžàžà¹àž³àžàž§à¹àž²àžàžàž²àžàžà¹àž³àžªàžžàžàžàž­àž  [{3}]
+default.invalid.validator.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžà¹àž²àžàžàž²àž£àžàž§àžàžªàž­àžàžà¹àž²àžàžµà¹àžàž±à¹àžàžàž¶à¹àž
+default.not.inlist.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹à¹àžà¹àž­àž¢àž¹à¹à¹àžàž£àž²àž¢àžàž²àž£àžà¹àž­à¹àžàžàžµà¹  [{3}]
+default.blank.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] à¹àž¡à¹àžªàž²àž¡àž²àž£àžà¹àžà¹àžàžà¹àž²àž§à¹àž²àžà¹àžà¹
+default.not.equal.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] à¹àž¡à¹àžªàž²àž¡àž²àž£àžà¹àžà¹àž²àžàž±àž [{3}] à¹àžà¹
+default.null.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] à¹àž¡à¹àžªàž²àž¡àž²àž£àžà¹àžà¹àž null à¹àžà¹
+default.not.unique.message=àžàžžàžàžªàž¡àžàž±àžàžŽ [{0}] àžàž­àžàžàž¥àž²àžª [{1}] àžàž¶à¹àžàž¡àžµàžà¹àž²à¹àžà¹àž [{2}] àžàž°àžà¹àž­àžà¹àž¡à¹àžà¹àž³ (unique)
+
+default.paginate.prev=àžà¹àž­àžàž«àžà¹àž²
+default.paginate.next=àžàž±àžà¹àž
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžà¹àžà¹àžàžà¹àž² URL àžàžµà¹àžàž¹àžàžà¹àž­àž
+typeMismatch.java.net.URI=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžà¹àžà¹àžàžà¹àž² URI àžàžµà¹àžàž¹àžàžà¹àž­àž
+typeMismatch.java.util.Date=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàž§àž±àžàžàžµà¹
+typeMismatch.java.lang.Double=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž Double
+typeMismatch.java.lang.Integer=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž Integer
+typeMismatch.java.lang.Long=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž Long
+typeMismatch.java.lang.Short=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž Short
+typeMismatch.java.math.BigDecimal=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž BigDecimal
+typeMismatch.java.math.BigInteger=àžàžžàžàžªàž¡àžàž±àžàžŽ '{0}' àžàž°àžà¹àž­àžàž¡àžµàžà¹àž²à¹àžà¹àžàžàž³àžàž§àžàžàž£àž°à¹àž àž BigInteger
Index: /trunk/grails-app/i18n/messages_zh_CN.properties
===================================================================
--- /trunk/grails-app/i18n/messages_zh_CN.properties	(revision 104)
+++ /trunk/grails-app/i18n/messages_zh_CN.properties	(revision 104)
@@ -0,0 +1,18 @@
+default.blank.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3A\u7A7A
+default.doesnt.match.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E\u5B9A\u4E49\u7684\u6A21\u5F0F [{3}]\u4E0D\u5339\u914D
+default.invalid.creditCard.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684\u4FE1\u7528\u5361\u53F7
+default.invalid.email.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740
+default.invalid.max.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
+default.invalid.max.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
+default.invalid.min.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
+default.invalid.min.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
+default.invalid.range.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
+default.invalid.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
+default.invalid.url.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684URL
+default.invalid.validator.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u672A\u80FD\u901A\u8FC7\u81EA\u5B9A\u4E49\u7684\u9A8C\u8BC1
+default.not.equal.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E[{3}]\u4E0D\u76F8\u7B49
+default.not.inlist.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5217\u8868\u7684\u53D6\u503C\u8303\u56F4\u5185
+default.not.unique.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u5FC5\u987B\u662F\u552F\u4E00\u7684
+default.null.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3Anull
+default.paginate.next=\u4E0B\u9875
+default.paginate.prev=\u4E0A\u9875
Index: /trunk/grails-app/views/_adminmenubar.gsp
===================================================================
--- /trunk/grails-app/views/_adminmenubar.gsp	(revision 104)
+++ /trunk/grails-app/views/_adminmenubar.gsp	(revision 104)
@@ -0,0 +1,27 @@
+<g:isLoggedIn>
+    <g:ifAnyGranted role="ROLE_Manager">
+        <span class="appControlButton">
+            <g:link controller="appCore" action="manager">
+                Manager
+            </g:link>
+        </span>
+    </g:ifAnyGranted>
+    <g:ifAnyGranted role="ROLE_AppAdmin">
+        <span class="appControlButton">
+            <g:link controller="appCore" action="admin">
+                Admin
+            </g:link>
+        </span>
+    </g:ifAnyGranted>
+    <span class="appControlButton">
+        <g:link controller="appCore" action="options">
+                Options
+        </g:link>
+    </span>
+    <span class="appControlButton">
+        <g:link controller="logout">
+                Log out (<g:loggedInUsername/>)
+        </g:link>
+    </span>
+</g:isLoggedIn>
+
Index: /trunk/grails-app/views/appCore/admin.gsp
===================================================================
--- /trunk/grails-app/views/appCore/admin.gsp	(revision 104)
+++ /trunk/grails-app/views/appCore/admin.gsp	(revision 104)
@@ -0,0 +1,29 @@
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Admin</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+        </div>
+        <div class="body">
+            <h1>Application Admin</h1>
+            <div class="errors">
+                Warning!<br />
+                The pages bellow this line are for use by the application admin only and NOT for daily use.<br />
+                They allow direct administration of the back-end data, cascade deletion and updates may occur.<br />
+                The manager authorisation and pages should be used for normal daily use.<br />
+            </div>
+            <br/>
+            <div class="dialog">
+                <ul>
+                <g:each var="c" in="${grailsApplication.controllerClasses}">
+                        <li class="controller"><g:link controller="${c.logicalPropertyName}">${c.fullName}</g:link></li>
+                </g:each>
+                </ul>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appCore/changePassword.gsp
===================================================================
--- /trunk/grails-app/views/appCore/changePassword.gsp	(revision 104)
+++ /trunk/grails-app/views/appCore/changePassword.gsp	(revision 104)
@@ -0,0 +1,52 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Change Password</title>
+</head>
+
+<body onload="document.changePasswordForm.pass.focus();">
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+    </div>
+
+	<div class="body">
+		<h1>Change Password</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${personInstance}">
+		<div class="errors">
+			<g:renderErrors bean="${personInstance}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<g:form name="changePasswordForm" id="changePasswordForm">
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="pass">Password:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:personInstance,field:'pass','errors')}">
+							<input type="password" id="pass" name="pass" />
+						</td>
+					</tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name"><label for="confirmPass">Confirm password:</label></td>
+                        <td valign="top" class="value">
+                            <input type="password" id="confirmPass" name="confirmPass" />
+                        </td>
+                    </tr>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Change Password" action="changePassword"/></span>
+			</div>
+		</g:form>
+
+	</div>
+
+</body>
Index: /trunk/grails-app/views/appCore/home.gsp
===================================================================
--- /trunk/grails-app/views/appCore/home.gsp	(revision 104)
+++ /trunk/grails-app/views/appCore/home.gsp	(revision 104)
@@ -0,0 +1,25 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html;
+      charset=UTF-8"/>
+<meta name="layout" content="main" />
+<title>Home</title>
+</head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><g:link class="list" controller="taskDetailed" action="list">Tasks</g:link></span>
+
+        </div>
+        <div class="body">
+            <g:if test="${flash.message}">
+                <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${appCore}">
+                <div class="errors">
+                    <g:renderErrors bean="${appCore}" as="list" />
+                </div>
+            </g:hasErrors>
+            
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appCore/manager.gsp
===================================================================
--- /trunk/grails-app/views/appCore/manager.gsp	(revision 104)
+++ /trunk/grails-app/views/appCore/manager.gsp	(revision 104)
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Admin</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link controller="person" action="create">New Person</g:link></span>
+            <span class="menuButton"><g:link controller="person" action="list">Person List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Welcome to Manager</h1>
+
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/appCore/options.gsp
===================================================================
--- /trunk/grails-app/views/appCore/options.gsp	(revision 104)
+++ /trunk/grails-app/views/appCore/options.gsp	(revision 104)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Options</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+        </div>
+        <div class="body">
+            <h1>Welcome to Options</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <br/>
+            <div class="dialog">
+            <a href="${createLink(action:'changePassword')}">Change Password</a>
+
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPerson/create.gsp
===================================================================
--- /trunk/grails-app/views/assignedPerson/create.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPerson/create.gsp	(revision 104)
@@ -0,0 +1,74 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create AssignedPerson</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${assignedPersonInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${assignedPersonInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${assignedPersonInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${assignedPersonInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedHour">Estimated Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedHour','errors')}">
+                                    <input type="text" id="estimatedHour" name="estimatedHour" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedMinute">Estimated Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedMinute','errors')}">
+                                    <input type="text" id="estimatedMinute" name="estimatedMinute" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPerson/edit.gsp
===================================================================
--- /trunk/grails-app/views/assignedPerson/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPerson/edit.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit AssignedPerson</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${assignedPersonInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${assignedPersonInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${assignedPersonInstance?.id}" />
+                <input type="hidden" name="version" value="${assignedPersonInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${assignedPersonInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${assignedPersonInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedHour">Estimated Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedHour','errors')}">
+                                    <input type="text" id="estimatedHour" name="estimatedHour" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedMinute">Estimated Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedMinute','errors')}">
+                                    <input type="text" id="estimatedMinute" name="estimatedMinute" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPerson/list.gsp
===================================================================
--- /trunk/grails-app/views/assignedPerson/list.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPerson/list.gsp	(revision 104)
@@ -0,0 +1,60 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>AssignedPerson List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>AssignedPerson List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <th>Task</th>
+                   	    
+                   	        <th>Person</th>
+                   	    
+                   	        <g:sortableColumn property="estimatedHour" title="Estimated Hour" />
+                        
+                   	        <g:sortableColumn property="estimatedMinute" title="Estimated Minute" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${assignedPersonInstanceList}" status="i" var="assignedPersonInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${assignedPersonInstance.id}">${fieldValue(bean:assignedPersonInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'task')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'person')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'estimatedHour')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'estimatedMinute')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${assignedPersonInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPerson/show.gsp
===================================================================
--- /trunk/grails-app/views/assignedPerson/show.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPerson/show.gsp	(revision 104)
@@ -0,0 +1,72 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show AssignedPerson</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="task" action="show" id="${assignedPersonInstance?.task?.id}">${assignedPersonInstance?.task?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Person:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="person" action="show" id="${assignedPersonInstance?.person?.id}">${assignedPersonInstance?.person?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Estimated Hour:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'estimatedHour')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Estimated Minute:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'estimatedMinute')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${assignedPersonInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPersonDetailed/create.gsp
===================================================================
--- /trunk/grails-app/views/assignedPersonDetailed/create.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPersonDetailed/create.gsp	(revision 104)
@@ -0,0 +1,74 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create AssignedPerson</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${assignedPersonInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${assignedPersonInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${assignedPersonInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${assignedPersonInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedHour">Estimated Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedHour','errors')}">
+                                    <input type="text" id="estimatedHour" name="estimatedHour" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedMinute">Estimated Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedMinute','errors')}">
+                                    <input type="text" id="estimatedMinute" name="estimatedMinute" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPersonDetailed/edit.gsp
===================================================================
--- /trunk/grails-app/views/assignedPersonDetailed/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPersonDetailed/edit.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit AssignedPerson</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${assignedPersonInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${assignedPersonInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${assignedPersonInstance?.id}" />
+                <input type="hidden" name="version" value="${assignedPersonInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${assignedPersonInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${assignedPersonInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedHour">Estimated Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedHour','errors')}">
+                                    <input type="text" id="estimatedHour" name="estimatedHour" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="estimatedMinute">Estimated Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:assignedPersonInstance,field:'estimatedMinute','errors')}">
+                                    <input type="text" id="estimatedMinute" name="estimatedMinute" value="${fieldValue(bean:assignedPersonInstance,field:'estimatedMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPersonDetailed/list.gsp
===================================================================
--- /trunk/grails-app/views/assignedPersonDetailed/list.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPersonDetailed/list.gsp	(revision 104)
@@ -0,0 +1,60 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>AssignedPerson List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>AssignedPerson List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <th>Task</th>
+                   	    
+                   	        <th>Person</th>
+                   	    
+                   	        <g:sortableColumn property="estimatedHour" title="Estimated Hour" />
+                        
+                   	        <g:sortableColumn property="estimatedMinute" title="Estimated Minute" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${assignedPersonInstanceList}" status="i" var="assignedPersonInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${assignedPersonInstance.id}">${fieldValue(bean:assignedPersonInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'task')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'person')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'estimatedHour')}</td>
+                        
+                            <td>${fieldValue(bean:assignedPersonInstance, field:'estimatedMinute')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${assignedPersonInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/assignedPersonDetailed/show.gsp
===================================================================
--- /trunk/grails-app/views/assignedPersonDetailed/show.gsp	(revision 104)
+++ /trunk/grails-app/views/assignedPersonDetailed/show.gsp	(revision 104)
@@ -0,0 +1,72 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show AssignedPerson</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">AssignedPerson List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New AssignedPerson</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show AssignedPerson</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="task" action="show" id="${assignedPersonInstance?.task?.id}">${assignedPersonInstance?.task?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Person:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="person" action="show" id="${assignedPersonInstance?.person?.id}">${assignedPersonInstance?.person?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Estimated Hour:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'estimatedHour')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Estimated Minute:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:assignedPersonInstance, field:'estimatedMinute')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${assignedPersonInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/authority/create.gsp
===================================================================
--- /trunk/grails-app/views/authority/create.gsp	(revision 104)
+++ /trunk/grails-app/views/authority/create.gsp	(revision 104)
@@ -0,0 +1,51 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Create Authority</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Authority List</g:link></span>
+	</div>
+
+	<div class="body">
+
+		<h1>Create Authority</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${authority}">
+		<div class="errors">
+		<g:renderErrors bean="${authority}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<g:form action="save">
+		<div class="dialog">
+		<table>
+		<tbody>
+			<tr class="prop">
+				<td valign="top" class="name"><label for="authority">Authority Name:</label></td>
+				<td valign="top" class="value ${hasErrors(bean:authority,field:'authority','errors')}">
+					<input type="text" id="authority" name="authority" value="${authority?.authority?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name"><label for="description">Description:</label></td>
+				<td valign="top" class="value ${hasErrors(bean:authority,field:'description','errors')}">
+					<input type="text" id="description" name="description" value="${authority?.description?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+		</tbody>
+		</table>
+		</div>
+
+		<div class="buttons">
+			<span class="button"><input class="save" type="submit" value="Create" /></span>
+		</div>
+		</g:form>
+	</div>
+</body>
Index: /trunk/grails-app/views/authority/edit.gsp
===================================================================
--- /trunk/grails-app/views/authority/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/authority/edit.gsp	(revision 104)
@@ -0,0 +1,69 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit Authority</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Authority List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New Authority</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit Authority</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${authority}">
+		<div class="errors">
+			<g:renderErrors bean="${authority}" as="list" />
+		</div>
+		</g:hasErrors>
+		<div class="prop">
+			<span class="name">ID:</span>
+			<span class="value">${authority.id}</span>
+		</div>
+		<g:form>
+			<input type="hidden" name="id" value="${authority.id}" />
+			<input type="hidden" name="version" value="${authority.version}" />
+			<div class="dialog">
+			<table>
+			<tbody>
+				<tr class="prop">
+					<td valign="top" class="name"><label for="authority">Authority Name:</label></td>
+					<td valign="top" class="value ${hasErrors(bean:authority,field:'authority','errors')}">
+						<input type="text" id="authority" name="authority" value="${authority.authority?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name"><label for="description">Description:</label></td>
+					<td valign="top" class="value ${hasErrors(bean:authority,field:'description','errors')}">
+						<input type="text" id="description" name="description" value="${authority.description?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name"><label for="persons">Persons:</label></td>
+					<td valign="top" class="value ${hasErrors(bean:authority,field:'persons','errors')}">
+						<ul>
+						<g:each var="p" in="${authority.persons?}">
+							<li>${p}</li>
+						</g:each>
+						</ul>
+					</td>
+				</tr>
+			</tbody>
+			</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Update" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</div>
+
+		</g:form>
+	</div>
+</body>
Index: /trunk/grails-app/views/authority/list.gsp
===================================================================
--- /trunk/grails-app/views/authority/list.gsp	(revision 104)
+++ /trunk/grails-app/views/authority/list.gsp	(revision 104)
@@ -0,0 +1,49 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Authority List</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="create" action="create">New Authority</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Authority List</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<div class="list">
+			<table>
+			<thead>
+				<tr>
+					<g:sortableColumn property="id" title="ID" />
+					<g:sortableColumn property="authority" title="Authority Name" />
+					<g:sortableColumn property="description" title="Description" />
+					<th>&nbsp;</th>
+				</tr>
+			</thead>
+			<tbody>
+			<g:each in="${authorityList}" status="i" var="authority">
+				<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+					<td>${authority.id}</td>
+					<td>${authority.authority?.encodeAsHTML()}</td>
+					<td>${authority.description?.encodeAsHTML()}</td>
+					<td class="actionButtons">
+						<span class="actionButton">
+							<g:link action="show" id="${authority.id}">Show</g:link>
+						</span>
+					</td>
+				</tr>
+			</g:each>
+			</tbody>
+			</table>
+		</div>
+
+		<div class="paginateButtons">
+			<g:paginate total="${Authority.count()}" />
+		</div>
+	</div>
+</body>
Index: /trunk/grails-app/views/authority/show.gsp
===================================================================
--- /trunk/grails-app/views/authority/show.gsp	(revision 104)
+++ /trunk/grails-app/views/authority/show.gsp	(revision 104)
@@ -0,0 +1,57 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Show Authority</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Authority List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New Authority</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Show Authority</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+			<table>
+			<tbody>
+
+				<tr class="prop">
+					<td valign="top" class="name">ID:</td>
+					<td valign="top" class="value">${authority.id}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Authority Name:</td>
+					<td valign="top" class="value">${authority.authority}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Description:</td>
+					<td valign="top" class="value">${authority.description}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Persons:</td>
+					<td valign="top" class="value">${authority.persons}</td>
+				</tr>
+
+			</tbody>
+			</table>
+		</div>
+
+		<div class="buttons">
+			<g:form>
+				<input type="hidden" name="id" value="${authority?.id}" />
+				<span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</g:form>
+		</div>
+
+	</div>
+
+</body>
Index: /trunk/grails-app/views/entry/create.gsp
===================================================================
--- /trunk/grails-app/views/entry/create.gsp	(revision 104)
+++ /trunk/grails-app/views/entry/create.gsp	(revision 104)
@@ -0,0 +1,110 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create Entry</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${entryInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:entryInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateDone">Date Done:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateDone','errors')}">
+                                    <g:datePicker name="dateDone" value="${entryInstance?.dateDone}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationHour">Duration Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'durationHour','errors')}">
+                                    <input type="text" id="durationHour" name="durationHour" value="${fieldValue(bean:entryInstance,field:'durationHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationMinute">Duration Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'durationMinute','errors')}">
+                                    <input type="text" id="durationMinute" name="durationMinute" value="${fieldValue(bean:entryInstance,field:'durationMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateEntered">Date Entered:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateEntered','errors')}">
+                                    <g:datePicker name="dateEntered" value="${entryInstance?.dateEntered}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="enteredBy">Entered By:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'enteredBy','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="enteredBy.id" value="${entryInstance?.enteredBy?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entryType">Entry Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'entryType','errors')}">
+                                    <g:select optionKey="id" from="${EntryType.list()}" name="entryType.id" value="${entryInstance?.entryType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entry/edit.gsp
===================================================================
--- /trunk/grails-app/views/entry/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/entry/edit.gsp	(revision 104)
@@ -0,0 +1,113 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit Entry</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${entryInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${entryInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:entryInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateDone">Date Done:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateDone','errors')}">
+                                    <g:datePicker name="dateDone" value="${entryInstance?.dateDone}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationHour">Duration Hour:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'durationHour','errors')}">
+                                    <input type="text" id="durationHour" name="durationHour" value="${fieldValue(bean:entryInstance,field:'durationHour')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationMinute">Duration Minute:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'durationMinute','errors')}">
+                                    <input type="text" id="durationMinute" name="durationMinute" value="${fieldValue(bean:entryInstance,field:'durationMinute')}" />
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateEntered">Date Entered:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateEntered','errors')}">
+                                    <g:datePicker name="dateEntered" value="${entryInstance?.dateEntered}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="enteredBy">Entered By:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'enteredBy','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="enteredBy.id" value="${entryInstance?.enteredBy?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entryType">Entry Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'entryType','errors')}">
+                                    <g:select optionKey="id" from="${EntryType.list()}" name="entryType.id" value="${entryInstance?.entryType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entry/list.gsp
===================================================================
--- /trunk/grails-app/views/entry/list.gsp	(revision 104)
+++ /trunk/grails-app/views/entry/list.gsp	(revision 104)
@@ -0,0 +1,64 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Entry List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Entry List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <th>Task</th>
+                   	    
+                   	        <g:sortableColumn property="comment" title="Comment" />
+                        
+                   	        <g:sortableColumn property="dateDone" title="Date Done" />
+                        
+                   	        <g:sortableColumn property="durationHour" title="Duration Hour" />
+                        
+                   	        <g:sortableColumn property="durationMinute" title="Duration Minute" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${entryInstanceList}" status="i" var="entryInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${entryInstance.id}">${fieldValue(bean:entryInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'task')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'comment')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'dateDone')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'durationHour')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'durationMinute')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${Entry.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entry/show.gsp
===================================================================
--- /trunk/grails-app/views/entry/show.gsp	(revision 104)
+++ /trunk/grails-app/views/entry/show.gsp	(revision 104)
@@ -0,0 +1,100 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show Entry</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="task" action="show" id="${entryInstance?.task?.id}">${entryInstance?.task?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Comment:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'comment')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Date Done:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'dateDone')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Duration Hour:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'durationHour')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Duration Minute:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'durationMinute')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Date Entered:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'dateEntered')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entered By:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="person" action="show" id="${entryInstance?.enteredBy?.id}">${entryInstance?.enteredBy?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entry Type:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="entryType" action="show" id="${entryInstance?.entryType?.id}">${entryInstance?.entryType?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${entryInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryDetailed/create.gsp
===================================================================
--- /trunk/grails-app/views/entryDetailed/create.gsp	(revision 104)
+++ /trunk/grails-app/views/entryDetailed/create.gsp	(revision 104)
@@ -0,0 +1,92 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create Entry</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <g:hiddenField name="task.id" value="${entryInstance.task.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="name">
+                                    ${entryInstance?.task?.description}
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:entryInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateDone">Date Done:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateDone','errors')}">
+                                    <g:datePicker name="dateDone" value="${entryInstance?.dateDone}" precision="day"></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationHour">Duration:</label>
+                                </td>
+
+                                <td valign="top" class="value">
+                                    <input class="duration ${hasErrors(bean:entryInstance,field:'durationHour','errors')}" 
+                                        type="text" id="durationHour" name="durationHour" 
+                                        value="${fieldValue(bean:entryInstance,field:'durationHour')}" />
+                                    :
+                                    <input class="duration ${hasErrors(bean:entryInstance,field:'durationMinute','errors')}" 
+                                        type="text" id="durationMinute" name="durationMinute" 
+                                        value="${fieldValue(bean:entryInstance,field:'durationMinute')}" />
+                                    <g:helpBalloon code="entry.duration" />
+                                </td> 
+                            </tr>
+                     
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entryType">Entry Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'entryType','errors')}">
+                                    <g:select optionKey="id" from="${EntryType.list()}" name="entryType.id" value="${entryInstance?.entryType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryDetailed/edit.gsp
===================================================================
--- /trunk/grails-app/views/entryDetailed/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/entryDetailed/edit.gsp	(revision 104)
@@ -0,0 +1,112 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit Entry</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${entryInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${entryInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:entryInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateDone">Date Done:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'dateDone','errors')}">
+                                    <g:datePicker name="dateDone" value="${entryInstance?.dateDone}" precision="day"></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="durationHour">Duration:</label>
+                                </td>
+
+                                <td valign="top" class="value">
+                                    <input class="duration ${hasErrors(bean:entryInstance,field:'durationHour','errors')}" 
+                                        type="text" id="durationHour" name="durationHour" 
+                                        value="${fieldValue(bean:entryInstance,field:'durationHour')}" />
+                                    :
+                                    <input class="duration ${hasErrors(bean:entryInstance,field:'durationMinute','errors')}" 
+                                        type="text" id="durationMinute" name="durationMinute" 
+                                        value="${fieldValue(bean:entryInstance,field:'durationMinute')}" />
+                                    <g:helpBalloon code="entry.duration" />
+                                </td> 
+                            </tr>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="dateEntered">Date Entered:</label>
+                                </td>
+                                <td valign="top" class="value">
+                                    <g:formatDate date="${entryInstance?.dateEntered}" format="EEE, dd MMM yyyy @ HH:mm"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="enteredBy">Entered By:</label>
+                                </td>
+                                <td valign="top" class="value">
+                                    ${entryInstance?.enteredBy?.toString()}
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entryType">Entry Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryInstance,field:'entryType','errors')}">
+                                    <g:select optionKey="id" from="${EntryType.list()}" name="entryType.id" value="${entryInstance?.entryType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryDetailed/list.gsp
===================================================================
--- /trunk/grails-app/views/entryDetailed/list.gsp	(revision 104)
+++ /trunk/grails-app/views/entryDetailed/list.gsp	(revision 104)
@@ -0,0 +1,68 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Entry List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Entry List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <th>Task</th>
+                   	    
+                   	        <g:sortableColumn property="comment" title="Comment" />
+                        
+                   	        <g:sortableColumn property="dateDone" title="Date Done" />
+                        
+                   	        <g:sortableColumn property="enteredBy" title="Entered By" />
+
+                            <th>Edit</th>
+
+                            <th>Show</th>
+
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${entryInstanceList}" status="i" var="entryInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'id')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'task')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'comment')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'dateDone')}</td>
+                        
+                            <td>${fieldValue(bean:entryInstance, field:'enteredBy')}</td>
+
+                            <td><g:link action="edit" id="${entryInstance.id}">Edit</g:link></td>
+
+                            <td><g:link action="show" id="${entryInstance.id}">Show</g:link></td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${Entry.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryDetailed/show.gsp
===================================================================
--- /trunk/grails-app/views/entryDetailed/show.gsp	(revision 104)
+++ /trunk/grails-app/views/entryDetailed/show.gsp	(revision 104)
@@ -0,0 +1,100 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show Entry</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Entry List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Entry</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show Entry</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskDetailed" action="show" id="${entryInstance?.task?.id}">${entryInstance?.task?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Comment:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'comment')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Date Done:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'dateDone')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Duration Hour:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'durationHour')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Duration Minute:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'durationMinute')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Date Entered:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryInstance, field:'dateEntered')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entered By:</td>
+                            
+                            <td valign="top" class="value">${entryInstance?.enteredBy?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entry Type:</td>
+                            
+                            <td valign="top" class="value">${entryInstance?.entryType?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${entryInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryType/create.gsp
===================================================================
--- /trunk/grails-app/views/entryType/create.gsp	(revision 104)
+++ /trunk/grails-app/views/entryType/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create EntryType</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">EntryType List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create EntryType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'description','errors')}">
+                                    <input type="text" id="description" name="description" value="${fieldValue(bean:entryTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${entryTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'name','errors')}">
+                                    <input type="text" id="name" name="name" value="${fieldValue(bean:entryTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryType/edit.gsp
===================================================================
--- /trunk/grails-app/views/entryType/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/entryType/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit EntryType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">EntryType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New EntryType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit EntryType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${entryTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${entryTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${entryTypeInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'description','errors')}">
+                                    <input type="text" id="description" name="description" value="${fieldValue(bean:entryTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entries">Entries:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'entries','errors')}">
+                                    
+<ul>
+<g:each var="e" in="${entryTypeInstance?.entries?}">
+    <li><g:link controller="entry" action="show" id="${e.id}">${e?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="entry" params="['entryType.id':entryTypeInstance?.id]" action="create">Add Entry</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${entryTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:entryTypeInstance,field:'name','errors')}">
+                                    <input type="text" id="name" name="name" value="${fieldValue(bean:entryTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryType/list.gsp
===================================================================
--- /trunk/grails-app/views/entryType/list.gsp	(revision 104)
+++ /trunk/grails-app/views/entryType/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>EntryType List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New EntryType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>EntryType List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${entryTypeInstanceList}" status="i" var="entryTypeInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${entryTypeInstance.id}">${fieldValue(bean:entryTypeInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:entryTypeInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:entryTypeInstance, field:'isActive')}</td>
+                        
+                            <td>${fieldValue(bean:entryTypeInstance, field:'name')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${EntryType.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/entryType/show.gsp
===================================================================
--- /trunk/grails-app/views/entryType/show.gsp	(revision 104)
+++ /trunk/grails-app/views/entryType/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show EntryType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">EntryType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New EntryType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show EntryType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryTypeInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryTypeInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entries:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="e" in="${entryTypeInstance.entries}">
+                                    <li><g:link controller="entry" action="show" id="${e.id}">${e?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryTypeInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:entryTypeInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${entryTypeInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/error.gsp
===================================================================
--- /trunk/grails-app/views/error.gsp	(revision 104)
+++ /trunk/grails-app/views/error.gsp	(revision 104)
@@ -0,0 +1,54 @@
+<html>
+  <head>
+	  <title>Grails Runtime Exception</title>
+	  <style type="text/css">
+	  		.message {
+	  			border: 1px solid black;
+	  			padding: 5px;
+	  			background-color:#E9E9E9;
+	  		}
+	  		.stack {
+	  			border: 1px solid black;
+	  			padding: 5px;
+	  			overflow:auto;
+	  			height: 300px;
+	  		}
+	  		.snippet {
+	  			padding: 5px;
+	  			background-color:white;
+	  			border:1px solid black;
+	  			margin:3px;
+	  			font-family:courier;
+	  		}
+	  </style>
+  </head>
+
+  <body>
+    <h1>Grails Runtime Exception</h1>
+    <h2>Error Details</h2>
+
+  	<div class="message">
+		<strong>Error ${request.'javax.servlet.error.status_code'}:</strong> ${request.'javax.servlet.error.message'.encodeAsHTML()}<br/>
+		<strong>Servlet:</strong> ${request.'javax.servlet.error.servlet_name'}<br/>
+		<strong>URI:</strong> ${request.'javax.servlet.error.request_uri'}<br/>
+		<g:if test="${exception}">
+	  		<strong>Exception Message:</strong> ${exception.message?.encodeAsHTML()} <br />
+	  		<strong>Caused by:</strong> ${exception.cause?.message?.encodeAsHTML()} <br />
+	  		<strong>Class:</strong> ${exception.className} <br />
+	  		<strong>At Line:</strong> [${exception.lineNumber}] <br />
+	  		<strong>Code Snippet:</strong><br />
+	  		<div class="snippet">
+	  			<g:each var="cs" in="${exception.codeSnippet}">
+	  				${cs?.encodeAsHTML()}<br />
+	  			</g:each>
+	  		</div>
+		</g:if>
+  	</div>
+	<g:if test="${exception}">
+	    <h2>Stack Trace</h2>
+	    <div class="stack">
+	      <pre><g:each in="${exception.stackTraceLines}">${it.encodeAsHTML()}<br/></g:each></pre>
+	    </div>
+	</g:if>
+  </body>
+</html>
Index: /trunk/grails-app/views/index.gsp
===================================================================
--- /trunk/grails-app/views/index.gsp	(revision 104)
+++ /trunk/grails-app/views/index.gsp	(revision 104)
@@ -0,0 +1,1 @@
+<%response.sendRedirect(request.getContextPath()+'/appCore/home')%>
Index: /trunk/grails-app/views/layouts/main.gsp
===================================================================
--- /trunk/grails-app/views/layouts/main.gsp	(revision 104)
+++ /trunk/grails-app/views/layouts/main.gsp	(revision 104)
@@ -0,0 +1,41 @@
+<html>
+    <head>
+        <title><g:layoutTitle default="gnuMims" /></title>
+        <link rel="stylesheet" href="${createLinkTo(dir:'css',file:'main.css')}" />
+        <link rel="shortcut icon" href="${createLinkTo(dir:'images',file:'gnuMimsIcon.ico')}" type="image/x-icon" />
+        <g:layoutHead />
+        <g:helpBalloons/>
+        <g:javascript library="application" />
+    </head>
+    
+    <!-- Added g:pageProperty so that onload in each page works -->
+    <body onload="<g:pageProperty name='body.onload'/>">
+        <div id="wrapper" style="height: 100%;">
+            <div id="top">
+            </div>
+            <div id="content" align="center">
+                <div id="spinner" class="spinner" style="display:none;">
+                    <img src="${createLinkTo(dir:'images',file:'spinner.gif')}" alt="Spinner" />
+                </div>	
+                <!-- <div class="logo" style="text-align: center; width: 980px; height: 220px">
+                <img src="${createLinkTo(dir:'images',file:'logo.png')}"
+                alt="gnuMims" />
+                <g:render template="/adminmenubar" />
+        
+                </div> -->
+                <div id="Header">
+                    <a href="http://www.gnumims.org" id=HeaderLink></a>
+                </div>
+                <div class="appControl">
+                    <g:render template="/adminmenubar" />
+                </div>
+                <!-- Body wrapper div for IE -->
+                <div style="text-align: center; width: 980px">
+                    <g:layoutBody />
+                </div><!--IE wrapper-->
+            </div><!--content-->
+            <div id="bottom">
+            </div>
+        </div>
+    </body>	
+</html>
Index: /trunk/grails-app/views/login/auth.gsp
===================================================================
--- /trunk/grails-app/views/login/auth.gsp	(revision 104)
+++ /trunk/grails-app/views/login/auth.gsp	(revision 104)
@@ -0,0 +1,82 @@
+<head>
+<meta name='layout' content='main' />
+<title>Login</title>
+<style type='text/css' media='screen'>
+#login {
+	margin:15px 0px; padding:0px;
+	text-align:center;
+}
+#login .inner {
+	width:260px;
+	margin:0px auto;
+	text-align:left;
+	padding:10px;
+/*	border-top:1px dashed #499ede;
+	border-bottom:1px dashed #499ede;*/
+/*	background-color:#EEF;*/
+}
+#login .inner .fheader {
+	padding:4px;margin:3px 0px 3px 0;color:#2e3741;font-size:14px;font-weight:bold;
+}
+#login .inner .cssform p {
+	clear: left;
+	margin: 0;
+	padding: 5px 0 8px 0;
+	padding-left: 105px;
+/*	border-top: 1px dashed gray;*/
+	margin-bottom: 10px;
+	height: 1%;
+}
+#login .inner .cssform input[type='text'] {
+	width: 120px;
+}
+#login .inner .cssform label {
+	font-weight: bold;
+	float: left;
+	margin-left: -105px;
+	width: 100px;
+}
+#login .inner .login_message {color:red;}
+#login .inner .text_ {width:120px;}
+#login .inner .chk {height:12px;}
+</style>
+</head>
+
+<body>
+	<div id='login'>
+        <h1>Welcome to gnuMims please log in</h1>
+        The demo users are "admin" and "user" both having password "pass"
+
+		<div class='inner'>
+			<g:if test='${flash.message}'>
+			<div class='login_message'>${flash.message}</div>
+			</g:if>
+			<div class='fheader'></div>
+			<form action='${postUrl}' method='POST' id='loginForm' class='cssform'>
+				<p>
+					<label for='j_username'>Login ID</label>
+					<input type='text' class='text_' name='j_username' id='j_username' value='${request.remoteUser}' />
+				</p>
+				<p>
+					<label for='j_password'>Password</label>
+					<input type='password' class='text_' name='j_password' id='j_password' />
+				</p>
+<!--				<p>
+					<label for='remember_me'>Remember me</label>
+					<input type='checkbox' class='chk' name='_spring_security_remember_me' id='remember_me'
+					<g:if test='${hasCookie}'>checked='checked'</g:if> />
+				</p>-->
+				<p>
+					<input type='submit' value='Login' />
+				</p>
+			</form>
+		</div>
+	</div>
+<script type='text/javascript'>
+<!--
+(function(){
+	document.forms['loginForm'].elements['j_username'].focus();
+})();
+// -->
+</script>
+</body>
Index: /trunk/grails-app/views/login/denied.gsp
===================================================================
--- /trunk/grails-app/views/login/denied.gsp	(revision 104)
+++ /trunk/grails-app/views/login/denied.gsp	(revision 104)
@@ -0,0 +1,6 @@
+<meta name='layout' content='main' />
+<title>Denied</title>
+
+<div class='body'>
+	<div class='errors'>Sorry, you're not authorized to view this page.</div>
+</div>
Index: /trunk/grails-app/views/login/openIdAuth.gsp
===================================================================
--- /trunk/grails-app/views/login/openIdAuth.gsp	(revision 104)
+++ /trunk/grails-app/views/login/openIdAuth.gsp	(revision 104)
@@ -0,0 +1,67 @@
+<head>
+<meta name='layout' content='main' />
+<title>Login</title>
+<style type='text/css' media='screen'>
+#login {
+	margin:15px 0px; padding:0px;
+	text-align:center;
+}
+#login .inner {
+	width:260px;
+	margin:0px auto;
+	text-align:left;
+	padding:10px;
+	border-top:1px dashed #499ede;
+	border-bottom:1px dashed #499ede;
+	background-color:#EEF;
+}
+#login .inner .fheader {
+	padding:4px;margin:3px 0px 3px 0;color:#2e3741;font-size:14px;font-weight:bold;
+}
+#login .inner .cssform p{
+	clear: left;
+	margin: 0;
+	padding: 5px 0 8px 0;
+	padding-left: 105px;
+	border-top: 1px dashed gray;
+	margin-bottom: 10px;
+	height: 1%;
+}
+#login .inner .cssform input[type='text']{ 
+	width: 120px;
+}
+#login .inner .cssform label{
+	font-weight: bold;
+	float: left;
+	margin-left: -105px; 
+	width: 100px;
+}
+#login .inner .login_message {color:red;}
+#login .inner .text_ {width:120px;}
+</style>
+</head>
+
+<body>
+	<div id='login'>
+		<div class='inner'>
+			<g:if test='${flash.message}'>
+			<div class='login_message'>${flash.message}</div>
+			</g:if>
+			<div class='fheader'>Please Login..</div>
+			<form action='${postUrl}' method='POST' id='loginForm' class='cssform'>
+				<p>
+					<label for='j_username'>OpenID Identity</label>
+					<input type='text' class='text_' name='j_username' />
+				</p>
+				<p>
+					<input type='submit' value='Login' />
+				</p>
+			</form>
+		</div>
+	</div>
+<script type='text/javascript'>
+(function(){
+	document.forms['loginForm'].elements['j_username'].focus();
+})();
+</script>
+</body>
Index: /trunk/grails-app/views/person/create.gsp
===================================================================
--- /trunk/grails-app/views/person/create.gsp	(revision 104)
+++ /trunk/grails-app/views/person/create.gsp	(revision 104)
@@ -0,0 +1,114 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Create Person</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Person List</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Create Person</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${person}">
+		<div class="errors">
+			<g:renderErrors bean="${person}" as="list" />
+		</div>
+		</g:hasErrors>
+		<g:form action="save">
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="loginName">Login Name:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'loginName','errors')}">
+							<input type="text" id="loginName" name="loginName" value="${person.loginName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="firstName">First Name:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'firstName','errors')}">
+							<input type="text" id="firstName" name="firstName" value="${person.firstName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name"><label for="lastName">Last Name:</label></td>
+                        <td valign="top" class="value ${hasErrors(bean:person,field:'lastName','errors')}">
+                            <input type="text" id="lastName" name="lastName" value="${person.lastName?.encodeAsHTML()}"/>
+                        </td>
+                    </tr>
+
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="pass">Password:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'pass','errors')}">
+							<input type="password" id="pass" name="pass"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="isActive">isActive:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'isActive','errors')}">
+							<g:checkBox name="isActive" value="${person.isActive}" ></g:checkBox>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="description">Description:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'description','errors')}">
+							<input type="text" id="description" name="description" value="${person.description?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="email">Email:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'email','errors')}">
+							<input type="text" id="email" name="email" value="${person.email?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="emailShow">Show Email:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'emailShow','errors')}">
+							<g:checkBox name="emailShow" value="${person.emailShow}"/>
+						</td>
+					</tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name"><label for="employeeID">employee ID:</label></td>
+                        <td valign="top" class="value ${hasErrors(bean:person,field:'employeeID','errors')}">
+                            <input type="text" id="employeeID" name="employeeID" value="${person.employeeID?.encodeAsHTML()}"/>
+                        </td>
+                    </tr>
+
+					<tr class="prop">
+						<td valign="top" class="name" align="left">Authorities:</td>
+					</tr>
+
+					<g:each in="${authorityList}">
+					<tr>
+						<td valign="top" class="name" align="left">${it.authority.encodeAsHTML()}</td>
+						<td align="left"><g:checkBox name="${it.authority}"/></td>
+					</tr>
+					</g:each>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><input class="save" type="submit" value="Create" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: /trunk/grails-app/views/person/edit.gsp
===================================================================
--- /trunk/grails-app/views/person/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/person/edit.gsp	(revision 104)
@@ -0,0 +1,123 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit Person</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Person List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New Person</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit Person</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${person}">
+		<div class="errors">
+			<g:renderErrors bean="${person}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<div class="prop">
+			<span class="name">ID: ${person.id}</span>
+		</div>
+
+		<g:form>
+			<input type="hidden" name="id" value="${person.id}" />
+			<input type="hidden" name="version" value="${person.version}" />
+            <input type="hidden" name="password" value="${person.password}" />
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="loginName">Login Name:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'loginName','errors')}">
+							<input type="text" id="loginName" name="loginName" value="${person.loginName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="firstName">First Name:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'firstName','errors')}">
+							<input type="text" id="firstName" name="firstName" value="${person.firstName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name"><label for="lastName">Last Name:</label></td>
+                        <td valign="top" class="value ${hasErrors(bean:person,field:'lastName','errors')}">
+                            <input type="text" id="lastName" name="lastName" value="${person.lastName?.encodeAsHTML()}"/>
+                        </td>
+                    </tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="pass">Password:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'pass','errors')}">
+							<input type="password" id="pass" name="pass"  value="${person.pass?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="isActive">isActive:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'isActive','errors')}">
+							<g:checkBox name="isActive" value="${person.isActive}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="description">Description:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'description','errors')}">
+							<input type="text" id="description" name="description" value="${person.description?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="email">Email:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'email','errors')}">
+							<input type="text" id="email" name="email" value="${person?.email?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="emailShow">Show Email:</label></td>
+						<td valign="top" class="value ${hasErrors(bean:person,field:'emailShow','errors')}">
+							<g:checkBox name="emailShow" value="${person.emailShow}"/>
+						</td>
+					</tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name"><label for="employeeID">employee ID:</label></td>
+                        <td valign="top" class="value ${hasErrors(bean:person,field:'employeeID','errors')}">
+                            <input type="text" id="employeeID" name="employeeID" value="${person.employeeID?.encodeAsHTML()}"/>
+                        </td>
+                    </tr>
+
+                    <tr class="prop">
+                        <td valign="top" class="name" align="left">Authorities:</td>
+                    </tr>
+
+                    <g:each var="entry" in="${roleMap}">
+                    <tr>
+                        <td valign="top" class="name" align="left">${entry.key.authority.encodeAsHTML()}</td>
+                        <td align="left"><g:checkBox name="${entry.key.authority}" value="${entry.value}"/></td>
+                    </tr>
+                    </g:each>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Update" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: /trunk/grails-app/views/person/list.gsp
===================================================================
--- /trunk/grails-app/views/person/list.gsp	(revision 104)
+++ /trunk/grails-app/views/person/list.gsp	(revision 104)
@@ -0,0 +1,60 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Person List</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="create" action="create">New Person</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Person List</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<div class="list">
+			<table>
+			<thead>
+				<tr>
+					<g:sortableColumn property="id" title="Id" />
+					<g:sortableColumn property="loginName" title="Login Name" />
+					<g:sortableColumn property="firstName" title="First Name" />
+                    <g:sortableColumn property="lastName" title="Last Name" />
+                    <g:sortableColumn property="isActive " title="isActive" />
+					<th>&nbsp;</th>
+                    <th>&nbsp;</th>
+				</tr>
+			</thead>
+			<tbody>
+			<g:each in="${personList}" status="i" var="person">
+				<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+					<td>${person.id}</td>
+					<td>${person.loginName?.encodeAsHTML()}</td>
+					<td>${person.firstName?.encodeAsHTML()}</td>
+                    <td>${person.lastName?.encodeAsHTML()}</td>
+					<td>${person.isActive?.encodeAsHTML()}</td>
+					<td class="actionButtons">
+						<span class="actionButton">
+							<g:link action="show" id="${person.id}">Show</g:link>
+						</span>
+					</td>
+                    <td class="actionButtons">
+                        <span class="actionButton">
+                            <g:link action="edit" id="${person.id}">Edit</g:link>
+                        </span>
+                    </td>
+				</tr>
+			</g:each>
+			</tbody>
+			</table>
+		</div>
+
+		<div class="paginateButtons">
+			<g:paginate total="${Person.count()}" />
+		</div>
+
+	</div>
+</body>
Index: /trunk/grails-app/views/person/show.gsp
===================================================================
--- /trunk/grails-app/views/person/show.gsp	(revision 104)
+++ /trunk/grails-app/views/person/show.gsp	(revision 104)
@@ -0,0 +1,92 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Show Person</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">Person List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New Person</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Show Person</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+			<table>
+			<tbody>
+
+				<tr class="prop">
+					<td valign="top" class="name">ID:</td>
+					<td valign="top" class="value">${person.id}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Login Name:</td>
+					<td valign="top" class="value">${person.loginName?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">First Name:</td>
+					<td valign="top" class="value">${person.firstName?.encodeAsHTML()}</td>
+				</tr>
+
+                <tr class="prop">
+                    <td valign="top" class="name">Last Name:</td>
+                    <td valign="top" class="value">${person.lastName?.encodeAsHTML()}</td>
+                </tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">isActive:</td>
+					<td valign="top" class="value">${person.isActive}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Description:</td>
+					<td valign="top" class="value">${person.description?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Email:</td>
+					<td valign="top" class="value">${person.email?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Show Email:</td>
+					<td valign="top" class="value">${person.emailShow}</td>
+				</tr>
+
+                <tr class="prop">
+                    <td valign="top" class="name">Employee ID:</td>
+                    <td valign="top" class="value">${person.employeeID}</td>
+                </tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Authorities:</td>
+					<td valign="top" class="value">
+						<ul>
+						<g:each in="${roleNames}" var='name'>
+							<li>${name}</li>
+						</g:each>
+						</ul>
+					</td>
+				</tr>
+
+			</tbody>
+			</table>
+		</div>
+
+		<div class="buttons">
+			<g:form>
+				<input type="hidden" name="id" value="${person.id}" />
+				<span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</g:form>
+		</div>
+
+	</div>
+</body>
Index: /trunk/grails-app/views/personGroup/create.gsp
===================================================================
--- /trunk/grails-app/views/personGroup/create.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroup/create.gsp	(revision 104)
@@ -0,0 +1,74 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create PersonGroup</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroup List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create PersonGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${personGroupInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${personGroupInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:personGroupInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:personGroupInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${personGroupInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="personGroupType">Person Group Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'personGroupType','errors')}">
+                                    <g:select optionKey="id" from="${PersonGroupType.list()}" name="personGroupType.id" value="${personGroupInstance?.personGroupType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroup/edit.gsp
===================================================================
--- /trunk/grails-app/views/personGroup/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroup/edit.gsp	(revision 104)
@@ -0,0 +1,93 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit PersonGroup</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroup List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit PersonGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${personGroupInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${personGroupInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${personGroupInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:personGroupInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:personGroupInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${personGroupInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="personGroupType">Person Group Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'personGroupType','errors')}">
+                                    <g:select optionKey="id" from="${PersonGroupType.list()}" name="personGroupType.id" value="${personGroupInstance?.personGroupType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="persons">Persons:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupInstance,field:'persons','errors')}">
+                                    
+<ul>
+<g:each var="p" in="${personGroupInstance?.persons?}">
+    <li><g:link controller="person" action="show" id="${p.id}">${p?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="person" params="['personGroup.id':personGroupInstance?.id]" action="create">Add Person</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroup/list.gsp
===================================================================
--- /trunk/grails-app/views/personGroup/list.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroup/list.gsp	(revision 104)
@@ -0,0 +1,60 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>PersonGroup List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>PersonGroup List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                   	        <th>Person Group Type</th>
+                   	    
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${personGroupInstanceList}" status="i" var="personGroupInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${personGroupInstance.id}">${fieldValue(bean:personGroupInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:personGroupInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:personGroupInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:personGroupInstance, field:'isActive')}</td>
+                        
+                            <td>${fieldValue(bean:personGroupInstance, field:'personGroupType')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${PersonGroup.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroup/show.gsp
===================================================================
--- /trunk/grails-app/views/personGroup/show.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroup/show.gsp	(revision 104)
@@ -0,0 +1,79 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show PersonGroup</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroup List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show PersonGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Person Group Type:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="personGroupType" action="show" id="${personGroupInstance?.personGroupType?.id}">${personGroupInstance?.personGroupType?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Persons:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupInstance, field:'persons')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${personGroupInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroupType/create.gsp
===================================================================
--- /trunk/grails-app/views/personGroupType/create.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroupType/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create PersonGroupType</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroupType List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create PersonGroupType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${personGroupTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${personGroupTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:personGroupTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:personGroupTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${personGroupTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroupType/edit.gsp
===================================================================
--- /trunk/grails-app/views/personGroupType/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroupType/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit PersonGroupType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroupType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroupType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit PersonGroupType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${personGroupTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${personGroupTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${personGroupTypeInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:personGroupTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:personGroupTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${personGroupTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="personGroups">Person Groups:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:personGroupTypeInstance,field:'personGroups','errors')}">
+                                    
+<ul>
+<g:each var="p" in="${personGroupTypeInstance?.personGroups?}">
+    <li><g:link controller="personGroup" action="show" id="${p.id}">${p?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="personGroup" params="['personGroupType.id':personGroupTypeInstance?.id]" action="create">Add PersonGroup</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroupType/list.gsp
===================================================================
--- /trunk/grails-app/views/personGroupType/list.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroupType/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>PersonGroupType List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroupType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>PersonGroupType List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${personGroupTypeInstanceList}" status="i" var="personGroupTypeInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${personGroupTypeInstance.id}">${fieldValue(bean:personGroupTypeInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:personGroupTypeInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:personGroupTypeInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:personGroupTypeInstance, field:'isActive')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${PersonGroupType.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/personGroupType/show.gsp
===================================================================
--- /trunk/grails-app/views/personGroupType/show.gsp	(revision 104)
+++ /trunk/grails-app/views/personGroupType/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show PersonGroupType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">PersonGroupType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New PersonGroupType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show PersonGroupType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupTypeInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupTypeInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupTypeInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:personGroupTypeInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Person Groups:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="p" in="${personGroupTypeInstance.personGroups}">
+                                    <li><g:link controller="personGroup" action="show" id="${p.id}">${p?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${personGroupTypeInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/task/create.gsp
===================================================================
--- /trunk/grails-app/views/task/create.gsp	(revision 104)
+++ /trunk/grails-app/views/task/create.gsp	(revision 104)
@@ -0,0 +1,155 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create Task</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetStartDate">Target Start Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetStartDate','errors')}">
+                                    <g:datePicker name="targetStartDate" value="${taskInstance?.targetStartDate}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="75" id="description" name="description" value="${fieldValue(bean:taskInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="leadPerson">Lead Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'leadPerson','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="leadPerson.id" value="${taskInstance?.leadPerson?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskPriority">Task Priority:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskPriority','errors')}">
+                                    <g:select optionKey="id" from="${TaskPriority.list()}" name="taskPriority.id" value="${taskInstance?.taskPriority?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskStatus">Task Status:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskStatus','errors')}">
+                                    <g:select optionKey="id" from="${TaskStatus.list()}" name="taskStatus.id" value="${taskInstance?.taskStatus?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="parentTask">Parent Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'parentTask','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="parentTask.id" value="${taskInstance?.parentTask?.id}" noSelection="['null':'']"></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:taskInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isApproved">Is Approved:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isApproved','errors')}">
+                                    <g:checkBox name="isApproved" value="${taskInstance?.isApproved}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isScheduled">Is Scheduled:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isScheduled','errors')}">
+                                    <g:checkBox name="isScheduled" value="${taskInstance?.isScheduled}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetCompletionDate">Target Completion Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetCompletionDate','errors')}">
+                                    <g:datePicker name="targetCompletionDate" value="${taskInstance?.targetCompletionDate}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskGroup">Task Group:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskGroup','errors')}">
+                                    <g:select optionKey="id" from="${TaskGroup.list()}" name="taskGroup.id" value="${taskInstance?.taskGroup?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskType">Task Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskType','errors')}">
+                                    <g:select optionKey="id" from="${TaskType.list()}" name="taskType.id" value="${taskInstance?.taskType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/task/edit.gsp
===================================================================
--- /trunk/grails-app/views/task/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/task/edit.gsp	(revision 104)
@@ -0,0 +1,223 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit Task</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskInstance?.id}" />
+                <input type="hidden" name="version" value="${taskInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetStartDate">Target Start Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetStartDate','errors')}">
+                                    <g:datePicker name="targetStartDate" value="${taskInstance?.targetStartDate}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="75" id="description" name="description" value="${fieldValue(bean:taskInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="leadPerson">Lead Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'leadPerson','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="leadPerson.id" value="${taskInstance?.leadPerson?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskPriority">Task Priority:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskPriority','errors')}">
+                                    <g:select optionKey="id" from="${TaskPriority.list()}" name="taskPriority.id" value="${taskInstance?.taskPriority?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskStatus">Task Status:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskStatus','errors')}">
+                                    <g:select optionKey="id" from="${TaskStatus.list()}" name="taskStatus.id" value="${taskInstance?.taskStatus?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="parentTask">Parent Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'parentTask','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="parentTask.id" value="${taskInstance?.parentTask?.id}" noSelection="['null':'']"></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'comment','errors')}">
+                                    <textarea rows="5" cols="40" name="comment">${fieldValue(bean:taskInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="assignedPersons">Assigned Persons:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'assignedPersons','errors')}">
+                                    
+<ul>
+<g:each var="a" in="${taskInstance?.assignedPersons?}">
+    <li><g:link controller="assignedPerson" action="show" id="${a.id}">${a?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="assignedPerson" params="['task.id':taskInstance?.id]" action="create">Add AssignedPerson</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="entries">Entries:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'entries','errors')}">
+                                    
+<ul>
+<g:each var="e" in="${taskInstance?.entries?}">
+    <li><g:link controller="entry" action="show" id="${e.id}">${e?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="entry" params="['task.id':taskInstance?.id]" action="create">Add Entry</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isApproved">Is Approved:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isApproved','errors')}">
+                                    <g:checkBox name="isApproved" value="${taskInstance?.isApproved}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isScheduled">Is Scheduled:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isScheduled','errors')}">
+                                    <g:checkBox name="isScheduled" value="${taskInstance?.isScheduled}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="subTasks">Sub Tasks:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'subTasks','errors')}">
+                                    
+<ul>
+<g:each var="s" in="${taskInstance?.subTasks?}">
+    <li><g:link controller="task" action="show" id="${s.id}">${s?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="task" params="['task.id':taskInstance?.id]" action="create">Add Task</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetCompletionDate">Target Completion Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetCompletionDate','errors')}">
+                                    <g:datePicker name="targetCompletionDate" value="${taskInstance?.targetCompletionDate}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskGroup">Task Group:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskGroup','errors')}">
+                                    <g:select optionKey="id" from="${TaskGroup.list()}" name="taskGroup.id" value="${taskInstance?.taskGroup?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskModifications">Task Modifications:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskModifications','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskInstance?.taskModifications?}">
+    <li><g:link controller="taskModification" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="taskModification" params="['task.id':taskInstance?.id]" action="create">Add TaskModification</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskType">Task Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskType','errors')}">
+                                    <g:select optionKey="id" from="${TaskType.list()}" name="taskType.id" value="${taskInstance?.taskType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/task/list.gsp
===================================================================
--- /trunk/grails-app/views/task/list.gsp	(revision 104)
+++ /trunk/grails-app/views/task/list.gsp	(revision 104)
@@ -0,0 +1,64 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Task List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Task List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="targetStartDate" title="Target Start Date" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <th>Lead Person</th>
+                   	    
+                   	        <th>Task Priority</th>
+                   	    
+                   	        <th>Task Status</th>
+                   	    
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskInstanceList}" status="i" var="taskInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskInstance.id}">${fieldValue(bean:taskInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'targetStartDate')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'leadPerson')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'taskPriority')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'taskStatus')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${taskInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/task/show.gsp
===================================================================
--- /trunk/grails-app/views/task/show.gsp	(revision 104)
+++ /trunk/grails-app/views/task/show.gsp	(revision 104)
@@ -0,0 +1,187 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show Task</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Target Start Date:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'targetStartDate')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Lead Person:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="person" action="show" id="${taskInstance?.leadPerson?.id}">${taskInstance?.leadPerson?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Priority:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskPriority" action="show" id="${taskInstance?.taskPriority?.id}">${taskInstance?.taskPriority?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Status:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskStatus" action="show" id="${taskInstance?.taskStatus?.id}">${taskInstance?.taskStatus?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Parent Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="task" action="show" id="${taskInstance?.parentTask?.id}">${taskInstance?.parentTask?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Comment:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'comment')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Assigned Persons:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="a" in="${taskInstance.assignedPersons}">
+                                    <li><g:link controller="assignedPerson" action="show" id="${a.id}">${a?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Entries:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="e" in="${taskInstance.entries}">
+                                    <li><g:link controller="entry" action="show" id="${e.id}">${e?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Approved:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isApproved')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Scheduled:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isScheduled')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Sub Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="s" in="${taskInstance.subTasks}">
+                                    <li><g:link controller="task" action="show" id="${s.id}">${s?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Target Completion Date:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'targetCompletionDate')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Group:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskGroup" action="show" id="${taskInstance?.taskGroup?.id}">${taskInstance?.taskGroup?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Modifications:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskInstance.taskModifications}">
+                                    <li><g:link controller="taskModification" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Type:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskType" action="show" id="${taskInstance?.taskType?.id}">${taskInstance?.taskType?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskDetailed/create.gsp
===================================================================
--- /trunk/grails-app/views/taskDetailed/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskDetailed/create.gsp	(revision 104)
@@ -0,0 +1,155 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create Task</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'description','errors')}">
+                                    <input type="text"  style="width:450px" maxlength="75" id="description" name="description" value="${fieldValue(bean:taskInstance,field:'description')}"/>
+                                </td>
+                            </tr>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'comment','errors')}">
+                                    <textarea  style="width:450px" rows="5" cols="40" name="comment">${fieldValue(bean:taskInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetStartDate">Target Start Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetStartDate','errors')}">
+                                    <g:datePicker name="targetStartDate" value="${taskInstance?.targetStartDate}" precision="day"></g:datePicker>
+                                </td>
+                            </tr> 
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetCompletionDate">Target Completion Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetCompletionDate','errors')}">
+                                    <g:datePicker name="targetCompletionDate" value="${taskInstance?.targetCompletionDate}" precision="day"></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="leadPerson">Lead Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'leadPerson','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="leadPerson.id" value="${taskInstance?.leadPerson?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskPriority">Task Priority:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskPriority','errors')}">
+                                    <g:select optionKey="id" from="${TaskPriority.list()}" name="taskPriority.id" value="${taskInstance?.taskPriority?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskStatus">Task Status:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskStatus','errors')}">
+                                    <g:select optionKey="id" from="${TaskStatus.list()}" name="taskStatus.id" value="${taskInstance?.taskStatus?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isApproved">Is Approved:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isApproved','errors')}">
+                                    <g:checkBox name="isApproved" value="${taskInstance?.isApproved}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isScheduled">Is Scheduled:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isScheduled','errors')}">
+                                    <g:checkBox name="isScheduled" value="${taskInstance?.isScheduled}" ></g:checkBox>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="parentTask">Parent Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'parentTask','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="parentTask.id" value="${taskInstance?.parentTask?.id}" noSelection="['null':'']"></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskGroup">Task Group:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskGroup','errors')}">
+                                    <g:select optionKey="id" from="${TaskGroup.list()}" name="taskGroup.id" value="${taskInstance?.taskGroup?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskType">Task Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskType','errors')}">
+                                    <g:select optionKey="id" from="${TaskType.list()}" name="taskType.id" value="${taskInstance?.taskType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskDetailed/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskDetailed/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskDetailed/edit.gsp	(revision 104)
@@ -0,0 +1,183 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit Task</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="id">Id:</label>
+                                </td>
+                                <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'id')}</td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'description','errors')}">
+                                    <input type="text" style="width:450px" maxlength="75" id="description" name="description" value="${fieldValue(bean:taskInstance,field:'description')}"/>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'comment','errors')}">
+                                    <textarea style="width:450px" rows="5" cols="40" name="comment">${fieldValue(bean:taskInstance, field:'comment')}</textarea>
+                                </td>
+                            </tr>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetStartDate">Target Start Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetStartDate','errors')}">
+                                    <g:datePicker name="targetStartDate" value="${taskInstance?.targetStartDate}" precision="day"></g:datePicker>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="targetCompletionDate">Target Completion Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'targetCompletionDate','errors')}">
+                                    <g:datePicker name="targetCompletionDate" value="${taskInstance?.targetCompletionDate}" precision="day"></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="leadPerson">Lead Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'leadPerson','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="leadPerson.id" value="${taskInstance?.leadPerson?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskPriority">Task Priority:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskPriority','errors')}">
+                                    <g:select optionKey="id" from="${TaskPriority.list()}" name="taskPriority.id" value="${taskInstance?.taskPriority?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskStatus">Task Status:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskStatus','errors')}">
+                                    <g:select optionKey="id" from="${TaskStatus.list()}" name="taskStatus.id" value="${taskInstance?.taskStatus?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isApproved">Is Approved:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isApproved','errors')}">
+                                    <g:checkBox name="isApproved" value="${taskInstance?.isApproved}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isScheduled">Is Scheduled:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'isScheduled','errors')}">
+                                    <g:checkBox name="isScheduled" value="${taskInstance?.isScheduled}" ></g:checkBox>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="parentTask">Parent Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'parentTask','errors')}">
+                                    <g:select optionKey="id" from="${possibleParentList}" name="parentTask.id" value="${taskInstance?.parentTask?.id}" noSelection="['null':'']"></g:select>
+                                </td>
+                            </tr>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskGroup">Task Group:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskGroup','errors')}">
+                                    <g:select optionKey="id" from="${TaskGroup.list()}" name="taskGroup.id" value="${taskInstance?.taskGroup?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskType">Task Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'taskType','errors')}">
+                                    <g:select optionKey="id" from="${TaskType.list()}" name="taskType.id" value="${taskInstance?.taskType?.id}" ></g:select>
+                                </td>
+                            </tr>
+
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="assignedPersons">Assigned Persons:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskInstance,field:'assignedPersons','errors')}">
+                                    
+<ul>
+<g:each var="a" in="${taskInstance?.assignedPersons?}">
+    <li><g:link controller="assignedPersonDetailed" action="show" id="${a.id}">${a?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="assignedPersonDetailed" params="['task.id':taskInstance?.id]" action="create">Add AssignedPerson</g:link>
+
+                                </td>
+                            </tr> 
+
+
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskDetailed/list.gsp
===================================================================
--- /trunk/grails-app/views/taskDetailed/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskDetailed/list.gsp	(revision 104)
@@ -0,0 +1,72 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Task List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Task List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="targetStartDate" title="Target Start Date" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <th>Lead Person</th>
+                   	    
+                   	        <th>Task Priority</th>
+                   	    
+                   	        <th>Task Status</th>
+            
+                            <th>Edit</th>
+
+                            <th>Show</th>
+                   	    
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskInstanceList}" status="i" var="taskInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'id')}</td>
+                        
+                            <td><g:formatDate date="${taskInstance.targetStartDate}" format="EEE, dd MMM yyyy"/></td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'leadPerson')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'taskPriority')}</td>
+                        
+                            <td>${fieldValue(bean:taskInstance, field:'taskStatus')}</td>
+
+                            <td><g:link action="edit" id="${taskInstance.id}">Edit</g:link></td>
+
+                            <td><g:link action="show" id="${taskInstance.id}">Show</g:link></td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${Task.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskDetailed/show.gsp
===================================================================
--- /trunk/grails-app/views/taskDetailed/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskDetailed/show.gsp	(revision 104)
@@ -0,0 +1,243 @@
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show Task</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">Task List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New Task</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show Task</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'id')}</td>
+                            
+                        </tr>
+
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'description')}</td>
+                            
+                        </tr>
+
+                        <tr class="prop">
+                            <td valign="top" class="name">Comment:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'comment')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Target Start Date:</td>
+                            
+                            <td valign="top" class="value"><g:formatDate date="${taskInstance.targetStartDate}" format="EEE, dd MMM yyyy"/></td>
+                            
+                        </tr>
+
+                        <tr class="prop">
+                            <td valign="top" class="name">Target Completion Date:</td>
+                            
+                            <td valign="top" class="value"><g:formatDate date="${taskInstance.targetCompletionDate}" format="EEE, dd MMM yyyy"/></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Lead Person:</td>
+                            
+                            <td valign="top" class="value">${taskInstance?.leadPerson?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Priority:</td>
+                            
+                            <td valign="top" class="value">${taskInstance?.taskPriority?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Status:</td>
+                            
+                            <td valign="top" class="value">${taskInstance?.taskStatus?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Approved:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isApproved')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Scheduled:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskInstance, field:'isScheduled')}</td>
+                            
+                        </tr>
+
+                        <tr class="prop">
+                            <td valign="top" class="name">Parent Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskDetailed" action="show" id="${taskInstance?.parentTask?.id}">${taskInstance?.parentTask?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Sub Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="s" in="${taskInstance.subTasks}">
+                                    <li><g:link controller="taskDetailed" action="show" id="${s.id}">${s?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Group:</td>
+                            
+                            <td valign="top" class="value">${taskInstance?.taskGroup?.encodeAsHTML()}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Type:</td>
+                            
+                            <td valign="top" class="value">${taskInstance?.taskType?.encodeAsHTML()}</td>
+                            
+                        </tr>
+
+                        <tr class="prop">
+                            <td valign="top" class="name">Assigned Persons:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="a" in="${taskInstance.assignedPersons}">
+                                    <li><g:link controller="assignedPersonDetailed" action="edit" id="${a.id}">${a?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+
+            <div class="list">
+                <h1>Faults</h1>
+                <table>
+                    <thead>
+                        <tr>
+                            <th style="color:Black">Comment</th>
+                            <th style="color:Black">Date Done</th>
+                            <th style="color:Black">Duration</th>
+                            <th style="color:Black">Entered By</th>
+                            <th style="color:Black">Edit</th>
+
+<!--                            <g:sortableColumn property="comment" title="Comment" />
+                        
+                            <g:sortableColumn property="dateDone" title="Date Done" />
+                        
+                            <g:sortableColumn property="enteredBy" title="Entered By" />-->
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskInstance?.entries}" status="i" var="entry">
+                        <g:if test="${entry.entryType == EntryType.findByName('Fault')}"> 
+                            <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                            
+                                <td width="65%">${entry.comment}</td>
+                                <td><g:formatDate date="${entry.dateDone}" format="EEE, dd MMM yyyy"/></td>
+                                <td>${entry.durationHour}:${entry.durationMinute}</td>
+                                <td>${entry.enteredBy}</td>
+                                <td><g:link controller="entryDetailed" action="edit" id="${entry.id}">Edit</g:link></td>
+
+                        </g:if>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+
+            <div class="list">
+                <h1>Work Done</h1>
+                <table>
+                    <thead>
+                        <tr>
+                            <th style="color:Black">Comment</th>
+                            <th style="color:Black">Date Done</th>
+                            <th style="color:Black">Duration</th>
+                            <th style="color:Black">Entered By</th>
+                            <th style="color:Black">Edit</th>
+<!--                            <g:sortableColumn property="commentW" title="Comment" />
+                        
+                            <g:sortableColumn property="dateDoneW" title="Date Done" />
+                        
+                            <g:sortableColumn property="enteredByW" title="Entered By" />-->
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskInstance?.entries}" status="i" var="entry">
+                        <g:if test="${entry.entryType == EntryType.findByName('WorkDone')}"> 
+                            <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                            
+                                <td width="65%">${entry.comment}</td>
+                                <td><g:formatDate date="${entry.dateDone}" format="EEE, dd MMM yyyy"/></td>
+                                <td>${entry.durationHour}:${entry.durationMinute}</td>
+                                <td>${entry.enteredBy}</td>
+                                <td><g:link controller="entryDetailed" action="edit" id="${entry.id}">Edit</g:link></td>
+                        </g:if>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+
+            <br />
+
+            <div class="buttons">
+                <g:form controller="entryDetailed">
+                    <input type="hidden" name="taskInstance.id" value="${taskInstance?.id}" />
+                    <span class="button">
+                        <g:actionSubmit value="Add Entry" action="create"  class="edit"/>
+                    </span>
+                </g:form>
+            </div>
+
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskGroup/create.gsp
===================================================================
--- /trunk/grails-app/views/taskGroup/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskGroup/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskGroup</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskGroup List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskGroupInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskGroupInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskGroupInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskGroupInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskGroupInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskGroup/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskGroup/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskGroup/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskGroup</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskGroup List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskGroupInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskGroupInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskGroupInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskGroupInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskGroupInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskGroupInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="tasks">Tasks:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskGroupInstance,field:'tasks','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskGroupInstance?.tasks?}">
+    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="task" params="['taskGroup.id':taskGroupInstance?.id]" action="create">Add Task</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskGroup/list.gsp
===================================================================
--- /trunk/grails-app/views/taskGroup/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskGroup/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskGroup List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskGroup List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskGroupInstanceList}" status="i" var="taskGroupInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskGroupInstance.id}">${fieldValue(bean:taskGroupInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskGroupInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:taskGroupInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskGroupInstance, field:'isActive')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${TaskGroup.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskGroup/show.gsp
===================================================================
--- /trunk/grails-app/views/taskGroup/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskGroup/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskGroup</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskGroup List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskGroup</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskGroup</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskGroupInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskGroupInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskGroupInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskGroupInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskGroupInstance.tasks}">
+                                    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskGroupInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModification/create.gsp
===================================================================
--- /trunk/grails-app/views/taskModification/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModification/create.gsp	(revision 104)
@@ -0,0 +1,83 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskModification</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModification List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskModification</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskModificationInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskModificationInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${taskModificationInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskModificationType">Task Modification Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'taskModificationType','errors')}">
+                                    <g:select optionKey="id" from="${TaskModificationType.list()}" name="taskModificationType.id" value="${taskModificationInstance?.taskModificationType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${taskModificationInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="date">Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'date','errors')}">
+                                    <g:datePicker name="date" value="${taskModificationInstance?.date}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'comment','errors')}">
+                                    <input type="text" id="comment" name="comment" value="${fieldValue(bean:taskModificationInstance,field:'comment')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModification/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskModification/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModification/edit.gsp	(revision 104)
@@ -0,0 +1,87 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskModification</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModification List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModification</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskModification</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskModificationInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskModificationInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskModificationInstance?.id}" />
+                <input type="hidden" name="version" value="${taskModificationInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="person">Person:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'person','errors')}">
+                                    <g:select optionKey="id" from="${Person.list()}" name="person.id" value="${taskModificationInstance?.person?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskModificationType">Task Modification Type:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'taskModificationType','errors')}">
+                                    <g:select optionKey="id" from="${TaskModificationType.list()}" name="taskModificationType.id" value="${taskModificationInstance?.taskModificationType?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="task">Task:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'task','errors')}">
+                                    <g:select optionKey="id" from="${Task.list()}" name="task.id" value="${taskModificationInstance?.task?.id}" ></g:select>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="date">Date:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'date','errors')}">
+                                    <g:datePicker name="date" value="${taskModificationInstance?.date}" ></g:datePicker>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="comment">Comment:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationInstance,field:'comment','errors')}">
+                                    <input type="text" id="comment" name="comment" value="${fieldValue(bean:taskModificationInstance,field:'comment')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModification/list.gsp
===================================================================
--- /trunk/grails-app/views/taskModification/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModification/list.gsp	(revision 104)
@@ -0,0 +1,64 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskModification List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModification</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskModification List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <th>Person</th>
+                   	    
+                   	        <th>Task Modification Type</th>
+                   	    
+                   	        <th>Task</th>
+                   	    
+                   	        <g:sortableColumn property="date" title="Date" />
+                        
+                   	        <g:sortableColumn property="comment" title="Comment" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskModificationInstanceList}" status="i" var="taskModificationInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskModificationInstance.id}">${fieldValue(bean:taskModificationInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskModificationInstance, field:'person')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationInstance, field:'taskModificationType')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationInstance, field:'task')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationInstance, field:'date')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationInstance, field:'comment')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${taskModificationInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModification/show.gsp
===================================================================
--- /trunk/grails-app/views/taskModification/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModification/show.gsp	(revision 104)
@@ -0,0 +1,79 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskModification</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModification List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModification</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskModification</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Person:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="person" action="show" id="${taskModificationInstance?.person?.id}">${taskModificationInstance?.person?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Modification Type:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="taskModificationType" action="show" id="${taskModificationInstance?.taskModificationType?.id}">${taskModificationInstance?.taskModificationType?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task:</td>
+                            
+                            <td valign="top" class="value"><g:link controller="task" action="show" id="${taskModificationInstance?.task?.id}">${taskModificationInstance?.task?.encodeAsHTML()}</g:link></td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Date:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationInstance, field:'date')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Comment:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationInstance, field:'comment')}</td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskModificationInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModificationType/create.gsp
===================================================================
--- /trunk/grails-app/views/taskModificationType/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModificationType/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskModificationType</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModificationType List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskModificationType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskModificationTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskModificationTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'description','errors')}">
+                                    <input type="text" id="description" name="description" value="${fieldValue(bean:taskModificationTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskModificationTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'name','errors')}">
+                                    <input type="text" id="name" name="name" value="${fieldValue(bean:taskModificationTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModificationType/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskModificationType/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModificationType/edit.gsp	(revision 104)
@@ -0,0 +1,85 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskModificationType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModificationType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModificationType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskModificationType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskModificationTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskModificationTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskModificationTypeInstance?.id}" />
+                <input type="hidden" name="version" value="${taskModificationTypeInstance?.version}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'description','errors')}">
+                                    <input type="text" id="description" name="description" value="${fieldValue(bean:taskModificationTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskModificationTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'name','errors')}">
+                                    <input type="text" id="name" name="name" value="${fieldValue(bean:taskModificationTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="taskModifications">Task Modifications:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskModificationTypeInstance,field:'taskModifications','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskModificationTypeInstance?.taskModifications?}">
+    <li><g:link controller="taskModification" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="taskModification" params="['taskModificationType.id':taskModificationTypeInstance?.id]" action="create">Add TaskModification</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModificationType/list.gsp
===================================================================
--- /trunk/grails-app/views/taskModificationType/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModificationType/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskModificationType List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModificationType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskModificationType List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskModificationTypeInstanceList}" status="i" var="taskModificationTypeInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskModificationTypeInstance.id}">${fieldValue(bean:taskModificationTypeInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskModificationTypeInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationTypeInstance, field:'isActive')}</td>
+                        
+                            <td>${fieldValue(bean:taskModificationTypeInstance, field:'name')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${taskModificationTypeInstanceTotal}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskModificationType/show.gsp
===================================================================
--- /trunk/grails-app/views/taskModificationType/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskModificationType/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskModificationType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskModificationType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskModificationType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskModificationType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationTypeInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationTypeInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationTypeInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskModificationTypeInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Task Modifications:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskModificationTypeInstance.taskModifications}">
+                                    <li><g:link controller="taskModification" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskModificationTypeInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskPriority/create.gsp
===================================================================
--- /trunk/grails-app/views/taskPriority/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskPriority/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskPriority</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskPriority List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskPriority</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskPriorityInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskPriorityInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskPriorityInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskPriorityInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskPriorityInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskPriority/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskPriority/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskPriority/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskPriority</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskPriority List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskPriority</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskPriority</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskPriorityInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskPriorityInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskPriorityInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskPriorityInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskPriorityInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskPriorityInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="tasks">Tasks:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskPriorityInstance,field:'tasks','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskPriorityInstance?.tasks?}">
+    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="task" params="['taskPriority.id':taskPriorityInstance?.id]" action="create">Add Task</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskPriority/list.gsp
===================================================================
--- /trunk/grails-app/views/taskPriority/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskPriority/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskPriority List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskPriority</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskPriority List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskPriorityInstanceList}" status="i" var="taskPriorityInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskPriorityInstance.id}">${fieldValue(bean:taskPriorityInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskPriorityInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:taskPriorityInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskPriorityInstance, field:'isActive')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${TaskPriority.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskPriority/show.gsp
===================================================================
--- /trunk/grails-app/views/taskPriority/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskPriority/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskPriority</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskPriority List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskPriority</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskPriority</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskPriorityInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskPriorityInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskPriorityInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskPriorityInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskPriorityInstance.tasks}">
+                                    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskPriorityInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskStatus/create.gsp
===================================================================
--- /trunk/grails-app/views/taskStatus/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskStatus/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskStatus</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskStatus List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskStatus</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskStatusInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskStatusInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskStatusInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskStatusInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskStatusInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskStatus/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskStatus/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskStatus/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskStatus</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskStatus List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskStatus</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskStatus</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskStatusInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskStatusInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskStatusInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskStatusInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskStatusInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskStatusInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="tasks">Tasks:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskStatusInstance,field:'tasks','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskStatusInstance?.tasks?}">
+    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="task" params="['taskStatus.id':taskStatusInstance?.id]" action="create">Add Task</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskStatus/list.gsp
===================================================================
--- /trunk/grails-app/views/taskStatus/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskStatus/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskStatus List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskStatus</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskStatus List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskStatusInstanceList}" status="i" var="taskStatusInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskStatusInstance.id}">${fieldValue(bean:taskStatusInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskStatusInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:taskStatusInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskStatusInstance, field:'isActive')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${TaskStatus.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskStatus/show.gsp
===================================================================
--- /trunk/grails-app/views/taskStatus/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskStatus/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskStatus</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskStatus List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskStatus</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskStatus</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskStatusInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskStatusInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskStatusInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskStatusInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskStatusInstance.tasks}">
+                                    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskStatusInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskType/create.gsp
===================================================================
--- /trunk/grails-app/views/taskType/create.gsp	(revision 104)
+++ /trunk/grails-app/views/taskType/create.gsp	(revision 104)
@@ -0,0 +1,65 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Create TaskType</title>         
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskType List</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Create TaskType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form action="save" method="post" >
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><input class="save" type="submit" value="Create" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskType/edit.gsp
===================================================================
--- /trunk/grails-app/views/taskType/edit.gsp	(revision 104)
+++ /trunk/grails-app/views/taskType/edit.gsp	(revision 104)
@@ -0,0 +1,84 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Edit TaskType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Edit TaskType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <g:hasErrors bean="${taskTypeInstance}">
+            <div class="errors">
+                <g:renderErrors bean="${taskTypeInstance}" as="list" />
+            </div>
+            </g:hasErrors>
+            <g:form method="post" >
+                <input type="hidden" name="id" value="${taskTypeInstance?.id}" />
+                <div class="dialog">
+                    <table>
+                        <tbody>
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="name">Name:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'name','errors')}">
+                                    <input type="text" maxlength="50" id="name" name="name" value="${fieldValue(bean:taskTypeInstance,field:'name')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="description">Description:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'description','errors')}">
+                                    <input type="text" maxlength="100" id="description" name="description" value="${fieldValue(bean:taskTypeInstance,field:'description')}"/>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="isActive">Is Active:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'isActive','errors')}">
+                                    <g:checkBox name="isActive" value="${taskTypeInstance?.isActive}" ></g:checkBox>
+                                </td>
+                            </tr> 
+                        
+                            <tr class="prop">
+                                <td valign="top" class="name">
+                                    <label for="tasks">Tasks:</label>
+                                </td>
+                                <td valign="top" class="value ${hasErrors(bean:taskTypeInstance,field:'tasks','errors')}">
+                                    
+<ul>
+<g:each var="t" in="${taskTypeInstance?.tasks?}">
+    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+</g:each>
+</ul>
+<g:link controller="task" params="['taskType.id':taskTypeInstance?.id]" action="create">Add Task</g:link>
+
+                                </td>
+                            </tr> 
+                        
+                        </tbody>
+                    </table>
+                </div>
+                <div class="buttons">
+                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </div>
+            </g:form>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskType/list.gsp
===================================================================
--- /trunk/grails-app/views/taskType/list.gsp	(revision 104)
+++ /trunk/grails-app/views/taskType/list.gsp	(revision 104)
@@ -0,0 +1,56 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>TaskType List</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>TaskType List</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="list">
+                <table>
+                    <thead>
+                        <tr>
+                        
+                   	        <g:sortableColumn property="id" title="Id" />
+                        
+                   	        <g:sortableColumn property="name" title="Name" />
+                        
+                   	        <g:sortableColumn property="description" title="Description" />
+                        
+                   	        <g:sortableColumn property="isActive" title="Is Active" />
+                        
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <g:each in="${taskTypeInstanceList}" status="i" var="taskTypeInstance">
+                        <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
+                        
+                            <td><g:link action="show" id="${taskTypeInstance.id}">${fieldValue(bean:taskTypeInstance, field:'id')}</g:link></td>
+                        
+                            <td>${fieldValue(bean:taskTypeInstance, field:'name')}</td>
+                        
+                            <td>${fieldValue(bean:taskTypeInstance, field:'description')}</td>
+                        
+                            <td>${fieldValue(bean:taskTypeInstance, field:'isActive')}</td>
+                        
+                        </tr>
+                    </g:each>
+                    </tbody>
+                </table>
+            </div>
+            <div class="paginateButtons">
+                <g:paginate total="${TaskType.count()}" />
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/grails-app/views/taskType/show.gsp
===================================================================
--- /trunk/grails-app/views/taskType/show.gsp	(revision 104)
+++ /trunk/grails-app/views/taskType/show.gsp	(revision 104)
@@ -0,0 +1,78 @@
+
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <meta name="layout" content="main" />
+        <title>Show TaskType</title>
+    </head>
+    <body>
+        <div class="nav">
+            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
+            <span class="menuButton"><g:link class="list" action="list">TaskType List</g:link></span>
+            <span class="menuButton"><g:link class="create" action="create">New TaskType</g:link></span>
+        </div>
+        <div class="body">
+            <h1>Show TaskType</h1>
+            <g:if test="${flash.message}">
+            <div class="message">${flash.message}</div>
+            </g:if>
+            <div class="dialog">
+                <table>
+                    <tbody>
+
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Id:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskTypeInstance, field:'id')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Name:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskTypeInstance, field:'name')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Description:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskTypeInstance, field:'description')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Is Active:</td>
+                            
+                            <td valign="top" class="value">${fieldValue(bean:taskTypeInstance, field:'isActive')}</td>
+                            
+                        </tr>
+                    
+                        <tr class="prop">
+                            <td valign="top" class="name">Tasks:</td>
+                            
+                            <td  valign="top" style="text-align:left;" class="value">
+                                <ul>
+                                <g:each var="t" in="${taskTypeInstance.tasks}">
+                                    <li><g:link controller="task" action="show" id="${t.id}">${t?.encodeAsHTML()}</g:link></li>
+                                </g:each>
+                                </ul>
+                            </td>
+                            
+                        </tr>
+                    
+                    </tbody>
+                </table>
+            </div>
+            <div class="buttons">
+                <g:form>
+                    <input type="hidden" name="id" value="${taskTypeInstance?.id}" />
+                    <span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+                </g:form>
+            </div>
+        </div>
+    </body>
+</html>
Index: /trunk/ivy.xml
===================================================================
--- /trunk/ivy.xml	(revision 104)
+++ /trunk/ivy.xml	(revision 104)
@@ -0,0 +1,27 @@
+<ivy-module version="2.0">
+    <info organisation="org.example" module="gnuMims"/>
+    <configurations defaultconfmapping="build->default;compile->compile(*),master(*);test,runtime->runtime(*),master(*)">
+        <conf name="build"/>
+        <conf name="compile"/>
+        <conf name="test" extends="compile"/>
+        <conf name="runtime" extends="compile"/>
+    </configurations>
+    <dependencies>
+        <dependency org="org.grails" name="grails-bootstrap" rev="1.1" conf="build"/>
+        <dependency org="org.grails" name="grails-scripts" rev="1.1" conf="build"/>
+        <dependency org="org.grails" name="grails-gorm" rev="1.1" conf="compile"/>
+        <dependency org="org.grails" name="grails-web" rev="1.1" conf="compile"/>
+        <dependency org="org.grails" name="grails-test" rev="1.1" conf="test"/>
+        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.5" conf="runtime"/>
+        <dependency org="opensymphony" name="oscache" rev="2.4" conf="runtime">
+            <exclude org="javax.jms" module="jms" name="*" type="*" ext="*" conf="" matcher="exact"/>
+            <exclude org="commons-logging" module="commons-logging" name="*" type="*" ext="*" conf="" matcher="exact"/>
+            <exclude org="javax.servlet" module="servlet-api" name="*" type="*" ext="*" conf="" matcher="exact"/>
+        </dependency>
+        <dependency org="hsqldb" name="hsqldb" rev="1.8.0.5" conf="runtime"/>
+        <!--
+        <dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="runtime"/>
+        <dependency org="postgresql" name="postgresql" rev="8.3-603.jdbc3" conf="runtime"/>
+        -->
+    </dependencies>
+</ivy-module>
Index: /trunk/ivysettings.xml
===================================================================
--- /trunk/ivysettings.xml	(revision 104)
+++ /trunk/ivysettings.xml	(revision 104)
@@ -0,0 +1,15 @@
+<ivysettings>
+  <settings defaultResolver="codehaus-plus"/>
+  <include url="${ivy.default.settings.dir}/ivysettings-public.xml" />
+  <include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
+  <include url="${ivy.default.settings.dir}/ivysettings-local.xml" />
+  <include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
+  <resolvers>
+    <chain name="codehaus-plus" dual="true">
+      <ibiblio name="codehaus-snapshots" root="http://snapshots.repository.codehaus.org" m2compatible="true" changingPattern=".*SNAPSHOT"/>
+      <ibiblio name="codehaus" root="http://repository.codehaus.org" m2compatible="true"/>
+      <ibiblio name="javanet" root="http://download.java.net/maven/2/" m2compatible="true"/>
+      <resolver ref="public"/>
+    </chain>
+  </resolvers>
+</ivysettings>
Index: /trunk/web-app/WEB-INF/applicationContext.xml
===================================================================
--- /trunk/web-app/WEB-INF/applicationContext.xml	(revision 104)
+++ /trunk/web-app/WEB-INF/applicationContext.xml	(revision 104)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+	<bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
+		<description>Grails application factory bean</description>
+        <property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
+        <property name="grailsResourceLoader" ref="grailsResourceLoader" />
+	</bean>
+
+	<bean id="pluginManager" class="org.codehaus.groovy.grails.plugins.GrailsPluginManagerFactoryBean">
+		<description>A bean that manages Grails plugins</description>
+        <property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
+        <property name="application" ref="grailsApplication" />
+	</bean>
+
+    <bean id="pluginMetaManager" class="org.codehaus.groovy.grails.plugins.DefaultPluginMetaManager">
+        <property name="grailsApplication" ref="grailsApplication" />
+        <property name="resourcePattern" value="/WEB-INF/plugins/*/plugin.xml" />
+    </bean>
+
+    <bean id="grailsConfigurator" class="org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator">
+        <constructor-arg>
+            <ref bean="grailsApplication" />
+        </constructor-arg>
+        <property name="pluginManager" ref="pluginManager" />
+    </bean>
+
+    <bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean">
+        <property name="grailsResourceHolder" ref="grailsResourceHolder" />
+    </bean>
+
+    <bean id="grailsResourceHolder" scope="prototype" class="org.codehaus.groovy.grails.commons.spring.GrailsResourceHolder">
+        <property name="resources">
+              <value>classpath*:**/grails-app/**/*.groovy</value>
+        </property>
+    </bean>    
+    
+   <bean id="characterEncodingFilter"
+      class="org.springframework.web.filter.CharacterEncodingFilter">
+        <property name="encoding">
+          <value>utf-8</value>
+        </property>
+   </bean>    	
+</beans>
Index: /trunk/web-app/WEB-INF/sitemesh.xml
===================================================================
--- /trunk/web-app/WEB-INF/sitemesh.xml	(revision 104)
+++ /trunk/web-app/WEB-INF/sitemesh.xml	(revision 104)
@@ -0,0 +1,14 @@
+<sitemesh>
+    <page-parsers>
+        <parser content-type="text/html"
+            class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
+        <parser content-type="text/html;charset=ISO-8859-1"
+            class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
+        <parser content-type="text/html;charset=UTF-8"
+            class="com.opensymphony.module.sitemesh.parser.FastPageParser" />            
+    </page-parsers>
+
+    <decorator-mappers>
+        <mapper class="org.codehaus.groovy.grails.web.sitemesh.GrailsLayoutDecoratorMapper" />
+    </decorator-mappers>
+</sitemesh>
Index: /trunk/web-app/WEB-INF/tld/c.tld
===================================================================
--- /trunk/web-app/WEB-INF/tld/c.tld	(revision 104)
+++ /trunk/web-app/WEB-INF/tld/c.tld	(revision 104)
@@ -0,0 +1,563 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+    version="2.0">
+    
+  <description>JSTL 1.1 core library</description>
+  <display-name>JSTL core</display-name>
+  <tlib-version>1.1</tlib-version>
+  <short-name>c</short-name>
+  <uri>http://java.sun.com/jsp/jstl/core</uri>
+
+  <validator>
+    <description>
+        Provides core validation features for JSTL tags.
+    </description>
+    <validator-class>
+        org.apache.taglibs.standard.tlv.JstlCoreTLV
+    </validator-class>
+  </validator>
+
+  <tag>
+    <description>
+        Catches any Throwable that occurs in its body and optionally
+        exposes it.
+    </description>
+    <name>catch</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+exception thrown from a nested action. The type of the
+scoped variable is the type of the exception thrown.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+	Simple conditional tag that establishes a context for
+	mutually exclusive conditional operations, marked by
+	&lt;when&gt; and &lt;otherwise&gt;
+    </description>
+    <name>choose</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
+    <body-content>JSP</body-content>
+  </tag>
+
+  <tag>
+    <description>
+	Simple conditional tag, which evalutes its body if the
+	supplied condition is true and optionally exposes a Boolean
+	scripting variable representing the evaluation of this condition
+    </description>
+    <name>if</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The test condition that determines whether or
+not the body content should be processed.
+        </description>
+        <name>test</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+	<type>boolean</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resulting value of the test condition. The type
+of the scoped variable is Boolean.        
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Retrieves an absolute or relative URL and exposes its contents
+        to either the page, a String in 'var', or a Reader in 'varReader'.
+    </description>
+    <name>import</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ImportTag</tag-class>
+    <tei-class>org.apache.taglibs.standard.tei.ImportTEI</tei-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The URL of the resource to import.
+        </description>
+        <name>url</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is Reader.
+        </description>
+        <name>varReader</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when accessing a relative
+URL resource that belongs to a foreign
+context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Character encoding of the content at the input
+resource.
+        </description>
+        <name>charEncoding</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+	The basic iteration tag, accepting many different
+        collection types and supporting subsetting and other
+        functionality
+    </description>
+    <name>forEach</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
+    <tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Collection of items to iterate over.
+        </description>
+	<name>items</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>java.lang.Object</type>
+    </attribute>
+    <attribute>
+        <description>
+If items specified:
+Iteration begins at the item located at the
+specified index. First item of the collection has
+index 0.
+If items not specified:
+Iteration begins with index set at the value
+specified.
+        </description>
+	<name>begin</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+If items specified:
+Iteration ends at the item located at the
+specified index (inclusive).
+If items not specified:
+Iteration ends when index reaches the value
+specified.
+        </description>
+	<name>end</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration will only process every step items of
+the collection, starting with the first one.
+        </description>
+	<name>step</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility. Its type depends
+on the object of the underlying collection.
+        </description>
+	<name>var</name>
+	<required>false</required>
+	<rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of type
+javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested
+visibility.
+        </description>
+	<name>varStatus</name>
+	<required>false</required>
+	<rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+	Iterates over tokens, separated by the supplied delimeters
+    </description>
+    <name>forTokens</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForTokensTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+String of tokens to iterate over.
+        </description>
+	<name>items</name>
+	<required>true</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>java.lang.String</type>
+    </attribute>
+    <attribute>
+        <description>
+The set of delimiters (the characters that
+separate the tokens in the string).
+        </description>
+	<name>delims</name>
+	<required>true</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>java.lang.String</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration begins at the token located at the
+specified index. First token has index 0.
+        </description>
+	<name>begin</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration ends at the token located at the
+specified index (inclusive).
+        </description>
+	<name>end</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration will only process every step tokens
+of the string, starting with the first one.
+        </description>
+	<name>step</name>
+	<required>false</required>
+	<rtexprvalue>true</rtexprvalue>
+	<type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility.
+        </description>
+	<name>var</name>
+	<required>false</required>
+	<rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of
+type
+javax.servlet.jsp.jstl.core.LoopTag
+Status. This scoped variable has nested
+visibility.
+        </description>
+	<name>varStatus</name>
+	<required>false</required>
+	<rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Like &lt;%= ... &gt;, but for expressions.
+    </description> 
+    <name>out</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.OutTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Expression to be evaluated.
+        </description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Default value if the resulting value is null.
+        </description>
+        <name>default</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Determines whether characters &lt;,&gt;,&amp;,'," in the
+resulting string should be converted to their
+corresponding character entity codes. Default value is
+true.
+        </description>
+        <name>escapeXml</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+
+  <tag>
+    <description>
+        Subtag of &lt;choose&gt; that follows &lt;when&gt; tags
+        and runs only if all of the prior conditions evaluated to
+        'false'
+    </description>
+    <name>otherwise</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
+    <body-content>JSP</body-content>
+  </tag>
+
+  <tag>
+    <description>
+        Adds a parameter to a containing 'import' tag's URL.
+    </description>
+    <name>param</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ParamTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the query string parameter.
+        </description>
+        <name>name</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Value of the parameter.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Redirects to a new URL.
+    </description>
+    <name>redirect</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.RedirectTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The URL of the resource to redirect to.
+        </description>
+        <name>url</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when redirecting to a relative URL
+resource that belongs to a foreign context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Removes a scoped variable (from a particular scope, if specified).
+    </description>
+    <name>remove</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.RemoveTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+Name of the scoped variable to be removed.
+        </description>
+        <name>var</name>
+        <required>true</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+ <tag>
+    <description>
+        Sets the result of an expression evaluation in a 'scope'
+    </description>
+    <name>set</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.SetTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable to hold the value
+specified in the action. The type of the scoped variable is
+whatever type the value expression evaluates to.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Expression to be evaluated.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Target object whose property will be set. Must evaluate to
+a JavaBeans object with setter property property, or to a
+java.util.Map object.
+        </description>
+        <name>target</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the property to be set in the target object.
+        </description>
+        <name>property</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Creates a URL with optional query parameters.
+    </description>
+    <name>url</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.UrlTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+processed url. The type of the scoped variable is
+String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+URL to be processed.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when specifying a relative URL
+resource that belongs to a foreign context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+	Subtag of &lt;choose&gt; that includes its body if its
+	condition evalutes to 'true'
+    </description>
+    <name>when</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.WhenTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The test condition that determines whether or not the
+body content should be processed.
+        </description>
+        <name>test</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+	<type>boolean</type>
+    </attribute>
+  </tag>
+
+</taglib>
Index: /trunk/web-app/WEB-INF/tld/fmt.tld
===================================================================
--- /trunk/web-app/WEB-INF/tld/fmt.tld	(revision 104)
+++ /trunk/web-app/WEB-INF/tld/fmt.tld	(revision 104)
@@ -0,0 +1,671 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+    version="2.0">
+    
+  <description>JSTL 1.1 i18n-capable formatting library</description>
+  <display-name>JSTL fmt</display-name>
+  <tlib-version>1.1</tlib-version>
+  <short-name>fmt</short-name>
+  <uri>http://java.sun.com/jsp/jstl/fmt</uri>
+
+  <validator>
+    <description>
+        Provides core validation features for JSTL tags.
+    </description>
+    <validator-class>
+        org.apache.taglibs.standard.tlv.JstlFmtTLV
+    </validator-class>
+  </validator>
+
+  <tag>
+    <description>
+        Sets the request character encoding
+    </description>
+    <name>requestEncoding</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.RequestEncodingTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+Name of character encoding to be applied when
+decoding request parameters.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Stores the given locale in the locale configuration variable
+    </description>
+    <name>setLocale</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.SetLocaleTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+A String value is interpreted as the
+printable representation of a locale, which
+must contain a two-letter (lower-case)
+language code (as defined by ISO-639),
+and may contain a two-letter (upper-case)
+country code (as defined by ISO-3166).
+Language and country codes must be
+separated by hyphen (-) or underscore
+(_).        
+	</description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Vendor- or browser-specific variant.
+See the java.util.Locale javadocs for
+more information on variants.
+        </description>
+        <name>variant</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of the locale configuration variable.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Specifies the time zone for any time formatting or parsing actions
+        nested in its body
+    </description>
+    <name>timeZone</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.TimeZoneTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The time zone. A String value is interpreted as
+a time zone ID. This may be one of the time zone
+IDs supported by the Java platform (such as
+"America/Los_Angeles") or a custom time zone
+ID (such as "GMT-8"). See
+java.util.TimeZone for more information on
+supported time zone formats.
+        </description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Stores the given time zone in the time zone configuration variable
+    </description>
+    <name>setTimeZone</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.SetTimeZoneTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+The time zone. A String value is interpreted as
+a time zone ID. This may be one of the time zone
+IDs supported by the Java platform (such as
+"America/Los_Angeles") or a custom time zone
+ID (such as "GMT-8"). See java.util.TimeZone for
+more information on supported time zone
+formats.
+        </description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable which
+stores the time zone of type
+java.util.TimeZone.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var or the time zone configuration
+variable.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Loads a resource bundle to be used by its tag body
+    </description>
+    <name>bundle</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.BundleTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Resource bundle base name. This is the bundle's
+fully-qualified resource name, which has the same
+form as a fully-qualified class name, that is, it uses
+"." as the package component separator and does not
+have any file type (such as ".class" or ".properties")
+suffix.
+        </description>
+        <name>basename</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Prefix to be prepended to the value of the message
+key of any nested &lt;fmt:message&gt; action.
+        </description>
+        <name>prefix</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Loads a resource bundle and stores it in the named scoped variable or
+        the bundle configuration variable
+    </description>
+    <name>setBundle</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.SetBundleTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+Resource bundle base name. This is the bundle's
+fully-qualified resource name, which has the same
+form as a fully-qualified class name, that is, it uses
+"." as the package component separator and does not
+have any file type (such as ".class" or ".properties")
+suffix.
+        </description>
+        <name>basename</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable which stores
+the i18n localization context of type
+javax.servlet.jsp.jstl.fmt.LocalizationC
+ontext.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var or the localization context
+configuration variable.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Maps key to localized message and performs parametric replacement
+    </description>
+    <name>message</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.MessageTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Message key to be looked up.
+        </description>
+        <name>key</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Localization context in whose resource
+bundle the message key is looked up.
+        </description>
+        <name>bundle</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable
+which stores the localized message.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Supplies an argument for parametric replacement to a containing
+        &lt;message&gt; tag
+    </description>
+    <name>param</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.ParamTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Argument used for parametric replacement.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Formats a numeric value as a number, currency, or percentage
+    </description>
+    <name>formatNumber</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.FormatNumberTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Numeric value to be formatted.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether the value is to be
+formatted as number, currency, or
+percentage.
+        </description>
+        <name>type</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Custom formatting pattern.
+        </description>
+        <name>pattern</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+ISO 4217 currency code. Applied only
+when formatting currencies (i.e. if type is
+equal to "currency"); ignored otherwise.
+        </description>
+        <name>currencyCode</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Currency symbol. Applied only when
+formatting currencies (i.e. if type is equal
+to "currency"); ignored otherwise.
+        </description>
+        <name>currencySymbol</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether the formatted output
+will contain any grouping separators.
+        </description>
+        <name>groupingUsed</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Maximum number of digits in the integer
+portion of the formatted output.
+        </description>
+        <name>maxIntegerDigits</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Minimum number of digits in the integer
+portion of the formatted output.
+        </description>
+        <name>minIntegerDigits</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Maximum number of digits in the
+fractional portion of the formatted output.
+        </description>
+        <name>maxFractionDigits</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Minimum number of digits in the
+fractional portion of the formatted output.
+        </description>
+        <name>minFractionDigits</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable
+which stores the formatted result as a
+String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Parses the string representation of a number, currency, or percentage
+    </description>
+    <name>parseNumber</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.ParseNumberTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+String to be parsed.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether the string in the value
+attribute should be parsed as a number,
+currency, or percentage.
+        </description>
+        <name>type</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Custom formatting pattern that determines
+how the string in the value attribute is to be
+parsed.
+        </description>
+        <name>pattern</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Locale whose default formatting pattern (for
+numbers, currencies, or percentages,
+respectively) is to be used during the parse
+operation, or to which the pattern specified
+via the pattern attribute (if present) is
+applied.
+        </description>
+        <name>parseLocale</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether just the integer portion of
+the given value should be parsed.
+        </description>
+        <name>integerOnly</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable which
+stores the parsed result (of type
+java.lang.Number).
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Formats a date and/or time using the supplied styles and pattern
+    </description>
+    <name>formatDate</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.FormatDateTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+Date and/or time to be formatted.
+        </description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether the time, the date, or both
+the time and date components of the given
+date are to be formatted. 
+        </description>
+        <name>type</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Predefined formatting style for dates. Follows
+the semantics defined in class
+java.text.DateFormat. Applied only
+when formatting a date or both a date and
+time (i.e. if type is missing or is equal to
+"date" or "both"); ignored otherwise.
+        </description>
+        <name>dateStyle</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Predefined formatting style for times. Follows
+the semantics defined in class
+java.text.DateFormat. Applied only
+when formatting a time or both a date and
+time (i.e. if type is equal to "time" or "both");
+ignored otherwise.
+        </description>
+        <name>timeStyle</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Custom formatting style for dates and times.
+        </description>
+        <name>pattern</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Time zone in which to represent the formatted
+time.
+        </description>
+        <name>timeZone</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable which
+stores the formatted result as a String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Parses the string representation of a date and/or time
+    </description>
+    <name>parseDate</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.fmt.ParseDateTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Date string to be parsed.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Specifies whether the date string in the
+value attribute is supposed to contain a
+time, a date, or both.
+        </description>
+        <name>type</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Predefined formatting style for days
+which determines how the date
+component of the date string is to be
+parsed. Applied only when formatting a
+date or both a date and time (i.e. if type
+is missing or is equal to "date" or "both");
+ignored otherwise.
+        </description>
+        <name>dateStyle</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Predefined formatting styles for times
+which determines how the time
+component in the date string is to be
+parsed. Applied only when formatting a
+time or both a date and time (i.e. if type
+is equal to "time" or "both"); ignored
+otherwise.
+        </description>
+        <name>timeStyle</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Custom formatting pattern which
+determines how the date string is to be
+parsed.
+        </description>
+        <name>pattern</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Time zone in which to interpret any time
+information in the date string.
+        </description>
+        <name>timeZone</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Locale whose predefined formatting styles
+for dates and times are to be used during
+the parse operation, or to which the
+pattern specified via the pattern
+attribute (if present) is applied.
+        </description>
+        <name>parseLocale</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable in
+which the parsing result (of type
+java.util.Date) is stored.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope of var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+</taglib>
Index: /trunk/web-app/WEB-INF/tld/grails.tld
===================================================================
--- /trunk/web-app/WEB-INF/tld/grails.tld	(revision 104)
+++ /trunk/web-app/WEB-INF/tld/grails.tld	(revision 104)
@@ -0,0 +1,959 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+            http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+        version="2.0">
+    <description>The Grails (Groovy on Rails) custom tag library</description>
+    <tlib-version>0.2</tlib-version>
+    <short-name>grails</short-name>
+    <uri>http://grails.codehaus.org/tags</uri>
+
+    <tag>
+        <description>
+        	Includes a javascript src file, library or inline script
+	 	if the tag has no src or library attributes its assumed to be an inline script
+        </description>
+        <name>javascript</name>
+        <tag-class>org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>A predefined JavaScript or AJAX library to load</description>
+            <name>library</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <description>A custom (or unknown to Grails) JavaScript source file</description>
+            <name>src</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <description>Since 0.6 Specifies the full base url to prepend to the library name</description>
+            <name>base</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    
+    <!--  
+    <tag>
+    	<description>
+    	</description>
+    	<name></name>
+    	<tag-class></tag-class>
+    	<body-content></body-content>
+    	<attribute>
+    		<description></description>
+    		<name></name>
+    		<required></required>
+    		<rtexprvalue></rtexprvalue>
+    	</attribute>
+    </tag>
+    -->
+    <tag>
+    	<description>Escapes a javasacript string replacing single/double quotes and new lines
+    	</description>
+    	<name>escapeJavascript</name>
+    	<tag-class>org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib</tag-class>
+    	<body-content>JSP</body-content>
+    </tag>
+    
+    <tag>
+    	<description>Creates a form submit button that submits the current form to a remote ajax call
+    	</description>
+    	<name>submitToRemote</name>
+    	<tag-class>org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib</tag-class>
+    	<body-content>JSP</body-content>
+    	<attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>class</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+    </tag>
+    
+    <tag>
+    	<description>A field that sends its value to a remote link
+    	</description>
+    	<name>remoteField</name>
+    	<tag-class>org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib</tag-class>
+    	<body-content>JSP</body-content>
+    	<attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>paramName</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+    	<attribute>
+            <name>before</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>after</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>asynchronous</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>method</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>update</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onSuccess</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onFailure</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onComplete</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoading</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoaded</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onUninitialized</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>on_ERROR_CODE</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <description>
+        	Enables the storing of a value into the given ${var}
+        </description>
+        <name>set</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovySetTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The value to store</description>
+            <name>value</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <description>The name of the variable to store the value in</description>
+            <name>var</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>
+        	Enables the storing of a value into the given ${var} into the page context
+        </description>
+        <name>def</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyDefTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The value to store</description>
+            <name>value</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <description>The name of the variable to store the value in</description>
+            <name>var</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>
+        	Logical if tag to test whether the given condition is true
+        </description>
+        <name>if</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyIfTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The condition to test</description>
+            <name>test</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>
+        	Logical else tag as fallback if the if condition fails
+        </description>
+        <name>else</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyElseTag</tag-class>
+        <body-content>JSP</body-content>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>
+        	Logical elseif tag to test whether the given condition is true
+        </description>
+        <name>elseif</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyElseIfTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The condition to test</description>
+            <name>test</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+	<tag>
+        <description>
+        	Tag to loop over a collection while the test expression returns true
+        </description>
+        <name>while</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyWhileTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The condition to test</description>
+            <name>test</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>Iterates over the given collection</description>
+        <name>each</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyEachTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+        	<description>The collection to iterate over</description>
+            <name>in</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>An optional var-name to reference onto the elements</description>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>status</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>Iterates over the given collection</description>
+        <name>findAll</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyFindAllTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+        	<description>The collection to iterate over</description>
+            <name>in</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>An optional var-name to reference onto the elements</description>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>The expression to filter the elements to iterate over</description>
+            <name>expr</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>status</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+
+	<tag>
+        <description>Iterates over the given collection and collects the elements
+        	you want to work with</description>
+        <name>collect</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyCollectTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+        	<description>The collection to iterate over</description>
+            <name>in</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>An optional var-name to reference onto the elements</description>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>The expression to use to collect the elements. The
+        	expression must retur true to add the element to the
+        	collection</description>
+            <name>expr</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>status</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+    <tag>
+        <description>Iterates over the given collection and filters the elements
+        with a regular expression</description>
+        <name>grep</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.GroovyGrepTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+        	<description>The collection to iterate over</description>
+            <name>in</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>An optional var-name to reference onto the elements</description>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+        	<description>The regular expression to filter the element with. The
+        	filter is a simple Groovy Regex</description>
+            <name>filter</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>status</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+
+
+    <tag>
+        <name>link</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspLinkTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>form</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspFormTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>method</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>select</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspSelectTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>optionKey</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>optionValue</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>datePicker</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspDatePickerTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>precision</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>currencySelect</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspCurrencySelectTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>localeSelect</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspLocaleSelectTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>timeZoneSelect</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspTimeZoneSelectTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>checkBox</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspCheckboxTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>hasErrors</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspHasErrorsTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>model</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>bean</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>field</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>eachError</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>model</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>bean</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>field</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>renderErrors</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>model</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>bean</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>field</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>as</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>message</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspMessageTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>code</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>error</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>default</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>false</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>remoteFunction</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteFunctionTag</tag-class>
+        <body-content>empty</body-content>
+        <attribute>
+            <name>before</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>after</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>asynchronous</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>method</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>update</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onSuccess</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onFailure</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onComplete</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoading</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoaded</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onInteractive</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>remoteLink</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteLinkTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>before</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>after</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>asynchronous</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>method</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>update</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onSuccess</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onFailure</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onComplete</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoading</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoaded</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onInteractive</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>formRemote</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspFormRemoteTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <name>before</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>after</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>controller</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>asynchronous</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>method</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>update</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onSuccess</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onFailure</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onComplete</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoading</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onLoaded</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>onInteractive</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+    <tag>
+        <name>invokeTag</name>
+        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspInvokeGrailsTagLibTag</tag-class>
+        <body-content>JSP</body-content>
+        <variable>
+            <name-given>it</name-given>
+            <variable-class>java.lang.Object</variable-class>
+            <declare>true</declare>
+            <scope>NESTED</scope>
+        </variable>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <dynamic-attributes>true</dynamic-attributes>
+    </tag>
+</taglib>
+
Index: /trunk/web-app/WEB-INF/tld/spring.tld
===================================================================
--- /trunk/web-app/WEB-INF/tld/spring.tld	(revision 104)
+++ /trunk/web-app/WEB-INF/tld/spring.tld	(revision 104)
@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
+
+<taglib>
+
+	<tlib-version>1.1.1</tlib-version>
+
+	<jsp-version>1.2</jsp-version>
+
+	<short-name>Spring</short-name>
+
+	<uri>http://www.springframework.org/tags</uri>
+
+	<description>Spring Framework JSP Tag Library. Authors: Rod Johnson, Juergen Hoeller</description>
+
+
+	<tag>
+
+		<name>htmlEscape</name>
+		<tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Sets default HTML escape value for the current page.
+			Overrides a "defaultHtmlEscape" context-param in web.xml, if any.
+		</description>
+
+		<attribute>
+			<name>defaultHtmlEscape</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>escapeBody</name>
+		<tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Escapes its enclosed body content, applying HTML escaping and/or JavaScript escaping.
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>javaScriptEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>message</name>
+		<tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Retrieves the message with the given code, or text if code isn't resolvable.
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<attribute>
+			<name>code</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>arguments</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>text</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>var</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>scope</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>javaScriptEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>theme</name>
+		<tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Retrieves the theme message with the given code, or text if code isn't resolvable.
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<attribute>
+			<name>code</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>arguments</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>text</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>var</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>scope</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>javaScriptEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>hasBindErrors</name>
+		<tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Provides Errors instance in case of bind errors.
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<variable>
+			<name-given>errors</name-given>
+			<variable-class>org.springframework.validation.Errors</variable-class>
+		</variable>
+
+		<attribute>
+			<name>name</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>nestedPath</name>
+		<tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Sets a nested path to be used by the bind tag's path.
+		</description>
+
+		<variable>
+			<name-given>nestedPath</name-given>
+			<variable-class>java.lang.String</variable-class>
+		</variable>
+
+		<attribute>
+			<name>path</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>bind</name>
+		<tag-class>org.springframework.web.servlet.tags.BindTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Provides BindStatus object for the given bind path.
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<variable>
+			<name-given>status</name-given>
+			<variable-class>org.springframework.web.servlet.support.BindStatus</variable-class>
+		</variable>
+
+		<attribute>
+			<name>path</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>ignoreNestedPath</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+
+	<tag>
+
+		<name>transform</name>
+		<tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class>
+		<body-content>JSP</body-content>
+
+		<description>
+			Provides transformation of variables to Strings, using an appropriate
+			custom PropertyEditor from BindTag (can only be used inside BindTag).
+			The HTML escaping flag participates in a page-wide or application-wide setting
+			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
+		</description>
+
+		<attribute>
+			<name>value</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>var</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>scope</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+		<attribute>
+			<name>htmlEscape</name>
+			<required>false</required>
+			<rtexprvalue>true</rtexprvalue>
+		</attribute>
+
+	</tag>
+
+</taglib>
Index: /trunk/web-app/css/main.css
===================================================================
--- /trunk/web-app/css/main.css	(revision 104)
+++ /trunk/web-app/css/main.css	(revision 104)
@@ -0,0 +1,340 @@
+html * {
+    margin: 0;
+    /*padding: 0; SELECT NOT DISPLAYED CORRECTLY IN FIREFOX */
+
+}
+
+/* GENERAL */
+
+.spinner {
+    padding: 5px;
+    position: absolute;
+    right: 0;
+}
+
+body {
+    text-align: center; 
+    width: 980px
+    background: #fff;
+    color: #333;
+    font: 14px verdana, arial, helvetica, sans-serif;
+    background: transparent url("../images/brushed_metal.png") repeat fixed center;
+}
+
+#wrapper {
+  margin: 0 auto;
+  padding: 0;
+  width: 1024px;
+}
+
+#top {
+  background: url("../images/topBg.png") no-repeat scroll center;
+  width: 1020px;
+  height: 44px;
+}
+
+#content {
+  padding: 0px 20px 20px;
+  background: url("../images/Contentbg.png") repeat-y scroll center;
+  width: 980px;
+}
+
+#Header {
+  background: transparent url("../images/logo.png") no-repeat scroll center;
+  width: 980px;
+  height: 160px;
+}
+#HeaderLink{
+  display: block;
+  width: 650px;
+  height: 160px;
+}
+
+a:link, a:visited, a:hover {
+    color: #666;
+    font-weight: bold;
+    text-decoration: none;
+}
+
+h1 {
+    color: #006dba;
+    font-weight: normal;
+    font-size: 17px;
+    margin: 0 0 .3em 0;
+}
+
+ul {
+    padding-left: 15px;
+}
+
+input, select, textarea {
+    background-color: #fcfcfc;
+    border: 1px solid #ccc;
+    font: 14px verdana, arial, helvetica, sans-serif;
+    margin: 2px 0;
+    padding: 2px 4px;
+}
+select {
+   padding: 2px 2px 2px 0;
+}
+textarea {
+	width: 450px;
+	height: 150px;
+	vertical-align: top;
+}
+
+input:focus, select:focus, textarea:focus {
+    border: 1px solid #b2d1ff;
+}
+
+.body {
+    padding: 0px 20px 20px;
+    background: url("../images/Contentbg.png") repeat-y scroll center;
+    height: 100%;
+}
+
+/* Login/out and Admin*/
+
+.appcontrol {
+    text-align: right;
+    padding: 5px 160px 5px 5px
+}
+
+.appcontrolButton {
+    font-size: 14px;
+    padding: 5px 5px;
+}
+
+/* NAVIGATION MENU */
+
+.nav {
+    text-align: centre;
+    background: url("../images/linkPanel_long.png") top no-repeat;
+    /*border: 1px solid #ccc;
+    border-style: solid none solid none;*/
+    padding: 15px 0px 0px 0px;
+    width: 980px;
+    height: 40px;
+}
+
+/*.nav a{
+    background: url("../images/linkPanel.png") no-repeat top;
+    display: inline-block;
+    width: 140px;
+    height: 40px;
+    text-decoration: none;
+}*/
+
+.menuButton {
+    font-size: 14px;
+    padding: 0 5px;
+}
+.menuButton a {
+    color: #333;
+    padding: 14px 25px;
+}
+.menuButton a.home {
+    /*background: url(../images/skin/house.png) center left no-repeat;*/
+    color: #333;
+    /*padding: 25px;*/
+}
+.menuButton a.list {
+    /*background: url(../images/skin/database_table.png) center left no-repeat;*/
+    color: #333;
+    /*padding-left: 25px;*/
+}
+.menuButton a.create {
+    /*background: url(../images/skin/database_add.png) center left no-repeat;*/
+    color: #333;
+    /*padding-left: 25px;*/
+}
+
+/* MESSAGES AND ERRORS */
+
+.message {
+    background: #f3f8fc url(../images/skin/information.png) 8px 50% no-repeat;
+    border: 1px solid #b2d1ff;
+    color: #006dba;
+    margin: 10px 0 5px 0;
+    padding: 5px 5px 5px 0px
+}
+
+div.errors {
+    background: #fff3f3;
+    border: 1px solid red;
+    color: #cc0000;
+    margin: 10px 0 5px 0;
+    padding: 5px 0 5px 0;
+}
+div.errors ul {
+    list-style: none;
+    padding: 0;
+}
+div.errors li {
+	background: url(../images/skin/exclamation.png) 8px 0% no-repeat;
+    line-height: 16px;
+    padding-left: 30px;
+}
+
+td.errors select {
+    border: 1px solid red;
+}
+td.errors input {
+    border: 1px solid red;
+}
+td.errors textarea {
+    border: 1px solid red;
+}
+
+input.duration {
+    width:40px;
+}
+input.duration.errors {
+    border: 1px solid red;
+}
+
+/* TABLES */
+
+table {
+    border: 1px solid #ccc;
+    width: 100%
+}
+tr {
+    border: 0;
+}
+td, th {
+    font: 14px verdana, arial, helvetica, sans-serif;
+    line-height: 17px;
+    padding: 5px 6px;
+    text-align: left;
+    vertical-align: top;
+}
+th {
+    background: #fff url(../images/skin/shadow.jpg);
+    color: #666;
+    font-size: 14px;
+    font-weight: bold;
+    line-height: 17px;
+    padding: 2px 6px;
+}
+th a:link, th a:visited, th a:hover {
+    color: #333;
+    display: block;
+    font-size: 14px;
+    text-decoration: none;
+    width: 100%;
+}
+th.asc a, th.desc a {
+    background-position: right;
+    background-repeat: no-repeat;
+}
+th.asc a {
+    background-image: url(../images/skin/sorted_asc.gif);
+}
+th.desc a {
+    background-image: url(../images/skin/sorted_desc.gif);
+}
+
+.odd {
+    background: #f7f7f7;
+}
+.even {
+    background: #fff;
+}
+
+/* LIST */
+
+.list table {
+    border-collapse: collapse;
+}
+.list th, .list td {
+    border-left: 1px solid #ddd;
+}
+.list th:hover, .list tr:hover {
+    background: #b2d1ff;
+}
+
+/* PAGINATION */
+
+.paginateButtons {
+    background: #fff url(../images/skin/shadow.jpg) bottom repeat-x;
+    border: 1px solid #ccc;
+    border-top: 0;
+    color: #666;
+    font-size: 14px;
+    overflow: hidden;
+    padding: 10px 3px;
+}
+.paginateButtons a {
+    background: #fff;
+    border: 1px solid #ccc;
+    border-color: #ccc #aaa #aaa #ccc;
+    color: #666;
+    margin: 0 3px;
+    padding: 2px 6px;
+}
+.paginateButtons span {
+    padding: 2px 3px;
+}
+
+/* DIALOG */
+
+.dialog table {
+    padding: 5px 0;
+}
+
+.prop {
+    padding: 5px;
+}
+.prop .name {
+    text-align: left;
+    width: 15%;
+    white-space: nowrap;
+}
+.prop .value {
+    text-align: left;
+    width: 85%;
+}
+
+/* ACTION BUTTONS */
+
+.buttons {
+    background: #fff url(../images/skin/shadow.jpg) bottom repeat-x;
+    border: 1px solid #ccc;
+    color: #666;
+    font-size: 14px;
+    margin-top: -1px;
+    margin-bottom: 5px;
+    overflow: hidden;
+    padding: 0;
+}
+
+.buttons input {
+    background: #fff;
+    border: 0;
+    color: #333;
+    cursor: pointer;
+    font-size: 14px;
+    font-weight: bold;
+    margin-left: 3px;
+    overflow: visible;
+    padding: 2px 6px;
+}
+.buttons input.delete {
+    background: transparent url(../images/skin/database_delete.png) 5px 50% no-repeat;
+    padding-left: 28px;
+}
+.buttons input.edit {
+    background: transparent url(../images/skin/database_edit.png) 5px 50% no-repeat;
+    padding-left: 28px;
+}
+.buttons input.save {
+    background: transparent url(../images/skin/database_save.png) 5px 50% no-repeat;
+    padding-left: 28px;
+}
+
+#bottom {
+  background: url("../images/bottomBg.png") no-repeat scroll center;
+  width: 1020px;
+  height: 100px;
+}
Index: /trunk/web-app/js/application.js
===================================================================
--- /trunk/web-app/js/application.js	(revision 104)
+++ /trunk/web-app/js/application.js	(revision 104)
@@ -0,0 +1,13 @@
+var Ajax;
+if (Ajax && (Ajax != null)) {
+	Ajax.Responders.register({
+	  onCreate: function() {
+        if($('spinner') && Ajax.activeRequestCount>0)
+          Effect.Appear('spinner',{duration:0.5,queue:'end'});
+	  },
+	  onComplete: function() {
+        if($('spinner') && Ajax.activeRequestCount==0)
+          Effect.Fade('spinner',{duration:0.5,queue:'end'});
+	  }
+	});
+}
Index: /trunk/web-app/js/prototype/animation.js
===================================================================
--- /trunk/web-app/js/prototype/animation.js	(revision 104)
+++ /trunk/web-app/js/prototype/animation.js	(revision 104)
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 0.10.0
+*/
+YAHOO.util.Anim=function(el,attributes,duration,method){if(el){this.init(el,attributes,duration,method);}};YAHOO.util.Anim.prototype={doMethod:function(attribute,start,end){return this.method(this.currentFrame,start,end-start,this.totalFrames);},setAttribute:function(attribute,val,unit){YAHOO.util.Dom.setStyle(this.getEl(),attribute,val+unit);},getAttribute:function(attribute){return parseFloat(YAHOO.util.Dom.getStyle(this.getEl(),attribute));},defaultUnit:'px',defaultUnits:{opacity:' '},init:function(el,attributes,duration,method){var isAnimated=false;var startTime=null;var endTime=null;var actualFrames=0;var defaultValues={};el=YAHOO.util.Dom.get(el);this.attributes=attributes||{};this.duration=duration||1;this.method=method||YAHOO.util.Easing.easeNone;this.useSeconds=true;this.currentFrame=0;this.totalFrames=YAHOO.util.AnimMgr.fps;this.getEl=function(){return el;};this.setDefault=function(attribute,val){if(val.constructor!=Array&&(val=='auto'||isNaN(val))){switch(attribute){case'width':val=el.clientWidth||el.offsetWidth;break;case'height':val=el.clientHeight||el.offsetHeight;break;case'left':if(YAHOO.util.Dom.getStyle(el,'position')=='absolute'){val=el.offsetLeft;}else{val=0;}break;case'top':if(YAHOO.util.Dom.getStyle(el,'position')=='absolute'){val=el.offsetTop;}else{val=0;}break;default:val=0;}}defaultValues[attribute]=val;};this.getDefault=function(attribute){return defaultValues[attribute];};this.isAnimated=function(){return isAnimated;};this.getStartTime=function(){return startTime;};this.animate=function(){if(this.isAnimated()){return false;}this.onStart.fire();this._onStart.fire();this.totalFrames=(this.useSeconds)?Math.ceil(YAHOO.util.AnimMgr.fps*this.duration):this.duration;YAHOO.util.AnimMgr.registerElement(this);var attributes=this.attributes;var el=this.getEl();var val;for(var attribute in attributes){val=this.getAttribute(attribute);this.setDefault(attribute,val);}isAnimated=true;actualFrames=0;startTime=new Date();};this.stop=function(){if(!this.isAnimated()){return false;}this.currentFrame=0;endTime=new Date();var data={time:endTime,duration:endTime-startTime,frames:actualFrames,fps:actualFrames/this.duration};isAnimated=false;actualFrames=0;this.onComplete.fire(data);};var onTween=function(){var start;var end=null;var val;var unit;var attributes=this['attributes'];for(var attribute in attributes){unit=attributes[attribute]['unit']||this.defaultUnits[attribute]||this.defaultUnit;if(typeof attributes[attribute]['from']!='undefined'){start=attributes[attribute]['from'];}else{start=this.getDefault(attribute);}if(typeof attributes[attribute]['to']!='undefined'){end=attributes[attribute]['to'];}else if(typeof attributes[attribute]['by']!='undefined'){if(start.constructor==Array){end=[];for(var i=0,len=start.length;i<len;++i){end[i]=start[i]+attributes[attribute]['by'][i];}}else{end=start+attributes[attribute]['by'];}}if(end!==null&&typeof end!='undefined'){val=this.doMethod(attribute,start,end);if((attribute=='width'||attribute=='height'||attribute=='opacity')&&val<0){val=0;}this.setAttribute(attribute,val,unit);}}actualFrames+=1;};this._onStart=new YAHOO.util.CustomEvent('_onStart',this);this.onStart=new YAHOO.util.CustomEvent('start',this);this.onTween=new YAHOO.util.CustomEvent('tween',this);this._onTween=new YAHOO.util.CustomEvent('_tween',this);this.onComplete=new YAHOO.util.CustomEvent('complete',this);this._onTween.subscribe(onTween);}};YAHOO.util.AnimMgr=new function(){var thread=null;var queue=[];var tweenCount=0;this.fps=200;this.delay=1;this.registerElement=function(tween){if(tween.isAnimated()){return false;}queue[queue.length]=tween;tweenCount+=1;this.start();};this.start=function(){if(thread===null){thread=setInterval(this.run,this.delay);}};this.stop=function(tween){if(!tween){clearInterval(thread);for(var i=0,len=queue.length;i<len;++i){if(queue[i].isAnimated()){queue[i].stop();}}queue=[];thread=null;tweenCount=0;}else{tween.stop();tweenCount-=1;if(tweenCount<=0){this.stop();}}};this.run=function(){for(var i=0,len=queue.length;i<len;++i){var tween=queue[i];if(!tween||!tween.isAnimated()){continue;}if(tween.currentFrame<tween.totalFrames||tween.totalFrames===null){tween.currentFrame+=1;if(tween.useSeconds){correctFrame(tween);}tween.onTween.fire();tween._onTween.fire();}else{YAHOO.util.AnimMgr.stop(tween);}}};var correctFrame=function(tween){var frames=tween.totalFrames;var frame=tween.currentFrame;var expected=(tween.currentFrame*tween.duration*1000/tween.totalFrames);var elapsed=(new Date()-tween.getStartTime());var tweak=0;if(elapsed<tween.duration*1000){tweak=Math.round((elapsed/expected-1)*tween.currentFrame);}else{tweak=frames-(frame+1);}if(tweak>0&&isFinite(tweak)){if(tween.currentFrame+tweak>=frames){tweak=frames-(frame+1);}tween.currentFrame+=tweak;}};};YAHOO.util.Bezier=new function(){this.getPosition=function(points,t){var n=points.length;var tmp=[];for(var i=0;i<n;++i){tmp[i]=[points[i][0],points[i][1]];}for(var j=1;j<n;++j){for(i=0;i<n-j;++i){tmp[i][0]=(1-t)*tmp[i][0]+t*tmp[parseInt(i+1,10)][0];tmp[i][1]=(1-t)*tmp[i][1]+t*tmp[parseInt(i+1,10)][1];}}return[tmp[0][0],tmp[0][1]];};};YAHOO.util.Easing=new function(){this.easeNone=function(t,b,c,d){return b+c*(t/=d);};this.easeIn=function(t,b,c,d){return b+c*((t/=d)*t*t);};this.easeOut=function(t,b,c,d){var ts=(t/=d)*t;var tc=ts*t;return b+c*(tc+-3*ts+3*t);};this.easeBoth=function(t,b,c,d){var ts=(t/=d)*t;var tc=ts*t;return b+c*(-2*tc+3*ts);};this.backIn=function(t,b,c,d){var ts=(t/=d)*t;var tc=ts*t;return b+c*(-3.4005*tc*ts+10.2*ts*ts+-6.2*tc+0.4*ts);};this.backOut=function(t,b,c,d){var ts=(t/=d)*t;var tc=ts*t;return b+c*(8.292*tc*ts+-21.88*ts*ts+22.08*tc+-12.69*ts+5.1975*t);};this.backBoth=function(t,b,c,d){var ts=(t/=d)*t;var tc=ts*t;return b+c*(0.402*tc*ts+-2.1525*ts*ts+-3.2*tc+8*ts+-2.05*t);};};YAHOO.util.Motion=function(el,attributes,duration,method){if(el){this.initMotion(el,attributes,duration,method);}};YAHOO.util.Motion.prototype=new YAHOO.util.Anim();YAHOO.util.Motion.prototype.defaultUnits.points='px';YAHOO.util.Motion.prototype.doMethod=function(attribute,start,end){var val=null;if(attribute=='points'){var translatedPoints=this.getTranslatedPoints();var t=this.method(this.currentFrame,0,100,this.totalFrames)/100;if(translatedPoints){val=YAHOO.util.Bezier.getPosition(translatedPoints,t);}}else{val=this.method(this.currentFrame,start,end-start,this.totalFrames);}return val;};YAHOO.util.Motion.prototype.getAttribute=function(attribute){var val=null;if(attribute=='points'){val=[this.getAttribute('left'),this.getAttribute('top')];if(isNaN(val[0])){val[0]=0;}if(isNaN(val[1])){val[1]=0;}}else{val=parseFloat(YAHOO.util.Dom.getStyle(this.getEl(),attribute));}return val;};YAHOO.util.Motion.prototype.setAttribute=function(attribute,val,unit){if(attribute=='points'){YAHOO.util.Dom.setStyle(this.getEl(),'left',val[0]+unit);YAHOO.util.Dom.setStyle(this.getEl(),'top',val[1]+unit);}else{YAHOO.util.Dom.setStyle(this.getEl(),attribute,val+unit);}};YAHOO.util.Motion.prototype.initMotion=function(el,attributes,duration,method){YAHOO.util.Anim.call(this,el,attributes,duration,method);attributes=attributes||{};attributes.points=attributes.points||{};attributes.points.control=attributes.points.control||[];this.attributes=attributes;var start;var end=null;var translatedPoints=null;this.getTranslatedPoints=function(){return translatedPoints;};var translateValues=function(val,self){var pageXY=YAHOO.util.Dom.getXY(self.getEl());val=[val[0]-pageXY[0]+start[0],val[1]-pageXY[1]+start[1]];return val;};var onStart=function(){start=this.getAttribute('points');var attributes=this.attributes;var control=attributes['points']['control']||[];if(control.length>0&&control[0].constructor!=Array){control=[control];}if(YAHOO.util.Dom.getStyle(this.getEl(),'position')=='static'){YAHOO.util.Dom.setStyle(this.getEl(),'position','relative');}if(typeof attributes['points']['from']!='undefined'){YAHOO.util.Dom.setXY(this.getEl(),attributes['points']['from']);start=this.getAttribute('points');}else if((start[0]===0||start[1]===0)){YAHOO.util.Dom.setXY(this.getEl(),YAHOO.util.Dom.getXY(this.getEl()));start=this.getAttribute('points');}var i,len;if(typeof attributes['points']['to']!='undefined'){end=translateValues(attributes['points']['to'],this);for(i=0,len=control.length;i<len;++i){control[i]=translateValues(control[i],this);}}else if(typeof attributes['points']['by']!='undefined'){end=[start[0]+attributes['points']['by'][0],start[1]+attributes['points']['by'][1]];for(i=0,len=control.length;i<len;++i){control[i]=[start[0]+control[i][0],start[1]+control[i][1]];}}if(end){translatedPoints=[start];if(control.length>0){translatedPoints=translatedPoints.concat(control);}translatedPoints[translatedPoints.length]=end;}};this._onStart.subscribe(onStart);};YAHOO.util.Scroll=function(el,attributes,duration,method){if(el){YAHOO.util.Anim.call(this,el,attributes,duration,method);}};YAHOO.util.Scroll.prototype=new YAHOO.util.Anim();YAHOO.util.Scroll.prototype.defaultUnits.scroll=' ';YAHOO.util.Scroll.prototype.doMethod=function(attribute,start,end){var val=null;if(attribute=='scroll'){val=[this.method(this.currentFrame,start[0],end[0]-start[0],this.totalFrames),this.method(this.currentFrame,start[1],end[1]-start[1],this.totalFrames)];}else{val=this.method(this.currentFrame,start,end-start,this.totalFrames);}return val;};YAHOO.util.Scroll.prototype.getAttribute=function(attribute){var val=null;var el=this.getEl();if(attribute=='scroll'){val=[el.scrollLeft,el.scrollTop];}else{val=parseFloat(YAHOO.util.Dom.getStyle(el,attribute));}return val;};YAHOO.util.Scroll.prototype.setAttribute=function(attribute,val,unit){var el=this.getEl();if(attribute=='scroll'){el.scrollLeft=val[0];el.scrollTop=val[1];}else{YAHOO.util.Dom.setStyle(el,attribute,val+unit);}};
Index: /trunk/web-app/js/prototype/builder.js
===================================================================
--- /trunk/web-app/js/prototype/builder.js	(revision 104)
+++ /trunk/web-app/js/prototype/builder.js	(revision 104)
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// 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/
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName.toUpperCase() != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array) ||
+        arguments[1].tagName) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName.toUpperCase() != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+          }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+
+  ATTR_MAP: {
+    'className': 'class',
+    'htmlFor': 'for'
+  },
+
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(children.tagName) {
+      element.appendChild(children);
+      return;
+    }
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children))
+        element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  },
+  build: function(html) {
+    var element = this.node('div');
+    $(element).update(html.strip());
+    return element.down();
+  },
+  dump: function(scope) { 
+    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
+  
+    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+  
+    tags.each( function(tag){ 
+      scope[tag] = function() { 
+        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
+      } 
+    });
+  }
+}
Index: /trunk/web-app/js/prototype/controls.js
===================================================================
--- /trunk/web-app/js/prototype/controls.js	(revision 104)
+++ /trunk/web-app/js/prototype/controls.js	(revision 104)
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { }
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element)
+    this.element     = element; 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+      function(element, update){ 
+        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}) };
+
+    if(typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(Prototype.Browser.WebKit) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(Prototype.Browser.WebKit) Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  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) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount = 
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+    
+    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+    
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw 'Server returned an invalid collection representation.';
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
Index: /trunk/web-app/js/prototype/dragdrop.js
===================================================================
--- /trunk/web-app/js/prototype/dragdrop.js	(revision 104)
+++ /trunk/web-app/js/prototype/dragdrop.js	(revision 104)
@@ -0,0 +1,974 @@
+// script.aculo.us dragdrop.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// 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(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+    
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event); 
+        return true; 
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(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() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // 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);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? 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,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      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 && Object.isString(options.handle))
+      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) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+        
+      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]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this.element._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+        
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+    
+    Draggables.notify('onDrag', this, event);
+    
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        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 + 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);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+    
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this.element._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this.element._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false; 
+    if(success) { 
+      dropped = Droppables.fire(event, this.element); 
+      if (!dropped) dropped = false; 
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+      
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  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 && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      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
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  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);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    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);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
+  sortables: { },
+  
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      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,
+      quiet:       false, 
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+      
+      // these take arrays of elements or ids and can be 
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+      
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e); 
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      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.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+    
+    Sortable._marker.show();
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+    
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+// 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);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {   
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
Index: /trunk/web-app/js/prototype/effects.js
===================================================================
--- /trunk/web-app/js/prototype/effects.js	(revision 104)
+++ /trunk/web-app/js/prototype/effects.js	(revision 104)
@@ -0,0 +1,1122 @@
+// script.aculo.us effects.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if (this.slice(0,1) == '#') {  
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if (this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return (color.length==7 ? color : (arguments[0] || this));  
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  element.setStyle({fontSize: (percent/100) + 'em'});   
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  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) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+    },
+    pulse: function(pos, pulses) { 
+      pulses = pulses || 5; 
+      return (
+        ((pos % (1/pulses)) * pulses).round() == 0 ? 
+              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
+          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+        );
+    },
+    spring: function(pos) { 
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+    
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') || 
+        Object.isFunction(element)) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;    
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = Object.isString(effect.options.queue) ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        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;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+    
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+    
+    eval('this.render = function(pos){ '+
+      'if (this.state=="idle"){this.state="running";'+
+      codeForEvent(this.options,'beforeSetup')+
+      (this.setup ? 'this.setup();':'')+ 
+      codeForEvent(this.options,'afterSetup')+
+      '};if (this.state=="running"){'+
+      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+      'this.position=pos;'+
+      codeForEvent(this.options,'beforeUpdate')+
+      (this.update ? 'this.update(pos);':'')+
+      codeForEvent(this.options,'afterUpdate')+
+      '}}');
+    
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(), 
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+    scrollOffsets = document.viewport.getScrollOffsets(),
+    elementOffsets = $(element).cumulativeOffset(),
+    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1] > max ? max : elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) { 
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity}); 
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    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'),
+    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) {
+        Position.absolutize(effect.effects[0].element)
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      } 
+    }, arguments[1] || { })
+  );
+};
+
+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().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, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        } 
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },  
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, 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(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { };
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+    
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        }
+      }
+    }
+    this.start(options);
+  },
+  
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return { 
+        style: property.camelize(), 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: 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 = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] = 
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]); 
+  });
+  
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
+      hash.set(property, css[property]);
+      return hash;
+    });
+    if (!styles.opacity) styles.set('opacity', element.getOpacity());
+    return styles;
+  };
+};
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element)
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) { 
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    }
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Index: /trunk/web-app/js/prototype/prototype.js
===================================================================
--- /trunk/web-app/js/prototype/prototype.js	(revision 104)
+++ /trunk/web-app/js/prototype/prototype.js	(revision 104)
@@ -0,0 +1,4184 @@
+/*  Prototype JavaScript framework, version 1.6.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://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div').__proto__ &&
+      document.createElement('div').__proto__ !==
+        document.createElement('form').__proto__
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+if (Prototype.Browser.WebKit)
+  Prototype.BrowserFeatures.XPath = false;
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value, value = Object.extend((function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method), {
+          valueOf:  function() { return method },
+          toString: function() { return method.toString() }
+        });
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  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);
+  },
+
+  isElement: function(object) {
+    return object && object.nodeType == 1;
+  },
+
+  isArray: function(object) {
+    return object && object.constructor === Array;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && arguments[0] === undefined) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    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(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    }.bind(this));
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    iterator = iterator.bind(context);
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    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, context) {
+    iterator = iterator.bind(context);
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    iterator = iterator.bind(context);
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  function $A(iterable) {
+    if (!iterable) return [];
+    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+        iterable.toArray) return iterable.toArray();
+    var length = iterable.length, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  }
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+  if (function() {
+    var i = 0, Test = function(value) { this.key = value };
+    Test.prototype.key = 'foo';
+    for (var property in new Test('bar')) i++;
+    return i > 1;
+  }()) {
+    function each(iterator) {
+      var cache = [];
+      for (var key in this._object) {
+        var value = this._object[key];
+        if (cache.include(key)) continue;
+        cache.push(key);
+        var pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    }
+  } else {
+    function each(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    }
+  }
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: each,
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.map(function(pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return values.map(toQueryPair.curry(key)).join('&');
+        }
+        return toQueryPair(key, values);
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.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 headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    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 }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = xml === undefined ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON);
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')))
+        return null;
+    try {
+      return this.transport.responseText.evalJSON(options.sanitizeJSON);
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = options || { };
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, param) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, param);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+
+    if (this.success()) {
+      if (this.onComplete) this.onComplete.bind(this).defer();
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    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(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, t, range;
+
+    for (position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      t = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        t.insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      range = element.ownerDocument.createRange();
+      t.initializeRange(element, range);
+      t.insert(element, range.createContextualFragment(content.stripScripts()));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = value === undefined ? true : value;
+
+    for (var attr in attributes) {
+      var name = t.names[attr] || attr, value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  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;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (element.sourceIndex && !Prototype.Browser.Opera) {
+      var e = element.sourceIndex, a = ancestor.sourceIndex,
+       nextAncestor = ancestor.nextSibling;
+      if (!nextAncestor) {
+        do { ancestor = ancestor.parentNode; }
+        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+      }
+      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+    }
+
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    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,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    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';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    if (element.offsetParent) return $(element.offsetParent);
+    if (element == document.body) return $(element);
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(document.body);
+  },
+
+  viewportOffset: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+
+if (!document.createRange || Prototype.Browser.Opera) {
+  Element.Methods.insert = function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = { bottom: insertions };
+
+    var t = Element._insertionTranslations, content, position, pos, tagName;
+
+    for (position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      pos      = t[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        pos.insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      if (t.tags[tagName]) {
+        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+        if (position == 'top' || position == 'after') fragments.reverse();
+        fragments.each(pos.insert.curry(element));
+      }
+      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  };
+}
+
+if (Prototype.Browser.Opera) {
+  Element.Methods._getStyle = Element.Methods.getStyle;
+  Element.Methods.getStyle = function(element, style) {
+    switch(style) {
+      case 'left':
+      case 'top':
+      case 'right':
+      case 'bottom':
+        if (Element._getStyle(element, 'position') == 'static') return null;
+      default: return Element._getStyle(element, style);
+    }
+  };
+  Element.Methods._readAttribute = Element.Methods.readAttribute;
+  Element.Methods.readAttribute = function(element, attribute) {
+    if (attribute == 'title') return element.title;
+    return Element._readAttribute(element, attribute);
+  };
+}
+
+else if (Prototype.Browser.IE) {
+  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        var position = element.getStyle('position');
+        if (position != 'static') return proceed(element);
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          var attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.clone(Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if (document.createElement('div').outerHTML) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  div.innerHTML = t[0] + html + t[1];
+  t[2].times(function() { div = div.firstChild });
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: {
+    adjacency: 'beforeBegin',
+    insert: function(element, node) {
+      element.parentNode.insertBefore(node, element);
+    },
+    initializeRange: function(element, range) {
+      range.setStartBefore(element);
+    }
+  },
+  top: {
+    adjacency: 'afterBegin',
+    insert: function(element, node) {
+      element.insertBefore(node, element.firstChild);
+    },
+    initializeRange: function(element, range) {
+      range.selectNodeContents(element);
+      range.collapse(true);
+    }
+  },
+  bottom: {
+    adjacency: 'beforeEnd',
+    insert: function(element, node) {
+      element.appendChild(node);
+    }
+  },
+  after: {
+    adjacency: 'afterEnd',
+    insert: function(element, node) {
+      element.parentNode.insertBefore(node, element.nextSibling);
+    },
+    initializeRange: function(element, range) {
+      range.setStartAfter(element);
+    }
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  this.bottom.initializeRange = this.top.initializeRange;
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div').__proto__) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName, property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { };
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      dimensions[d] = self['inner' + D] ||
+       (document.documentElement['client' + D] || document.body['client' + D]);
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocumâs DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+    	      new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return Selector.operators[matches[2]](nodeValue, matches[3]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+	      if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (options.hash === undefined) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (value === undefined) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (value === undefined) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, index) {
+    if (index === undefined)
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, value, single = !Object.isArray(index);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        value = this.optionValue(opt);
+        if (single) {
+          if (value == index) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = index.include(value);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    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) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      var node = Event.extend(event).target;
+      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      return element.match(expression) ? element : element.up(expression);
+    },
+
+    pointer: function(event) {
+      return {
+        x: event.pageX || (event.clientX +
+          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+        y: event.pageY || (event.clientY +
+          (document.documentElement.scrollTop || document.body.scrollTop))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._eventID) return element._eventID;
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._eventID = ++arguments.callee.id;
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event)
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      if (document.createEvent) {
+        var event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        var event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return event;
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize()
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer, fired = false;
+
+  function fireContentLoadedEvent() {
+    if (fired) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    fired = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
Index: /trunk/web-app/js/prototype/rico.js
===================================================================
--- /trunk/web-app/js/prototype/rico.js	(revision 104)
+++ /trunk/web-app/js/prototype/rico.js	(revision 104)
@@ -0,0 +1,2691 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+  *  file except in compliance with the License. You may obtain a copy of the License at
+  *
+  *         http://www.apache.org/licenses/LICENSE-2.0
+  *
+  *  Unless required by applicable law or agreed to in writing, software distributed under the
+  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+  *  either express or implied. See the License for the specific language governing permissions
+  *  and limitations under the License.
+  **/
+
+
+//-------------------- rico.js
+var Rico = {
+  Version: '1.1-beta2'
+}
+
+Rico.ArrayExtensions = new Array();
+
+if (Object.prototype.extend) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}
+
+if (Array.prototype.push) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
+}
+
+if (!Array.prototype.remove) {
+   Array.prototype.remove = function(dx) {
+      if( isNaN(dx) || dx > this.length )
+         return false;
+      for( var i=0,n=0; i<this.length; i++ )
+         if( i != dx )
+            this[n++]=this[i];
+      this.length-=1;
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
+}
+
+if (!Array.prototype.removeItem) {
+   Array.prototype.removeItem = function(item) {
+      for ( var i = 0 ; i < this.length ; i++ )
+         if ( this[i] == item ) {
+            this.remove(i);
+            break;
+         }
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
+}
+
+if (!Array.prototype.indices) {
+   Array.prototype.indices = function() {
+      var indexArray = new Array();
+      for ( index in this ) {
+         var ignoreThis = false;
+         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
+            if ( this[index] == Rico.ArrayExtensions[i] ) {
+               ignoreThis = true;
+               break;
+            }
+         }
+         if ( !ignoreThis )
+            indexArray[ indexArray.length ] = index;
+      }
+      return indexArray;
+   }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
+}
+
+// Create the loadXML method and xml getter for Mozilla
+if ( window.DOMParser &&
+	  window.XMLSerializer &&
+	  window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
+
+   if (!Document.prototype.loadXML) {
+      Document.prototype.loadXML = function (s) {
+         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
+         while (this.hasChildNodes())
+            this.removeChild(this.lastChild);
+
+         for (var i = 0; i < doc2.childNodes.length; i++) {
+            this.appendChild(this.importNode(doc2.childNodes[i], true));
+         }
+      };
+	}
+
+	Document.prototype.__defineGetter__( "xml",
+	   function () {
+		   return (new XMLSerializer()).serializeToString(this);
+	   }
+	 );
+}
+
+document.getElementsByTagAndClassName = function(tagName, className) {
+  if ( tagName == null )
+     tagName = '*';
+
+  var children = document.getElementsByTagName(tagName) || document.all;
+  var elements = new Array();
+
+  if ( className == null )
+    return children;
+
+  for (var i = 0; i < children.length; i++) {
+    var child = children[i];
+    var classNames = child.className.split(' ');
+    for (var j = 0; j < classNames.length; j++) {
+      if (classNames[j] == className) {
+        elements.push(child);
+        break;
+      }
+    }
+  }
+
+  return elements;
+}
+
+
+//-------------------- ricoAccordion.js
+
+Rico.Accordion = Class.create();
+
+Rico.Accordion.prototype = {
+
+   initialize: function(container, options) {
+      this.container            = $(container);
+      this.lastExpandedTab      = null;
+      this.accordionTabs        = new Array();
+      this.setOptions(options);
+      this._attachBehaviors();
+
+      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
+
+      // set the initial visual state...
+      for ( var i=1 ; i < this.accordionTabs.length ; i++ )
+      {
+         this.accordionTabs[i].collapse();
+         this.accordionTabs[i].content.style.display = 'none';
+      }
+      this.lastExpandedTab = this.accordionTabs[0];
+      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
+      this.lastExpandedTab.showExpanded();
+      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         expandedBg          : '#63699c',
+         hoverBg             : '#63699c',
+         collapsedBg         : '#6b79a5',
+         expandedTextColor   : '#ffffff',
+         expandedFontWeight  : 'bold',
+         hoverTextColor      : '#ffffff',
+         collapsedTextColor  : '#ced7ef',
+         collapsedFontWeight : 'normal',
+         hoverTextColor      : '#ffffff',
+         borderColor         : '#1f669b',
+         panelHeight         : 200,
+         onHideTab           : null,
+         onShowTab           : null
+      }.extend(options || {});
+   },
+
+   showTabByIndex: function( anIndex, animate ) {
+      var doAnimate = arguments.length == 1 ? true : animate;
+      this.showTab( this.accordionTabs[anIndex], doAnimate );
+   },
+
+   showTab: function( accordionTab, animate ) {
+
+      var doAnimate = arguments.length == 1 ? true : animate;
+
+      if ( this.options.onHideTab )
+         this.options.onHideTab(this.lastExpandedTab);
+
+      this.lastExpandedTab.showCollapsed(); 
+      var accordion = this;
+      var lastExpandedTab = this.lastExpandedTab;
+
+      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
+      accordionTab.content.style.display = '';
+
+      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+
+      if ( doAnimate ) {
+         new Effect.AccordionSize( this.lastExpandedTab.content,
+                                   accordionTab.content,
+                                   1,
+                                   this.options.panelHeight,
+                                   100, 10,
+                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
+         this.lastExpandedTab = accordionTab;
+      }
+      else {
+         this.lastExpandedTab.content.style.height = "1px";
+         accordionTab.content.style.height = this.options.panelHeight + "px";
+         this.lastExpandedTab = accordionTab;
+         this.showTabDone(lastExpandedTab);
+      }
+   },
+
+   showTabDone: function(collapsedTab) {
+      collapsedTab.content.style.display = 'none';
+      this.lastExpandedTab.showExpanded();
+      if ( this.options.onShowTab )
+         this.options.onShowTab(this.lastExpandedTab);
+   },
+
+   _attachBehaviors: function() {
+      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
+      for ( var i = 0 ; i < panels.length ; i++ ) {
+
+         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
+         if ( tabChildren.length != 2 )
+            continue; // unexpected
+
+         var tabTitleBar   = tabChildren[0];
+         var tabContentBox = tabChildren[1];
+         this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
+      }
+   },
+
+   _getDirectChildrenByTag: function(e, tagName) {
+      var kids = new Array();
+      var allKids = e.childNodes;
+      for( var i = 0 ; i < allKids.length ; i++ )
+         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
+            kids.push(allKids[i]);
+      return kids;
+   }
+
+};
+
+Rico.Accordion.Tab = Class.create();
+
+Rico.Accordion.Tab.prototype = {
+
+   initialize: function(accordion, titleBar, content) {
+      this.accordion = accordion;
+      this.titleBar  = titleBar;
+      this.content   = content;
+      this._attachBehaviors();
+   },
+
+   collapse: function() {
+      this.showCollapsed();
+      this.content.style.height = "1px";
+   },
+
+   showCollapsed: function() {
+      this.expanded = false;
+      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
+      this.content.style.overflow = "hidden";
+   },
+
+   showExpanded: function() {
+      this.expanded = true;
+      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      this.content.style.overflow         = "visible";
+   },
+
+   titleBarClicked: function(e) {
+      if ( this.accordion.lastExpandedTab == this )
+         return;
+      this.accordion.showTab(this);
+   },
+
+   hover: function(e) {
+		this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
+		this.titleBar.style.color           = this.accordion.options.hoverTextColor;
+   },
+
+   unhover: function(e) {
+      if ( this.expanded ) {
+         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      }
+      else {
+         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      }
+   },
+
+   _attachBehaviors: function() {
+      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
+      this.content.style.borderTopWidth    = "0px";
+      this.content.style.borderBottomWidth = "0px";
+      this.content.style.margin            = "0px";
+
+      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
+      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
+      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
+   }
+
+};
+
+
+//-------------------- ricoAjaxEngine.js
+
+Rico.AjaxEngine = Class.create();
+
+Rico.AjaxEngine.prototype = {
+
+   initialize: function() {
+      this.ajaxElements = new Array();
+      this.ajaxObjects  = new Array();
+      this.requestURLS  = new Array();
+   },
+
+   registerAjaxElement: function( anId, anElement ) {
+      if ( arguments.length == 1 )
+         anElement = $(anId);
+      this.ajaxElements[anId] = anElement;
+   },
+
+   registerAjaxObject: function( anId, anObject ) {
+      this.ajaxObjects[anId] = anObject;
+   },
+
+   registerRequest: function (requestLogicalName, requestURL) {
+      this.requestURLS[requestLogicalName] = requestURL;
+   },
+
+   sendRequest: function(requestName) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      
+      if ( arguments.length > 1 ) {
+      	 if(typeof(arguments[1]) == "object" && arguments[1].length != undefined) {
+	      	 queryString = this._createQueryString(arguments[1], 0);
+      	 }
+      	 else {
+	         queryString = this._createQueryString(arguments, 1);
+	     }         
+       }
+             
+      new Ajax.Request(requestURL, this._requestOptions(queryString));
+   },
+
+   sendRequestWithData: function(requestName, xmlDocument) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 2 ) {
+      	 if(typeof(arguments[2]) == "object" && arguments[2].length != undefined) {
+	      	 queryString = this._createQueryString(arguments[2], 0);
+      	 }
+      	 else {
+	         queryString = this._createQueryString(arguments, 2);
+	     }         
+       }             
+
+      new Ajax.Request(requestURL + "?" + queryString, this._requestOptions(null,xmlDocument));
+   },
+
+   sendRequestAndUpdate: function(requestName,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 3 ) {
+      	 if(typeof(arguments[3]) == "object" && arguments[3].length != undefined) {
+	      	 queryString = this._createQueryString(arguments[3], 0);
+      	 }
+      	 else {
+	         queryString = this._createQueryString(arguments, 3);
+	     }         
+       }  
+             
+      var updaterOptions = this._requestOptions(queryString);
+      updaterOptions.onComplete = null;
+      updaterOptions.extend(options);
+
+      new Ajax.Updater(container, requestURL, updaterOptions);
+   },
+
+   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 4 ) {
+      	 if(typeof(arguments[4]) == "object" && arguments[4].length != undefined) {
+	      	 queryString = this._createQueryString(arguments[4], 0);
+      	 }
+      	 else {
+	         queryString = this._createQueryString(arguments, 4);
+	     }         
+       }
+
+
+      var updaterOptions = this._requestOptions(queryString,xmlDocument);
+      updaterOptions.onComplete = null;
+      updaterOptions.extend(options);
+
+      new Ajax.Updater(container, requestURL + "?" + queryString, updaterOptions);
+   },
+
+   // Private -- not part of intended engine API --------------------------------------------------------------------
+
+   _requestOptions: function(queryString,xmlDoc) {
+      var self = this;
+
+      var requestHeaders = ['X-Rico-Version', Rico.Version ];
+      var sendMethod = "post"
+      if ( arguments[1] )
+         requestHeaders.push( 'Content-type', 'text/xml' );
+      else
+         sendMethod = "get";
+
+      return { requestHeaders: requestHeaders,
+               parameters:     queryString,
+               postBody:       arguments[1] ? xmlDoc : null,
+               method:         sendMethod,
+               onComplete:     self._onRequestComplete.bind(self) };
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+   	  var self = this;
+      var queryString = ""
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+                  
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+
+      return queryString;
+   },
+   _onRequestComplete : function(request) {
+
+      //!!TODO: error handling infrastructure?? 
+      if (request.status != 200)
+        return;
+
+      var response = request.responseXML.getElementsByTagName("ajax-response");
+      if (response == null || response.length != 1)
+         return;
+      this._processAjaxResponse( response[0].childNodes );
+   },
+
+   _processAjaxResponse: function( xmlResponseElements ) {
+      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
+         var responseElement = xmlResponseElements[i];
+
+         // only process nodes of type element.....
+         if ( responseElement.nodeType != 1 )
+            continue;
+
+         var responseType = responseElement.getAttribute("type");
+         var responseId   = responseElement.getAttribute("id");
+
+         if ( responseType == "object" )
+            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
+         else if ( responseType == "element" )
+            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
+         else
+            alert('unrecognized AjaxResponse type : ' + responseType );
+      }
+   },
+
+   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
+      ajaxObject.ajaxUpdate( responseElement );
+   },
+
+   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
+      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
+   }
+
+}
+
+var ajaxEngine = new Rico.AjaxEngine();
+
+
+//-------------------- ricoColor.js
+Rico.Color = Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+
+   if ( hexCode.indexOf('#') == 0 )
+      hexCode = hexCode.substring(1);
+   var red   = hexCode.substring(0,2);
+   var green = hexCode.substring(2,4);
+   var blue  = hexCode.substring(4,6);
+   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
+
+   if ( actualColor == "transparent" && elem.parent )
+      return Rico.Color.createColorFromBackground(elem.parent);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      var redPart   = parseInt(actualColor.substring(1,3), 16);
+      var greenPart = parseInt(actualColor.substring(3,5), 16);
+      var bluePart  = parseInt(actualColor.substring(5), 16);
+      return new Rico.Color( redPart, greenPart, bluePart );
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+	var green = 0;
+	var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+	   green = red;
+	   blue = red;
+	}
+	else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+	    }
+	}
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturaton;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+    	var greenc = (cmax - g)/(cmax - cmin);
+    	var bluec  = (cmax - b)/(cmax - cmin);
+
+    	if (r == cmax)
+    	   hue = bluec - greenc;
+    	else if (g == cmax)
+    	   hue = 2.0 + redc - bluec;
+      else
+    	   hue = 4.0 + greenc - redc;
+
+    	hue = hue / 6.0;
+    	if (hue < 0)
+    	   hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+}
+
+
+//-------------------- ricoCorner.js
+
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+
+      var color = this.options.color;
+      if ( this.options.color == "fromElement" )
+         color = this._background(e);
+
+      var bgColor = this.options.bgColor;
+      if ( this.options.bgColor == "fromParent" )
+         bgColor = this._background(e.offsetParent);
+
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = 0;
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }.extend(options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+}
+
+
+//-------------------- ricoDragAndDrop.js
+Rico.DragAndDrop = Class.create();
+
+Rico.DragAndDrop.prototype = {
+
+   initialize: function() {
+      this.dropZones                = new Array();
+      this.draggables               = new Array();
+      this.currentDragObjects       = new Array();
+      this.dragElement              = null;
+      this.lastSelectedDraggable    = null;
+      this.currentDragObjectVisible = false;
+      this.interestedInMotionEvents = false;
+   },
+
+   registerDropZone: function(aDropZone) {
+      this.dropZones[ this.dropZones.length ] = aDropZone;
+   },
+
+   deregisterDropZone: function(aDropZone) {
+      var newDropZones = new Array();
+      var j = 0;
+      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
+         if ( this.dropZones[i] != aDropZone )
+            newDropZones[j++] = this.dropZones[i];
+      }
+
+      this.dropZones = newDropZones;
+   },
+
+   clearDropZones: function() {
+      this.dropZones = new Array();
+   },
+
+   registerDraggable: function( aDraggable ) {
+      this.draggables[ this.draggables.length ] = aDraggable;
+      this._addMouseDownHandler( aDraggable );
+   },
+
+   clearSelection: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].deselect();
+      this.currentDragObjects = new Array();
+      this.lastSelectedDraggable = null;
+   },
+
+   hasSelection: function() {
+      return this.currentDragObjects.length > 0;
+   },
+
+   setStartDragFromElement: function( e, mouseDownElement ) {
+      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
+      this.startx = e.screenX - this.origPos.x
+      this.starty = e.screenY - this.origPos.y
+      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
+      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
+      //this.adjustedForDraggableSize = false;
+
+      this.interestedInMotionEvents = this.hasSelection();
+      this._terminateEvent(e);
+   },
+
+   updateSelection: function( draggable, extendSelection ) {
+      if ( ! extendSelection )
+         this.clearSelection();
+
+      if ( draggable.isSelected() ) {
+         this.currentDragObjects.removeItem(draggable);
+         draggable.deselect();
+         if ( draggable == this.lastSelectedDraggable )
+            this.lastSelectedDraggable = null;
+      }
+      else {
+         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
+         draggable.select();
+         this.lastSelectedDraggable = draggable;
+      }
+   },
+
+   _mouseDownHandler: function(e) {
+      if ( arguments.length == 0 )
+         e = event;
+
+      // if not button 1 ignore it...
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      var eventTarget      = e.target ? e.target : e.srcElement;
+      var draggableObject  = eventTarget.draggable;
+
+      var candidate = eventTarget;
+      while (draggableObject == null && candidate.parentNode) {
+         candidate = candidate.parentNode;
+         draggableObject = candidate.draggable;
+      }
+   
+      if ( draggableObject == null )
+         return;
+
+      this.updateSelection( draggableObject, e.ctrlKey );
+
+      // clear the drop zones postion cache...
+      if ( this.hasSelection() )
+         for ( var i = 0 ; i < this.dropZones.length ; i++ )
+            this.dropZones[i].clearPositionCache();
+
+      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
+   },
+
+
+   _mouseMoveHandler: function(e) {
+      var nsEvent = e.which != undefined;
+      if ( !this.interestedInMotionEvents ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( ! this.hasSelection() )
+         return;
+
+      if ( ! this.currentDragObjectVisible )
+         this._startDrag(e);
+
+      if ( !this.activatedDropZones )
+         this._activateRegisteredDropZones();
+
+      //if ( !this.adjustedForDraggableSize )
+      //   this._adjustForDraggableSize(e);
+
+      this._updateDraggableLocation(e);
+      this._updateDropZonesHover(e);
+
+      this._terminateEvent(e);
+   },
+
+   _makeDraggableObjectVisible: function(e)
+   {
+      if ( !this.hasSelection() )
+         return;
+
+      var dragElement;
+      if ( this.currentDragObjects.length > 1 )
+         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
+      else
+         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
+
+      // go ahead and absolute position it...
+      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
+         dragElement.style.position = "absolute";
+
+      // need to parent him into the document...
+      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
+         document.body.appendChild(dragElement);
+
+      this.dragElement = dragElement;
+      this._updateDraggableLocation(e);
+
+      this.currentDragObjectVisible = true;
+   },
+
+   /**
+   _adjustForDraggableSize: function(e) {
+      var dragElementWidth  = this.dragElement.offsetWidth;
+      var dragElementHeight = this.dragElement.offsetHeight;
+      if ( this.startComponentX > dragElementWidth )
+         this.startx -= this.startComponentX - dragElementWidth + 2;
+      if ( e.offsetY ) {
+         if ( this.startComponentY > dragElementHeight )
+            this.starty -= this.startComponentY - dragElementHeight + 2;
+      }
+      this.adjustedForDraggableSize = true;
+   },
+   **/
+
+   _updateDraggableLocation: function(e) {
+      var dragObjectStyle = this.dragElement.style;
+      dragObjectStyle.left = (e.screenX - this.startx) + "px"
+      dragObjectStyle.top  = (e.screenY - this.starty) + "px";
+   },
+
+   _updateDropZonesHover: function(e) {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
+            this.dropZones[i].hideHover();
+      }
+
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
+               this.dropZones[i].showHover();
+         }
+      }
+   },
+
+   _startDrag: function(e) {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].startDrag();
+
+      this._makeDraggableObjectVisible(e);
+   },
+
+   _mouseUpHandler: function(e) {
+      if ( ! this.hasSelection() )
+         return;
+
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      this.interestedInMotionEvents = false;
+
+      if ( this.dragElement == null ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( this._placeDraggableInDropZone(e) )
+         this._completeDropOperation(e);
+      else {
+         this._terminateEvent(e);
+         new Effect.Position( this.dragElement,
+                              this.origPos.x,
+                              this.origPos.y,
+                              200,
+                              20,
+                              { complete : this._doCancelDragProcessing.bind(this) } );
+      }
+   },
+
+   _completeDropOperation: function(e) {
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null )
+            this.dragElement.parentNode.removeChild(this.dragElement);
+      }
+
+      this._deactivateRegisteredDropZones();
+      this._endDrag();
+      this.clearSelection();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+      this._terminateEvent(e);
+   },
+
+   _doCancelDragProcessing: function() {
+      this._cancelDrag();
+
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null ) {
+            this.dragElement.parentNode.removeChild(this.dragElement);
+         }
+      }
+
+      this._deactivateRegisteredDropZones();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+   },
+
+   _placeDraggableInDropZone: function(e) {
+      var foundDropZone = false;
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
+               this.dropZones[i].hideHover();
+               this.dropZones[i].accept(this.currentDragObjects);
+               foundDropZone = true;
+               break;
+            }
+         }
+      }
+
+      return foundDropZone;
+   },
+
+   _cancelDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].cancelDrag();
+   },
+
+   _endDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].endDrag();
+   },
+
+   _mousePointInDropZone: function( e, dropZone ) {
+
+      var absoluteRect = dropZone.getAbsoluteRect();
+
+      return e.clientX  > absoluteRect.left  &&
+             e.clientX  < absoluteRect.right &&
+             e.clientY  > absoluteRect.top   &&
+             e.clientY  < absoluteRect.bottom;
+   },
+
+   _addMouseDownHandler: function( aDraggable )
+   {
+      var htmlElement = aDraggable.getMouseDownHTMLElement();
+      if ( htmlElement != null ) {
+         htmlElement.draggable = aDraggable;
+         this._addMouseDownEvent( htmlElement );
+      }
+   },
+
+   _activateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         var dropZone = this.dropZones[i];
+         if ( dropZone.canAccept(this.currentDragObjects) )
+            dropZone.activate();
+      }
+
+      this.activatedDropZones = true;
+   },
+
+   _deactivateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ )
+         this.dropZones[i].deactivate();
+      this.activatedDropZones = false;
+   },
+
+   _addMouseDownEvent: function( htmlElement ) {
+      if ( typeof document.implementation != "undefined" &&
+         document.implementation.hasFeature("HTML",   "1.0") &&
+         document.implementation.hasFeature("Events", "2.0") &&
+         document.implementation.hasFeature("CSS",    "2.0") ) {
+         htmlElement.addEventListener("mousedown", this._mouseDownHandler.bindAsEventListener(this), false);
+      }
+      else {
+         htmlElement.attachEvent( "onmousedown", this._mouseDownHandler.bindAsEventListener(this) );
+      }
+   },
+
+   _terminateEvent: function(e) {
+      if ( e.stopPropagation != undefined )
+         e.stopPropagation();
+      else if ( e.cancelBubble != undefined )
+         e.cancelBubble = true;
+
+      if ( e.preventDefault != undefined )
+         e.preventDefault();
+      else
+         e.returnValue = false;
+   },
+
+   initializeEventHandlers: function() {
+      if ( typeof document.implementation != "undefined" &&
+         document.implementation.hasFeature("HTML",   "1.0") &&
+         document.implementation.hasFeature("Events", "2.0") &&
+         document.implementation.hasFeature("CSS",    "2.0") ) {
+         document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
+         document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
+      }
+      else {
+         document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
+         document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
+      }
+   }
+}
+
+//var dndMgr = new Rico.DragAndDrop();
+//dndMgr.initializeEventHandlers();
+
+
+//-------------------- ricoDraggable.js
+Rico.Draggable = Class.create();
+
+Rico.Draggable.prototype = {
+
+   initialize: function( type, htmlElement ) {
+      this.type          = type;
+      this.htmlElement   = $(htmlElement);
+      this.selected      = false;
+   },
+
+   /**
+    *   Returns the HTML element that should have a mouse down event
+    *   added to it in order to initiate a drag operation
+    *
+    **/
+   getMouseDownHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   select: function() {
+      this.selected = true;
+
+      if ( this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      var color = Rico.Color.createColorFromBackground(htmlElement);
+      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
+
+      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
+      htmlElement.style.backgroundColor = color.asHex();
+      this.showingSelected = true;
+   },
+
+   deselect: function() {
+      this.selected = false;
+      if ( !this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      htmlElement.style.backgroundColor = this.saveBackground;
+      this.showingSelected = false;
+   },
+
+   isSelected: function() {
+      return this.selected;
+   },
+
+   startDrag: function() {
+   },
+
+   cancelDrag: function() {
+   },
+
+   endDrag: function() {
+   },
+
+   getSingleObjectDragGUI: function() {
+      return this.htmlElement;
+   },
+
+   getMultiObjectDragGUI: function( draggables ) {
+      return this.htmlElement;
+   },
+
+   getDroppedGUI: function() {
+      return this.htmlElement;
+   },
+
+   toString: function() {
+      return this.type + ":" + this.htmlElement + ":";
+   }
+
+}
+
+
+//-------------------- ricoDropzone.js
+Rico.Dropzone = Class.create();
+
+Rico.Dropzone.prototype = {
+
+   initialize: function( htmlElement ) {
+      this.htmlElement  = $(htmlElement);
+      this.absoluteRect = null;
+   },
+
+   getHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   clearPositionCache: function() {
+      this.absoluteRect = null;
+   },
+
+   getAbsoluteRect: function() {
+      if ( this.absoluteRect == null ) {
+         var htmlElement = this.getHTMLElement();
+         var pos = RicoUtil.toViewportPosition(htmlElement);
+
+         this.absoluteRect = {
+            top:    pos.y,
+            left:   pos.x,
+            bottom: pos.y + htmlElement.offsetHeight,
+            right:  pos.x + htmlElement.offsetWidth
+         };
+      }
+      return this.absoluteRect;
+   },
+
+   activate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null  || this.showingActive)
+         return;
+
+      this.showingActive = true;
+      this.saveBackgroundColor = htmlElement.style.backgroundColor;
+
+      var fallbackColor = "#ffea84";
+      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
+      if ( currentColor == null )
+         htmlElement.style.backgroundColor = fallbackColor;
+      else {
+         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
+         htmlElement.style.backgroundColor = currentColor.asHex();
+      }
+   },
+
+   deactivate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null || !this.showingActive)
+         return;
+
+      htmlElement.style.backgroundColor = this.saveBackgroundColor;
+      this.showingActive = false;
+      this.saveBackgroundColor = null;
+   },
+
+   showHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || this.showingHover )
+         return;
+
+      this.saveBorderWidth = htmlElement.style.borderWidth;
+      this.saveBorderStyle = htmlElement.style.borderStyle;
+      this.saveBorderColor = htmlElement.style.borderColor;
+
+      this.showingHover = true;
+      htmlElement.style.borderWidth = "1px";
+      htmlElement.style.borderStyle = "solid";
+      //htmlElement.style.borderColor = "#ff9900";
+      htmlElement.style.borderColor = "#ffff00";
+   },
+
+   hideHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || !this.showingHover )
+         return;
+
+      htmlElement.style.borderWidth = this.saveBorderWidth;
+      htmlElement.style.borderStyle = this.saveBorderStyle;
+      htmlElement.style.borderColor = this.saveBorderColor;
+      this.showingHover = false;
+   },
+
+   canAccept: function(draggableObjects) {
+      return true;
+   },
+
+   accept: function(draggableObjects) {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null )
+         return;
+
+      n = draggableObjects.length;
+      for ( var i = 0 ; i < n ; i++ )
+      {
+         var theGUI = draggableObjects[i].getDroppedGUI();
+         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
+         {
+            theGUI.style.position = "static";
+            theGUI.style.top = "";
+            theGUI.style.top = "";
+         }
+         htmlElement.appendChild(theGUI);
+      }
+   }
+}
+
+
+//-------------------- ricoEffects.js
+
+/**
+  *  Use the Effect namespace for effects.  If using scriptaculous effects
+  *  this will already be defined, otherwise we'll just create an empty
+  *  object for it...
+ **/
+if ( window.Effect == undefined )
+   Effect = {};
+
+Effect.SizeAndPosition = Class.create();
+Effect.SizeAndPosition.prototype = {
+
+   initialize: function(element, x, y, w, h, duration, steps, options) {
+      this.element = $(element);
+      this.x = x;
+      this.y = y;
+      this.w = w;
+      this.h = h;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[7] || {};
+
+      this.sizeAndPosition();
+   },
+
+   sizeAndPosition: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      // Get original values: x,y = top left corner;  w,h = width height
+      var currentX = this.element.offsetLeft;
+      var currentY = this.element.offsetTop;
+      var currentW = this.element.offsetWidth;
+      var currentH = this.element.offsetHeight;
+
+      // If values not set, or zero, we do not modify them, and take original as final as well
+      this.x = (this.x) ? this.x : currentX;
+      this.y = (this.y) ? this.y : currentY;
+      this.w = (this.w) ? this.w : currentW;
+      this.h = (this.h) ? this.h : currentH;
+
+      // how much do we need to modify our values for each step?
+      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
+      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
+      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
+      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
+
+      this.moveBy(difX, difY);
+      this.resizeBy(difW, difH);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   moveBy: function( difX, difY ) {
+      var currentLeft = this.element.offsetLeft;
+      var currentTop  = this.element.offsetTop;
+      var intDifX     = parseInt(difX);
+      var intDifY     = parseInt(difY);
+
+      var style = this.element.style;
+      if ( intDifX != 0 )
+         style.left = (currentLeft + intDifX) + "px";
+      if ( intDifY != 0 )
+         style.top  = (currentTop + intDifY) + "px";
+   },
+
+   resizeBy: function( difW, difH ) {
+      var currentWidth  = this.element.offsetWidth;
+      var currentHeight = this.element.offsetHeight;
+      var intDifW       = parseInt(difW);
+      var intDifH       = parseInt(difH);
+
+      var style = this.element.style;
+      if ( intDifW != 0 )
+         style.width   = (currentWidth  + intDifW) + "px";
+      if ( intDifH != 0 )
+         style.height  = (currentHeight + intDifH) + "px";
+   }
+}
+
+Effect.Size = Class.create();
+Effect.Size.prototype = {
+
+   initialize: function(element, w, h, duration, steps, options) {
+      new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
+  }
+}
+
+Effect.Position = Class.create();
+Effect.Position.prototype = {
+
+   initialize: function(element, x, y, duration, steps, options) {
+      new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
+  }
+}
+
+Effect.Round = Class.create();
+Effect.Round.prototype = {
+
+   initialize: function(tagName, className, options) {
+      var elements = document.getElementsByTagAndClassName(tagName,className);
+      for ( var i = 0 ; i < elements.length ; i++ )
+         Rico.Corner.round( elements[i], options );
+   }
+};
+
+Effect.FadeTo = Class.create();
+Effect.FadeTo.prototype = {
+
+   initialize: function( element, opacity, duration, steps, options) {
+      this.element  = $(element);
+      this.opacity  = opacity;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[4] || {};
+      this.fadeTo();
+   },
+
+   fadeTo: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+      var currentOpacity = this.getElementOpacity();
+      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
+
+      this.changeOpacityBy(delta);
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
+   },
+
+   changeOpacityBy: function(v) {
+      var currentOpacity = this.getElementOpacity();
+      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
+      this.element.ricoOpacity = newOpacity;
+
+      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
+      this.element.style.opacity = newOpacity; /*//*/;
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   getElementOpacity: function() {
+      if ( this.element.ricoOpacity == undefined ) {
+         var opacity;
+         if ( this.element.currentStyle ) {
+            opacity = this.element.currentStyle.opacity;
+         }
+         else if ( document.defaultView.getComputedStyle != undefined ) {
+            var computedStyle = document.defaultView.getComputedStyle;
+            opacity = computedStyle(this.element, null).getPropertyValue('opacity');
+         }
+
+         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
+      }
+
+      return parseFloat(this.element.ricoOpacity);
+   }
+}
+
+Effect.AccordionSize = Class.create();
+
+Effect.AccordionSize.prototype = {
+
+   initialize: function(e1, e2, start, end, duration, steps, options) {
+      this.e1       = $(e1);
+      this.e2       = $(e2);
+      this.start    = start;
+      this.end      = end;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[6] || {};
+
+      this.accordionSize();
+   },
+
+   accordionSize: function() {
+
+      if (this.isFinished()) {
+         // just in case there are round errors or such...
+         this.e1.style.height = this.start + "px";
+         this.e2.style.height = this.end + "px";
+
+         if(this.options.complete)
+            this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
+      this.resizeBy(diff);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   resizeBy: function(diff) {
+      var h1Height = this.e1.offsetHeight;
+      var h2Height = this.e2.offsetHeight;
+      var intDiff = parseInt(diff);
+      if ( diff != 0 ) {
+         this.e1.style.height = (h1Height - intDiff) + "px";
+         this.e2.style.height = (h2Height + intDiff) + "px";
+      }
+   }
+
+};
+
+
+//-------------------- ricoLiveGrid.js
+
+// Rico.LiveGridMetaData -----------------------------------------------------
+
+Rico.LiveGridMetaData = Class.create();
+
+Rico.LiveGridMetaData.prototype = {
+
+   initialize: function( pageSize, totalRows, columnCount, options ) {
+      this.pageSize  = pageSize;
+      this.totalRows = totalRows;
+      this.setOptions(options);
+      this.scrollArrowHeight = 16;
+      this.columnCount = columnCount;
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         largeBufferSize    : 7.0,   // 7 pages
+         nearLimitFactor    : 0.2    // 20% of buffer
+      }.extend(options || {});
+   },
+
+   getPageSize: function() {
+      return this.pageSize;
+   },
+
+   getTotalRows: function() {
+      return this.totalRows;
+   },
+
+   setTotalRows: function(n) {
+      this.totalRows = n;
+   },
+
+   getLargeBufferSize: function() {
+      return parseInt(this.options.largeBufferSize * this.pageSize);
+   },
+
+   getLimitTolerance: function() {
+      return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
+   }
+};
+
+// Rico.LiveGridScroller -----------------------------------------------------
+
+Rico.LiveGridScroller = Class.create();
+
+Rico.LiveGridScroller.prototype = {
+
+   initialize: function(liveGrid, viewPort) {
+      this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
+      this.liveGrid = liveGrid;
+      this.metaData = liveGrid.metaData;
+      this.createScrollBar();
+      this.scrollTimeout = null;
+      this.lastScrollPos = 0;
+      this.viewPort = viewPort;
+      this.rows = new Array();
+   },
+
+   isUnPlugged: function() {
+      return this.scrollerDiv.onscroll == null;
+   },
+
+   plugin: function() {
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+   },
+
+   unplug: function() {
+      this.scrollerDiv.onscroll = null;
+   },
+
+   sizeIEHeaderHack: function() {
+      if ( !this.isIE ) return;
+      var headerTable = $(this.liveGrid.tableId + "_header");
+      if ( headerTable )
+         headerTable.rows[0].cells[0].style.width =
+            (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
+   },
+
+   createScrollBar: function() {
+      var visibleHeight = this.liveGrid.viewPort.visibleHeight();
+      // create the outer div...
+      this.scrollerDiv  = document.createElement("div");
+      var scrollerStyle = this.scrollerDiv.style;
+      scrollerStyle.borderRight = "1px solid #ababab"; // hard coded color!!!
+      scrollerStyle.position    = "relative";
+      scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
+      scrollerStyle.width       = "19px";
+      scrollerStyle.height      = visibleHeight + "px";
+      scrollerStyle.overflow    = "auto";
+
+      // create the inner div...
+      this.heightDiv = document.createElement("div");
+      this.heightDiv.style.width  = "1px";
+
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                        this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
+      this.scrollerDiv.appendChild(this.heightDiv);
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+
+     var table = this.liveGrid.table;
+     table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling );
+   },
+
+   updateSize: function() {
+      var table = this.liveGrid.table;
+      var visibleHeight = this.viewPort.visibleHeight();
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                                  this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
+   },
+
+   rowToPixel: function(rowOffset) {
+      return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
+   },
+   
+   moveScroll: function(rowOffset) {
+      this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset);
+      if ( this.metaData.options.onscroll )
+         this.metaData.options.onscroll( this.liveGrid, rowOffset );    
+   },
+
+   handleScroll: function() {
+     if ( this.scrollTimeout )
+         clearTimeout( this.scrollTimeout );
+
+      var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
+      this.liveGrid.requestContentRefresh(contentOffset);
+      this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
+      
+      if ( this.metaData.options.onscroll )
+         this.metaData.options.onscroll( this.liveGrid, contentOffset );
+
+      this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 );
+   },
+
+   scrollIdle: function() {
+      if ( this.metaData.options.onscrollidle )
+         this.metaData.options.onscrollidle();
+   }
+};
+
+// Rico.LiveGridBuffer -----------------------------------------------------
+
+Rico.LiveGridBuffer = Class.create();
+
+Rico.LiveGridBuffer.prototype = {
+
+   initialize: function(metaData, viewPort) {
+      this.startPos = 0;
+      this.size     = 0;
+      this.metaData = metaData;
+      this.rows     = new Array();
+      this.updateInProgress = false;
+      this.viewPort = viewPort;
+      this.maxBufferSize = metaData.getLargeBufferSize() * 2;
+      this.maxFetchSize = metaData.getLargeBufferSize();
+      this.lastOffset = 0;
+   },
+
+   getBlankRow: function() {
+      if (!this.blankRow ) {
+         this.blankRow = new Array();
+         for ( var i=0; i < this.metaData.columnCount ; i++ ) 
+            this.blankRow[i] = "&nbsp;";
+     }
+     return this.blankRow;
+   },
+   
+   loadRows: function(ajaxResponse) {
+      var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
+      this.updateUI = rowsElement.getAttribute("update_ui") == "true"
+      var newRows = new Array()
+      var trs = rowsElement.getElementsByTagName("tr");
+      for ( var i=0 ; i < trs.length; i++ ) {
+         var row = newRows[i] = new Array(); 
+         var cells = trs[i].getElementsByTagName("td");
+         for ( var j=0; j < cells.length ; j++ ) {
+            var cell = cells[j];
+            var convertSpaces = cell.getAttribute("convert_spaces") == "true";
+            var cellContent = RicoUtil.getContentAsString(cell);
+            row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
+            if (!row[j]) 
+               row[j] = '&nbsp;';
+         }
+      }
+      return newRows;
+   },
+      
+   update: function(ajaxResponse, start) {
+     var newRows = this.loadRows(ajaxResponse);
+      if (this.rows.length == 0) { // initial load
+         this.rows = newRows;
+         this.size = this.rows.length;
+         this.startPos = start;
+         return;
+      }
+      if (start > this.startPos) { //appending
+         if (this.startPos + this.rows.length < start) {
+            this.rows =  newRows;
+            this.startPos = start;//
+         } else {
+              this.rows = this.rows.concat( newRows.slice(0, newRows.length));
+            if (this.rows.length > this.maxBufferSize) {
+               var fullSize = this.rows.length;
+               this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
+               this.startPos = this.startPos +  (fullSize - this.rows.length);
+            }
+         }
+      } else { //prepending
+         if (start + newRows.length < this.startPos) {
+            this.rows =  newRows;
+         } else {
+            this.rows = newRows.slice(0, this.startPos).concat(this.rows);
+            if (this.rows.length > this.maxBufferSize) 
+               this.rows = this.rows.slice(0, this.maxBufferSize)
+         }
+         this.startPos =  start;
+      }
+      this.size = this.rows.length;
+   },
+   
+   clear: function() {
+      this.rows = new Array();
+      this.startPos = 0;
+      this.size = 0;
+   },
+
+   isOverlapping: function(start, size) {
+      return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0)
+   },
+
+   isInRange: function(position) {
+      return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos()); 
+             //&& this.size()  != 0;
+   },
+
+   isNearingTopLimit: function(position) {
+      return position - this.startPos < this.metaData.getLimitTolerance();
+   },
+
+   endPos: function() {
+      return this.startPos + this.rows.length;
+   },
+   
+   isNearingBottomLimit: function(position) {
+      return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance();
+   },
+
+   isAtTop: function() {
+      return this.startPos == 0;
+   },
+
+   isAtBottom: function() {
+      return this.endPos() == this.metaData.getTotalRows();
+   },
+
+   isNearingLimit: function(position) {
+      return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
+             ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
+   },
+
+   getFetchSize: function(offset) {
+      var adjustedOffset = this.getFetchOffset(offset);
+      var adjustedSize = 0;
+      if (adjustedOffset >= this.startPos) { //apending
+         var endFetchOffset = this.maxFetchSize  + adjustedOffset;
+         if (endFetchOffset > this.metaData.totalRows)
+            endFetchOffset = this.metaData.totalRows;
+         adjustedSize = endFetchOffset - adjustedOffset;   
+      } else {//prepending
+         var adjustedSize = this.startPos - adjustedOffset;
+         if (adjustedSize > this.maxFetchSize)
+            adjustedSize = this.maxFetchSize;
+      }
+      return adjustedSize;
+   }, 
+
+   getFetchOffset: function(offset) {
+      var adjustedOffset = offset;
+      if (offset > this.startPos)  //apending
+         adjustedOffset = (offset > this.endPos()) ? offset :  this.endPos(); 
+      else { //prepending
+         if (offset + this.maxFetchSize >= this.startPos) {
+            var adjustedOffset = this.startPos - this.maxFetchSize;
+            if (adjustedOffset < 0)
+               adjustedOffset = 0;
+         }
+      }
+      this.lastOffset = adjustedOffset;
+      return adjustedOffset;
+   },
+
+   getRows: function(start, count) {
+      var begPos = start - this.startPos
+      var endPos = begPos + count
+
+      // er? need more data...
+      if ( endPos > this.size )
+         endPos = this.size
+
+      var results = new Array()
+      var index = 0;
+      for ( var i=begPos ; i < endPos; i++ ) {
+         results[index++] = this.rows[i]
+      }
+      return results
+   },
+
+   convertSpaces: function(s) {
+      return s.split(" ").join("&nbsp;");
+   }
+
+};
+
+
+//Rico.GridViewPort --------------------------------------------------
+Rico.GridViewPort = Class.create();
+
+Rico.GridViewPort.prototype = {
+
+   initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) {
+      this.lastDisplayedStartPos = 0;
+      this.div = table.parentNode;
+      this.table = table
+      this.rowHeight = rowHeight;
+      this.div.style.height = this.rowHeight * visibleRows;
+      this.div.style.overflow = "hidden";
+      this.buffer = buffer;
+      this.liveGrid = liveGrid;
+      this.visibleRows = visibleRows + 1;
+      this.lastPixelOffset = 0;
+      this.startPos = 0;
+   },
+
+   populateRow: function(htmlRow, row) {
+      for (var j=0; j < row.length; j++) {
+         htmlRow.cells[j].innerHTML = row[j]
+      }
+   },
+   
+   bufferChanged: function() {
+      this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight));
+   },
+   
+   clearRows: function() {
+      if (!this.isBlank) {
+         for (var i=0; i < this.visibleRows; i++)
+            this.populateRow(this.table.rows[i], this.buffer.getBlankRow());
+         this.isBlank = true;
+      }
+   },
+   
+   clearContents: function() {   
+      this.clearRows();
+      this.scrollTo(0);
+      this.startPos = 0;
+      this.lastStartPos = -1;   
+   },
+   
+   refreshContents: function(startPos) {
+      if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
+         return;
+      }
+      if ((startPos + this.visibleRows < this.buffer.startPos)  
+          || (this.buffer.startPos + this.buffer.size < startPos) 
+          || (this.buffer.size == 0)) {
+         this.clearRows();
+         return;
+      }
+      this.isBlank = false;
+      var viewPrecedesBuffer = this.buffer.startPos > startPos
+      var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
+   
+      var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows) 
+                                 ? this.buffer.startPos + this.buffer.size
+                                 : startPos + this.visibleRows;       
+      var rowSize = contentEndPos - contentStartPos;
+      var rows = this.buffer.getRows(contentStartPos, rowSize ); 
+      var blankSize = this.visibleRows - rowSize;
+      var blankOffset = viewPrecedesBuffer ? 0: rowSize;
+      var contentOffset = viewPrecedesBuffer ? blankSize: 0;
+
+      for (var i=0; i < rows.length; i++) {//initialize what we have
+        this.populateRow(this.table.rows[i + contentOffset], rows[i]);
+      }       
+      for (var i=0; i < blankSize; i++) {// blank out the rest 
+        this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow());
+      }
+      this.isPartialBlank = blankSize > 0;
+      this.lastRowPos = startPos;   
+   },
+
+   scrollTo: function(pixelOffset) {      
+      if (this.lastPixelOffset == pixelOffset)
+         return;
+
+      this.refreshContents(parseInt(pixelOffset / this.rowHeight))
+      this.div.scrollTop = pixelOffset % this.rowHeight        
+      
+      this.lastPixelOffset = pixelOffset;
+   },
+   
+   visibleHeight: function() {
+      return parseInt(this.div.style.height);
+   }
+   
+};
+
+
+Rico.LiveGridRequest = Class.create();
+Rico.LiveGridRequest.prototype = {
+   initialize: function( requestOffset, options ) {
+      this.requestOffset = requestOffset;
+   }
+};
+
+// Rico.LiveGrid -----------------------------------------------------
+
+Rico.LiveGrid = Class.create();
+
+Rico.LiveGrid.prototype = {
+
+   initialize: function( tableId, visibleRows, totalRows, url, options ) {
+      if ( options == null )
+         options = {};
+
+      this.tableId     = tableId; 
+      this.table       = $(tableId);
+      var columnCount  = this.table.rows[0].cells.length
+      this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options);
+      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
+
+      var rowCount = this.table.rows.length;
+      this.viewPort =  new Rico.GridViewPort(this.table, 
+                                            this.table.offsetHeight/rowCount,
+                                            visibleRows,
+                                            this.buffer, this);
+      this.scroller    = new Rico.LiveGridScroller(this,this.viewPort);
+      
+      this.additionalParms       = options.requestParameters || [];
+      
+      options.sortHandler = this.sortHandler.bind(this);
+
+      if ( $(tableId + '_header') )
+         this.sort = new Rico.LiveGridSort(tableId + '_header', options)
+
+      this.processingRequest = null;
+      this.unprocessedRequest = null;
+
+      this.initAjax(url);
+      if ( options.prefetchBuffer || options.prefetchOffset > 0) {
+         var offset = 0;
+         if (options.offset ) {
+            offset = options.offset;            
+            this.scroller.moveScroll(offset);
+            this.viewPort.scrollTo(this.scroller.rowToPixel(offset));            
+         }
+         if (options.sortCol) {
+             this.sortCol = options.sortCol;
+             this.sortDir = options.sortDir;
+         }
+         this.requestContentRefresh(offset);
+      }
+   },
+
+   resetContents: function() {
+      this.scroller.moveScroll(0);
+      this.buffer.clear();
+      this.viewPort.clearContents();
+   },
+   
+   sortHandler: function(column) {
+      this.sortCol = column.name;
+      this.sortDir = column.currentSort;
+
+      this.resetContents();
+      this.requestContentRefresh(0) 
+   },
+   
+   setRequestParams: function() {
+      this.additionalParms = [];
+      for ( var i=0 ; i < arguments.length ; i++ )
+         this.additionalParms[i] = arguments[i];
+   },
+
+   setTotalRows: function( newTotalRows ) {
+      this.resetContents();
+      this.metaData.setTotalRows(newTotalRows);
+      this.scroller.updateSize();
+   },
+
+   initAjax: function(url) {
+      ajaxEngine.registerRequest( this.tableId + '_request', url );
+      ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
+   },
+
+   invokeAjax: function() {
+   },
+
+   handleTimedOut: function() {
+      //server did not respond in 4 seconds... assume that there could have been
+      //an error or something, and allow requests to be processed again...
+      this.processingRequest = null;
+      this.processQueuedRequest();
+   },
+
+   fetchBuffer: function(offset) {
+      if ( this.buffer.isInRange(offset) &&
+         !this.buffer.isNearingLimit(offset)) {
+         return;
+      }
+      if (this.processingRequest) {
+          this.unprocessedRequest = new Rico.LiveGridRequest(offset);
+         return;
+      }
+      var bufferStartPos = this.buffer.getFetchOffset(offset);
+      this.processingRequest = new Rico.LiveGridRequest(offset);
+      this.processingRequest.bufferOffset = bufferStartPos;   
+      var fetchSize = this.buffer.getFetchSize(offset);
+      var partialLoaded = false;
+      var callParms = []; 
+      callParms.push(this.tableId + '_request');
+      callParms.push('id='        + this.tableId);
+      callParms.push('page_size=' + fetchSize);
+      callParms.push('offset='    + bufferStartPos);
+      if ( this.sortCol) {
+         callParms.push('sort_col='    + this.sortCol);
+         callParms.push('sort_dir='    + this.sortDir);
+      }
+      
+      for( var i=0 ; i < this.additionalParms.length ; i++ )
+         callParms.push(this.additionalParms[i]);
+      ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
+        
+      this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 20000 ); //todo: make as option
+   },
+
+   requestContentRefresh: function(contentOffset) {
+      this.fetchBuffer(contentOffset);
+   },
+
+   ajaxUpdate: function(ajaxResponse) {
+      try {
+         clearTimeout( this.timeoutHandler );
+         this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
+         this.viewPort.bufferChanged();
+      }
+      catch(err) {}
+      finally {this.processingRequest = null; }
+      this.processQueuedRequest();
+   },
+
+   processQueuedRequest: function() {
+      if (this.unprocessedRequest != null) {
+         this.requestContentRefresh(this.unprocessedRequest.requestOffset);
+         this.unprocessedRequest = null
+      }  
+   }
+ 
+};
+
+
+//-------------------- ricoLiveGridSort.js
+Rico.LiveGridSort = Class.create();
+
+Rico.LiveGridSort.prototype = {
+
+   initialize: function(headerTableId, options) {
+      this.headerTableId = headerTableId;
+      this.headerTable   = $(headerTableId);
+      this.setOptions(options);
+      this.applySortBehavior();
+
+      if ( this.options.sortCol ) {
+         this.setSortUI( this.options.sortCol, this.options.sortDir );
+      }
+   },
+
+   setSortUI: function( columnName, sortDirection ) {
+      var cols = this.options.columns;
+      for ( var i = 0 ; i < cols.length ; i++ ) {
+         if ( cols[i].name == columnName ) {
+            this.setColumnSort(i, sortDirection);
+            break;
+         }
+      }
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         sortAscendImg:    'images/sort_asc.gif',
+         sortDescendImg:   'images/sort_desc.gif',
+         imageWidth:       9,
+         imageHeight:      5,
+         ajaxSortURLParms: []
+      }.extend(options);
+
+      // preload the images...
+      new Image().src = this.options.sortAscendImg;
+      new Image().src = this.options.sortDescendImg;
+
+      this.sort = options.sortHandler;
+      if ( !this.options.columns )
+         this.options.columns = this.introspectForColumnInfo();
+      else {
+         // allow client to pass { columns: [ ["a", true], ["b", false] ] }
+         // and convert to an array of Rico.TableColumn objs...
+         this.options.columns = this.convertToTableColumns(this.options.columns);
+      }
+   },
+
+   applySortBehavior: function() {
+      var headerRow   = this.headerTable.rows[0];
+      var headerCells = headerRow.cells;
+      for ( var i = 0 ; i < headerCells.length ; i++ ) {
+         this.addSortBehaviorToColumn( i, headerCells[i] );
+      }
+   },
+
+   addSortBehaviorToColumn: function( n, cell ) {
+      if ( this.options.columns[n].isSortable() ) {
+         cell.id            = this.headerTableId + '_' + n;
+         cell.style.cursor  = 'pointer';
+         cell.onclick       = this.headerCellClicked.bindAsEventListener(this);
+         cell.innerHTML     = cell.innerHTML + '<span id="' + this.headerTableId + '_img_' + n + '">'
+                           + '&nbsp;&nbsp;&nbsp;</span>';
+      }
+   },
+
+   // event handler....
+   headerCellClicked: function(evt) {
+      var eventTarget = evt.target ? evt.target : evt.srcElement;
+      var cellId = eventTarget.id;
+      var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 ));
+      var sortedColumnIndex = this.getSortedColumnIndex();
+      if ( sortedColumnIndex != -1 ) {
+         if ( sortedColumnIndex != columnNumber ) {
+            this.removeColumnSort(sortedColumnIndex);
+            this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
+         }
+         else
+            this.toggleColumnSort(sortedColumnIndex);
+      }
+      else
+         this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
+
+      if (this.options.sortHandler) {
+         this.options.sortHandler(this.options.columns[columnNumber]);
+      }
+   },
+
+   removeColumnSort: function(n) {
+      this.options.columns[n].setUnsorted();
+      this.setSortImage(n);
+   },
+
+   setColumnSort: function(n, direction) {
+      this.options.columns[n].setSorted(direction);
+      this.setSortImage(n);
+   },
+
+   toggleColumnSort: function(n) {
+      this.options.columns[n].toggleSort();
+      this.setSortImage(n);
+   },
+
+   setSortImage: function(n) {
+      var sortDirection = this.options.columns[n].getSortDirection();
+
+      var sortImageSpan = $( this.headerTableId + '_img_' + n );
+      if ( sortDirection == Rico.TableColumn.UNSORTED )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;';
+      else if ( sortDirection == Rico.TableColumn.SORT_ASC )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.imageWidth    + '" ' +
+                                                     'height="'+ this.options.imageHeight   + '" ' +
+                                                     'src="'   + this.options.sortAscendImg + '"/>';
+      else if ( sortDirection == Rico.TableColumn.SORT_DESC )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.imageWidth    + '" ' +
+                                                     'height="'+ this.options.imageHeight   + '" ' +
+                                                     'src="'   + this.options.sortDescendImg + '"/>';
+   },
+
+   getSortedColumnIndex: function() {
+      var cols = this.options.columns;
+      for ( var i = 0 ; i < cols.length ; i++ ) {
+         if ( cols[i].isSorted() )
+            return i;
+      }
+
+      return -1;
+   },
+
+   introspectForColumnInfo: function() {
+      var columns = new Array();
+      var headerRow   = this.headerTable.rows[0];
+      var headerCells = headerRow.cells;
+      for ( var i = 0 ; i < headerCells.length ; i++ )
+         columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) );
+      return columns;
+   },
+
+   convertToTableColumns: function(cols) {
+      var columns = new Array();
+      for ( var i = 0 ; i < cols.length ; i++ )
+         columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) );
+   },
+
+   deriveColumnNameFromCell: function(cell,columnNumber) {
+      var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent;
+      return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber;
+   }
+};
+
+Rico.TableColumn = Class.create();
+
+Rico.TableColumn.UNSORTED  = 0;
+Rico.TableColumn.SORT_ASC  = "ASC";
+Rico.TableColumn.SORT_DESC = "DESC";
+
+Rico.TableColumn.prototype = {
+   initialize: function(name, sortable) {
+      this.name        = name;
+      this.sortable    = sortable;
+      this.currentSort = Rico.TableColumn.UNSORTED;
+   },
+
+   isSortable: function() {
+      return this.sortable;
+   },
+
+   isSorted: function() {
+      return this.currentSort != Rico.TableColumn.UNSORTED;
+   },
+
+   getSortDirection: function() {
+      return this.currentSort;
+   },
+
+   toggleSort: function() {
+      if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC )
+         this.currentSort = Rico.TableColumn.SORT_ASC;
+      else if ( this.currentSort == Rico.TableColumn.SORT_ASC )
+         this.currentSort = Rico.TableColumn.SORT_DESC;
+   },
+
+   setUnsorted: function(direction) {
+      this.setSorted(Rico.TableColumn.UNSORTED);
+   },
+
+   setSorted: function(direction) {
+      // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SET_DESC...
+      this.currentSort = direction;
+   }
+
+};
+
+
+//-------------------- ricoUtil.js
+
+var RicoUtil = {
+
+   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
+      if ( arguments.length == 2 )
+         mozillaEquivalentCSS = cssProperty;
+
+      var el = $(htmlElement);
+      if ( el.currentStyle )
+         return el.currentStyle[cssProperty];
+      else
+         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getContentAsString: function( parentNode ) {
+      return parentNode.xml != undefined ? 
+         this._getContentAsStringIE(parentNode) :
+         this._getContentAsStringMozilla(parentNode);
+   },
+
+   _getContentAsStringIE: function(parentNode) {
+      var contentStr = "";
+      for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+         contentStr += parentNode.childNodes[i].xml;
+      return contentStr;
+   },
+
+   _getContentAsStringMozilla: function(parentNode) {
+      var xmlSerializer = new XMLSerializer();
+      var contentStr = "";
+      for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+         contentStr += xmlSerializer.serializeToString(parentNode.childNodes[i]);
+      return contentStr;
+   },
+
+   toViewportPosition: function(element) {
+      return this._toAbsolute(element,true);
+   },
+
+   toDocumentPosition: function(element) {
+      return this._toAbsolute(element,false);
+   },
+
+   /**
+    *  Compute the elements position in terms of the window viewport
+    *  so that it can be compared to the position of the mouse (dnd)
+    *  This is additions of all the offsetTop,offsetLeft values up the
+    *  offsetParent hierarchy, ...taking into account any scrollTop,
+    *  scrollLeft values along the way...
+    *
+    * IE has a bug reporting a correct offsetLeft of elements within a
+    * a relatively positioned parent!!!
+    **/
+   _toAbsolute: function(element,accountForDocScroll) {
+
+      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
+         return this._toAbsoluteMozilla(element,accountForDocScroll);
+
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+
+         var borderXOffset = 0;
+         var borderYOffset = 0;
+         if ( parent != element ) {
+            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
+            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
+            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
+            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
+         }
+
+         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
+         y += parent.offsetTop - parent.scrollTop + borderYOffset;
+         parent = parent.offsetParent;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   /**
+    *  Mozilla did not report all of the parents up the hierarchy via the
+    *  offsetParent property that IE did.  So for the calculation of the
+    *  offsets we use the offsetParent property, but for the calculation of
+    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
+    *  property instead so as to get the scroll offsets...
+    *
+    **/
+   _toAbsoluteMozilla: function(element,accountForDocScroll) {
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+         x += parent.offsetLeft;
+         y += parent.offsetTop;
+         parent = parent.offsetParent;
+      }
+
+      parent = element;
+      while ( parent &&
+              parent != document.body &&
+              parent != document.documentElement ) {
+         if ( parent.scrollLeft  )
+            x -= parent.scrollLeft;
+         if ( parent.scrollTop )
+            y -= parent.scrollTop;
+         parent = parent.parentNode;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   docScrollLeft: function() {
+      if ( window.pageXOffset )
+         return window.pageXOffset;
+      else if ( document.documentElement && document.documentElement.scrollLeft )
+         return document.documentElement.scrollLeft;
+      else if ( document.body )
+         return document.body.scrollLeft;
+      else
+         return 0;
+   },
+
+   docScrollTop: function() {
+      if ( window.pageYOffset )
+         return window.pageYOffset;
+      else if ( document.documentElement && document.documentElement.scrollTop )
+         return document.documentElement.scrollTop;
+      else if ( document.body )
+         return document.body.scrollTop;
+      else
+         return 0;
+   }
+
+};
Index: /trunk/web-app/js/prototype/scriptaculous.js
===================================================================
--- /trunk/web-app/js/prototype/scriptaculous.js	(revision 104)
+++ /trunk/web-app/js/prototype/scriptaculous.js	(revision 104)
@@ -0,0 +1,58 @@
+// script.aculo.us scriptaculous.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Scriptaculous = {
+  Version: '1.8.0',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+  },
+  REQUIRED_PROTOTYPE: '1.6.0',
+  load: function() {
+    function convertVersionString(versionString){
+      var r = versionString.split('.');
+      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+    }
+ 
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       (convertVersionString(Prototype.Version) < 
+        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+        Scriptaculous.REQUIRED_PROTOTYPE);
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
Index: /trunk/web-app/js/prototype/slider.js
===================================================================
--- /trunk/web-app/js/prototype/slider.js	(revision 104)
+++ /trunk/web-app/js/prototype/slider.js	(revision 104)
@@ -0,0 +1,275 @@
+// script.aculo.us slider.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs 
+//
+// 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 (!Control) var Control = { };
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider = Class.create({
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if (Object.isArray(handle)) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || { };
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+
+    this.handleLength = this.isVertical() ? 
+      (this.handles[0].offsetHeight != 0 ? 
+        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 
+      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 
+        this.handles[0].style.width.replace(/px$/,""));
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if (this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if (this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (Object.isArray(slider.options.sliderValue) ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      h.makePositioned().observe("mousedown", slider.eventMouseDown);
+    });
+    
+    this.track.observe("mousedown", this.eventMouseDown);
+    document.observe("mouseup", this.eventMouseUp);
+    document.observe("mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if (this.allowedValues){
+      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if (value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if (currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if (value > this.range.end) return this.range.end;
+    if (value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if (!this.active) {
+      this.activeHandleIdx = handleIdx || 0;
+      this.activeHandle    = this.handles[this.activeHandleIdx];
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if (this.initialized && this.restricted) {
+      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if (!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ? 
+      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
+        this.track.style.height.replace(/px$/,"")) - this.alignY : 
+      (this.track.offsetWidth != 0 ? this.track.offsetWidth : 
+        this.track.style.width.replace(/px$/,"")) - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if (this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if (this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if (this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if (this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if (Event.isLeftClick(event)) {
+      if (!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        var track = handle;
+        if (track==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+            
+          if (this.handles.indexOf(handle)!=-1) {
+            this.activeHandle    = handle;
+            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+            this.updateStyles();
+            
+            var offsets  = Position.cumulativeOffset(this.activeHandle);
+            this.offsetX = (pointer[0] - offsets[0]);
+            this.offsetY = (pointer[1] - offsets[1]);
+          }
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if (this.active) {
+      if (!this.dragging) this.dragging = true;
+      this.draw(event);
+      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if (this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if (this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if (this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+});
Index: /trunk/web-app/js/prototype/sound.js
===================================================================
--- /trunk/web-app/js/prototype/sound.js	(revision 104)
+++ /trunk/web-app/js/prototype/sound.js	(revision 104)
@@ -0,0 +1,55 @@
+// script.aculo.us sound.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Based on code created by Jules Gravinese (http://www.webveteran.com/)
+//
+// 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/
+
+Sound = {
+  tracks: {},
+  _enabled: true,
+  template:
+    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
+  enable: function(){
+    Sound._enabled = true;
+  },
+  disable: function(){
+    Sound._enabled = false;
+  },
+  play: function(url){
+    if(!Sound._enabled) return;
+    var options = Object.extend({
+      track: 'global', url: url, replace: false
+    }, arguments[1] || {});
+    
+    if(options.replace && this.tracks[options.track]) {
+      $R(0, this.tracks[options.track].id).each(function(id){
+        var sound = $('sound_'+options.track+'_'+id);
+        sound.Stop && sound.Stop();
+        sound.remove();
+      })
+      this.tracks[options.track] = null;
+    }
+      
+    if(!this.tracks[options.track])
+      this.tracks[options.track] = { id: 0 }
+    else
+      this.tracks[options.track].id++;
+      
+    options.id = this.tracks[options.track].id;
+    $$('body')[0].insert( 
+      Prototype.Browser.IE ? new Element('bgsound',{
+        id: 'sound_'+options.track+'_'+options.id,
+        src: options.url, loop: 1, autostart: true
+      }) : Sound.template.evaluate(options));
+  }
+};
+
+if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
+  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
+    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
+  else
+    Sound.play = function(){}
+}
Index: /trunk/web-app/js/prototype/unittest.js
===================================================================
--- /trunk/web-app/js/prototype/unittest.js	(revision 104)
+++ /trunk/web-app/js/prototype/unittest.js	(revision 104)
@@ -0,0 +1,568 @@
+// script.aculo.us unittest.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// 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/
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons:  0,
+    ctrlKey:  false,
+    altKey:   false,
+    shiftKey: false,
+    metaKey:  false
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.className = "nameCell";
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+    this.addLinksToResults();
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  },
+  addLinksToResults: function(){ 
+    $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run only this test"
+      Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
+    });
+    $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
+      td.title = "Run all tests"
+      Event.observe(td, 'click', function(){ window.location.search = "";});
+    });
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    this.options.tests      = this.parseTestsQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(
+               new Test.Unit.Testcase(
+                 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, 
+                 testcases[testcase], testcases["setup"], testcases["teardown"]
+               ));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  parseTestsQueryParameter: function(){
+    if (window.location.search.parseQuery()["tests"]){
+        return window.location.search.parseQuery()["tests"].split(',');
+    };
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      (this.options.context ? this.options.context + ': ': '') + 
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInspect: function(expected, actual) {
+    var message = arguments[2] || "assertInspect";
+    try { (expected == actual.inspect()) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertIdentical"; 
+    try { (expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNotIdentical: function(expected, actual) { 
+    var message = arguments[2] || "assertNotIdentical"; 
+    try { !(expected === actual) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
+    catch(e) { this.error(e); } 
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertMatch: function(expected, actual) {
+    var message = arguments[2] || 'assertMatch';
+    var regex = new RegExp(expected);
+    try { (regex.exec(actual)) ? this.pass() :
+      this.fail(message + ' : regex: "' +  Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertType: function(expected, actual) {
+    var message = arguments[2] || 'assertType';
+    try { 
+      (actual.constructor == expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNotOfType: function(expected, actual) {
+    var message = arguments[2] || 'assertNotOfType';
+    try { 
+      (actual.constructor != expected) ? this.pass() : 
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
+        '", actual "' + (actual.constructor) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertRespondsTo: function(method, obj) {
+    var message = arguments[2] || 'assertRespondsTo';
+    try {
+      (obj[method] && typeof obj[method] == 'function') ? this.pass() : 
+      this.fail(message + ": object doesn't respond to [" + method + "]"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsTrue: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsTrue';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      m() ? this.pass() : 
+      this.fail(message + ": method returned false"); }
+    catch(e) { this.error(e); }
+  },
+  assertReturnsFalse: function(method, obj) {
+    var message = arguments[2] || 'assertReturnsFalse';
+    try {
+      var m = obj[method];
+      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+      !m() ? this.pass() : 
+      this.fail(message + ": method returned true"); }
+    catch(e) { this.error(e); }
+  },
+  assertRaise: function(exceptionName, method) {
+    var message = arguments[2] || 'assertRaise';
+    try { 
+      method();
+      this.fail(message + ": exception expected but none was raised"); }
+    catch(e) {
+      ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); 
+    }
+  },
+  assertElementsMatch: function() {
+    var expressions = $A(arguments), elements = $A(expressions.shift());
+    if (elements.length != expressions.length) {
+      this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
+      return false;
+    }
+    elements.zip(expressions).all(function(pair, index) {
+      var element = $(pair.first()), expression = pair.last();
+      if (element.match(expression)) return true;
+      this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
+    }.bind(this)) && this.pass();
+  },
+  assertElementMatches: function(element, expression) {
+    this.assertElementsMatch([element], expression);
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    
+    if(typeof test == 'string') {
+      test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
+      test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
+      this.test = function() {
+        eval('with(this){'+test+'}');
+      }
+    } else {
+      this.test = test || function() {};
+    }
+    
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});
+
+// *EXPERIMENTAL* BDD-style testing to please non-technical folk
+// This draws many ideas from RSpec http://rspec.rubyforge.org/
+
+Test.setupBDDExtensionMethods = function(){
+  var METHODMAP = {
+    shouldEqual:     'assertEqual',
+    shouldNotEqual:  'assertNotEqual',
+    shouldEqualEnum: 'assertEnumEqual',
+    shouldBeA:       'assertType',
+    shouldNotBeA:    'assertNotOfType',
+    shouldBeAn:      'assertType',
+    shouldNotBeAn:   'assertNotOfType',
+    shouldBeNull:    'assertNull',
+    shouldNotBeNull: 'assertNotNull',
+    
+    shouldBe:        'assertReturnsTrue',
+    shouldNotBe:     'assertReturnsFalse',
+    shouldRespondTo: 'assertRespondsTo'
+  };
+  var makeAssertion = function(assertion, args, object) { 
+   	this[assertion].apply(this,(args || []).concat([object]));
+  }
+  
+  Test.BDDMethods = {};   
+  $H(METHODMAP).each(function(pair) { 
+    Test.BDDMethods[pair.key] = function() { 
+       var args = $A(arguments); 
+       var scope = args.shift(); 
+       makeAssertion.apply(scope, [pair.value, args, this]); }; 
+  });
+  
+  [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
+    function(p){ Object.extend(p, Test.BDDMethods) }
+  );
+}
+
+Test.context = function(name, spec, log){
+  Test.setupBDDExtensionMethods();
+  
+  var compiledSpec = {};
+  var titles = {};
+  for(specName in spec) {
+    switch(specName){
+      case "setup":
+      case "teardown":
+        compiledSpec[specName] = spec[specName];
+        break;
+      default:
+        var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
+        var body = spec[specName].toString().split('\n').slice(1);
+        if(/^\{/.test(body[0])) body = body.slice(1);
+        body.pop();
+        body = body.map(function(statement){ 
+          return statement.strip()
+        });
+        compiledSpec[testName] = body.join('\n');
+        titles[testName] = specName;
+    }
+  }
+  new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
+};
