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/11/02 00:35:21 UTC

[1/2] git commit: Re-implement the client-side "required" validator

Updated Branches:
  refs/heads/5.4-js-rewrite e067e5519 -> b70bcee47


Re-implement the client-side "required" validator


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

Branch: refs/heads/5.4-js-rewrite
Commit: b70bcee47f1bf3beeae58b0c9e17352768fad29e
Parents: e992792
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Thu Nov 1 16:35:09 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Thu Nov 1 16:35:09 2012 -0700

----------------------------------------------------------------------
 .../META-INF/modules/core/events.coffee            |   50 ++++++++++++---
 .../META-INF/modules/core/fields.coffee            |   41 +++++++++++-
 .../META-INF/modules/core/forms.coffee             |    9 ++-
 .../META-INF/modules/core/utils.coffee             |   20 ++++++
 .../META-INF/modules/core/validation.coffee        |   30 +++++++++
 .../translator/NumericTranslatorSupportImpl.java   |   34 +++++-----
 .../org/apache/tapestry5/services/FormSupport.java |   31 ++++++---
 .../apache/tapestry5/services/TapestryModule.java  |    2 +-
 .../tapestry5/validator/AbstractValidator.java     |    8 ++-
 .../java/org/apache/tapestry5/validator/Email.java |    4 +-
 .../java/org/apache/tapestry5/validator/Max.java   |    4 +-
 .../org/apache/tapestry5/validator/MaxLength.java  |    4 +-
 .../java/org/apache/tapestry5/validator/Min.java   |    4 +-
 .../org/apache/tapestry5/validator/MinLength.java  |    4 +-
 .../java/org/apache/tapestry5/validator/None.java  |    4 +-
 .../org/apache/tapestry5/validator/Regexp.java     |    4 +-
 .../org/apache/tapestry5/validator/Required.java   |   18 ++++-
 .../apache/tapestry5/validator/RequiredTest.java   |   16 +++---
 18 files changed, 215 insertions(+), 72 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/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 2a4b045..89b5290 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
@@ -27,18 +27,49 @@ define
 
     # Triggered after `validate` (when there are no prior validation exceptions), to allow certain elements
     # to configure themselves immediately before the form is submitted. This exists primarily for components such
-    # as FormFragment, which will update a enable or disable a hidden field to match the visibility of the fragment.
-    # The `core/spi.EventWrapper` for the form element is passed as the memo.
+    # as FormFragment, which will enable or disable a hidden field to match the visibility of the fragment.
+    # There is no event memo.
     prepareForSubmit: "t5:form:prepare-for-submit"
 
+  # Events releated to form input fields. Primarily, these events are related to form input validation.
+  # Validating a field involves three major steps:
+  #
+  # * optional - check for a required field that has no value
+  # * translate - translate a string to another representation, such as `Date`, or a number
+  # * validate - validate the field against any number of other constraints (such as ranges)
+  #
+  # The latter two steps occur only if the field's value is non-blank. A field that is blank but not
+  # required is considered valid. In each step, if the event listener detects an input validation error,
+  # it is expected to set the memo's `error`property to true _and_ trigger a `showValidationError'
+  # event (to present a message specific to the case).
   field:
-    # Triggered by the Form on all enclosed elements with the `data-validation` attribute (indicating they are
-    # interested in participating with user input validation). The memo object passed to the event has an error property
-    # that can be set to true to indicate a validation error. Individual fields should determine if the field is in
-    # error and remove or add/update decorations for the validation error (decorations will transition from 5.3 style
-    # popups to Twitter Bootstrap in the near future).
+
+    # Perform the optionality check. The event memo includes a `value` property. If the field is required
+    # but the value is blank, then the `error` property should be set to true.
+    optional: "t5:field:optional"
+
+    # Trigged by the field if there is a field value. The event memo includes the value as the `value` property.
+    # An event handler may update the event, setting the `translated` property to an alternate formatting, or
+    # alternate representation (e.g., `Date`, or a number) for the input value. If the input can not be translated,
+    # then the handler should set the memo's `error` property to true, and trigger a `showValidationError` event.
+    translate: "t5:field:translate"
+
+    # Triggered by the field if there is a field value, and the `translate` event succeeded. The event memo
+    # includes a `value' property, and a `translated` property. If any constraints on the field are invalid,
+    # then the event handler should set the memo's `error` property and trigger a `showValidationError` event.
     validate: "t5:field:validate"
 
