Date: Thu, 28 Mar 2024 09:57:37 +0000 (UTC) Message-ID: <673254886.31.1711619857522@23d7b0ef6778> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_30_754672510.1711619857521" ------=_Part_30_754672510.1711619857521 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
This document describes how valiati= on works in Stripes, how you specify validations, and how ActionBeans inter= act with validation. It includes the following sections:
Validation is conceptually broken into three areas in Stripes:
Annotation Driven Validation is named thusly because the way the validat= ion rules are specified is through annotations in the ActionBean. These Val= idations apply to single fields in the form/ActionBean, and encapsulate com= mon and often used validations.
Type Conversion is the process of converting a single form field input f= rom the String value in the HttpServletRequest into a Java object (of any C= lass, including String). Type conversion is a core part of the validation p= rocess, and is easy to extend to new or custom types.
Custom Validation is the name given to the validation performed by arbit= rary Java code in one or more validation methods in an ActionBean. An Actio= nBean can define zero, one or many validation methods and apply them in dif= ferent scenarios.
Given the different kinds of validation on offer, one might ask how they= are applied, and what happens when errors are produced at any stage. The f= ollowing is the general flow:
This strategy is designed to catch the largest possible number of valida= tion errors possible in a single pass. Thus if the user forgets to enter on= e value, and mis-types another, they will be presented with both errors the= first time they submit the form - but they will not be presented with any = additional (e.g. minimum length violation) errors for the field that was re= quired but not supplied. Lastly it should be noted that the reason custom v= alidation is run only when other validations succeed is so that it can rely= on the ActionBean being in a well defined. This is, however, configurable = (see the Configuration Reference).
As we will see in the sections Selectively Disabling Validation and Changing Validation Outcom= es this is not the whole story, but it's good enough for now.
Before we go much further, a quick tour of the different validation erro= r classes is in order. We'll be seeing a lot about validation errors in the= next few sections, so it is worthwhile to understand the different types a= vailable, and how they are used.
ValidationError is an interface that specifies the method= s that all validation error types must support. This makes it easy to have = multiple different error classes that offer different functionality. It als= o means that you can write your own error classes easily if you are not sat= isfied with the ones that Stripes supplies (hopefully that won't be the cas= e).
SimpleError is, as it says, a simple implementation or =
ValidationError
. It allows a hard-coded message (or one supplied at =
construction time) to be used, and provides the ability to insert the name =
and value of the field (and other arbitrary bits of information) into the m=
essage.
LocalizableError extends SimpleError
with t=
he capability to look up the error message in an external resource bundle. =
The name of the bundle can be configured - to do this take a look at the Con=
figuration Reference. Looks twice, once with the key prefixed with the =
action path of the ActionBean being used, and once with the exact key provi=
ded.
ScopedLocalizableError extends LocalizableEr=
ror
and provides the ability to perform "scoped searches" for an err=
or message. In reality this just means looking for error messages in the re=
source bundle with names corresponding to:
<actionPath>.<fieldName>.<errorName>
<actionPath>.<fieldName>.errorMessage
<fieldName>.<errorName>
<fieldName>.errorMessage
<actionPath>.<errorName>
<defaultScope>.<errorName>
In the descriptions below you will see reference to "default scope" and =
"error name", which refer to how a ScopedLocalizableError
is f=
ound. All the built in validations in Stripes use ScopedLocalizableEr=
ror
, so that you can specify fairly generic messages at a high level=
and override them as you see fit.
All of the error messages in Stripes use java.text.MessageFormat which allows you to provide par=
ameters to be merged in to the message. The first two parameters (0 and 1 r=
espectively) are always the field name and field value respectively. An exa=
mple follows, but check the individual error class javadocs, and the javado=
c for MessageFormat
for more details:
myCustomError= =3D{0} must be formatted mm/dd/yy. {1} is not a valid date.=20
There are two annotations for adding validations to fields. The @V=
alidate
annotation is used to specify validations for scalar or simp=
le properties within an ActionBean. The @ValidateNestedProperties annotation is used to aggregate one or more
@Validate
anno=
tations in order to specify validations for nested properties of complex ty=
pes. The annotations can be attached to either the getter or setter methods=
on the ActionBean. They can also be attached directly to the properties (e=
ven private ones).
Each @Validate
can specify multiple validations for a singl=
e property. The following example demonstrates how to use the annotations:<=
/p>
@Validate(req= uired=3Dtrue, minvalue=3D13, maxvalue=3D120)=20 private int age; // annotated right on the field=20 @ValidateNestedProperties({=20 =09@Validate(field=3D"line1", required=3Dtrue, minlength=3D5, maxlength=3D5= 0),=20 =09@Validate(field=3D"line2", minlength=3D5, maxlength=3D50),=20 =09@Validate(field=3D"zip", required=3Dtrue, mask=3D"\\d{5}(-\\d{4})?") })=20 public Address getAddress() { ... }=20
The following sections walk through the syntax of each validation that c=
an be specified with the @Validate
annotation.
The field property is used only when the @Validate
annotati=
on is nested within a @ValidateNestedProperties
annotation. It=
is used to specify the property of the complex type to which the validatio=
n applies.
Specifies whether or not the annotated field is required when the form i=
s submitted. Defaults to false (so it's never necessary to write required=
=3Dfalse even though you can). If a required field is not supplied a single=
error is generated. The "default scope" is specified as validation.r=
equired
and the error name is valueNotPresent
. This res=
ults in an attempt to find error messages in the resource bundle with the f=
ollowing keys:
The 'on' attribute of the @Validate
annotation does not per=
fom any validation itself, but restricts the application of the requi=
red
check. To reiterate: it modifies when the required
=
check is applied - not other validations specified at the same time.
By default required field validation is applied to all events not marked=
with @DontValidate
. However, by specifying one or more event =
names in the 'on' attribute it can be restricted to just those events. For =
example:
@Validate(req= uired=3Dtrue, on=3D{"save", "update"}) private String description;=20 @Validate(required=3Dtrue, on=3D"delete") private boolean confirm;
The list can also be inverted by supplying event names prefixed with '!'= s, for example:
@Validate(req= uired=3Dtrue, on=3D"!delete") private String description;=20 @Validate(required=3Dtrue, on=3D"delete") private boolean confirm;
Note: you cannot mix event names with and without '!'s = for the same property, e.g. {"!delete", "save"}.
Specifies the minimum length that the pre-conversion String must be. For=
example you might specify that a password field must have a minimum length=
of 6. Doing so would require the password to be six characters in length o=
r greater. If a field fails the minimum length test a single validation err=
or is generated. The default scope is validation.minlength
and=
the error name is valueTooShort
. This results in an attempt t=
o find error messages in the resource bundle with the following keys:
The error message is supplied with an additional parameter (which can be= used as {2} in the message text) which is the minimum length constraint th= at was violated - e.g. 6 in the example above.
Specifies the maximum length that the pre-conversion String may be. For =
example you might specify that a username field may have a maximum length o=
f 25. Doing so would require the username to be twenty-five characters in l=
ength or less. If a field fails the maximum length test a single validation=
error is generated. The default scope is validation.maxlength
=
and the error name is valueTooLong
. This results in an attemp=
t to find error messages in the resource bundle with the following keys:
The error message is supplied with an additional parameter (which can be= used as {2} in the message text) which is the maximum length constraint th= at was violated - e.g. 25 in the example above.
Specifies the minimum value for a numeric type (after it has been conver=
ted to a number). For example you might specify an age field has a minvalue=
of 0 to enforce non-negative ages! If a field fails the minimum value test=
a single validation error is generated. The default scope is validat=
ion.minvalue
and the error name is valueBelowMinimum
. T=
his results in an attempt to find error messages in the resource bundle wit=
h the following keys:
The error message is supplied with an additional parameter (which can be= used as {2} in the message text) which is the minimum value constraint tha= t was violated - e.g. 0 in the example above.
Specifies the maximum value for a numeric type (after it has been conver=
ted to a number). If a field fails the maximum value test a single validati=
on error is generated. The default scope is validation.maxvalue
and the error name is valueAboveMaximum
. This results in an =
attempt to find error messages in the resource bundle with the following ke=
ys:
The error message is supplied with an additional parameter (which can be= used as {2} in the message text) which is the maximum value constraint tha= t was violated.
Specifies a regular expression that the user's input must match. The mas=
k is compiled using java.uti=
l.regex.Pattern - if you are not familiar with regular expressions, the=
documentation for the Pattern class provides a good introduction to the sy=
ntax. The entire String submitted must match the mask/pattern supplied, as =
specified by Pattern.matcher(input).matches()
.
If the input does not match the mask, a single validation error is outpu=
t with the default scope validation.mask
and the error name
Specifies an expression using the JSP Expression Language (EL) to be eva= luated. Expressions must return a boolean value. Expression validation is r= un after all form properties (that have passed validation so far) have been= bound into the ActionBean. As a result expressions have access to all the = values in the ActionBean!
The value being validated is also available (of course) under the name '= this'. Other named values in the expression are resolved first by looking f= or an ActionBean property of the same name, and if one is not found, then b= y then a value is looked for in the request scope, and then the session sco= pe. All the usual EL builtins are available, so it is also possible to acce= ss the request parameters etc.
Care should be taken because, while expression validation will not be ex= ecuted on null or empty values, other values referenced by the expression c= ould still be null. Given the EL's penchant for safely handling nulls, and = converting types as needed, this may lead to unexpected outcomes!
Some examples are in order:
// Validate t= hat a number is odd=20 @Validate(expression=3D"self % 2 =3D=3D 1") private int oddNumber;=20 // Validate that this number is bigger than the odd number=20 @Validate(expression=3D"oddNumber !=3D null && self > oddNumber"= ) private long biggerNumber;=20 // Validate that a username is the current user's, or the current user is '= root'=20 @Validate(expression=3D"context.user.name=3D=3D'root' || self=3D=3Dcontext.= user.name") private String name;=20
If the expression does not evaluate to true an error is created with the=
default scope validation.expression
and the error name =
valueFailedExpression
. This results in an attempt to find error mess=
ages in the resource bundle with the following keys:
NOTE: The "self" keyword is a substitute for the= keyword "this" which indicates the field itself. The keyword "this" = is also supported by Stripes, but some containers may not permit the usage = of "this" within EL expressions by default (e.g. Tomcat 7). In those = cases, you would have to check your container's documentation to see if it = offers a configuration option to skip identifier checking. Because of= this issue, "self" is the preferred method of referencing the field being = validated rather than "this".
Specifies the TypeConverter that should be used to convert = this field. This is not usually necessary as in most cases Stripes will use= the right converter for the target type. Two common cases where this is us= eful are:
Since this is not strictly a validation, but a way of modifying the type= conversion, it does not produce any error messages.
Type conversion is quite simply the process of converting the String par= ameters that are supplied in the HttpServletRequest into the types of the c= orresponding ActionBean properties. The TypeConverter interfac= e specifies how Stripes will interact with classes that perform type conver= sion. There are type converters in Stripes for most common types in Java, a= nd adding support for additional types is as easy as coding up a new TypeCo= nverter.
Stripes gets to know about TypeConverters in one of two ways. If an converter
then that c=
onverter will be used, no questions asked. Otherwise Stripes will ask the c=
onfigured TypeConverterFactory to supply the appropriat=
e TypeConverter
for the type being converted to. Extending the=
DefaultTypeConverterFactory is fairly straight-=
forward. See the Configuration Reference for details on how to conf=
igure Stripes to use your custom TypeConverterFactory
implemen=
tation.
The following sections document the individual type converters used in S= tripes.
The BooleanTypeConverter determines that a value is = "true" if it matches (ignoring case) any of the values "true", "t", "yes", = "y" or "on" or if the value can be parsed as a number and does not equal ze= ro.
Please note that Stripes does not convert and bind parameters submitted = in the request with the value "" (empty string). So while you might think t= hat "" would convert to false, in fact conversion is never run, so the valu= e on your ActionBean will be whatever default value is assigned to the prop= erty.
Since any String can be converted to a boolean (either false or true), n= o validation errors are produced.
The DateTypeConverter employs a number of techniques to=
get the input to parse as a java.util.Date
. If the input cann=
ot be parsed into a Date then a single validation error is produced. The de=
fault scope is converter.date
and the error name is inva=
lidDate
.
The EmailTypeConverter, strictly speaking is not a type converter. It uses JavaMail to par= se the String and return a well formatted email address as a String.
A single validation error is produced if the address cannot be parsed. T=
he default scope is converter.email
and the error name is
The EnumeratedTypeConverter converts Strings repr=
esenting the names of enumerated type values, back into an instance of the =
enumerated type. If the value supplied does not match any of the values of =
the target enumerated type, a single validation error is produced. The defa=
ult scope is converter.enum
and the error name is notAnE=
numeratedValue
.
Stripes supplied a set of converters for converting to the Java numeric = types: byte, short, int, long, float, double and their corresponding wrappe= r types as well as BigDecimal and BigInteger. They all operate similarly (u= sing one or more java.text.Nu= mberFormats). The converters make a best effort to parse a String as a = number. They can handle the following without any additional effort:
The number TypeConverters produce two types of error. The first error is=
common to all the number TypeConverters; it is generated when the String i=
s not parsable as a number. The default scope is converter.number and the error name is
invalidNumber
.
The second error is named differently for each number TypeConverter, but=
means the same; it is generated when the supplied number is out of the ran=
ge for the target type. The default scope is converter.[byte|short|in=
teger|float]
and the error name is outOfRange
. For all =
outOfRange errors two additional parameters ({2} and {3}) are supplied to t=
he error message. The parameters are the minimum and maximum allowable valu=
es for the type being converted to, e.g. Integer.MIN_VALUE and Integer.MAX_=
VALUE.
The PercentageTypeConverter converts numbers disp=
layed as percentages (e.g. 95%) into decimal numbers (e.g. 0.95). It requir=
es that the target type is either float
, double
(=
or the wrapper types) or BigDecimal
. It produces the same erro=
r as other number classes, with a default scope of converter.number=
code> and the error name
invalidNumber
.
The OneToManyTypeConverter is a special=
type converter that converts a single value in the request into a List of =
values to be bound to the ActionBean. For example, if a user were to enter =
one or more numbers into a single field, '123, 456 789' that is converted u=
sing the OneToManyTypeConverter
, a List would be bound to the =
ActionBean containing three numbers.
The type of the elements in the List is determined based on the declared=
type in the ActionBean, so if you declare List<Long> you'll get Long=
s, if you declare List<Boolean> you'll get Booleans. Under the covers=
the OneToManyTypeConverter
will request the appropriate type =
converter for the individual items, and hence will generate the same valida=
tion errors as the LongTypeConverter
, BooleanTypeConvert=
er
etc.
Custom validation refers to the execution of arbitrary, custom, validati= on logic within the ActionBean. ActionBean classes may optionally include o= ne or more validation methods. Validation methods are regular methods that = are marked with the annotation @ValidationMethod= . These methods are invoked after all other validations have been run and a= ll values have been converted and bound to the ActionBean.
By default validation methods will not be invoked if preceeding validati= ons resulted in errors. Since the ActionBean will be fully populated when v= alidation methods are called it is safe to refer to any property of the Act= ionBean, or through the ActionBeanContext access values in the HttpServletR= equest (though hopefully this will not be common since it makes the ActionB= ean less testable).
It is possible to configure Stripes to, by default, invoke validation me= thods regardless of whether or not validation errors exist. To do this refe= r to the Configuration Reference. Note that by doing this you are no longer g= uaranteed that all required fields have been supplied or that type conversi= ons and binding succeeded before validation methods are called.
The @ValidationMethod
annotation has three attributes that =
allow the application of the method to be altered, all of which are complet=
ely optional. The first is the 'when' attribute which allows the developer =
to specify whether or not the method should be run when errors exist, regar=
dless of the application default policy. E.g.
@ValidationMe= thod(when=3DValidationState.ALWAYS) public void validateSomething { ... }= =20 @ValidationMethod(when=3DValidationState.NO_ERRORS) public void validateSom= ethingElse { ... }=20
The 'on' attribute (similar to the 'on' attribute of @Validate) restricts the method to run only on certain events. By default validat=
ion methods are run without regard to the event being handled. The 'on' att=
ribute can specify a single event, a list of events, or one or more events =
preceeded by '!'s. In the last case the meaning is inverted, and the method=
is executed on all events except those listed. E.g.
@ValidationMe= thod(on=3D"create") public void one() { ... }=20 @ValidationMethod(on=3D"update") public void two() { ... }=20 @ValidationMethod(on=3D{"update","delete"}) public void three() { ... }=20 @ValidationMethod(on=3D"!delete") public void four() { ... }=20
The 'priority' attribute is useful only when multiple validation methods= exist within an ActionBean and the order in which they are executed is imp= ortant. Methods with lower (nearer to zero) values for the 'priority' attri= bute are run before those with higher values.
Lastly, it should be noted that there are two ways to get access to the =
ValidationErrors
object in order to save validation error mess=
ages. Firstly, as with any other method in the ActionBean, validation metho=
ds can call getContext().getValidationErrors()
to retrieve the=
validation errors. However, they may also take the ValidationErrors object=
s as a parameter, in which case it will be supplied to the method. E.g.
@ValidationMe= thod public void validateSomething(ValidationErrors errors) { ... }=20
Lastly it should be noted that validation methods must be public, must t= ake either zero parameters or one parameter of type ValidationErrors and ma= y return any type, though returns will be ignored.
Sometimes it's just not possible (or reasonable) to validate absolutely = everything up front in validation methods. Maybe you don't have all the dat= a you need, maybe you won't know if something really is valid until you try= and do it! For example, you might not be able to validate that a withdrawa= l from a bank account is valid until you do it due to concurrency issues (w= hat if two threads check a $100 balance then allow $75 withdrawals?).
In these situations you can create validation errors in your handler met= hod, and send the user back to the page they came from (or elsewhere if you= choose). The following is an example:
@HandlesEvent= ("Withdrawal")=20 public Resolution withdrawFunds() {=20 =09try {=20 =09=09getAccount().withdraw( getAmount() );=20 =09=09return new RedirectResolution("/account/summary.jsp");=20 =09}=20 =09catch (InsufficientBalanceException ibe) {=20 =09=09ValidationErrors errors =3D new ValidationErrors();=20 =09=09errors.add( "amount", new LocalizableError("/account/transaction.acti= on.insufficientFunds") );=20 =09=09getContext().setValidationErrors(errors);=20 =09=09return getContext().getSourcePageResolution();=20 =09}=20 }=20
Often is it desirable to have validation run for one or more events in a=
n ActionBean, but not for others. For example the "save" method might requi=
re validation, as might the "add" method. But the "prepopulate" method migh=
t not. While usage of the 'on' attribute of @Validate
and
Stripes uses the @DontValidate
annotation to make this easi=
er. Any handler method that is annotated with @DontValidate
wi=
ll be invoked directly skipping validation. Type conversion and binding are=
still executed, and may produce validation errors. However, it is expected=
that in most (all?) cases the @DontValidate
annotation will b=
e used with events whose only input is not user-input (e.g. hidden fields, =
select lists etc.).
When one or more validation errors are registered during a request, Stri=
pes will execute the Resolution
returned by calling Acti=
onBeanContext.getSourcePageResolution()
. By default this returns a <=
code>ForwardResolution which will forward the request to the same JS=
P page from which it originated. The page is identified by a request parame=
ter called _sourcePage
which is written as a hidden field by t=
he Stripes form tag, and included as a parameter by the Stripes link tag.=
p>
To change this behaviour at the global level it is possible to simply ov=
erride the ActionBeanContext.getSourcePageResolution()
method =
to return a different resolution.
There are times when even the most flexible system cannot give you every=
thing, and you want to manage the validation process at a more detailed lev=
el. You can do this by implementing the ValidationErrorHa=
ndler interface in your ActionBean. The interface defines a single meth=
od handleValidationErrors(ValidationErrors errors)
that is inv=
oked when validation fails, but before determining what to do next.
The ActionBean can manipulate the ValidationErrors
in any w=
ay it sees fit and the changes will be taken into account before proceeding=
. If all errors are cleared out, it will be as if they never existed! The A=
ctionBean can also use this opportunity to undo any side-affects that might=
be caused by the binding process if necessary, or to substitute a differen=
t Resolution. Check out the ValidationErrorHandler ja=
vadoc for more information.
fi=
eld-metadata
tagStripes provides field meta data which can be used for client side valid= ation and layout. Here is an example provided by Aaron Porter, using the jQ= uery JavaScript framework:
(http://article.gmane.org/gmane.com= p.java.stripes.user/7575)
(Required scripts available at http://stripes-stuff.svn.sourceforge.net/vi= ewvc/stripes-stuff/JavaScript/trunk/scripts/jquery/)
<s:field-me= tadata var=3D"fieldMetadata">=20 $(function(){applyStripesValidation('${fieldMetadata.formId}', ${fieldMetad= ata});});=20 </s:field-metadata>=20
The field-metadata
tag provides meta data for fields refere=
nced by input tags. The tag can only provide meta data for fields reference=
d by tags that appear before its own position in the page code, so usually =
you'll put it at the end of your form, just before the closing form tag. (I=
f you put it just below opening form tag, for example, no field meta data w=
ill be available.) Also, if you have more than one form on your page, be su=
re to set the form id yourself, otherwise you'll end up with a duplicated f=
orm id on your page and validation will only work for one of them.
The tag binds a block of Javascript (actually, JSON) code to a JSP varia=
ble, and generates an HTML script
block around its body. The b=
ody therefore should be Javascript code that makes use of the meta data. He=
re is also an example of how to use the tag with a Jquery validation plugin=
:
(http://machak.com/code.html)
Note: visit the above link for updates and improvements. The code below = is a skeletal example for illustration:
/**=20 *=20 * Jquery addapter class so we can use built in Jquery validator.=20 * NOTE: this is only skeleton, implementing only small part of possible va= lidation,=20 * Usage:=20 * <s:field-metadata var=3D"metaData">=20 * $(function(){$.fn.stripesValidation('${metaData.formId}', ${metaData});}= );=20 * </s:field-metadata>=20 * dependancies:=20 * jquery.1.3.1=20 * jquery.form=20 * jquery.validate=20 */=20 (function($){=20 =09/**=20 =09 * Processess Stripes meta data and converting it so jquery=20 =09 * validation plugin is used for client side validation=20 =09 * @param formId id of a form to be processed=20 =09 * @param metaData metadata that is produced by stripes tag=20 =09 */=20 =09$.fn.stripesValidation =3D function(formId, metaData) {=20 =09=09var form =3D $('#' + formId);=20 =09=09var mask_count =3D 0;=20 =09for (var fieldName in metaData) {=20 =09=09var field =3D form.find('[name=3D' + fieldName + ']');=20 =09=09addValidation(field, metaData[fieldName]);=20 =09=09// run validation:=20 =09=09form.validate();=20 =09}=20 =09function addValidation(field, obj) {=20 =09=09for (var prop in obj) {=20 =09=09=09debug(prop);=20 =09=09=09switch(prop){=20 =09=09=09=09case 'label':=20 =09=09=09=09=09break;=20 =09=09=09=09case 'required':=20 =09=09=09=09=09if (obj[prop]) { // add only if true=20 =09=09=09=09=09=09field.addClass(prop);=20 =09=09=09=09=09}=20 =09=09=09=09=09break;=20 =09=09=09=09case 'minlength': // should already be there=20 =09=09=09=09=09field.attr(prop, obj[prop]);=20 =09=09=09=09=09break;=20 =09=09=09=09case 'maxlength': // should already be there=20 =09=09=09=09=09field.attr(prop, obj[prop]);=20 =09=09=09=09=09break;=20 =09=09=09=09case 'mask':=20 =09=09=09=09=09setMask(field, obj[prop]);=20 =09=09=09=09=09break;=20 =09=09=09=09case 'typeConverter':=20 =09=09=09=09=09setConverter(field, obj[prop]);=20 =09=09=09=09=09break;=20 =09=09=09=09default:=20 =09=09=09=09=09debug('missing this:' + prop + ':' + [obj[prop]]);=20 =09=09=09}=20 =09=09} =09}=20 =09/**=20 =09 * Adds regular expression validation=20 =09 * @param field field reference=20 =09 * @param mask regular expression mask=20 =09 */=20 =09function setMask(field, mask) {=20 =09=09mask_count++;=20 =09=09var methodRef =3D 'maskMethod' + mask_count;=20 =09=09field.addClass(methodRef);=20 =09=09$.validator.addClassRules({=20 =09=09=09methodRef: {=20 =09=09=09=09methodRef: true=20 =09=09=09}=20 =09=09});=20 =09=09$.validator.addMethod(methodRef, function (value) {=20 =09=09=09return mask.test(value);=20 =09=09}, 'Value is invalid');=20 =09}=20 =09/**=20 =09 * Add converter mappings=20 =09 * @param field field reference=20 =09 * @param converter converter used by stripes for given field=20 =09 */=20 =09function setConverter(field, converter) {=20 =09=09if (converter =3D=3D 'EmailTypeConverter') {=20 =09=09=09field.addClass('email');=20 =09=09}=20 =09=09else {=20 =09=09=09debug('missing converter mapping:' + converter);=20 =09=09}=20 =09}=20 =09function debug(msg){=20 =09=09if (window.console && window.console.log) {=20 =09=09=09window.console.log(msg);=20 =09=09}=20 =09}=20 };=20 })(jQuery);=20