You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/10/18 02:17:10 UTC

[3/9] git commit: Rename ElementWrapper.find() to findFirst() Rename ElementWrapper.findAll() to find() Add some additional ElementWrapper functions Straighten out some problems related to client-side validation Introduce module core/fields, responsible

Rename ElementWrapper.find() to findFirst()
Rename ElementWrapper.findAll() to find()
Add some additional ElementWrapper functions
Straighten out some problems related to client-side validation
Introduce module core/fields, responsible initially for field validation messages
Use the core.properties message catalog file to set defaults for some component parameters
Modify the default PropertyEditBlocks to provide the necessary Bootstrap CSS around the fields and labels
Modify BeanEditor to provide a div.control-group around each property editor
Remove DefaultValidationDecorator, and deprecate ValidationDecorator and ValidationDecoratorFactory


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/027d19d1
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/027d19d1
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/027d19d1

Branch: refs/heads/5.4-js-rewrite
Commit: 027d19d1c72f11d9af4af1d903a8590f01a4dd9b
Parents: 61199a3
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Wed Oct 17 16:39:06 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed Oct 17 16:39:06 2012 -0700

----------------------------------------------------------------------
 54_RELEASE_NOTES.txt                               |   26 ++-
 .../META-INF/modules/core/events.coffee            |   30 ++-
 .../META-INF/modules/core/exceptionframe.coffee    |    2 +-
 .../META-INF/modules/core/fields.coffee            |  120 +++++++++
 .../META-INF/modules/core/form-fragment.coffee     |    4 +-
 .../META-INF/modules/core/forms.coffee             |    9 +-
 .../coffeescript/META-INF/modules/core/spi.coffee  |   23 ++-
 .../org/apache/tapestry5/CSSClassConstants.java    |   21 ++-
 .../tapestry5/ComponentParameterConstants.java     |    9 -
 .../org/apache/tapestry5/ValidationDecorator.java  |   15 +-
 .../tapestry5/corelib/base/AbstractField.java      |    4 +-
 .../tapestry5/corelib/components/BeanEditForm.java |    7 +
 .../tapestry5/corelib/components/Submit.java       |    2 +-
 .../internal/DefaultValidationDecorator.java       |   86 -------
 .../services/ValidationDecoratorFactoryImpl.java   |   12 +-
 .../apache/tapestry5/services/TapestryModule.java  |    2 -
 .../services/ValidationDecoratorFactory.java       |    3 +-
 .../resources/org/apache/tapestry5/core.properties |    8 +-
 .../tapestry5/corelib/components/BeanEditForm.tml  |   19 +-
 .../tapestry5/corelib/components/BeanEditor.tml    |   11 +-
 .../tapestry5/corelib/pages/PropertyEditBlocks.tml |   69 ++++--
 .../resources/org/apache/tapestry5/tapestry.js     |  197 +--------------
 tapestry-core/src/test/app1/BeanEditorDemo.tml     |   77 +++---
 .../integration/app1/pages/test-spi.coffee         |   12 +-
 .../internal/DefaultValidationDecoratorTest.java   |  157 ------------
 25 files changed, 373 insertions(+), 552 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/54_RELEASE_NOTES.txt
----------------------------------------------------------------------
diff --git a/54_RELEASE_NOTES.txt b/54_RELEASE_NOTES.txt
index 4585ab7..4721985 100644
--- a/54_RELEASE_NOTES.txt
+++ b/54_RELEASE_NOTES.txt
@@ -91,8 +91,7 @@ was introduced in Tapestry 5.2, and has now been removed.
 Tapestry.FormEventManager has been entirely removed, along with supporting function Element.getFormEventManager().
 
 Tapestry.FieldEventManager.getIcon() now always returns null. The icon image is no longer rendered (on the server)
-by the DefaultValidationDecorator. Shortly, a switch to using Twitter Bootstrap to present validation errors
-will occur.
+by the DefaultValidationDecorator.
 
 Tapestry 5.3 contained code that attempted to prevent Ajax requests until after the page had loaded; this was based
 on the function Tapestry.waitForPage().  Server components no longer make use of this function, and the function
@@ -102,3 +101,26 @@ the page initialization has completed may be implemented in the future.
 Many functions on the Tapestry object have been removed, as they were only used internally:
 - rebuildURL
 - stripToLastSlash
