]> Dogcows Code - chaz/p5-CGI-Ex/blob - lib/CGI/Ex/validate.js
CGI::Ex 2.08
[chaz/p5-CGI-Ex] / lib / CGI / Ex / validate.js
1 /**----------------------------------------------------------------***
2 * Copyright 2007 - Paul Seamons *
3 * Distributed under the Perl Artistic License without warranty *
4 * Based upon CGI/Ex/Validate.pm v1.14 from Perl *
5 * For instructions on usage, see perldoc of CGI::Ex::Validate *
6 ***----------------------------------------------------------------**/
7 // $Revision: 1.41 $
8
9 function Validate () {
10 this.error = vob_error;
11 this.validate = vob_validate;
12 this.check_conditional = vob_check_conditional;
13 this.filter_types = vob_filter_types;
14 this.add_error = vob_add_error;
15 this.validate_buddy = vob_validate_buddy;
16 this.check_type = vob_check_type;
17 this.get_form_value = vob_get_form_value;
18 }
19
20 function ValidateError (errors, extra) {
21 this.errors = errors;
22 this.extra = extra;
23
24 this.as_string = eob_as_string;
25 this.as_array = eob_as_array;
26 this.as_hash = eob_as_hash;
27 this.get_error_text = eob_get_error_text;
28 this.first_field = eob_first_field;
29 }
30
31 ///----------------------------------------------------------------///
32
33 function vob_error (err) {
34 alert (err);
35 }
36
37 function vob_validate (form, val_hash) {
38 if (typeof(val_hash) == 'string') {
39 if (! document.yaml_load)
40 return this.error("Cannot parse yaml string - document.yaml_load is not loaded");
41 val_hash = document.yaml_load(val_hash);
42 }
43
44 var ERRORS = new Array ();
45 var EXTRA = new Array ();
46 // var USED_GROUPS = new Array();
47
48 // distinguishing between associative and index based arrays is harder than in perl
49 if (! val_hash.length) val_hash = new Array(val_hash);
50 for (var i = 0; i < val_hash.length; i ++) {
51 var group_val = val_hash[i];
52 if (typeof(group_val) != 'object' || group_val.length) return this.error("Validation groups must be a hash");
53 var title = group_val['group title'];
54 var validate_if = group_val['group validate_if'];
55
56 if (validate_if && ! this.check_conditional(form, validate_if)) continue;
57 // USED_GROUPS.push(group_val);
58
59 /// if the validation items were not passed as an arrayref
60 /// look for a group order and then fail back to the keys of the group
61 var fields = group_val['group fields'];
62 var order = new Array();
63 for (var key in group_val) {
64 if (key == 'extend') continue; // Protoype Array() fix
65 order[order.length] = key;
66 }
67 order = order.sort();
68 if (fields) {
69 if (typeof(fields) != 'object' || ! fields.length)
70 return this.error("'group fields' must be a non-empty array");
71 } else {
72 fields = new Array();
73 var _order = (group_val['group order']) ? group_val['group order'] : order;
74 if (typeof(_order) != 'object' || ! _order.length)
75 return this.error("'group order' must be a non-empty array");
76 for (var j = 0; j < _order.length; j ++) {
77 var field = _order[j];
78 if (field.match('^(group|general)\\s')) continue;
79 var field_val = group_val[field];
80 if (! field_val) {
81 if (field == 'OR') field_val = 'OR';
82 else return this.error('No element found in group for '+field);
83 }
84 if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field;
85 fields[fields.length] = field_val;
86 }
87 }
88
89 /// check which fields have been used
90 var found = new Array();
91 for (var j = 0; j < fields.length; j ++) {
92 var field_val = fields[j];
93 var field = field_val['field'];
94 if (! field) return this.error("Missing field key in validation");
95 // if (found[field]) return this.error('Duplicate order found for '+field+' in group order or fields');
96 found[field] = 1;
97 }
98
99 /// add any remaining fields from the order
100 for (var j = 0; j < order.length; j ++) {
101 var field = order[j];
102 if (found[field] || field.match('^(group|general)\\s')) continue;
103 var field_val = group_val[field];
104 if (typeof(field_val) != 'object' || field_val.length) return this.error('Found a non-hash value on field '+field);
105 if (! field_val['field']) field_val['field'] = field;
106 fields[fields.length] = field_val;
107 }
108
109 /// now lets do the validation
110 var is_found = 1;
111 var errors = new Array();
112 var hold_error;
113
114 for (var j = 0; j < fields.length; j ++) {
115 var ref = fields[j];
116 if (typeof(ref) != 'object' && ref == 'OR') {
117 if (is_found) j ++;
118 is_found = 1;
119 continue;
120 }
121 is_found = 1;
122 if (! ref['field']) return this.error("Missing field key during normal validation");
123 var err = this.validate_buddy(form, ref['field'], ref);
124
125 /// test the error - if errors occur allow for OR - if OR fails use errors from first fail
126 if (err.length) {
127 if (j <= fields.length && typeof(fields[j + 1] != 'object') && fields[j + 1] == 'OR') {
128 hold_error = err;
129 } else {
130 if (hold_error) err = hold_error;
131 for (var k = 0; k < err.length; k ++) errors[errors.length] = err[k];
132 hold_error = '';
133 }
134 } else {
135 hold_error = '';
136 }
137 }
138
139 /// add on errors as requested
140 if (errors.length) {
141 if (title) ERRORS[ERRORS.length] = title;
142 for (var j = 0; j < errors.length; j ++) ERRORS[ERRORS.length] = errors[j];
143 }
144
145 /// add on general options, and group options if errors in group occurred
146 var m;
147 for (var j = 0; j < order.length; j ++) {
148 var field = order[j];
149 if (! (m = field.match('^(general|group)\\s+(\\w+)$'))) continue;
150 if (m[1] == 'group' && (errors.length == 0 || m[2].match('^(field|order|title)$'))) continue;
151 EXTRA[m[2]] = group_val[field];
152 }
153 }
154
155 /// store any extra items from self
156 for (var key in this) {
157 if (key == 'extend') continue; // Protoype Array() fix
158 if (! key.match('_error$')
159 && ! key.match('^(raise_error|as_hash_\\w+|as_array_\\w+|as_string_\\w+)$')) continue;
160 EXTRA[key] = this[key];
161 }
162
163 /// allow for checking for unused keys
164 // if (EXTRA['no_extra_fields'])
165 // won't do anything about this for now - let the server handle it
166
167 /// return what they want
168 if (ERRORS.length) return new ValidateError(ERRORS, EXTRA);
169 return;
170 }
171
172
173 /// allow for optional validation on groups and on individual items
174 function vob_check_conditional (form, ifs, N_level, ifs_match) {
175
176 if (! N_level) N_level = 0;
177 N_level ++;
178
179 /// can pass a single hash - or an array ref of hashes
180 if (! ifs) {
181 return this.error("Need reference passed to check_conditional");
182 } else if (typeof(ifs) != 'object') {
183 ifs = new Array(ifs);
184 } else if (! ifs.length) { // turn hash into array of hash
185 ifs = new Array(ifs);
186 }
187
188 /// run the if options here
189 /// multiple items can be passed - all are required unless OR is used to separate
190 var is_found = 1;
191 var m;
192 for (var i = 0; i < ifs.length; i ++) {
193 var ref = ifs[i];
194 if (typeof(ref) != 'object') {
195 if (ref == 'OR') {
196 if (is_found) i++;
197 is_found = 1;
198 continue;
199 } else {
200 var field = ref;
201 ref = new Array();
202 if (m = field.match('^(\\s*!\\s*)')) {
203 field = field.substring(m[1].length);
204 ref['max_in_set'] = '0 of ' + field;
205 } else {
206 ref['required'] = 1;
207 }
208 ref['field'] = field;
209 }
210 }
211 if (! is_found) break;
212
213 /// get the field - allow for custom variables based upon a match
214 var field = ref['field'];
215 if (! field) return this.error("Missing field key during validate_if");
216 field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
217 if (typeof(ifs_match) != 'object'
218 || typeof(ifs_match[N]) == 'undefined') return ''
219 return ifs_match[N];
220 });
221
222 var err = this.validate_buddy(form, field, ref, N_level);
223 if (err.length) is_found = 0;
224 }
225 return is_found;
226 }
227
228 function vob_filter_types (type, types) {
229 var values = new Array();
230 var regexp = new RegExp('^'+type+'_?\\d*$');
231 for (var i = 0; i < types.length; i++)
232 if (types[i].match(regexp)) values[values.length] = types[i];
233 return values;
234 }
235
236 function vob_add_error (errors,field,type,field_val,ifs_match) {
237 errors[errors.length] = new Array(field, type, field_val, ifs_match);
238 }
239
240 /// this is where the main checking goes on
241 function vob_validate_buddy (form, field, field_val, N_level, ifs_match) {
242 if (! N_level) N_level = 0;
243 if (++ N_level > 10) return this.error("Max dependency level reached " + N_level);
244 if (! form.elements) return;
245
246 var errors = new Array();
247 var types = new Array();
248 for (var key in field_val) {
249 if (key == 'extend') continue; // Protoype Array() fix
250 types[types.length] = key;
251 }
252 types = types.sort();
253
254 /// allow for not running some tests in the cgi
255 if (this.filter_types('exclude_js', types).length) return errors;
256
257 /// allow for field names that contain regular expressions
258 var m;
259 if (m = field.match('^(!\\s*|)m([^\\s\\w])(.*)\\2([eigsmx]*)$')) {
260 var not = m[1];
261 var pat = m[3];
262 var opt = m[4];
263 if (opt.indexOf('e') != -1) return this.error("The e option cannot be used on field "+field);
264 opt = opt.replace(new RegExp('[sg]','g'),'');
265 var reg = new RegExp(pat, opt);
266
267 var keys = new Array();
268 for (var i = 0; i < form.elements.length; i ++) {
269 var _field = form.elements[i].name;
270 if (! _field) continue;
271 if ( (not && ! (m = _field.match(reg))) || (m = _field.match(reg))) {
272 var err = this.validate_buddy(form, _field, field_val, N_level, m);
273 for (var j = 0; j < err.length; j ++) errors[errors.length] = err[j];
274 }
275 }
276 return errors;
277 }
278
279 var _value = this.get_form_value(form[field]);
280 var values;
281 if (typeof(_value) == 'object') {
282 values = _value;
283 } else {
284 values = new Array();
285 values[values.length] = _value;
286 }
287 var n_values = (typeof(_value) == 'undefined') ? 0 : values.length;
288
289 /// allow for default value
290 var tests = this.filter_types('default', types);
291 if (n_values == 0 || (n_values == 1 && values[0].length == 0)) {
292 for (var i = 0; i < tests.length; i ++) {
293 var el = form[field];
294 if (! el) continue;
295 var type = el.type;
296 if (type && (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit'))
297 el.value = values[0] = '' + field_val[tests[i]];
298 }
299 }
300
301 /// allow for a few form modifiers
302 var modified = 0;
303 for (var i = 0; i < values.length; i ++) {
304 if (typeof(values[i]) == 'undefined') continue;
305 if (! this.filter_types('do_not_trim',types).length)
306 values[i] = values[i].replace('^\\s+','').replace(new RegExp('\\s+$',''),'');
307 if (this.filter_types('trim_control_chars',types).length)
308 values[i] = values[i].replace(new RegExp('\t', 'g'),' ').replace(new RegExp('[\\x00-\\x1F]+','g'),'');
309 if (this.filter_types('to_upper_case',types).length) {
310 values[i] = values[i].toUpperCase();
311 } else if (this.filter_types('to_lower_case',types).length) {
312 values[i] = values[i].toLowerCase();
313 }
314 }
315 var tests = this.filter_types('replace', types);
316 for (var i = 0; i < tests.length; i ++) {
317 var ref = field_val[tests[i]];
318 ref = (typeof(ref) == 'object') ? ref : ref.split(new RegExp('\\s*\\|\\|\\s*'));
319 for (var j = 0; j < ref.length; j ++) {
320 if (! (m = ref[j].match('^\\s*s([^\\s\\w])(.+)\\1(.*)\\1([eigmx]*)$')))
321 return this.error("Not sure how to parse that replace "+ref[j]);
322 var pat = m[2];
323 var swap = m[3];
324 var opt = m[4];
325 if (opt.indexOf('e') != -1)
326 return this.error("The e option cannot be used on field "+field+", replace "+tests[i]);
327 var regexp = new RegExp(pat, opt);
328 for (var k = 0; k < values.length; k ++) {
329 if (values[k].match(regexp)) modified = 1;
330 values[k] = values[k].replace(regexp,swap);
331 }
332 }
333 }
334 if (modified && n_values == 1) {
335 var el = form[field];
336 var type = el.type;
337 if (! type) return '';
338 if (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit')
339 el.value = values[0];
340 }
341
342 /// only continue if a validate_if is not present or passes test
343 var needs_val = 0;
344 var n_vif = 0;
345 var tests = this.filter_types('validate_if', types);
346 for (var i = 0; i < tests.length; i ++) {
347 n_vif ++;
348 var ifs = field_val[tests[i]];
349 var ret = this.check_conditional(form, ifs, N_level, ifs_match);
350 if (ret) needs_val ++;
351 }
352 if (! needs_val && n_vif) return errors;
353
354
355 /// check for simple existence
356 /// optionally check only if another condition is met
357 var is_required = '';
358 var tests = this.filter_types('required', types);
359 for (var i = 0; i < tests.length; i ++) {
360 if (! field_val[tests[i]] || field_val[tests[i]] == 0) continue;
361 is_required = tests[i];
362 break;
363 }
364 if (! is_required) {
365 var tests = this.filter_types('required_if', types);
366 for (var i = 0; i < tests.length; i ++) {
367 var ifs = field_val[tests[i]];
368 if (! this.check_conditional(form, ifs, N_level, ifs_match)) continue;
369 is_required = tests[i];
370 break;
371 }
372 }
373 if (is_required && (typeof(_value) == 'undefined'
374 || ((typeof(_value) == 'object' && _value.length == 0)
375 || ! _value.length))) {
376 this.add_error(errors, field, is_required, field_val, ifs_match);
377 return errors;
378 }
379
380 /// min values check
381 var tests = this.filter_types('min_values', types);
382 for (var i = 0; i < tests.length; i ++) {
383 var n = field_val[tests[i]];
384 if (n_values < n) {
385 this.add_error(errors, field, tests[i], field_val, ifs_match);
386 return errors;
387 }
388 }
389
390 /// max values check
391 var tests = this.filter_types('max_values', types);
392 if (! tests.length) {
393 tests[tests.length] = 'max_values';
394 field_val['max_values'] = 1;
395 }
396 for (var i = 0; i < tests.length; i ++) {
397 var n = field_val[tests[i]];
398 if (n_values > n) {
399 this.add_error(errors, field, tests[i], field_val, ifs_match);
400 return errors;
401 }
402 }
403
404 /// min_in_set and max_in_set check
405 for (var h = 0; h < 2 ; h++) {
406 var minmax = (h == 0) ? 'min' : 'max';
407 var tests = this.filter_types(minmax+'_in_set', types);
408 for (var i = 0; i < tests.length; i ++) {
409 if (! (m = field_val[tests[i]].match('^\\s*(\\d+)(?:\\s*[oO][fF])?\\s+(.+)\\s*$')))
410 return this.error("Invalid in_set check "+field_val[tests[i]]);
411 var n = m[1];
412 var _fields = m[2].split(new RegExp('[\\s,]+'));
413 for (var k = 0; k < _fields.length; k ++) {
414 var _value = this.get_form_value(form[_fields[k]]);
415 var _values;
416 if (typeof(_value) == 'undefined') continue;
417 if (typeof(_value) == 'object') {
418 _values = _value;
419 } else {
420 _values = new Array();
421 _values[_values.length] = _value;
422 }
423 for (var l = 0; l < _values.length; l ++) {
424 var _value = _values[l];
425 if (typeof(_value) != 'undefined' && _value.length) n --;
426 }
427 }
428 if ( (minmax == 'min' && n > 0)
429 || (minmax == 'max' && n < 0)) {
430 this.add_error(errors, field, tests[i], field_val, ifs_match);
431 return errors;
432 }
433 }
434 }
435
436 // the remaining tests operate on each value of a field
437 for (var n = 0; n < values.length; n ++) {
438 var value = values[n];
439
440 /// allow for enum types
441 var tests = this.filter_types('enum', types);
442 for (var i = 0; i < tests.length; i ++) {
443 var hold = field_val[tests[i]];
444 var _enum = (typeof(hold) == 'object') ? hold : hold.split(new RegExp('\\s*\\|\\|\\s*'));
445 var is_found = 0;
446 for (var j = 0; j < _enum.length; j ++) {
447 if (value != _enum[j]) continue;
448 is_found = 1;
449 break;
450 }
451 if (! is_found) this.add_error(errors, field, tests[i], field_val, ifs_match);
452 }
453
454 /// field equality test
455 var tests = this.filter_types('equals', types);
456 for (var i = 0; i < tests.length; i ++) {
457 var field2 = field_val[tests[i]];
458 var not = field2.match('^!\\s*');
459 if (not) field2 = field2.substring(not[0].length);
460 var success = 0;
461 if (m = field2.match('^(["\'])(.*)\\1$')) {
462 if (value == m[2]) success = 1;
463 } else {
464 var value2 = this.get_form_value(form[field2]);
465 if (typeof(value2) == 'undefined') value2 = '';
466 if (value == value2) success = 1;
467 }
468 if (not && success || ! not && ! success)
469 this.add_error(errors, field, tests[i], field_val, ifs_match);
470 }
471
472 /// length min check
473 var tests = this.filter_types('min_len', types);
474 for (var i = 0; i < tests.length; i ++) {
475 var n = field_val[tests[i]];
476 if (value.length < n) this.add_error(errors, field, tests[i], field_val, ifs_match);
477 }
478
479 /// length max check
480 var tests = this.filter_types('max_len', types);
481 for (var i = 0; i < tests.length; i ++) {
482 var n = field_val[tests[i]];
483 if (value.length > n) this.add_error(errors, field, tests[i], field_val, ifs_match);
484 }
485
486 /// now do match types
487 var tests = this.filter_types('match', types);
488 for (var i = 0; i < tests.length; i ++) {
489 var ref = field_val[tests[i]];
490 ref = (typeof(ref) == 'object') ? ref
491 : (typeof(ref) == 'function') ? new Array(ref)
492 : ref.split(new RegExp('\\s*\\|\\|\\s*'));
493 for (var j = 0; j < ref.length; j ++) {
494 if (typeof(ref[j]) == 'function') {
495 if (! value.match(ref[j])) this.add_error(errors, field, tests[i], field_val, ifs_match);
496 } else {
497 if (! (m = ref[j].match('^\\s*(!\\s*|)m([^\\s\\w])(.*)\\2([eigsmx]*)\\s*$')))
498 return this.error("Not sure how to parse that match ("+ref[j]+")");
499 var not = m[1];
500 var pat = m[3];
501 var opt = m[4];
502 if (opt.indexOf('e') != -1)
503 return this.error("The e option cannot be used on field "+field+", test "+tests[i]);
504 opt = opt.replace(new RegExp('[sg]','g'),'');
505 var regexp = new RegExp(pat, opt);
506 if ( ( not && value.match(regexp))
507 || (! not && ! value.match(regexp))) {
508 this.add_error(errors, field, tests[i], field_val, ifs_match);
509 }
510 }
511 }
512 }
513
514 /// allow for comparison checks
515 var tests = this.filter_types('compare', types);
516 for (var i = 0; i < tests.length; i ++) {
517 var ref = field_val[tests[i]];
518 ref = (typeof(ref) == 'object') ? ref : ref.split(new RegExp('\\s*\\|\\|\\s*'));
519 for (var j = 0; j < ref.length; j ++) {
520 var comp = ref[j];
521 if (! comp) continue;
522 var hold = false;
523 var copy = value;
524 if (m = comp.match('^\\s*(>|<|[><!=]=)\\s*([\\d\.\-]+)\\s*$')) {
525 if (! copy) copy = 0;
526 copy *= 1;
527 if (m[1] == '>' ) hold = (copy > m[2])
528 else if (m[1] == '<' ) hold = (copy < m[2])
529 else if (m[1] == '>=') hold = (copy >= m[2])
530 else if (m[1] == '<=') hold = (copy <= m[2])
531 else if (m[1] == '!=') hold = (copy != m[2])
532 else if (m[1] == '==') hold = (copy == m[2])
533 } else if (m = comp.match('^\\s*(eq|ne|gt|ge|lt|le)\\s+(.+?)\\s*$')) {
534 m[2] = m[2].replace('^(["\'])(.*)\\1$','$1');
535 if (m[1] == 'gt') hold = (copy > m[2])
536 else if (m[1] == 'lt') hold = (copy < m[2])
537 else if (m[1] == 'ge') hold = (copy >= m[2])
538 else if (m[1] == 'le') hold = (copy <= m[2])
539 else if (m[1] == 'ne') hold = (copy != m[2])
540 else if (m[1] == 'eq') hold = (copy == m[2])
541 } else {
542 return this.error("Not sure how to compare \""+comp+"\"");
543 }
544 if (! hold) this.add_error(errors, field, tests[i], field_val, ifs_match);
545 }
546 }
547
548 /// do specific type checks
549 var tests = this.filter_types('type',types);
550 for (var i = 0; i < tests.length; i ++)
551 if (! this.check_type(value, field_val[tests[i]], field, form))
552 this.add_error(errors, field, tests[i], field_val, ifs_match);
553
554 /// do custom_js type checks
555 // this will allow for a custom piece of javascript
556 // the js is evaluated and should return 1 for success
557 // or 0 for failure - the variables field, value, and field_val (the hash) are available
558 var tests = this.filter_types('custom_js',types);
559 for (var i = 0; i < tests.length; i ++)
560 if (! eval(field_val[tests[i]]))
561 this.add_error(errors, field, tests[i], field_val, ifs_match);
562 }
563
564 /// all done - time to return
565 return errors;
566 }
567
568 /// used to validate specific types
569 function vob_check_type (value, type, field, form) {
570 var m;
571
572 /// do valid email address for our system
573 if (type == 'EMAIL') {
574 if (! value) return 0;
575 if (! (m = value.match('^(.+)\@(.+?)$'))) return 0;
576 if (m[1].length > 60) return 0;
577 if (m[2].length > 100) return 0;
578 if (! this.check_type(m[2],'DOMAIN') && ! this.check_type(m[2],'IP')) return 0;
579 if (! this.check_type(m[1],'LOCAL_PART')) return 0;
580
581 /// the "username" portion of an email address
582 } else if (type == 'LOCAL_PART') {
583 if (typeof(value) == 'undefined' || ! value.length) return 0;
584 if (! value.match('[^a-z0-9.\\-!&+]')) return 0;
585 if (! value.match('^[.\\-]')) return 0;
586 if (! value.match('[.\\-&]$')) return 0;
587 if (! value.match('(\\.-|-\\.|\\.\\.)')) return 0;
588
589 /// standard IP address
590 } else if (type == 'IP') {
591 if (! value) return 0;
592 var dig = value.split(new RegExp('\\.'));
593 if (dig.length != 4) return 0;
594 for (var i = 0; i < 4; i ++)
595 if (typeof(dig[i]) == 'undefined' || dig[i].match('\\D') || dig[i] > 255) return 0;
596
597 /// domain name - including tld and subdomains (which are all domains)
598 } else if (type == 'DOMAIN') {
599 if (! value) return 0;
600 if (! value.match('^[a-z0-9.-]{4,255}$')) return 0;
601 if (value.match('^[.\\-]')) return 0;
602 if (value.match('(\\.-|-\\.|\\.\\.)')) return 0;
603 if (! (m = value.match('\.([a-z]+)$'))) return 0;
604 value = value.substring(0,value.lastIndexOf('.'));
605
606 if (m[1] == 'name') {
607 if (! value.match('^[a-z0-9][a-z0-9\\-]{0,62}\\.[a-z0-9][a-z0-9\\-]{0,62}$')) return 0;
608 } else
609 if (! value.match('^([a-z0-9][a-z0-9\\-]{0,62}\\.)*[a-z0-9][a-z0-9\\-]{0,62}$')) return 0;
610
611 /// validate a url
612 } else if (type == 'URL') {
613 if (! value) return 0;
614 if (! (m = value.match(new RegExp('^https?://([^/]+)','i'),''))) return 0;
615 value = value.substring(m[0].length);
616 if (! this.check_type(m[1],'DOMAIN') && ! this.check_type(m[1],'IP')) return 0;
617 if (value && ! this.check_type(value,'URI')) return 0;
618
619 /// validate a uri - the path portion of a request
620 } else if (type == 'URI') {
621 if (! value) return 0;
622 if (value.match('\\s')) return 0;
623
624 } else if (type == 'CC') {
625 if (! value) return 0;
626 if (value.match('[^\\d\\- ]') || value.length > 16 || value.length < 13) return;
627 /// simple mod10 check
628 value = value.replace(new RegExp('[\\- ]','g'), '');
629 var sum = 0;
630 var swc = 0;
631
632 for (var i = value.length - 1; i >= 0; i --) {
633 if (++ swc > 2) swc = 1;
634 var y = value.charAt(i) * swc;
635 if (y > 9) y -= 9;
636 sum += y;
637 }
638 if (sum % 10) return 0;
639
640 }
641
642 return 1;
643 }
644
645 // little routine that will get the values from the form
646 // it will return multiple values as an array
647 function vob_get_form_value (el) {
648 if (! el) return '';
649 if (el.disabled) return '';
650 var type = el.type ? el.type.toLowerCase() : '';
651 if (el.length && type != 'select-one') {
652 var a = new Array();
653 for (var j=0;j<el.length;j++) {
654 if (type.indexOf('multiple') != -1) {
655 if (el[j].selected) a[a.length] = el[j].value;
656 } else {
657 if (el[j].checked) a[a.length] = vob_get_form_value(el[j]);
658 }
659 }
660 if (a.length == 0) return '';
661 if (a.length == 1) return a[0];
662 return a;
663 }
664 if (! type) return '';
665 if (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit')
666 return el.value;
667 if (type.indexOf('select') != -1) {
668 if (! el.length) return '';
669 return el[el.selectedIndex].value;
670 }
671 if (type == 'checkbox' || type == 'radio') {
672 return el.checked ? el.value : '';
673 }
674 if (type == 'file') {
675 return el.value; // hope this works
676 }
677 alert('Unknown form type for '+el.name+': '+type);
678 return '';
679 }
680
681 ///----------------------------------------------------------------///
682
683 function eob_get_val (key, extra2, extra1, _default) {
684 if (typeof(extra2[key]) != 'undefined') return extra2[key];
685 if (typeof(extra1[key]) != 'undefined') return extra1[key];
686 return _default;
687 }
688
689 function eob_as_string (extra2) {
690 var extra1 = this.extra;
691 if (! extra2) extra2 = new Array();
692
693 var joiner = eob_get_val('as_string_join', extra2, extra1, '\n');
694 var header = eob_get_val('as_string_header', extra2, extra1, '');
695 var footer = eob_get_val('as_string_footer', extra2, extra1, '');
696
697 return header + this.as_array(extra2).join(joiner) + footer;
698 }
699
700 /// return an array of applicable errors
701 function eob_as_array (extra2) {
702 var errors = this.errors;
703 var extra1 = this.extra;
704 if (! extra2) extra2 = new Array();
705
706 var title = eob_get_val('as_array_title', extra2, extra1, 'Please correct the following items:');
707
708 /// if there are heading items then we may end up needing a prefix
709 var has_headings;
710 if (title) has_headings = 1;
711 else {
712 for (var i = 0; i < errors.length; i ++) {
713 if (typeof(errors[i]) != 'string') continue;
714 has_headings = 1;
715 break;
716 }
717 }
718
719 var prefix = eob_get_val('as_array_prefix', extra2, extra1, has_headings ? ' ' : '');
720
721 /// get the array ready
722 var arr = new Array();
723 if (title && title.length) arr[arr.length] = title;
724 /// add the errors
725 var found = new Array();
726 for (var i = 0; i < errors.length; i ++) {
727 if (typeof(errors[i]) == 'string') {
728 arr[arr.length] = errors[i];
729 found = new Array();
730 } else {
731 var text = this.get_error_text(errors[i]);
732 if (found[text]) continue;
733 found[text] = 1;
734 arr[arr.length] = prefix + text;
735 }
736 }
737
738 return arr;
739 }
740
741 /// return a hash of applicable errors
742 function eob_as_hash (extra2) {
743 var errors = this.errors;
744 var extra1 = this.extra;
745 if (! extra2) extra2 = new Array();
746 var suffix = eob_get_val('as_hash_suffix', extra2, extra1, '_error');
747 var joiner = eob_get_val('as_hash_join', extra2, extra1, '<br />');
748
749 /// now add to the hash
750 var found = new Array();
751 var ret = new Array();
752 for (var i = 0; i < errors.length; i ++) {
753 if (typeof(errors[i]) == 'string') continue;
754 if (! errors[i].length) continue;
755
756 var field = errors[i][0];
757 var type = errors[i][1];
758 var field_val = errors[i][2];
759 var ifs_match = errors[i][3];
760
761 if (! field) return alert("Missing field name");
762 if (field_val['delegate_error']) {
763 field = field_val['delegate_error'];
764 field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
765 if (typeof(ifs_match) != 'object'
766 || typeof(ifs_match[N]) == 'undefined') return ''
767 return ifs_match[N];
768 });
769 }
770
771 var text = this.get_error_text(errors[i]);
772 if (! found[field]) found[field] = new Array();
773 if (found[field][text]) continue;
774 found[field][text] = 1;
775
776 field += suffix;
777 if (! ret[field]) ret[field] = new Array();
778 ret[field].push(text);
779 }
780
781 /// allow for elements returned as
782 if (joiner) {
783 var header = eob_get_val('as_hash_header', extra2, extra1, '');
784 var footer = eob_get_val('as_hash_footer', extra2, extra1, '');
785 for (var key in ret) {
786 if (key == 'extend') continue; // Protoype Array() fix
787 ret[key] = header + ret[key].join(joiner) + footer;
788 }
789 }
790
791 return ret;
792 }
793
794 /// return a user friendly error message
795 function eob_get_error_text (err) {
796 var extra = this.extra;
797 var field = err[0];
798 var type = err[1];
799 var field_val = err[2];
800 var ifs_match = err[3];
801 var m;
802
803 var dig = (m = type.match('(_?\\d+)$')) ? m[1] : '';
804 var type_lc = type.toLowerCase();
805
806 /// allow for delegated field names - only used for defaults
807 if (field_val['delegate_error']) {
808 field = field_val['delegate_error'];
809 field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
810 if (typeof(ifs_match) != 'object'
811 || typeof(ifs_match[N]) == 'undefined') return ''
812 return ifs_match[N];
813 });
814 }
815
816 /// the the name of this thing
817 var name = (field_val['name']) ? field_val['name'] : "The field " +field;
818 name = name.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
819 if (typeof(ifs_match) != 'object'
820 || typeof(ifs_match[N]) == 'undefined') return ''
821 return ifs_match[N];
822 });
823
824
825 /// type can look like "required" or "required2" or "required100023"
826 /// allow for fallback from required100023_error through required_error
827 var possible_keys = new Array(type + '_error');
828 if (dig.length) possible_keys.unshift(type + dig + '_error');
829
830 /// look in the passed hash or self first
831 for (var i = 0; i < possible_keys.length; i ++) {
832 var key = possible_keys[i];
833 var ret = field_val[key];
834 if (! ret) {
835 if (extra[key]) ret = extra[key];
836 else continue;
837 }
838 ret = ret.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
839 if (typeof(ifs_match) != 'object'
840 || typeof(ifs_match[N]) == 'undefined') return ''
841 return ifs_match[N];
842 });
843 ret = ret.replace(new RegExp('\\$field','g'), field);
844 ret = ret.replace(new RegExp('\\$name' ,'g'), name);
845 if (field_val[type + dig] && typeof(field_val[type + dig]) == 'string')
846 ret = ret.replace(new RegExp('\\$value' ,'g'), field_val[type + dig]);
847 return ret;
848 }
849
850 /// set default messages
851 if (type == 'required' || type == 'required_if') {
852 return name + " is required.";
853
854 } else if (type == 'min_values') {
855 var n = field_val["min_values" + dig];
856 var values = (n == 1) ? 'value' : 'values';
857 return name + " had less than "+n+" "+values+".";
858
859 } else if (type == 'max_values') {
860 var n = field_val["max_values" + dig];
861 var values = (n == 1) ? 'value' : 'values';
862 return name + " had more than "+n+" "+values+".";
863
864 } else if (type == 'min_in_set') {
865 var set = field_val["min_in_set" + dig];
866 return "Not enough fields were chosen from the set ("+set+")";
867 return "Too many fields were chosen from the set ("+set+")";
868
869 } else if (type == 'max_in_set') {
870 var set = field_val["max_in_set" + dig];
871 return "Too many fields were chosen from the set ("+set+")";
872
873 } else if (type == 'enum') {
874 return name + " is not in the given list.";
875
876 } else if (type == 'equals') {
877 var field2 = field_val["equals" + dig];
878 var name2 = field_val["equals" +dig+ "_name"];
879 if (! name2) name2 = "the field " +field2;
880 name2 = name2.replace(new RegExp('\\$(\\d+)','g'), function (all, N) {
881 if (typeof(ifs_match) != 'object'
882 || typeof(ifs_match[N]) == 'undefined') return ''
883 return ifs_match[N];
884 });
885 return name + " did not equal " + name2 +".";
886
887 } else if (type == 'min_len') {
888 var n = field_val["min_len"+dig];
889 var chars = (n == 1) ? 'character' : 'characters';
890 return name + " was less than "+n+" "+chars+".";
891
892 } else if (type == 'max_len') {
893 var n = field_val["max_len"+dig];
894 var chars = (n == 1) ? 'character' : 'characters';
895 return name + " was more than "+n+" "+chars+".";
896
897 } else if (type == 'match') {
898 return name + " contains invalid characters.";
899
900 } else if (type == 'compare') {
901 return name + " did not fit comparison.";
902
903 } else if (type == 'type') {
904 var _type = field_val["type"+dig];
905 return name + " did not match type "+_type+".";
906
907 } else if (type == 'custom_js') {
908 return name + " did not match custom_js"+dig+" check.";
909
910 }
911
912 return alert("Missing error on field "+field+" for type "+type+dig);
913 }
914
915 function eob_first_field () {
916 for (var i = 0; i < this.errors.length; i++) {
917 if (typeof(this.errors[i]) != 'object') continue;
918 if (! this.errors[i][0]) continue;
919 return this.errors[i][0];
920 }
921 return;
922 }
923
924 ///----------------------------------------------------------------///
925
926 document.validate = function (form, val_hash) {
927 // undo previous inline
928 if (document.did_inline) {
929 for (var key in document.did_inline) {
930 if (key == 'extend') continue; // Protoype Array() fix
931 var el = document.getElementById(key);
932 if (el) el.innerHTML = '';
933 }
934 document.did_inline = undefined;
935 }
936
937 // do the validate
938 val_hash = document.load_val_hash(form, val_hash);
939 if (typeof(val_hash) == 'undefined') return true;
940 if (! document.val_obj) document.val_obj = new Validate();
941 var err_obj = document.val_obj.validate(form, val_hash);
942
943 // return success
944 if (! err_obj) return true;
945
946 // focus
947 var field = err_obj.first_field();
948 if (field && form[field] && form[field].focus) form[field].focus();
949
950 // inline
951 if (! err_obj.extra.no_inline) {
952 var d = document.did_inline = new Array();
953 var hash = err_obj.as_hash();
954 for (var key in hash) {
955 if (key == 'extend') continue; // Protoype Array() fix
956 var el = document.getElementById(key);
957 if (el) el.innerHTML = hash[key];
958 d[key] = 1;
959 }
960 }
961
962 // alert
963 if (! err_obj.extra.no_confirm) {
964 return confirm(err_obj.as_string()) ? false : true;
965 } else if (! err_obj.extra.no_alert) {
966 alert(err_obj.as_string());
967 return false;
968 } else if (! err_obj.extra.no_inline) {
969 return false;
970 } else {
971 return true;
972 }
973 }
974
975 document.load_val_hash = function (form, val_hash) {
976 // check the form we are using
977 if (! form) return alert('Missing form or form name');
978 if (typeof(form) == 'string') {
979 if (! document[form]) return alert('No form by name '+form);
980 form = document[form];
981 }
982
983 // if we already have validation - use it
984 if (form.val_hash) return form.val_hash;
985
986 // load in the validation and save it for future use
987 if (typeof(val_hash) != 'object') {
988 // get the hash from a javascript function
989 if (typeof(val_hash) == 'function') {
990 val_hash = val_hash(formname);
991 } else if (typeof(val_hash) == 'undefined') {
992 var el;
993 // get hash from a global js variable
994 if (typeof(document.validation) != 'undefined') {
995 val_hash = document.validation;
996 // get hash from a element by if of validation
997 } else if (el = document.getElementById('validation')) {
998 val_hash = el.innerHTML;
999 val_hash = val_hash.replace(new RegExp('&lt;', 'ig'),'<');
1000 val_hash = val_hash.replace(new RegExp('&gt;', 'ig'),'>');
1001 val_hash = val_hash.replace(new RegExp('&amp;','ig'),'&');
1002 // read hash from <input name=foo validation="">
1003 } else {
1004 var order = new Array();
1005 var str = '';
1006 var yaml = form.getAttribute('validation');
1007 if (yaml) {
1008 if (m = yaml.match('^( +)')) yaml = yaml.replace(new RegExp('^'+m[1], 'g'), ''); //unindent
1009 yaml = yaml.replace(new RegExp('\\s*$',''),'\n'); // add trailing
1010 str += yaml;
1011 }
1012 var m;
1013 for (var i = 0; i < form.elements.length; i ++) {
1014 var name = form.elements[i].name;
1015 var yaml = form.elements[i].getAttribute('validation');
1016 if (! name || ! yaml) continue;
1017 yaml = yaml.replace(new RegExp('\\s*$',''),'\n'); // add trailing
1018 yaml = yaml.replace(new RegExp('^(.)','mg'),' $1'); // indent all
1019 yaml = yaml.replace(new RegExp('^( *[^\\s&*\\[\\{])',''),'\n$1'); // add newline
1020 str += name +':' + yaml;
1021 order[order.length] = name;
1022 }
1023 if (str) val_hash = str + "group order: [" + order.join(', ') + "]\n";
1024 }
1025 }
1026 if (typeof(val_hash) == 'string') {
1027 if (! document.yaml_load) return;
1028 document.hide_yaml_errors = (! document.show_yaml_errors);
1029 if (location.search && location.search.indexOf('show_yaml_errors') != -1)
1030 document.hide_yaml_errors = 0;
1031 val_hash = document.yaml_load(val_hash);
1032 if (document.yaml_error_occured) return;
1033 }
1034 }
1035
1036 // attach to the form
1037 form.val_hash = val_hash;
1038 return form.val_hash;
1039 }
1040
1041
1042 document.check_form = function (form, val_hash) {
1043 // check the form we are using
1044 if (! form) return alert('Missing form or form name');
1045 if (typeof(form) == 'string') {
1046 if (! document[form]) return alert('No form by name '+form);
1047 form = document[form];
1048 }
1049
1050 // void call - allow for getting it at run time rather than later
1051 document.load_val_hash(form, val_hash);
1052
1053 // attach handler
1054 var orig_submit = form.onsubmit || function () { return true };
1055 form.onsubmit = function (e) { return document.validate(this) && orig_submit(e, this) };
1056 }
1057
1058 // the end //
This page took 0.104352 seconds and 4 git commands to generate.