+    # Triggered by the form on all enclosed elements with the `data-validation` attribute (indicating they are
+    # interested in participating with user input validation). The default implementation fires a series of
+    # events: `optional`, `translate`, `validate`. The latter two are always skipped if the input is blank, or if
+    # a preceding event set the memo's `error` property to true.  If all events complete without setting an error,
+    # then the `clearValidationError` event is triggered, so remove any validation errors from previous
+    # validation cycles.
+    #
+    # This event is passed a memo object; it should set the memo's `error` property to true if validation failed
+    # for the field.
+    inputValidation: "t5:field:input-validation"
+
     # 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.
@@ -50,13 +81,16 @@ define
     # 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)
+    # * If found, set the `data-error-block-for` attribute to the field's `id` (assigning a unique id to the field
+    #   if not already present)
     #
     # 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

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/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
index d1cb3d2..141b6de 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/fields.coffee
@@ -16,9 +16,8 @@
 #
 # 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) ->
+define ["_", "core/events", "core/spi", "core/builder", "core/utils", "core/forms"],
+  (_, events, spi, builder, utils) ->
 
     ensureFieldId = (field) ->
       fieldId = field.attribute "id"
@@ -93,6 +92,42 @@ define ["_", "core/events", "core/spi", "core/builder"],
 
     # Default registrations:
 
+    spi.onDocument events.field.inputValidation, (event, formMemo) ->
+
+      # When not visible to the user, ignore the input validation. Components
+      # are generally configured so that they do not submit a value to the server
+      # when not visible ... this is what the core/FormFragment component is responsible
+      # for.
+      return unless this.deepVisible()
+
+      failure = false
+
+      memo = value: this.value()
+
+      this.trigger events.field.optional, memo
+
+      if memo.error
+        failure = true
+      else
+        unless utils.isBlank memo.value
+          this.trigger events.field.translate, memo
+
+          if memo.error
+            failure = true
+          else
+            memo.translated |= memo.value
+
+            this.trigger events.field.validate, memo
+
+            failure |= memo.error
+
+      if failure
+        formMemo.error = true
+      else
+        this.trigger events.field.clearValidationError
+
+      return
+
     spi.onDocument events.field.clearValidationError, ->
       block = exports.findHelpBlock this
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/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 e5e295a..e54cad4 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
@@ -104,22 +104,23 @@ define ["core/events", "core/spi", "core/builder", "_"],
 
         # This will become more relevant shortly, when field validation is modernized:
         for field in this.find "[data-validation]"
-           field.trigger events.field.validate, memo
+          field.trigger events.field.inputValidation, memo
 
         # Only do form validation if all individual field validation
         # was successful.
-        this.trigger events.form.validate, memo unless memo.error
+        unless memo.error
+          this.trigger events.form.validate, memo
 
         if memo.error
           clearSubmittingHidden this
+          # Cancel the original submit event when there's an error
           return false
 
       # Allow certain types of elements to do last-moment set up. Basically, this is for
       # FormFragment, or similar, to make their hidden field enabled or disabled to match
       # their UI's visible/hidden status. This is assumed to work or throw an exception; there
       # is no memo.
-      this.trigger events.form.prepareForSubmit, this
-
+      this.trigger events.form.prepareForSubmit
 
       # Otherwise, the event is good, there are no validation problems, let the normal processing commence.
       # Possibly, the document event handler in core/zone will intercept form submission if this

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
new file mode 100644
index 0000000..d4e9905
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
@@ -0,0 +1,20 @@
+# 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/utils
+#
+# A few handy functions.
+define [], ->
+
+  isBlank: (input) -> input is null or input.trim().length == 0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
new file mode 100644
index 0000000..3ff32a5
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
@@ -0,0 +1,30 @@
+# 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/translator
+#
+# Support for Tapestry's built-in set of translators and validators.
+#
+define ["core/spi", "core/events", "core/utils", "core/fields"],
+  (spi, events, utils) ->
+
+    spi.onDocument events.field.optional, "[data-optionality=required]", (event, memo) ->
+
+      if utils.isBlank memo.value
+        message = (this.attribute "data-required-message") || "REQUIRED"
+        this.trigger events.field.showValidationError, { message }
+        memo.error = true
+        return false
+
+    configureDecimals: ->
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java
index a704023..a8eb771 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010 The Apache Software Foundation
+// Copyright 2009, 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.
@@ -14,16 +14,6 @@
 
 package org.apache.tapestry5.internal.translator;
 
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.text.ParseException;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.tapestry5.Field;
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.ioc.annotations.Symbol;
@@ -33,9 +23,18 @@ import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.services.ClientBehaviorSupport;
 import org.apache.tapestry5.services.Request;