+
+== Twitter Bootstrap
+
+Tapestry now includes a default copy of Twitter Bootstrap in addition to its own default set of CSS rules (the
+file "default.css" automatically included in rendered pages). The Tapestry CSS has been largely eliminated; instead
+components now refer to standard Bootstrap CSS classes.
+
+ValidationDecorator and ValidationDecoratorFactory are deprecated in 5.4 and will be removed in 5.5. The default
+implementation of ValidationDecorator now does nothing. All the logic related to presentation of errors has moved
+to the client, and expects and leverages the Twitter Bootstrap CSS.
+
+Tapestry.ErrorPopup (the controller that managed Tapestry 5.3's floating error bubbles) has been removed entirely.
+Fields that require validation messages to be displayed fire events, and the default handlers show and update
+help blocks that appear (by default) beneath the fields. To get the full effect, you should enclose your fields inside
+.control-group elements, as described in the Bootstrap documentation:
+http://twitter.github.com/bootstrap/base-css.html#forms
+
+== BeanEditor / BeanEditForm
+
+The property edit blocks contributed to the BeanBlockSource service should expect to be nested inside a
+div.control-group, which is provided around the editor for each property.
+
+

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
index e61d53f..c17b1c0 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
@@ -21,11 +21,6 @@ define
   # Defines events related to the validation and submission of forms. See module `core/forms` for further details.
   # All events are triggered on a specific HTML `<form>` element, and top-level handlers take it from there.
   form:
-
-    # Triggered early, to kick off a search for fields that should have `events.field.validate` triggered.
-    # This is also where decorations are added or removed from individual fields.
-    validateFields: "t5:form:validate-fields"
-
     # Triggered after fields have been validated, when there are no field validation exceptions, to allow for
     # cross-form validation.
     validate: "t5:form:validate"
@@ -52,6 +47,31 @@ define
     # popups to Twitter Bootstrap in the near future).
     validate: "t5:field:validate"
 
+    # Clears and hides the element used to display validation error messages. There is no memo for
+    # this event. The p.help-block for the field is located (if it exists) and empties and hidden.
+    # The containing .control-group element (if it exists) has its "error" class name removed.
+    clearValidationError: "t5:field:clear-validation-error"
+
+    # Presents a validation error for a field. The event memo should have a `message` key; the message to present
+    # (as a string, or even as a detached DOM element). The help block for the field will be located or created,
+    # made visible, and have its content updated to `memo.message`.  If a containing element has the class ".control-group",
+    # then the class "error" will be added.
+    #
+    # The rules for locating the help block:
+    # * Search for element with attribute `data-error-block-for` set to the field's `id` attribute
+    # * If not found, find the enclosing .controls or .control-group element
+    # * Search enclosing element for an element with attribute `data-presentation="error"`
+    # * Otherwise, it is not found (but may be created dynamically)
+    # * If found, set the `data-error-block-for` attribute to the field's `id` (assigning the id if necesssary)
+    #
+    # The rules for creating the help block:
+    # * The element is created as `p.help-block` with `data-error-block-for` attribute set to the
+    #   field's id.  The field will be assigned an id if necesary.
+    # * Normally, the block is inserted after the field
+    # * If the field's container has class "input-append" or "input-prepend", then the block is inserted after the container
+    #
+    showValidationError: "t5:field:show-validation-error"
+
   # Defines a number of event names specific to Tapestry Zones. Zones are Tapestry components that are structured
   # to correctly support dynamic updates from the server via an Ajax request, and a standard response
   # (the partial page render reponse). More details are available in the `core/zone` module.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/exceptionframe.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/exceptionframe.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/exceptionframe.coffee
index 0e8b9c3..a44cdcc 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/exceptionframe.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/exceptionframe.coffee
@@ -50,7 +50,7 @@ define ["core/spi", "core/builder", "_"],
 
       spi.body().append container.hide()
 
-      iframe = (container.find "iframe").element
+      iframe = (container.findFirst "iframe").element
 
       # See http://xkr.us/articles/dom/iframe-document/
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee
new file mode 100644
index 0000000..95c62c1
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee
@@ -0,0 +1,120 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# 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.
+
+# ##core/fields
+#
+# Module for logic relating to form input fields (input, select, textarea); specifically
+# presenting validation errors and perfoming input validation when necessary.
+
+define ["_", "core/events", "core/spi", "core/builder"],
+  (_, events, spi, builder) ->
+
+    ensureFieldId = (field) ->
+      fieldId = field.getAttribute "id"
+
+      unless fieldId
+        fieldId = _.uniqueId "field"
+        field.setAttribute "id", fieldId
+
+      return fieldId
+
+    # Finds a `p.help-block` used for presenting errors for the provided field.
+    # Returns the found block as an ElementWrapper. May modify attributes of the field
+    # or the block to make future
+    #
+    # * field - element wrapper for the field
+    findHelpBlock = (field) ->
+
+      fieldId = field.getAttribute "id"
+
+      # When the field has an id (the normal case!), search the body for
+      # the matching help block.
+      if fieldId
+        block = spi.body().findFirst "[data-error-block-for=#{fieldId}]"
+
+        return block if block
+      else
+        # Assign a unique (hopefully!) client id for the field, which will be
+        # used to link the field and the label together.
+        fieldId = ensureFieldId field
+
+      # Not found by id, but see if an empty placeholder was provided within
+      # the same .controls or .control-group.
+
+      group = field.findContainer ".controls, .control-group"
+
+      return null unless group
+
+      block = group.findFirst "[data-presentation=error]"
+
+      if block
+
+        unless fieldId
+          # Assign a unique (hopefully!) client id for the field, which will be
+          # used to link the field and the label together.
+          fieldId = _.uniqueId "field"
+          field.setAttribute "id", fieldId
+
+        block.setAttribute "data-error-block-for", fieldId
+
+      return block
+
+    createHelpBlock = (field) ->
+
+      fieldId = ensureFieldId field
+
+      # No containing group ... this is a problem, probably an old 5.3 application upgraded to 5.4
+      # or beyond.  Place the block just after the field.
+
+      container = field.container()
+
+      block = builder "p.help-block", "data-error-block-for": fieldId
+
+      # The .input-append and .input-prepend are used to attach buttons or markers to the field.
+      # In which case, the block can go
+      if container.hasClass("input-append") or container.hasClass("input-prepend")
+        container.insertAfter block
+      else
+        field.insertAfter block
+
+      return block
+
+    # Default registrations:
+
+    spi.onDocument events.field.clearValidationError, ->
+      block = exports.findHelpBlock this
+
+      if block
+        block.hide().update("")
+
+      group = this.findContainer ".control-group"
+
+      group and group.removeClass "error"
+
+      return
+
+    spi.onDocument events.field.showValidationError, (event, memo) ->
+
+      block = exports.findHelpBlock this
+
+      unless block
+        block = exports.createHelpBlock this
+
+      block.show().update(memo.message)
+
+      group = this.findContainer ".control-group"
+
+      group and group.addClass "error"
+
+    exports = {findHelpBlock, createHelpBlock}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
index 493291f..d6415a5 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
@@ -31,13 +31,13 @@ define ["_", "core/spi", "core/events", "core/compat/tapestry"],
     # to enabled/disable their hidden field.
     spi.onDocument events.form.prepareForSubmit, "form", (event) ->
 
-      fragments = this.findAll SELECTOR
+      fragments = this.find SELECTOR
 
       _.each fragments, (frag) ->
 
         fragmentId = frag.getAttribute "id"
 
-        hidden = frag.find "input[type=hidden][data-for-fragment=#{fragmentId}]"
+        hidden = frag.findFirst "input[type=hidden][data-for-fragment=#{fragmentId}]"
 
         # If found (e.g., not alwaysSubmit), then enable/disable the field.
         hidden && hidden.setAttribute "disabled", not frag.deepVisible()

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
index 641ae54..f069401 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
@@ -26,17 +26,17 @@ define ["core/events", "core/spi", "core/builder", "core/compat/tapestry"],
       (element.getAttribute "data-prevent-submission")
 
     clearSubmittingHidden = (form) ->
-      hidden = form.find "[name='t:submit']"
+      hidden = form.findFirst "[name='t:submit']"
 
       hidden.setValue null if hidden
 
       return
 
     setSubmittingHidden = (form, wrapper) ->
-      hidden = form.find "[name='t:submit']"
+      hidden = form.findFirst "[name='t:submit']"
 
       unless hidden
-        firstHidden = form.find "input[type=hidden]"
+        firstHidden = form.findFirst "input[type=hidden]"
         hidden = builder "input", type:"hidden", name:"t:submit"
         firstHidden.insertBefore hidden
 
@@ -54,7 +54,8 @@ define ["core/events", "core/spi", "core/builder", "core/compat/tapestry"],
 
         memo = error: false
 
-        for field in this.findAll "[data-validation]"
+        # This will become more relevant shortly, when field validation is modernized:
+        for field in this.find "[data-validation]"
            field.trigger events.field.validate, memo
 
         # Only do form validation if all individual field validation

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
index c943fcc..a0c7fad 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
@@ -245,6 +245,13 @@ define ["_", "prototype"], (_) ->
 
       return this
 
+    # Inserts new content (Element, ElementWrapper, or HTML markup string) into the DOM immediately after
+    # this ElementWrapper's element.
+    insertAfter: (content) ->
+      @element.insert after: (convertContent content)
+
+      return this
+
     # Runs an animation to fade-in the element over the specified duration. The element may be hidden (via `hide()`)
     # initially, and will be made visible (with initial opacity 0, which will increase over time) when the animation
     # starts.
@@ -271,7 +278,7 @@ define ["_", "prototype"], (_) ->
 
     # Finds the first child element that matches the CSS selector, wrapped as an ElementWrapper.
     # Returns null if not found.
-    find: (selector) ->
+    findFirst: (selector) ->
       match = @element.down selector
 
       # Prototype returns undefined if not found, we want to return null.
@@ -280,13 +287,23 @@ define ["_", "prototype"], (_) ->
       else
         return null
 
-    # Finds all child elements matching the CSS selector, returning them
+    # Finds _all_ child elements matching the CSS selector, returning them
     # as an array of ElementWrappers.
-    findAll: (selector) ->
+    find: (selector) ->
       matches = @element.select selector
 
       _.map matches, (e) -> new ElementWrapper e
 
+    # Find the first container element that matches the selector (wrapped as an ElementWrapper),
+    # or returns null.
+    findContainer: (selector) ->
+      container = @element.up selector
+
+      if container
+        return new ElementWrapper container
+      else
+        return null
+
     # Returns an ElementWrapper for this element's containing element.  The ElementWrapper is created lazily, and
     # cached. Returns null if this element has no parentNode (either because this element is the document object, or
     # because this element is not yet attached to the DOM).

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/CSSClassConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/CSSClassConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/CSSClassConstants.java
index 0cf35f4..61fd9fb 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/CSSClassConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/CSSClassConstants.java
@@ -1,3 +1,17 @@
+// Copyright 2008, 2010, 2012 The Apache Software Foundation
+//
+// 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.
+
 package org.apache.tapestry5;
 
 /**
@@ -9,13 +23,18 @@ public class CSSClassConstants
      * CSS class name that causes a rendered element to be invisible on the client side.
      */
     public static final String INVISIBLE = "t-invisible";
+
     /**
      * All purpose CSS class name for anything related to Tapestry errors.
+     *
+     * @deprecated Deprecated in 5.4 with no replacement; decoration of fields with validation errors
+     *             has moved to the client.
      */
     public static final String ERROR = "t-error";
+
     /**
      * CSS class name for individual validation errors.
-     * 
+     *
      * @since 5.2.0
      */
     public static final String ERROR_SINGLE = "t-error-single";

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/ComponentParameterConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentParameterConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentParameterConstants.java
index 235efd0..0c4a561 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentParameterConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentParameterConstants.java
@@ -106,13 +106,4 @@ public class ComponentParameterConstants
      * @deprecated Deprecated in 5.4 with no replacement.
      */
     public static final String ZONE_UPDATE_METHOD = "tapestry.components.zone_update_method";
-
-    /**
-     * Default CSS class for the {@link org.apache.tapestry5.corelib.components.Submit}
-     * component.
-     *
-     * @since 5.4.
-     */
-    public static final String SUBMIT_CSS_CLASS = "tapestry.components.submit_css_class";
-
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/ValidationDecorator.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/ValidationDecorator.java b/tapestry-core/src/main/java/org/apache/tapestry5/ValidationDecorator.java
index e68824d..51e380a 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ValidationDecorator.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ValidationDecorator.java
@@ -24,13 +24,20 @@ import org.apache.tapestry5.dom.Element;
  * Field may not be set yet (or may reflect a previous looping's rendering). It may be necessary to {@linkplain
  * org.apache.tapestry5.services.Heartbeat#defer(Runnable)} defer any rendering} until after the Label and the Field have
  * both had their change to initialize and render.
+ * <p/>
+ * Modern HTML and CSS, especially under HTML5 and CSS3, really makes this pointless; it is possible to handle all
+ * of these issues directly in the client. ValidationDecorator will be supported in Tapestry 5.4,
+ * but the default implementation will be changed to do nothing.
+ *
+ * @deprecated Deprecated in 5.4 with no replacement.
  */
 public interface ValidationDecorator
 {
     /**
      * Invoked by a {@link org.apache.tapestry5.corelib.components.Label} before rendering itself.
      *
-     * @param field for this label
+     * @param field
+     *         for this label
      */
     void beforeLabel(Field field);
 
@@ -38,8 +45,10 @@ public interface ValidationDecorator
      * Invoked after the label has rendered its tag, but before it has rendered content inside the tag, to allow the
      * decorator to write additional attributes.
      *
-     * @param field        the field corresponding to the label
-     * @param labelElement the element for this label
+     * @param field
+     *         the field corresponding to the label
+     * @param labelElement
+     *         the element for this label
      */
     void insideLabel(Field field, Element labelElement);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
index f702432..82cf672 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
@@ -34,6 +34,7 @@ import java.io.Serializable;
  * {@link RenderDisabled} and {@link DiscardBody} mixins.
  */
 @SupportsInformalParameters
+@Import(modules = "core/fields")
 public abstract class AbstractField implements Field
 {
     /**
@@ -198,7 +199,8 @@ public abstract class AbstractField implements Field
      * controlName property will already have been set. This method is only invoked if the field is <strong>not
      * {@link #isDisabled() disabled}</strong>.
      *
-     * @param controlName the control name of the rendered element (used to find the correct parameter in the request)
+     * @param controlName
+     *         the control name of the rendered element (used to find the correct parameter in the request)
      */
     protected abstract void processSubmission(String controlName);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
index 8fd0fba..3604b0c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
@@ -104,6 +104,13 @@ public class BeanEditForm implements ClientElement, FormValidationControl
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
     private String add;
 
+    /**
+     * Specifies the CSS class attribute for the form; the factory default is "well".
+     */
+    @Property
+    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL, value = "message:core-components.beaneditform.class")
+    private String className;
+
     @Component(parameters = "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
     private Form form;
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Submit.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Submit.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Submit.java
index 1d83507..7e691a7 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Submit.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Submit.java
@@ -100,7 +100,7 @@ public class Submit implements ClientElement
      * @since 5.4
      */
     @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL,
-            value = BindingConstants.SYMBOL + ":" + ComponentParameterConstants.SUBMIT_CSS_CLASS)
+            value = "message:core-components.submit.class")
     private String cssClass;
 
     @Environmental

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValidationDecorator.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValidationDecorator.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValidationDecorator.java
deleted file mode 100644
index 5e2ce23..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValidationDecorator.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2007, 2008, 2009, 2010 The Apache Software Foundation
-//
-// 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.
-
-package org.apache.tapestry5.internal;
-
-import org.apache.tapestry5.*;
-import org.apache.tapestry5.dom.Element;
-import org.apache.tapestry5.services.Environment;
-import org.apache.tapestry5.services.FormSupport;
-
-/**
- * Default implementation that writes an attribute into fields or labels that are in error.
- */
-public final class DefaultValidationDecorator extends BaseValidationDecorator
-{
-    private final Environment environment;
-
-    private final MarkupWriter markupWriter;
-
-    /**
-     * @param environment
-     *         used to locate objects and services during the render
-     * @param markupWriter
-     */
-    public DefaultValidationDecorator(Environment environment, MarkupWriter markupWriter)
-    {
-        this.environment = environment;
-        this.markupWriter = markupWriter;
-    }
-
-    @Override
-    public void insideField(Field field)
-    {
-        if (inError(field))
-            addErrorClassToCurrentElement();
-    }
-
-    @Override
-    public void insideLabel(Field field, Element element)
-    {
-        if (field == null)
-            return;
-
-        if (inError(field))
-            element.addClassName(CSSClassConstants.ERROR);
-    }
-
-    /**
-     * Does nothing; prior releases would write an error icon.
-     *
-     * @param field
-     *         which just completed rendering itself
-     */
-    @Override
-    public void afterField(Field field)
-    {
-    }
-
-    private FormSupport getFormSupport()
-    {
-        return environment.peekRequired(FormSupport.class);
-    }
-
-    private boolean inError(Field field)
-    {
-        ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);
-
-        return tracker.inError(field);
-    }
-
-    private void addErrorClassToCurrentElement()
-    {
-        markupWriter.getElement().addClassName(CSSClassConstants.ERROR);
-    }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidationDecoratorFactoryImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidationDecoratorFactoryImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidationDecoratorFactoryImpl.java
index 4385734..90fe903 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidationDecoratorFactoryImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidationDecoratorFactoryImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,23 +14,19 @@
 
 package org.apache.tapestry5.internal.services;
 
+import org.apache.tapestry5.BaseValidationDecorator;
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.ValidationDecorator;
-import org.apache.tapestry5.internal.DefaultValidationDecorator;
-import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.ValidationDecoratorFactory;
 
 public class ValidationDecoratorFactoryImpl implements ValidationDecoratorFactory
 {
-    private final Environment environment;
-
-    public ValidationDecoratorFactoryImpl(Environment environment)
+    public ValidationDecoratorFactoryImpl()
     {
-        this.environment = environment;
     }
 
     public ValidationDecorator newInstance(MarkupWriter writer)
     {
-        return new DefaultValidationDecorator(environment, writer);
+        return new BaseValidationDecorator();
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
index 122a9e1..d7b30e2 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
@@ -2182,8 +2182,6 @@ public final class TapestryModule
 
         configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
 
-        configuration.add(ComponentParameterConstants.SUBMIT_CSS_CLASS, "btn btn-primary");
-
         // Grid component parameters defaults
         configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
         configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/java/org/apache/tapestry5/services/ValidationDecoratorFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/ValidationDecoratorFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/ValidationDecoratorFactory.java
index cab393f..a3d4313 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/ValidationDecoratorFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/ValidationDecoratorFactory.java
@@ -20,9 +20,10 @@ import org.apache.tapestry5.ValidationDecorator;
 /**
  * Creates an instance of {@link org.apache.tapestry5.ValidationDecorator} for a
  * {@link org.apache.tapestry5.MarkupWriter}.    This service is overridden in applications
- * that do not wish to use {@link org.apache.tapestry5.internal.DefaultValidationDecorator}.
+ * that do not wish to use the {@linkplain org.apache.tapestry5.BaseValidationDecorator default no-op validation decorator}.
  *
  * @since 5.3
+ * @deprecated Deprecated in 5.4 with no replacement, as {@link ValidationDecorator} is being phased out.
  */
 public interface ValidationDecoratorFactory
 {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
index 5488e7a..d3cc13a 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
@@ -50,4 +50,10 @@ required=You must provide a value for %s.
 core-page-initialization-template=\
 require(["core/pageinit"], function(pageinit) {\
   pageinit.loadLibrariesAndInitialize(%s, %s, %s, %s); \
-});
\ No newline at end of file
+});
+
+# Default values for selected core component parameters.
+
+core-components.beaneditform.class=well
+core-components.submit.class=btn btn-primary
+

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
index 8b4f07d..6cdb8a1 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
@@ -1,17 +1,18 @@
-<form t:id="form" validate="object"
+<form class="${className}" t:id="form" validate="object"
       xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
     <t:errors/>
 
-    <div class="t-beaneditor">
+    <t:beaneditor t:id="editor" object="object" model="model" overrides="this"/>
 
-        <t:beaneditor t:id="editor" object="object" model="model" overrides="this"/>
-
-        <div class="t-beaneditor-row">
-            <input type="submit" class="t-beaneditor-submit" value="${submitLabel}"/>
-            <t:if test="cancel">
-              <t:submit t:id="cancel" mode="cancel" value="message:core-cancel-label"/>
-            </t:if>
+    <div class="form-actions btn-toolbar">
+        <div class="btn-group">
+            <input type="submit" class="btn btn-primary" value="${submitLabel}"/>
         </div>
+        <t:if test="cancel">
+            <div class="btn-group">
+                <t:submit t:id="cancel" mode="cancel" class="btn" value="message:core-cancel-label"/>
+            </div>
+        </t:if>
     </div>
 
 </form>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditor.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditor.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditor.tml
index cd26b78..1b74477 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditor.tml
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditor.tml
@@ -1,4 +1,7 @@
-<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xml:space="default" class="t-beaneditor-row"
-  t:type="loop" t:source="model.propertyNames" t:formState="ITERATION" t:value="propertyName">
-  <t:propertyEditor property="propertyName" object="object" model="model" overrides="overrides"/>
-</div>
\ No newline at end of file
+<t:loop xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
+        source="model.propertyNames" formState="ITERATION"
+        value="propertyName">
+    <div class="control-group">
+        <t:propertyEditor property="propertyName" object="object" model="model" overrides="overrides"/>
+    </div>
+</t:loop>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.tml
index b857675..e7096ef 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.tml
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.tml
@@ -1,44 +1,71 @@
-<div xml:space="default" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+<div xml:space="default" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
 
     <t:block id="text">
-        <t:label for="textField"/>
-        <input t:id="textField"/>
+        <t:label class="control-label" for="textField"/>
+        <div class="controls">
+            <input t:id="textField"/>
+        </div>
     </t:block>
 
     <t:block id="number">
-        <t:label for="numberField"/>
-        <input t:id="numberField" class="t-number"/>
+        <t:label class="control-label" for="numberField"/>
+        <div class="controls">
+            <input t:id="numberField" class="t-number"/>
+        </div>
     </t:block>
 
 
     <t:block id="enum">
-        <t:label for="select"/>
-        <input t:id="select"/>
+
+        <t:label class="control-label" for="select"/>
+        <div class="controls">
+            <input t:id="select"/>
+        </div>
+
     </t:block>
 
     <t:block id="boolean">
-        <t:label for="checkboxField"/>
-        <input t:id="checkboxField"/>
+
+        <div class="controls">
+            <label class="checkbox">
+                <input t:id="checkboxField"/>
+                ${context.label}
+            </label>
+        </div>
+
     </t:block>
 
     <t:block id="date">
-        <t:label for="dateField"/>
-        <input t:id="dateField"/>
-    </t:block>
-
-    <t:block id="calendar">
-        <t:label for="calendarField"/>
-        <input t:id="calendarField"/>
+
+        <t:label class="control-label" for="dateField"/>
+        <div class="controls">
+            <input t:id="dateField"/>
+        </div>
+
+    </t:block>
+
+    <t:block id="calendar">
+
+        <t:label class="control-label" for="calendarField"/>
+        <div class="controls">
+            <input t:id="calendarField"/>
+        </div>
+
     </t:block>
 
     <t:block id="password">
-        <t:label for="passwordField"/>
-        <input t:id="passwordField"/>
+
+        <t:label class="control-label" for="passwordField"/>
+        <div class="controls">
+            <input t:id="passwordField"/>
+        </div>
+
     </t:block>
 
     <t:block id="longtext">
-        <t:label for="textarea"/>
-        <textarea t:id="textarea"/>
+        <t:label class="control-label" for="textarea"/>
+        <div class="controls">
+            <textarea t:id="textarea"/>
+        </div>
     </t:block>
-
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js b/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
index c645387..d3a8cb5 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
@@ -56,14 +56,14 @@ define("core/compat/tapestry", [
          * identify that the field is in error (and decorate the field and show a
          * popup error message).
          */
-        FIELD_VALIDATE_EVENT: events.field.validate,
+        FIELD_VALIDATE_EVENT: "tapestry:field-validate",
 
         /**
          * Event notification, on a form object, that is used to trigger validation
          * on all fields within the form (observed by each field's
-         * Tapestry.FieldEventManager).
+         * Tapestry.FieldEventManager).  Note: no longer used in 5.4.
          */
-        FORM_VALIDATE_FIELDS_EVENT: events.form.validateFields,
+        FORM_VALIDATE_FIELDS_EVENT: "tapestry:form-validate-fields",
 
         /**
          * Event, fired on the document object, which identifies the current focus
@@ -979,174 +979,6 @@ define("core/compat/tapestry", [
         }
     };
 
-    Tapestry.ErrorPopup = Class.create({
-
-        /*
-         * If the images associated with the error popup are overridden (by
-         * overriding Tapestry's default.css stylesheet), then some of these values
-         * may also need to be adjusted.
-         */
-        BUBBLE_VERT_OFFSET: -34,
-
-        BUBBLE_HORIZONTAL_OFFSET: -20,
-
-        BUBBLE_WIDTH: "auto",
-
-        BUBBLE_HEIGHT: "39px",
-
-        IE_FADE_TIME: 500,
-
-        initialize: function (field) {
-            this.field = $(field);
-
-            // The UI elements (outerDiv and friends) are created by the first call to setMessage().
-            this.outerDiv = null;
-        },
-
-        /**
-         * Invoked once, from setMessage(), to create the outerDiv and innerSpan elements, as well as necessary listeners
-         *  (to hide the popup if clicked), and reposition the popup as necessary when the window resizes.
-         */
-        createUI: function () {
-            this.innerSpan = new Element("span");
-            this.outerDiv = $(new Element("div", {
-                'id': this.field.id + "_errorpopup",
-                'class': 't-error-popup'
-            })).update(this.innerSpan).hide();
-
-            var body = $(document.body);
-
-            body.insert({
-                bottom: this.outerDiv
-            });
-
-            this.outerDiv.absolutize();
-
-            this.outerDiv.observe("click", function (event) {
-                this.ignoreNextFocus = true;
-
-                this.stopAnimation();
-
-                this.outerDiv.hide();
-
-                this.field.activate();
-
-                event.stop();
-            }.bindAsEventListener(this));
-
-            this.queue = {
-                position: 'end',
-                scope: this.field.id
-            };
-
-            Event.observe(window, "resize", this.repositionBubble.bind(this));
-        },
-
-        showMessage: function (message) {
-
-            if (this.outerDiv == null) {
-                this.createUI();
-            }
-
-            this.stopAnimation();
-
-            this.innerSpan.update(message);
-
-            this.hasMessage = true;
-
-            this.fadeIn();
-        },
-
-        repositionBubble: function () {
-            var fieldPos = this.field.cumulativeOffset();
-
-            this.outerDiv.setStyle({
-                top: (fieldPos[1] + this.BUBBLE_VERT_OFFSET) + "px",
-                left: (fieldPos[0] + this.BUBBLE_HORIZONTAL_OFFSET) + "px",
-                width: this.BUBBLE_WIDTH,
-                height: this.BUBBLE_HEIGHT
-            });
-        },
-
-        fadeIn: function () {
-            if (!this.hasMessage)
-                return;
-
-            this.repositionBubble();
-
-            if (this.animation)
-                return;
-
-            if (Prototype.Browser.IE) {
-
-                this.outerDiv.show();
-
-                var bound = _.bind(this.hideIfNotFocused, this);
-
-                _.delay(bound, this.IE_FADE_TIME);
-
-                return;
-            }
-
-            this.animation = new Effect.Appear(this.outerDiv, {
-                queue: this.queue,
-                afterFinish: function () {
-                    this.animation = null;
-
-                    if (this.field != Tapestry.currentFocusField)
-                        this.fadeOut();
-                }.bind(this)
-            });
-        },
-
-        /** Used in IE to hide the field if not the focus field. */
-        hideIfNotFocused: function () {
-
-            if (this.outerDiv != null && this.field != Tapestry.currentFocusField) {
-                this.outerDiv.hide();
-            }
-        },
-
-
-        stopAnimation: function () {
-            if (this.animation)
-                this.animation.cancel();
-
-            this.animation = null;
-        },
-
-        fadeOut: function () {
-            if (this.animation || this.outerDiv == null)
-                return;
-
-            if (Prototype.Browser.IE) {
-
-                var div = this.outerDiv;
-
-                _.delay(function () {
-                    div.hide();
-                }, this.IE_FADE_TIME);
-
-                return;
-            }
-
-            this.animation = new Effect.Fade(this.outerDiv, {
-                queue: this.queue,
-                afterFinish: function () {
-                    this.animation = null;
-                }.bind(this)
-            });
-        },
-
-        hide: function () {
-            this.hasMessage = false;
-
-            this.stopAnimation();
-
-            this.outerDiv && this.outerDiv.hide();
-        }
-    });
-
     Tapestry.FieldEventManager = Class.create({
 
         initialize: function (field) {
@@ -1161,7 +993,7 @@ define("core/compat/tapestry", [
 
             var _this = this;
 
-            $(this.field).observe(Tapestry.FORM_VALIDATE_FIELDS_EVENT,
+            $(this.field).observe(events.field.validate,
                     function (event) {
 
                         _this.validateInput();
@@ -1187,22 +1019,16 @@ define("core/compat/tapestry", [
         },
 
         /**
-         * Removes validation decorations if present. Hides the ErrorPopup, if it
-         * exists.
+         * Removes validation messages, etc.
          */
         removeDecorations: function () {
-            this.field.removeClassName("t-error");
-
-            this.getLabel() && this.getLabel().removeClassName("t-error");
 
-            if (this.errorPopup)
-                this.errorPopup.hide();
+            this.field.removeClassName("t-error");
+            this.field.fire(events.field.clearValidationError);
         },
 
         /**
-         * Show a validation error message, which will add decorations to the field
-         * and it label, make the icon visible, and raise the field's
-         * Tapestry.ErrorPopup to show the message.
+         * Show a validation error message.
          *
          * @param message
          *            validation message to display
@@ -1211,12 +1037,7 @@ define("core/compat/tapestry", [
 
             this.field.addClassName("t-error");
 
-            this.getLabel() && this.getLabel().addClassName("t-error");
-
-            if (this.errorPopup == undefined)
-                this.errorPopup = new Tapestry.ErrorPopup(this.field);
-
-            this.errorPopup.showMessage(message);
+            this.field.fire(events.field.showValidationError, { message: message });
         },
 
         inError: function () {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/test/app1/BeanEditorDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/BeanEditorDemo.tml b/tapestry-core/src/test/app1/BeanEditorDemo.tml
index 7bc1a28..327b5cb 100644
--- a/tapestry-core/src/test/app1/BeanEditorDemo.tml
+++ b/tapestry-core/src/test/app1/BeanEditorDemo.tml
@@ -1,39 +1,42 @@
 <t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-  <h1>${pageTitle}</h1>
-
-  <p id="message">
-    <strong>${message}</strong>
-  </p>
-
-  <form t:id="registrationData" submitlabel="Register" cancel="true">
-
-    <t:parameter name="firstName">
-      <t:label for="firstName"/>
-      <input t:type="TextField" t:id="firstName" value="registrationData.firstName" size="40"/>
-      (First Name is Required)
-    </t:parameter>
-
-  </form>
-
-  <p>
-    [
-    <a t:type="ActionLink" t:id="clear">Clear Data</a>
-    ]
-  </p>
-
-  <h2>Second Form</h2>
-
-  <p>
-    Used to test tabbing
-    <em>between</em>
-    forms.
-  </p>
-
-  <p>
-    <t:form t:id="search">
-      <t:label for="searchTerm"/>
-      <t:textfield t:id="searchTerm"/>
-      <input type="submit"/>
-    </t:form>
-  </p>
+    <h1>${pageTitle}</h1>
+
+    <p id="message">
+        <strong>${message}</strong>
+    </p>
+
+    <form t:id="registrationData" submitlabel="Register" cancel="true">
+
+        <t:parameter name="firstName">
+            <t:label for="firstName"/>
+            <div class="controls">
+                <div class="input-append">
+                    <input t:type="TextField" t:id="firstName" value="registrationData.firstName" size="40"/>
+                    <span class="add-on">FN</span>
+                </div>
+                <p class="help-block">First name is required.</p>
+            </div>
+        </t:parameter>
+
+    </form>
+
+    <p>
+        <a t:type="ActionLink" class="btn btn-warning" t:id="clear">Clear Data</a>
+    </p>
+
+    <h2>Second Form</h2>
+
+    <p>
+        Used to test tabbing
+        <em>between</em>
+        forms.
+    </p>
+
+    <p>
+        <t:form t:id="search">
+            <t:label for="searchTerm"/>
+            <t:textfield t:id="searchTerm"/>
+            <input type="submit"/>
+        </t:form>
+    </p>
 </t:border>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-spi.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-spi.coffee b/tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-spi.coffee
index 234ed3f..3c04fce 100644
--- a/tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-spi.coffee
+++ b/tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-spi.coffee
@@ -15,7 +15,7 @@ require ["core/spi"], (spi) ->
 
     clicks = 0
     container = spi "spi-eventelement"
-    button = container.find "a"
+    button = container.findFirst "a"
 
     # Remember that Prototype will never trigger a native event, just a
     # custom event, so we create a custom event here.
@@ -46,7 +46,7 @@ require ["core/spi"], (spi) ->
 
     clicks = 0
     container = spi "spi-eventelement"
-    button = container.find "a"
+    button = container.findFirst "a"
 
     eh = container.on "click", "a", (event) ->
       event.stop()
@@ -62,8 +62,8 @@ require ["core/spi"], (spi) ->
 
     clicks = 0
     container = spi "spi-eventelement"
-    primary = container.find "a.btn-primary"
-    secondary = container.find "a[data-use=secondary]"
+    primary = container.findFirst "a.btn-primary"
+    secondary = container.findFirst "a[data-use=secondary]"
 
     eh = container.on "x:click", "a.btn-primary", (event) ->
       event.stop()
@@ -82,7 +82,7 @@ require ["core/spi"], (spi) ->
   test "this is matched element in handler", ->
 
     container = spi "spi-eventelement"
-    primary = container.find "a.btn-primary"
+    primary = container.findFirst "a.btn-primary"
 
     eh = container.on "x:click", "a.btn-primary", (event) ->
       event.stop()
@@ -95,7 +95,7 @@ require ["core/spi"], (spi) ->
 
   test "visibility, hide(), and show()", ->
 
-    e = (spi "spi-visibility").find "span"
+    e = (spi "spi-visibility").findFirst "span"
 
     equal e.visible(), true, "element is initially visible"
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/027d19d1/tapestry-core/src/test/java/org/apache/tapestry5/internal/DefaultValidationDecoratorTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/DefaultValidationDecoratorTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/DefaultValidationDecoratorTest.java
deleted file mode 100644
index c3cf7cf..0000000
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/DefaultValidationDecoratorTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2007, 2009 The Apache Software Foundation
-//
-// 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.
-
-package org.apache.tapestry5.internal;
-
-import org.apache.tapestry5.Field;
-import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.ValidationDecorator;
-import org.apache.tapestry5.ValidationTracker;
-import org.apache.tapestry5.dom.Element;
-import org.apache.tapestry5.dom.XMLMarkupModel;
-import org.apache.tapestry5.internal.services.MarkupWriterImpl;
-import org.apache.tapestry5.services.Environment;
-import org.apache.tapestry5.test.TapestryTestCase;
-import org.testng.annotations.Test;
-
-public class DefaultValidationDecoratorTest extends TapestryTestCase
-{
-    @Test
-    public void label_has_no_field()
-    {
-        Environment env = mockEnvironment();
-
-        replay();
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, null);
-
-        decorator.insideLabel(null, null);
-
-        verify();
-    }
-
-    @Test
-    public void label_error_no_existing_class_attribute()
-    {
-        MarkupWriter writer = new MarkupWriterImpl(new XMLMarkupModel());
-        Environment env = mockEnvironment();
-        Field field = mockField();
-        ValidationTracker tracker = mockValidationTracker();
-
-        train_peekRequired(env, ValidationTracker.class, tracker);
-        train_inError(tracker, field, true);
-
-        replay();
-
-        Element e = writer.element("label", "accesskey", "f");
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, null);
-
-        decorator.insideLabel(field, e);
-
-        assertEquals(writer.toString(),
-                     "<?xml version=\"1.0\"?>\n" +
-                             "<label class=\"t-error\" accesskey=\"f\"/>");
-
-        verify();
-    }
-
-    @Test
-    public void label_error_with_existing_class_attribute()
-    {
-        MarkupWriter writer = new MarkupWriterImpl(new XMLMarkupModel());
-        Environment env = mockEnvironment();
-        Field field = mockField();
-        ValidationTracker tracker = mockValidationTracker();
-
-        train_peekRequired(env, ValidationTracker.class, tracker);
-        train_inError(tracker, field, true);
-
-        replay();
-
-        Element e = writer.element("label", "accesskey", "f", "class", "foo");
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, null);
-
-        decorator.insideLabel(field, e);
-
-        assertEquals(writer.toString(), "<?xml version=\"1.0\"?>\n" +
-                "<label class=\"foo t-error\" accesskey=\"f\"/>");
-
-        verify();
-    }
-
-    @Test
-    public void field_error()
-    {
-        MarkupWriter writer = new MarkupWriterImpl(new XMLMarkupModel());
-        Environment env = mockEnvironment();
-        Field field = mockField();
-        ValidationTracker tracker = mockValidationTracker();
-
-        train_peekRequired(env, ValidationTracker.class, tracker);
-        train_inError(tracker, field, true);
-
-        replay();
-
-        writer.element("input", "type", "text", "name", "ex", "class", "foo", "value", "freddy", "size", "30");
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, writer);
-
-        decorator.insideField(field);
-
-        assertEquals(writer.toString(), "<?xml version=\"1.0\"?>\n" +
-                "<input size=\"30\" value=\"freddy\" class=\"foo t-error\" name=\"ex\" type=\"text\"/>");
-
-        verify();
-    }
-
-    @Test
-    public void field_ok()
-    {
-        Environment env = mockEnvironment();
-        Field field = mockField();
-        ValidationTracker tracker = mockValidationTracker();
-
-        train_peekRequired(env, ValidationTracker.class, tracker);
-        train_inError(tracker, field, false);
-
-        replay();
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, null);
-
-        decorator.insideField(field);
-
-        verify();
-    }
-
-    @Test
-    public void label_when_field_not_in_error()
-    {
-        Environment env = mockEnvironment();
-        Field field = mockField();
-        ValidationTracker tracker = mockValidationTracker();
-
-        train_peekRequired(env, ValidationTracker.class, tracker);
-        train_inError(tracker, field, false);
-
-        replay();
-
-        ValidationDecorator decorator = new DefaultValidationDecorator(env, null);
-
-        decorator.insideLabel(field, null);
-
-        verify();
-    }
-}