source: Dev/trunk/src/client/dojox/validate/check.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 12.4 KB
Line 
1define(["dojo/_base/kernel", "dojo/_base/lang", "./_base"],
2 function(kernel, lang, validate){
3kernel.experimental("dojox.validate.check");
4
5/**
6        FIXME: How much does this overlap with dojox.form.Manager and friends?
7
8        Procedural API Description
9
10                The main aim is to make input validation expressible in a simple format.
11                You define profiles which declare the required and optional fields and any constraints they might have.
12                The results are provided as an object that makes it easy to handle missing and invalid input.
13
14        Usage
15
16                var results = dojox.validate.check(form, profile);
17
18        Profile Object
19
20                var profile = {
21                        // filters change the field value and are applied before validation.
22                        trim: ["tx1", "tx2"],
23                        uppercase: ["tx9"],
24                        lowercase: ["tx5", "tx6", "tx7"],
25                        ucfirst: ["tx10"],
26                        digit: ["tx11"],
27
28                        // required input fields that are blank will be reported missing.
29                        // required radio button groups and drop-down lists with no selection will be reported missing.
30                        // checkbox groups and selectboxes can be required to have more than one value selected.
31                        // List required fields by name and use this notation to require more than one value: {checkboxgroup: 2}, {selectboxname: 3}.
32                        required: ["tx7", "tx8", "pw1", "ta1", "rb1", "rb2", "cb3", "s1", {"doubledip":2}, {"tripledip":3}],
33
34                        // dependant/conditional fields are required if the target field is present and not blank.
35                        // At present only textbox, password, and textarea fields are supported.
36                        dependencies:   {
37                                cc_exp: "cc_no",
38                                cc_type: "cc_no"
39                        },
40
41                        // Fields can be validated using any boolean valued function.
42                        // Use arrays to specify parameters in addition to the field value.
43                        constraints: {
44                                field_name1: myValidationFunction,
45                                field_name2: dojox.validate.isInteger,
46                                field_name3: [myValidationFunction, additional parameters],
47                                field_name4: [dojox.validate.isValidDate, "YYYY.MM.DD"],
48                                field_name5: [dojox.validate.isEmailAddress, false, true]
49                        },
50
51                        // Confirm is a sort of conditional validation.
52                        // It associates each field in its property list with another field whose value should be equal.
53                        // If the values are not equal, the field in the property list is reported as Invalid. Unless the target field is blank.
54                        confirm: {
55                                email_confirm: "email",
56                                pw2: "pw1"
57                        }
58                };
59
60        Results Object
61
62                isSuccessful(): Returns true if there were no invalid or missing fields, else it returns false.
63                hasMissing():  Returns true if the results contain any missing fields.
64                getMissing():  Returns a list of required fields that have values missing.
65                isMissing(field):  Returns true if the field is required and the value is missing.
66                hasInvalid():  Returns true if the results contain fields with invalid data.
67                getInvalid():  Returns a list of fields that have invalid values.
68                isInvalid(field):  Returns true if the field has an invalid value.
69
70*/
71
72validate.check = function(/*HTMLFormElement*/form, /*Object*/profile){
73        // summary:
74        //              validates user input of an HTML form based on input profile
75        // description:
76        //              returns an object that contains several methods summarizing the results of the validation
77        // form:
78        //              form to be validated
79        // profile:
80        //              specifies how the form fields are to be validated
81        //              {trim:Array, uppercase:Array, lowercase:Array, ucfirst:Array, digit:Array,
82        //              required:Array, dependencies:Object, constraints:Object, confirm:Object}
83
84        // Essentially private properties of results object
85        var missing = [];
86        var invalid = [];
87
88        // results object summarizes the validation
89        var results = {
90                isSuccessful: function() {return ( !this.hasInvalid() && !this.hasMissing() );},
91                hasMissing: function() {return ( missing.length > 0 );},
92                getMissing: function() {return missing;},
93                isMissing: function(elemname) {
94                        for(var i = 0; i < missing.length; i++){
95                                if(elemname == missing[i]){ return true; }
96                        }
97                        return false;
98                },
99                hasInvalid: function() {return ( invalid.length > 0 );},
100                getInvalid: function() {return invalid;},
101                isInvalid: function(elemname){
102                        for(var i = 0; i < invalid.length; i++){
103                                if(elemname == invalid[i]){ return true; }
104                        }
105                        return false;
106                }
107        };
108
109        var _undef = function(name,object){
110                return (typeof object[name] == "undefined");
111        };
112
113        // Filters are applied before fields are validated.
114        // Trim removes white space at the front and end of the fields.
115        if(profile.trim instanceof Array){
116                for(var i = 0; i < profile.trim.length; i++){
117                        var elem = form[profile.trim[i]];
118                        if(_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; }
119                        elem.value = elem.value.replace(/(^\s*|\s*$)/g, "");
120                }
121        }
122        // Convert to uppercase
123        if(profile.uppercase instanceof Array){
124                for(var i = 0; i < profile.uppercase.length; i++){
125                        var elem = form[profile.uppercase[i]];
126                        if(_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; }
127                        elem.value = elem.value.toUpperCase();
128                }
129        }
130        // Convert to lowercase
131        if(profile.lowercase instanceof Array){
132                for (var i = 0; i < profile.lowercase.length; i++){
133                        var elem = form[profile.lowercase[i]];
134                        if(_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; }
135                        elem.value = elem.value.toLowerCase();
136                }
137        }
138        // Uppercase first letter
139        if(profile.ucfirst instanceof Array){
140                for(var i = 0; i < profile.ucfirst.length; i++){
141                        var elem = form[profile.ucfirst[i]];
142                        if(_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; }
143                        elem.value = elem.value.replace(/\b\w+\b/g, function(word) { return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase(); });
144                }
145        }
146        // Remove non digits characters from the input.
147        if(profile.digit instanceof Array){
148                for(var i = 0; i < profile.digit.length; i++){
149                        var elem = form[profile.digit[i]];
150                        if(_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; }
151                        elem.value = elem.value.replace(/\D/g, "");
152                }
153        }
154
155        // See if required input fields have values missing.
156        if(profile.required instanceof Array){
157                for(var i = 0; i < profile.required.length; i++){
158                        if(!lang.isString(profile.required[i])){ continue; }
159                        var elem = form[profile.required[i]];
160                        // Are textbox, textarea, or password fields blank.
161                        if(!_undef("type", elem)
162                                && (elem.type == "text" || elem.type == "textarea" || elem.type == "password" || elem.type == "file")
163                                && /^\s*$/.test(elem.value)){
164                                missing[missing.length] = elem.name;
165                        }
166                        // Does drop-down box have option selected.
167                        else if(!_undef("type", elem) && (elem.type == "select-one" || elem.type == "select-multiple")
168                                                && (elem.selectedIndex == -1
169                                                || /^\s*$/.test(elem.options[elem.selectedIndex].value))){
170                                missing[missing.length] = elem.name;
171                        }
172                        // Does radio button group (or check box group) have option checked.
173                        else if(elem instanceof Array){
174                                var checked = false;
175                                for(var j = 0; j < elem.length; j++){
176                                        if (elem[j].checked) { checked = true; }
177                                }
178                                if(!checked){
179                                        missing[missing.length] = elem[0].name;
180                                }
181                        }
182                }
183        }
184
185        // See if checkbox groups and select boxes have x number of required values.
186        if(profile.required instanceof Array){
187                for (var i = 0; i < profile.required.length; i++){
188                        if(!lang.isObject(profile.required[i])){ continue; }
189                        var elem, numRequired;
190                        for(var name in profile.required[i]){
191                                elem = form[name];
192                                numRequired = profile.required[i][name];
193                        }
194                        // case 1: elem is a check box group
195                        if(elem instanceof Array){
196                                var checked = 0;
197                                for(var j = 0; j < elem.length; j++){
198                                        if(elem[j].checked){ checked++; }
199                                }
200                                if(checked < numRequired){
201                                        missing[missing.length] = elem[0].name;
202                                }
203                        }
204                        // case 2: elem is a select box
205                        else if(!_undef("type", elem) && elem.type == "select-multiple" ){
206                                var selected = 0;
207                                for(var j = 0; j < elem.options.length; j++){
208                                        if (elem.options[j].selected && !/^\s*$/.test(elem.options[j].value)) { selected++; }
209                                }
210                                if(selected < numRequired){
211                                        missing[missing.length] = elem.name;
212                                }
213                        }
214                }
215        }
216
217        // Dependent fields are required when the target field is present (not blank).
218        // Todo: Support dependent and target fields that are radio button groups, or select drop-down lists.
219        // Todo: Make the dependency based on a specific value of the target field.
220        // Todo: allow dependent fields to have several required values, like {checkboxgroup: 3}.
221        if(lang.isObject(profile.dependencies)){
222                // properties of dependencies object are the names of dependent fields to be checked
223                for(name in profile.dependencies){
224                        var elem = form[name];  // the dependent element
225                        if(_undef("type", elem)){continue;}
226                        if(elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } // limited support
227                        if(/\S+/.test(elem.value)){ continue; } // has a value already
228                        if(results.isMissing(elem.name)){ continue; }   // already listed as missing
229                        var target = form[profile.dependencies[name]];
230                        if(target.type != "text" && target.type != "textarea" && target.type != "password"){ continue; }        // limited support
231                        if(/^\s*$/.test(target.value)){ continue; }     // skip if blank
232                        missing[missing.length] = elem.name;    // ok the dependent field is missing
233                }
234        }
235
236        // Find invalid input fields.
237        if(lang.isObject(profile.constraints)){
238                // constraint properties are the names of fields to bevalidated
239                for(name in profile.constraints){
240                        var elem = form[name];
241                        if(!elem) {continue;}
242                       
243                        // skip if blank - its optional unless required, in which case it
244                        // is already listed as missing.
245                        if(!_undef("tagName",elem)
246                                && (elem.tagName.toLowerCase().indexOf("input") >= 0
247                                        || elem.tagName.toLowerCase().indexOf("textarea") >= 0)
248                                && /^\s*$/.test(elem.value)){
249                                continue;
250                        }
251                       
252                        var isValid = true;
253                        // case 1: constraint value is validation function
254                        if(lang.isFunction(profile.constraints[name])){
255                                isValid = profile.constraints[name](elem.value);
256                        }else if(lang.isArray(profile.constraints[name])){
257                               
258                                // handle nested arrays for multiple constraints
259                                if(lang.isArray(profile.constraints[name][0])){
260                                        for (var i=0; i<profile.constraints[name].length; i++){
261                                                isValid = validate.evaluateConstraint(profile, profile.constraints[name][i], name, elem);
262                                                if(!isValid){ break; }
263                                        }
264                                }else{
265                                        // case 2: constraint value is array, first elem is function,
266                                        // tail is parameters
267                                        isValid = validate.evaluateConstraint(profile, profile.constraints[name], name, elem);
268                                }
269                        }
270                       
271                        if(!isValid){
272                                invalid[invalid.length] = elem.name;
273                        }
274                }
275        }
276
277        // Find unequal confirm fields and report them as Invalid.
278        if(lang.isObject(profile.confirm)){
279                for(name in profile.confirm){
280                        var elem = form[name];  // the confirm element
281                        var target = form[profile.confirm[name]];
282                        if (_undef("type", elem) || _undef("type", target) || (elem.type != "text" && elem.type != "textarea" && elem.type != "password")
283                                ||(target.type != elem.type)
284                                ||(target.value == elem.value)  // it's valid
285                                ||(results.isInvalid(elem.name))// already listed as invalid
286                                ||(/^\s*$/.test(target.value))) // skip if blank - only confirm if target has a value
287                        {
288                                continue;
289                        }
290                        invalid[invalid.length] = elem.name;
291                }
292        }
293        return results; // Object
294};
295
296//TODO: evaluateConstraint doesn't use profile or fieldName args?
297validate.evaluateConstraint=function(profile, /*Array*/constraint, fieldName, elem){
298        // summary:
299        //              Evaluates dojo.validate.check() constraints that are specified as array
300        //              arguments
301        // description:
302        //              The arrays are expected to be in the format of:
303        //      |    constraints:{
304        //      |            fieldName: [functionToCall, param1, param2, etc.],
305        //      |            fieldName: [[functionToCallFirst, param1],[functionToCallSecond,param2]]
306        //      |    }
307        //
308        //              This function evaluates a single array function in the format of:
309        //              [functionName, argument1, argument2, etc]
310        //
311        //              The function will be parsed out and evaluated against the incoming parameters.
312        // profile:
313        //              The dojo.validate.check() profile that this evaluation is against.
314        // constraint:
315        //              The single [] array of function and arguments for the function.
316        // fieldName:
317        //              The form dom name of the field being validated.
318        // elem:
319        //              The form element field.
320       
321        var isValidSomething = constraint[0];
322        var params = constraint.slice(1);
323        params.unshift(elem.value);
324        if(typeof isValidSomething != "undefined"){
325                return isValidSomething.apply(null, params);
326        }
327        return false; // Boolean
328};
329
330return validate.check;
331});
Note: See TracBrowser for help on using the repository browser.