-import org.apache.tapestry5.services.javascript.InitializationPriority;
 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
 public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
 {
     private final TypeCoercer typeCoercer;
@@ -57,9 +56,9 @@ public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
     private static final String DECIMAL_FORMAT_SYMBOLS_PROVIDED = "tapestry.decimal-format-symbols-provided";
 
     public NumericTranslatorSupportImpl(TypeCoercer typeCoercer, ThreadLocale threadLocale, Request request,
-            JavaScriptSupport javascriptSupport, ClientBehaviorSupport clientBehaviorSupport, 
-            @Symbol(SymbolConstants.COMPACT_JSON)
-            boolean compactJSON)
+                                        JavaScriptSupport javascriptSupport, ClientBehaviorSupport clientBehaviorSupport,
+                                        @Symbol(SymbolConstants.COMPACT_JSON)
+                                        boolean compactJSON)
     {
         this.typeCoercer = typeCoercer;
         this.threadLocale = threadLocale;
@@ -69,7 +68,7 @@ public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
         this.compactJSON = compactJSON;
 
         Class[] integerTypes =
-        { Byte.class, Short.class, Integer.class, Long.class, BigInteger.class };
+                {Byte.class, Short.class, Integer.class, Long.class, BigInteger.class};
 
         for (Class c : integerTypes)
             this.integerTypes.add(c);
@@ -80,8 +79,7 @@ public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
     {
         if (request.getAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED) == null)
         {
-            javascriptSupport.addScript(InitializationPriority.IMMEDIATE, "Tapestry.decimalFormatSymbols = %s;",
-                    createJSONDecimalFormatSymbols().toString(compactJSON));
+            javascriptSupport.require("core/validation").invoke("configureDecimals").with(createJSONDecimalFormatSymbols());
 
             request.setAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED, true);
         }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/services/FormSupport.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/FormSupport.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/FormSupport.java
index 4e8e9f6..ccf029b 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/FormSupport.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/FormSupport.java
@@ -29,7 +29,8 @@ public interface FormSupport extends ClientElement
     /**
      * Allocates a unique (within the form) control name for some enclosed component, based on the component's id.
      *
-     * @param id the component's id
+     * @param id
+     *         the component's id
      * @return a unique string, usually the component's id, but sometime extended with a unique number or string
      */
     String allocateControlName(String id);
@@ -45,8 +46,10 @@ public interface FormSupport extends ClientElement
      * immediately. This is useful for defining an action that should occur symmetrically in both the render request and
      * the form submission's action request.
      *
-     * @param component component against which to trigger the action
-     * @param action    the action that will be triggered (and passed the component)
+     * @param component
+     *         component against which to trigger the action
+     * @param action
+     *         the action that will be triggered (and passed the component)
      */
     <T> void storeAndExecute(T component, ComponentAction<T> action);
 
@@ -57,15 +60,18 @@ public interface FormSupport extends ClientElement
      * components can not be determined. During a form render, runnables are executed after the body of the form has
      * rendered.
      *
-     * @param command to be executed
+     * @param command
+     *         to be executed
      */
     void defer(Runnable command);
 
     /**
      * Sets the encoding type for the Form. This should only be set once, and if
      *
-     * @param encodingType MIME type indicating type of encoding for the form
-     * @throws IllegalStateException if the encoding type has already been set to a value different than the supplied
+     * @param encodingType
+     *         MIME type indicating type of encoding for the form
+     * @throws IllegalStateException
+     *         if the encoding type has already been set to a value different than the supplied
      */
     void setEncodingType(String encodingType);
 
