X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=lib%2FCGI%2FEx%2Fvalidate.js;h=7b469115ba07c99fc76b2498ccf5f64454201544;hb=fdecaac30a1168ed894c46d61b6c95524ec62a4e;hp=72cca57da30c5634834bb5c5f64cf90731a366e3;hpb=490b94ab4051adf93abf16a4ed34efb923d6e8fc;p=chaz%2Fp5-CGI-Ex diff --git a/lib/CGI/Ex/validate.js b/lib/CGI/Ex/validate.js index 72cca57..7b46911 100644 --- a/lib/CGI/Ex/validate.js +++ b/lib/CGI/Ex/validate.js @@ -1,8 +1,9 @@ -// Copyright 2007 - Paul Seamons - $Revision: 1.62 $ +// Copyright 2008 - Paul Seamons - $Revision: 1.18 $ // Distributed under the Perl Artistic License without warranty // See perldoc CGI::Ex::Validate for usage var v_did_inline = {}; +var v_event; function ValidateError (errors, extra) { this.errors = errors; @@ -17,72 +18,79 @@ function ValidateError (errors, extra) { function v_error (err) { alert (err); return 1 } -function v_clean_val_hash (val_hash) { +function v_get_ordered_fields (val_hash) { if (typeof(val_hash) != 'object') return {error: v_error("Validation must be an associative array (hash)")}; - var order = []; + var ARGS = {}; + var field_keys = []; + var m; for (var key in val_hash) { if (key == 'extend') continue; // Protoype Array() - if (key.match(/^general\s/)) { - var new_key = key.replace(/^general\s+/, 'group '); - val_hash[new_key] = val_hash[key]; - delete(val_hash[key]); - key = new_key; + if (m = key.match(/^(general|group)\s+(\w+)/)) { + ARGS[m[2]] = val_hash[key]; + continue; } - order.push(key); + field_keys.push(key); } - order = order.sort(); + field_keys = field_keys.sort(); - var f = val_hash['group set_hook']; - if (f && typeof(f) == 'string') val_hash['group set_hook'] = eval(f); - f = val_hash['group clear_hook']; - if (f && typeof(f) == 'string') val_hash['group clear_hook'] = eval(f); + var f = ARGS.set_hook; if (f && typeof(f) == 'string') eval("ARGS.set_hook = "+f); + f = ARGS.clear_hook; if (f && typeof(f) == 'string') eval("ARGS.clear_hook = "+f); + f = ARGS.set_all_hook; if (f && typeof(f) == 'string') eval("ARGS.set_all_hook = "+f); + f = ARGS.clear_all_hook; if (f && typeof(f) == 'string') eval("ARGS.clear_all_hook = "+f); - var fields = val_hash['group fields']; - if (fields) { - if (typeof(fields) != 'object' || ! fields.length) + if (f = ARGS.validate_if) { + if (typeof(f) == 'string' || ! f.length) f = [f]; + var deps = v_clean_cond(f); + } + + var fields = []; + var ref; + if (ref = ARGS.fields || ARGS['order']) { + if (typeof(ref) != 'object' || ! ref.length) return {error:v_error("'group fields' must be a non-empty array")}; - } else { - fields = []; - var _order = (val_hash['group order']) ? val_hash['group order'] : order; - if (typeof(_order) != 'object' || ! _order.length) - return {error:v_error("'group order' must be a non-empty array")}; - for (var i = 0; i < _order.length; i++) { - var field = _order[i]; - if (field.match(/^group\s/)) continue; - var field_val = val_hash[field]; - if (! field_val) { - if (field == 'OR') field_val = 'OR'; - else return {error:v_error('No element found in group for '+field)}; + for (var i = 0; i < ref.length; i++) { + var field = ref[i]; + if (typeof(field) == 'object') { + if (! field.field) return {error:v_error("Missing field key in validation")}; + fields.push(field); + } else if (field == 'OR') { + fields.push('OR'); + } else { + var field_val = val_hash[field]; + if (! field_val) return {error:v_error('No element found in group for '+field)}; + if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field; + fields.push(field_val); } - if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field; - fields.push(field_val); } } var found = {}; for (var i = 0; i < fields.length; i++) { var field_val = fields[i]; - var field = field_val.field; - if (! field) return {error:v_error("Missing field key in validation")}; - found[field] = 1; + if (typeof(field_val) != 'object') continue; + found[field_val.field] = 1; } - for (var i = 0; i < order.length; i++) { - var field = order[i]; - if (found[field] || field.match(/^group\s/)) continue; + for (var i = 0; i < field_keys.length; i++) { + var field = field_keys[i]; + if (found[field]) continue; var field_val = val_hash[field]; - if (typeof(field_val) != 'object' || field_val.length) {debug(val_hash);alert(field);return {error:v_error('Found a non-hash value on field '+field)};} + if (typeof(field_val) != 'object' || field_val.length) return {error:v_error('Found a non-hash value on field '+field)}; if (! field_val.field) field_val.field = field; fields.push(field_val); } for (var i = 0; i < fields.length; i++) v_clean_field_val(fields[i]); - return {'fields':fields, 'order':order}; + val_hash['group was_checked'] = {}; + val_hash['group was_valid'] = {}; + val_hash['group had_error'] = {}; + + return {'fields':fields, 'args':ARGS}; } -function v_clean_field_val (field_val) { +function v_clean_field_val (field_val, N_level) { if (! field_val.order) field_val.order = v_field_order(field_val); if (! field_val.deps) field_val.deps = {}; for (var i = 0; i < field_val.order.length; i++) { @@ -113,27 +121,56 @@ function v_clean_field_val (field_val) { if (not) v.splice(j, 0, '!'); } } else if (k.match(/^custom_js\d*$/)) { - if (typeof(v) == 'string' && v.match(/^\s*function\s*/)) field_val[k] = eval(v); + if (typeof(v) == 'string' && v.match(/^\s*function\s*\(/)) eval("field_val[k] = "+v); + } else if (k.match(/^(validate|required)_if\d*$/)) { + if (typeof(v) == 'string' || ! v.length) v = field_val[k] = [v]; + var deps = v_clean_cond(v, N_level); + for (var k in deps) field_val.deps[k] = 2; + } + } +} + +function v_clean_cond (ifs, N_level, ifs_match) { + if (typeof(ifs) != 'object') { v_error("Need reference v_clean_cond "+typeof(ifs)); return [] } + if (! N_level) N_level = 0; + if (++N_level > 10) { v_error("Max dependency level reached " + N_level); return [] } + + var deps = {}; + var m; + for (var i = 0; i < ifs.length; i++) { + if (typeof(ifs[i]) == 'string') { + if (ifs[i].match(/^\s*function\s*\(/)) eval("ifs[i] = "+ifs[i]); + else if (m = ifs[i].match(/^(.+?)\s+was_valid$/)) ifs[i] = {field: m[1], was_valid:1} + else if (m = ifs[i].match(/^(.+?)\s+had_error$/)) ifs[i] = {field: m[1], had_error:1} + else if (m = ifs[i].match(/^(.+?)\s+was_checked$/)) ifs[i] = {field: m[1], was_checked:1} + else if (m = ifs[i].match(/^(\s*!\s*)(.+)\s*$/)) ifs[i] = {field: m[2], max_in_set: [0, m[2]]}; + else if (ifs[i] != 'OR') ifs[i] = {field: ifs[i], required: 1}; } + if (typeof(ifs[i]) != 'object') continue; + if (! ifs[i].field) { v_error("Missing field key during validate_if"); return [] } + deps[ifs[i].field] = 2; + v_clean_field_val(ifs[i], N_level); + for (var k in ifs[i].deps) deps[k] = 2; } + return deps; } function v_validate (form, val_hash) { - var clean = v_clean_val_hash(val_hash); + var clean = v_get_ordered_fields(val_hash); if (clean.error) return; - var order = clean.order; var fields = clean.fields; var ERRORS = []; var EXTRA = []; var title = val_hash['group title']; - var validate_if = val_hash['group validate_if']; - if (validate_if && ! v_check_conditional(form, validate_if)) return; + var v_if = val_hash['group validate_if']; + if (v_if && ! v_check_conditional(form, v_if, val_hash)) return; var is_found = 1; var errors = []; var hold_error; + var chk = {}; for (var j = 0; j < fields.length; j++) { var ref = fields[j]; if (typeof(ref) != 'object' && ref == 'OR') { @@ -142,10 +179,17 @@ function v_validate (form, val_hash) { continue; } is_found = 1; - if (! ref.field) return v_error("Missing field key during normal validation"); - var err = v_validate_buddy(form, ref.field, ref); - + var f = ref.field; + if (! chk[f]) { + chk[f] = 1; + val_hash['group was_checked'][f] = 1; + val_hash['group was_valid'][f] = 1; + val_hash['group had_error'][f] = 0; + } + var err = v_validate_buddy(form, f, ref, val_hash); if (err.length) { + val_hash['group had_error'][f] = 1; + val_hash['group was_valid'][f] = 0; if (j <= fields.length && typeof(fields[j + 1] != 'object') && fields[j + 1] == 'OR') { hold_error = err; } else { @@ -163,59 +207,37 @@ function v_validate (form, val_hash) { for (var j = 0; j < errors.length; j++) ERRORS.push(errors[j]); } - var m; - for (var j = 0; j < order.length; j++) { - var field = order[j]; - if (! (m = field.match(/^group\s+(\w+)$/))) continue; - if (errors.length == 0 || m[1].match(/^(field|order|title|validate_if)$/)) continue; - EXTRA[m[1]] = val_hash[field]; + for (var field in clean.args) { + if (errors.length == 0 || field.match(/^(field|order|title|validate_if)$/)) continue; + EXTRA[field] = clean.args[field]; } if (ERRORS.length) return new ValidateError(ERRORS, EXTRA); return; } -function v_check_conditional (form, ifs, N_level, ifs_match) { - if (! N_level) N_level = 0; - N_level++; - - if (! ifs) return v_error("Need reference passed to check_conditional"); - if (typeof(ifs) != 'object' || ! ifs.length) ifs = [ifs]; - - var is_found = 1; - var m; +function v_check_conditional (form, ifs, val_hash, ifs_match) { + var is_ok = 1; for (var i = 0; i < ifs.length; i++) { - var ref = ifs[i]; - if (typeof(ref) != 'object') { - if (ref == 'OR') { - if (is_found) i++; - is_found = 1; - continue; - } else { - var field = ref; - ref = {}; - if (m = field.match(/^(\s*!\s*)/)) { - field = field.substring(m[1].length); - ref.max_in_set = [0, field]; - } else { - ref.required = 1; - } - ref.field = field; - } + if (typeof(ifs[i]) == 'function') { + if (! is_ok) break; + if (! ifs[i]({'form':form})) is_ok = 0; + } else if (typeof(ifs[i]) == 'string') { + if (ifs[i] != 'OR') { v_error("Found non-OR string"); return } + if (is_ok) i++; + is_ok = 1; + continue; + } else { + if (! is_ok) break; + var field = ifs[i].field; + field = field.replace(/\$(\d+)/g, function (all, N) { + return (typeof(ifs_match) != 'object' || typeof(ifs_match[N]) == 'undefined') ? '' : ifs_match[N]; + }); + var err = v_validate_buddy(form, field, ifs[i], val_hash); + if (err.length) is_ok = 0; } - if (! is_found) break; - - var field = ref.field; - if (! field) return v_error("Missing field key during validate_if"); - field = field.replace(/\$(\d+)/g, function (all, N) { - return (typeof(ifs_match) != 'object' || typeof(ifs_match[N]) == 'undefined') ? '' : ifs_match[N]; - }); - - v_clean_field_val(ref); - var err = v_validate_buddy(form, field, ref, N_level); - if (err.length) is_found = 0; } - return is_found; + return is_ok; } function v_filter_types (type, types) { @@ -237,15 +259,14 @@ function v_add_error (errors,field,type,field_val,ifs_match,form) { function v_field_order (field_val) { var o = []; - for (var k in field_val) if (! k.match(/^(extend|field|name)$/) && ! k.match(/_error$/)) o.push(k); + for (var k in field_val) + if (! k.match(/^(extend|field|name|required|was_valid|was_checked|had_error)$/) && ! k.match(/_error$/)) o.push(k); return o.sort(); } -function v_validate_buddy (form, field, field_val, N_level, ifs_match) { +function v_validate_buddy (form, field, field_val, val_hash, ifs_match) { var errors = []; - if (! N_level) N_level = 0; - if (++N_level > 10) { v_error("Max dependency level reached " + N_level); return errors } - if (! form.elements || field_val.exclude_js) return errors; + if (! form.elements || field_val.exclude_js) return []; var types = field_val.order || v_field_order(field_val); var m; @@ -253,7 +274,7 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { var not = m[1]; var pat = m[3]; var opt = m[4]; - if (opt.indexOf('e') != -1) { v_error("The e option cannot be used on field "+field); return errors } + if (opt.indexOf('e') != -1) { v_error("The e option cannot be used on field "+field); return [] } opt = opt.replace(/[sg]/g,''); var reg = new RegExp(pat, opt); @@ -261,13 +282,17 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { var _field = form.elements[i].name; if (! _field) continue; if ( (not && ! (m = _field.match(reg))) || (m = _field.match(reg))) { - var err = v_validate_buddy(form, _field, field_val, N_level, m); + var err = v_validate_buddy(form, _field, field_val, val_hash, m); for (var j = 0; j < err.length; j++) errors.push(err[j]); } } return errors; } + if (field_val.was_valid && ! val_hash['group was_valid'][field]) return v_add_error(errors, field, 'was_valid', field_val, ifs_match, form); + if (field_val.had_error && ! val_hash['group had_error'][field]) return v_add_error(errors, field, 'had_error', field_val, ifs_match, form); + if (field_val.was_checked && ! val_hash['group was_checked'][field]) return v_add_error(errors, field, 'was_checked', field_val, ifs_match, form); + var _value = v_get_form_value(form[field]); var modified = 0; @@ -285,7 +310,10 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { for (var i = 0; i < values.length; i++) { if (typeof(values[i]) == 'undefined') continue; var orig = values[i]; - if (! field_val.do_not_trim) values[i] = values[i].replace(/^\s+/,'').replace(/\s+$/,''); + if (! field_val.do_not_trim) { + values[i] = values[i].replace(/^\s+/,''); + if (v_event != 'change') values[i] = values[i].replace(/\s+$/,''); + } if (field_val.trim_control_chars) values[i] = values[i].replace(/\t/g,' ').replace(/[\x00-\x1F]/g,''); if (field_val.to_upper_case) values[i] = values[i].toUpperCase(); if (field_val.to_lower_case) values[i] = values[i].toLowerCase(); @@ -300,7 +328,7 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { var pat = m[2]; var swap = m[3]; var opt = m[4]; - if (opt.indexOf('e') != -1) { v_error("The e option cannot be used on field "+field+", replace "+tests[i]); return errors } + if (opt.indexOf('e') != -1) { v_error("The e option cannot be used on field "+field+", replace "+tests[i]); return [] } var regexp = new RegExp(pat, opt); values[i] = values[i].replace(regexp, swap); } @@ -308,34 +336,32 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { if (orig != values[i]) modified = 1; } - if (modified && n_values == 1) { + if (modified) { var el = form[field]; - if (el && el.type && el.type.match(/(hidden|password|text|textarea|submit)/)) el.value = values[0]; + if (el) v_set_form_value(el, values); } + var needs_val = 0; - var n_vif = 0; var tests = v_filter_types('validate_if', types); for (var i = 0; i < tests.length; i++) { - n_vif++; var ifs = field_val[tests[i]]; - var ret = v_check_conditional(form, ifs, N_level, ifs_match); + var ret = v_check_conditional(form, ifs, val_hash, ifs_match); if (ret) needs_val++; } - if (! needs_val && n_vif) return errors; - - var is_required = ''; - var tests = v_filter_types('required', types); - for (var i = 0; i < tests.length; i++) { - if (! field_val[tests[i]] || field_val[tests[i]] == 0) continue; - is_required = tests[i]; - break; + if (tests.length && ! needs_val) { + if (field_val.vif_disable && val_hash['group was_valid'][field]) v_set_disable(form[field], true); + val_hash['group was_valid'][field] = 0; + return []; } + if (field_val.vif_disable) v_set_disable(form[field], false); + + var is_required = field_val['required'] ? 'required' : ''; if (! is_required) { var tests = v_filter_types('required_if', types); for (var i = 0; i < tests.length; i++) { var ifs = field_val[tests[i]]; - if (! v_check_conditional(form, ifs, N_level, ifs_match)) continue; + if (! v_check_conditional(form, ifs, val_hash, ifs_match)) continue; is_required = tests[i]; break; } @@ -463,7 +489,7 @@ function v_validate_buddy (form, field, field_val, N_level, ifs_match) { if (type.match(/^custom_js\d*$/)) { var value = values.length == 1 ? values[0] : values; if (typeof(_fv) == 'function' - ? ! _fv({'value':value, 'field_val':field_val, 'form':form, 'key':field_val.field}) + ? ! _fv({'value':value, 'field_val':field_val, 'form':form, 'key':field_val.field, 'errors':errors, 'event':v_event}) : ! eval(_fv)) v_add_error(errors, field, type, field_val, ifs_match, form); } } @@ -486,10 +512,8 @@ function v_check_type (value, type, field, form) { } else if (type == 'LOCAL_PART') { if (typeof(value) == 'undefined' || ! value.length) return 0; if (typeof(v_local_part) != 'undefined') return (value.match(v_local_part) ? 1 : 0); - if (value.match(/[^a-z0-9.\-!&+]/)) return 0; - if (value.match(/^[.\-]/)) return 0; - if (value.match(/[.\-&]$/)) return 0; - if (value.match(/(\.-|-\.|\.\.)/)) return 0; + // ignoring all valid quoted string local parts + if (value.match(/[^\w.~!\#\$%\^&*\-=+?]/)) return 0; } else if (type == 'IP') { if (! value) return 0; @@ -503,12 +527,8 @@ function v_check_type (value, type, field, form) { if (! value.match(/^[a-z0-9.-]{4,255}$/)) return 0; if (value.match(/^[.\-]/)) return 0; if (value.match(/(\.-|-\.|\.\.)/)) return 0; - if (! (m = value.match(/\.([a-z]+)$/))) return 0; - value = value.substring(0,value.lastIndexOf('.')); - if (m[1] == 'name') { - if (! value.match(/^[a-z0-9][a-z0-9\-]{0,62}\.[a-z0-9][a-z0-9\-]{0,62}$/)) return 0; - } else - if (! value.match(/^([a-z0-9][a-z0-9\-]{0,62}\.)*[a-z0-9][a-z0-9\-]{0,62}$/)) return 0; + if (! (m = value.match(/^(.+\.)([a-z]{2,10})$/))) return 0; + if (! m[1].match(/^([a-z0-9\-]{1,63}\.)+$/)) return 0; } else if (type == 'URL') { if (! value) return 0; @@ -542,10 +562,46 @@ function v_check_type (value, type, field, form) { return 1; } -function v_get_form_value (el) { - if (! el) return ''; - if (el.disabled) return ''; +function v_set_form_value (el, values, form) { + if (typeof(el) == 'string') el = form[el]; + if (typeof(values) != 'object') values = [values]; + if (! el) return; + var type = (el.type && ! (''+el).match(/HTMLCollection/)) ? el.type.toLowerCase() : ''; + if (el.length && type != 'select-one') { + for (var i = 0; i < el.length; i++) { + if (! el[i] || ! el[i].type) continue; + v_set_form_value(el[i], (el[i].type.match(/^(checkbox|radio)$/) ? values : i < values.length ? [values[i]] : [''])); + } + return; + } + if (! type) return; + if (type.match(/(hidden|password|text|textarea|submit)/)) return el.value = values[0]; + if (type.indexOf('select') != -1) { + if (el.length) for (var i = 0; i < el.length; i++) el[i].selected = (el[i].value == values[0]) ? true : false; + return; + } + if (type == 'checkbox' || type == 'radio') { + var f; for (var i = 0; i < values.length; i++) if (values[i] == el.value) f = 1; + return el.checked = f ? true : false; + } + if (type == 'file') return; + + alert('Unknown form type for '+el.name+': '+type); + return; +} + +function v_set_disable (el, disable) { + if (! el) return var type = el.type ? el.type.toLowerCase() : ''; + if (el.length && type != 'select-one') { + for (var j=0;j