@@ -73,10 +79,15 @@ public interface FormSupport extends ClientElement
      * Collects field validation information. A Form may turn off client-side validation, in which case these calls will
      * be ignored.
      *
-     * @param field          for which validation is being generated
-     * @param validationName name of validation method (see Tapestry.Validation in tapestry.js)
-     * @param message        the error message to display if the field is invalid
-     * @param constraint     additional constraint value, or null for validations that don't require a constraint
+     * @param field
+     *         for which validation is being generated
+     * @param validationName
+     *         name of validation method (see Tapestry.Validation in tapestry.js)
+     * @param message
+     *         the error message to display if the field is invalid
+     * @param constraint
+     *         additional constraint value, or null for validations that don't require a constraint
+     * @deprecated Deprecated in 5.4 with no exact replacement
      */
     void addValidation(Field field, String validationName, String message, Object constraint);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/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 d3eeb2c..6d5f82c 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
@@ -767,7 +767,7 @@ public final class TapestryModule
      */
     public static void contributeFieldValidatorSource(MappedConfiguration<String, Validator> configuration)
     {
-        configuration.add("required", new Required());
+        configuration.addInstance("required", Required.class);
         configuration.add("minlength", new MinLength());
         configuration.add("maxlength", new MaxLength());
         configuration.add("min", new Min());

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/AbstractValidator.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/AbstractValidator.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/AbstractValidator.java
index 28ae1cd..3c23fd2 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/AbstractValidator.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/AbstractValidator.java
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 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.
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.validator;
 
 import org.apache.tapestry5.Validator;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
 /**
  * Base class for constructing a {@link org.apache.tapestry5.Validator}.
@@ -27,11 +28,14 @@ public abstract class AbstractValidator<C, T> implements Validator<C, T>
 
     private final String messageKey;
 
-    protected AbstractValidator(Class<C> constraintType, Class<T> valueType, String messageKey)
+    protected final JavaScriptSupport javaScriptSupport;
+
+    protected AbstractValidator(Class<C> constraintType, Class<T> valueType, String messageKey, JavaScriptSupport javaScriptSupport)
     {
         this.constraintType = constraintType;
         this.valueType = valueType;
         this.messageKey = messageKey;
+        this.javaScriptSupport = javaScriptSupport;
     }
 
     public final Class<C> getConstraintType()

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/Email.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Email.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Email.java
index 25d9698..c4bcb30 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Email.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Email.java
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 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.
@@ -38,7 +38,7 @@ public class Email extends AbstractValidator<Void, String>
 
     public Email()
     {
-        super(null, String.class, "invalid-email");
+        super(null, String.class, "invalid-email", null);
     }
 
     public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter markupWriter,

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/Max.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Max.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Max.java
index c314c80..a07dac4 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Max.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Max.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -27,7 +27,7 @@ public class Max extends AbstractValidator<Long, Number>
 {
     public Max()
     {
-        super(Long.class, Number.class, "max-integer");
+        super(Long.class, Number.class, "max-integer", null);
     }
 
     public void validate(Field field, Long constraintValue, MessageFormatter formatter, Number value)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/MaxLength.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/MaxLength.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/MaxLength.java
index a44ecf6..44e4eae 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/MaxLength.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/MaxLength.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -24,7 +24,7 @@ public final class MaxLength extends AbstractValidator<Integer, String>
 {
     public MaxLength()
     {
-        super(Integer.class, String.class, "maximum-string-length");
+        super(Integer.class, String.class, "maximum-string-length", null);
     }
 
     public void validate(Field field, Integer constraintValue, MessageFormatter formatter, String value)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/Min.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Min.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Min.java
index 8ee7bb5..16b9b47 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Min.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Min.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -25,7 +25,7 @@ public class Min extends AbstractValidator<Long, Number>
 {
     public Min()
     {
-        super(Long.class, Number.class, "min-integer");
+        super(Long.class, Number.class, "min-integer", null);
     }
 
     public void validate(Field field, Long constraintValue, MessageFormatter formatter, Number value)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/MinLength.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/MinLength.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/MinLength.java
index c18a7cd..8ae6c3c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/MinLength.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/MinLength.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -27,7 +27,7 @@ public final class MinLength extends AbstractValidator<Integer, String>
 {
     public MinLength()
     {
-        super(Integer.class, String.class, "minimum-string-length");
+        super(Integer.class, String.class, "minimum-string-length", null);
     }
 
     public void validate(Field field, Integer constraintValue, MessageFormatter formatter, String value)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/None.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/None.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/None.java
index d7f7c75..49644a3 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/None.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/None.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 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.
@@ -24,7 +24,7 @@ public class None extends AbstractValidator<Void, Object>
 {
     public None()
     {
-        super(null, Object.class, "required");
+        super(null, Object.class, "required", null);
     }
 
     /** Does nothing. */

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/Regexp.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Regexp.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Regexp.java
index 495ad43..3d28d69 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Regexp.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Regexp.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -27,7 +27,7 @@ public class Regexp extends AbstractValidator<Pattern, String>
 {
     public Regexp()
     {
-        super(Pattern.class, String.class, "regexp");
+        super(Pattern.class, String.class, "regexp", null);
     }
 
     private String buildMessage(MessageFormatter formatter, Field field, Pattern constraintValue)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
index d21e5f6..bf1cd34 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 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.
@@ -20,15 +20,16 @@ import org.apache.tapestry5.ValidationException;
 import org.apache.tapestry5.ioc.MessageFormatter;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.services.FormSupport;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
 /**
  * A validator that enforces that the value is not null and not the empty string. This validator is not configurable.
  */
 public final class Required extends AbstractValidator<Void, Object>
 {
-    public Required()
+    public Required(JavaScriptSupport javaScriptSupport)
     {
-        super(null, Object.class, "required");
+        super(null, Object.class, "required", javaScriptSupport);
     }
 
     public void validate(Field field, Void constraintValue, MessageFormatter formatter, Object value)
@@ -54,6 +55,15 @@ public final class Required extends AbstractValidator<Void, Object>
     public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter writer,
                        FormSupport formSupport)
     {
-        formSupport.addValidation(field, "required", buildMessage(formatter, field), null);
+
+        if (formSupport.isClientValidationEnabled())
+        {
+            javaScriptSupport.require("core/validation");
+
+            writer.getElement().attributes(
+                    "data-validation", "true",
+                    "data-optionality", "required",
+                    "data-required-message", buildMessage(formatter, field));
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/b70bcee4/tapestry-core/src/test/java/org/apache/tapestry5/validator/RequiredTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/validator/RequiredTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/validator/RequiredTest.java
index ef337e5..7794a97 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/validator/RequiredTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/validator/RequiredTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 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,14 +14,14 @@
 
 package org.apache.tapestry5.validator;
 
-import java.util.Arrays;
-
 import org.apache.tapestry5.Field;
 import org.apache.tapestry5.ValidationException;
 import org.apache.tapestry5.ioc.MessageFormatter;
 import org.apache.tapestry5.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
+import java.util.Arrays;
+
 public class RequiredTest extends TapestryTestCase
 {
     @Test
@@ -36,7 +36,7 @@ public class RequiredTest extends TapestryTestCase
 
         try
         {
-            new Required().validate(field, null, formatter, null);
+            new Required(null).validate(field, null, formatter, null);
             unreachable();
         }
         catch (ValidationException ex)
@@ -59,7 +59,7 @@ public class RequiredTest extends TapestryTestCase
 
         try
         {
-            new Required().validate(field, null, formatter, "");
+            new Required(null).validate(field, null, formatter, "");
             unreachable();
         }
         catch (ValidationException ex)
@@ -82,7 +82,7 @@ public class RequiredTest extends TapestryTestCase
 
         try
         {
-            new Required().validate(field, null, formatter, Arrays.asList());
+            new Required(null).validate(field, null, formatter, Arrays.asList());
             unreachable();
         }
         catch (ValidationException ex)
@@ -101,7 +101,7 @@ public class RequiredTest extends TapestryTestCase
 
         replay();
 
-        new Required().validate(field, null, formatter, Arrays.asList("A", "B"));
+        new Required(null).validate(field, null, formatter, Arrays.asList("A", "B"));
 
         verify();
     }
@@ -114,7 +114,7 @@ public class RequiredTest extends TapestryTestCase
 
         replay();
 
-        new Required().validate(field, null, formatter, "not null");
+        new Required(null).validate(field, null, formatter, "not null");
 
         verify();
     }