From 0b04f67c06c1db11969096f07dfc7dbb23bf99ba Mon Sep 17 00:00:00 2001 From: Paul Seamons Date: Sat, 22 Dec 2007 00:00:00 +0000 Subject: [PATCH] CGI::Ex 2.23 --- Changes | 7 + META.yml | 2 +- lib/CGI/Ex.pm | 2 +- lib/CGI/Ex/App.pm | 2 +- lib/CGI/Ex/Auth.pm | 2 +- lib/CGI/Ex/Conf.pm | 2 +- lib/CGI/Ex/Die.pm | 2 +- lib/CGI/Ex/Dump.pm | 2 +- lib/CGI/Ex/Fill.pm | 2 +- lib/CGI/Ex/JSONDump.pm | 2 +- lib/CGI/Ex/Template.pm | 2 +- lib/CGI/Ex/Validate.pm | 103 ++++++++-- lib/CGI/Ex/validate.js | 286 ++++++++++++++++++---------- samples/validate_js_0_tests.html | 22 ++- samples/validate_js_2_onchange.html | 26 ++- samples/validate_js_yaml_3.html | 26 ++- t/1_validate_05_types.t | 23 ++- 17 files changed, 366 insertions(+), 147 deletions(-) diff --git a/Changes b/Changes index d051859..60acdaa 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,10 @@ +2.23 + 2007-12-20 + * Add the onevent load option - make sure validate_if figures into dependencies + * Add was_valid, had_error, and was_checked validation options + * Add vif_disable option. + * Pass more information to clear/set hooks using named args + 2.22 2007-12-14 * Allow for no errors with a username of "0" diff --git a/META.yml b/META.yml index 91afcc0..b314ee3 100644 --- a/META.yml +++ b/META.yml @@ -1,7 +1,7 @@ # http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: CGI-Ex -version: 2.22 +version: 2.23 version_from: lib/CGI/Ex.pm installdirs: site requires: diff --git a/lib/CGI/Ex.pm b/lib/CGI/Ex.pm index f8b666c..f1842d6 100644 --- a/lib/CGI/Ex.pm +++ b/lib/CGI/Ex.pm @@ -24,7 +24,7 @@ use vars qw($VERSION use base qw(Exporter); BEGIN { - $VERSION = '2.22'; + $VERSION = '2.23'; $PREFERRED_CGI_MODULE ||= 'CGI'; @EXPORT = (); @EXPORT_OK = qw(get_form diff --git a/lib/CGI/Ex/App.pm b/lib/CGI/Ex/App.pm index d03f90a..cf97e67 100644 --- a/lib/CGI/Ex/App.pm +++ b/lib/CGI/Ex/App.pm @@ -13,7 +13,7 @@ BEGIN { eval { use Scalar::Util }; } -our $VERSION = '2.22'; +our $VERSION = '2.23'; sub new { my $class = shift || croak "Usage: ".__PACKAGE__."->new"; diff --git a/lib/CGI/Ex/Auth.pm b/lib/CGI/Ex/Auth.pm index 33e2a30..5e9cbcb 100644 --- a/lib/CGI/Ex/Auth.pm +++ b/lib/CGI/Ex/Auth.pm @@ -18,7 +18,7 @@ use MIME::Base64 qw(encode_base64 decode_base64); use Digest::MD5 qw(md5_hex); use CGI::Ex; -$VERSION = '2.22'; +$VERSION = '2.23'; ###----------------------------------------------------------------### diff --git a/lib/CGI/Ex/Conf.pm b/lib/CGI/Ex/Conf.pm index 365ece2..872b113 100644 --- a/lib/CGI/Ex/Conf.pm +++ b/lib/CGI/Ex/Conf.pm @@ -29,7 +29,7 @@ use vars qw($VERSION ); @EXPORT_OK = qw(conf_read conf_write in_cache); -$VERSION = '2.22'; +$VERSION = '2.23'; $DEFAULT_EXT = 'conf'; diff --git a/lib/CGI/Ex/Die.pm b/lib/CGI/Ex/Die.pm index afeab9b..75c3003 100644 --- a/lib/CGI/Ex/Die.pm +++ b/lib/CGI/Ex/Die.pm @@ -23,7 +23,7 @@ use CGI::Ex; use CGI::Ex::Dump qw(debug ctrace dex_html); BEGIN { - $VERSION = '2.22'; + $VERSION = '2.23'; $SHOW_TRACE = 0 if ! defined $SHOW_TRACE; $IGNORE_EVAL = 0 if ! defined $IGNORE_EVAL; $EXTENDED_ERRORS = 1 if ! defined $EXTENDED_ERRORS; diff --git a/lib/CGI/Ex/Dump.pm b/lib/CGI/Ex/Dump.pm index 6065272..18ecb1e 100644 --- a/lib/CGI/Ex/Dump.pm +++ b/lib/CGI/Ex/Dump.pm @@ -17,7 +17,7 @@ use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION use strict; use Exporter; -$VERSION = '2.22'; +$VERSION = '2.23'; @ISA = qw(Exporter); @EXPORT = qw(dex dex_warn dex_text dex_html ctrace dex_trace); @EXPORT_OK = qw(dex dex_warn dex_text dex_html ctrace dex_trace debug); diff --git a/lib/CGI/Ex/Fill.pm b/lib/CGI/Ex/Fill.pm index 1bc90f3..0d1a210 100644 --- a/lib/CGI/Ex/Fill.pm +++ b/lib/CGI/Ex/Fill.pm @@ -24,7 +24,7 @@ use vars qw($VERSION use base qw(Exporter); BEGIN { - $VERSION = '2.22'; + $VERSION = '2.23'; @EXPORT = qw(form_fill); @EXPORT_OK = qw(fill form_fill html_escape get_tagval_by_key swap_tagval_by_key); }; diff --git a/lib/CGI/Ex/JSONDump.pm b/lib/CGI/Ex/JSONDump.pm index 6fdd2d0..c2bafee 100644 --- a/lib/CGI/Ex/JSONDump.pm +++ b/lib/CGI/Ex/JSONDump.pm @@ -17,7 +17,7 @@ use strict; use base qw(Exporter); BEGIN { - $VERSION = '2.22'; + $VERSION = '2.23'; @EXPORT = qw(JSONDump); @EXPORT_OK = @EXPORT; diff --git a/lib/CGI/Ex/Template.pm b/lib/CGI/Ex/Template.pm index c3acbbc..51d6444 100644 --- a/lib/CGI/Ex/Template.pm +++ b/lib/CGI/Ex/Template.pm @@ -25,7 +25,7 @@ use vars qw($VERSION $VOBJS ); -$VERSION = '2.22'; +$VERSION = '2.23'; ### install true symbol table aliases that can be localized *QR_PRIVATE = *Template::Alloy::QR_PRIVATE; diff --git a/lib/CGI/Ex/Validate.pm b/lib/CGI/Ex/Validate.pm index 3720195..f67a4ba 100644 --- a/lib/CGI/Ex/Validate.pm +++ b/lib/CGI/Ex/Validate.pm @@ -22,7 +22,7 @@ use vars qw($VERSION @UNSUPPORTED_BROWSERS ); -$VERSION = '2.22'; +$VERSION = '2.23'; $DEFAULT_EXT = 'val'; $QR_EXTRA = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/; @@ -124,9 +124,13 @@ sub validate { ### Finally we have our arrayref of hashrefs that each have their 'field' key ### now lets do the validation + $self->{'was_checked'} = {}; + $self->{'was_valid'} = {}; + $self->{'had_error'} = {}; my $found = 1; my @errors; my $hold_error; # hold the error for a moment - to allow for an "OR" operation + my %checked; foreach (my $i = 0; $i < @$fields; $i++) { my $ref = $fields->[$i]; if (! ref($ref) && $ref eq 'OR') { @@ -135,15 +139,24 @@ sub validate { next; } $found = 1; - die "Missing field key during normal validation" if ! $ref->{'field'}; + my $field = $ref->{'field'} || die "Missing field key during normal validation"; + if (! $checked{$field}++) { + $self->{'was_checked'}->{$field} = 1; + $self->{'was_valid'}->{$field} = 1; + $self->{'had_error'}->{$field} = 0; + } local $ref->{'was_validated'} = 1; - my $err = $self->validate_buddy($form, $ref->{'field'}, $ref); + my $err = $self->validate_buddy($form, $field, $ref); if ($ref->{'was_validated'} && $what_was_validated) { push @$what_was_validated, $ref; + } else { + $self->{'was_valid'}->{$field} = 0; } ### test the error - if errors occur allow for OR - if OR fails use errors from first fail if ($err) { + $self->{'was_valid'}->{$field} = 0; + $self->{'had_error'}->{$field} = 0; if ($i < $#$fields && ! ref($fields->[$i + 1]) && $fields->[$i + 1] eq 'OR') { $hold_error = $err; } else { @@ -210,7 +223,11 @@ sub check_conditional { $found = 1; # reset next; } else { - if ($ref =~ s/^\s*!\s*//) { + if ($ref =~ /^function\s*\(/) { + next; + } elsif ($ref =~ /^(.*?)\s+(was_valid|had_error|was_checked)$/) { + $ref = {field => $1, $2 => 1}; + } elsif ($ref =~ s/^\s*!\s*//) { $ref = {field => $ref, max_in_set => "0 of $ref"}; } else { $ref = {field => $ref, required => 1}; @@ -261,6 +278,10 @@ sub validate_buddy { return @errors ? \@errors : 0; } + if ($field_val->{was_valid} && ! $self->{'_was_valid'}->{$field}) { return [[$field, 'was_valid', $field_val, $ifs_match]]; } + if ($field_val->{had_error} && ! $self->{'_had_error'}->{$field}) { return [[$field, 'had_error', $field_val, $ifs_match]]; } + if ($field_val->{was_checked} && ! $self->{'_was_checked'}->{$field}) { return [[$field, 'was_checked', $field_val, $ifs_match]]; } + my $values = UNIVERSAL::isa($form->{$field},'ARRAY') ? $form->{$field} : [$form->{$field}]; my $n_values = $#$values + 1; @@ -1491,6 +1512,18 @@ Allows for comparison of two form elements. Can have an optional !. equals => '!domain2', # make sure the fields are not the same } +=item C + +Typically used by a validate_if. Allows for checking if this item has had +an error. + + { + field => 'alt_password', + validate_if => {field => 'password', had_error => 1}, + } + +This is basically the opposite of was_valid. + =item C Allows for regular expression comparison. Multiple matches may @@ -1604,6 +1637,10 @@ if the conditions are met. Works in JS. # SAME as validate_if => {field => 'name', max_in_set => '0 of name'}, + validate_if => 'name was_valid', + # SAME as + validate_if => {field => 'name', was_valid => 1}, + validate_if => {field => 'country', compare => "eq US"}, # only if country's value is equal to US @@ -1633,6 +1670,18 @@ passes validation the item after 'OR' will not be tested. validate_if => [qw(zip OR postalcode)], +=item C + +Typically used by a validate_if. Allows for checking if this item has successfully +been validated. + + { + field => 'password2', + validate_if => {field => 'password', was_valid => 1}, + } + +This is basically the opposite of was_valid. + =back =head1 SPECIAL VALIDATION TYPES @@ -1700,6 +1749,13 @@ being run in the cgi exclude_cgi => 1, } +=item C + +Only functions in javascript. Will mark set the form element to +disabled if validate_if fails. It will mark it as enabled if +validate_if is successful. This item should normally only be used +when onevent includes "change" or "blur". + =back =head1 MODIFYING VALIDATION TYPES @@ -1978,14 +2034,14 @@ a string that will be postpended on to the error string. =item C Defaults to {submit => 1}. This controls when the javascript validation -will take place. May be passed any or all or submit, change, or blur. +will take place. May be passed any or all or load, submit, change, or blur. Multiple events may be passed in the hash. 'group onevent' => {submit => 1, change => 1}', A comma separated string of types may also be passed: - 'group onevent' => 'submit,change,blur', + 'group onevent' => 'submit,change,blur,load', Currently, change and blur will not work for dynamically matched field names such as 'm/\w+/'. Support will be added. @@ -2002,10 +2058,12 @@ inline error. This gives full control over setting inline errors. samples/validate_js_2_onchange.html has a good example of using these hooks. - 'group set_hook' => "function (key, val, val_hash, form) { - alert("Setting error to field "+key); + 'group set_hook' => "function (args) { + alert("Setting error to field "+args.key); }", +The args parameter includes key, value, val_hash, and form. + The document.validate_set_hook option is probably the better option to use, as it helps to separate display functionality out into your html templates rather than storing too much html logic in your CGI. @@ -2016,10 +2074,12 @@ Similar to set_hook, but called when inline error is cleared. Its corresponding default is document.validate_clear_hook. The clear hook is also sampled in samples/validate_js_2_onchange.html - 'group clear_hook' => "function (key, val_hash, form) { - alert("Clear error on field "+key); + 'group clear_hook' => "function (args) { + alert("Clear error on field "+args.key); }", +The args parameter includes key, val_hash, form, and was_valid. + =item C If set to true, the javascript validation will not attempt to generate @@ -2159,20 +2219,25 @@ to submit as normal (fail gracefully). Additionally, there are two hooks that are called when ever an inline error is set or cleared. The following hooks are used in -samples/validate_js_2_onchange.html. +samples/validate_js_2_onchange.html to highlight the row and set an icon. - document.validate_set_hook = function (key, val, val_hash, form) { - document.getElementById(key+'_img').innerHTML + document.validate_set_hook = function (args) { + document.getElementById(args.key+'_img').innerHTML = '!'; - document.getElementById(key+'_row').style.background + document.getElementById(args.key+'_row').style.background = '#ffdddd'; }; - document.validate_clear_hook = function (key, val_hash, form) { - document.getElementById(key+'_img').innerHTML - = '+'; - document.getElementById(key+'_row').style.background - = '#ddffdd'; + document.validate_clear_hook = function (args) { + if (args.was_valid) { + document.getElementById(args.key+'_img').innerHTML + = '+'; + document.getElementById(args.key+'_row').style.background + = '#ddffdd'; + } else { + document.getElementById(args.key+'_img').innerHTML = ''; + document.getElementById(args.key+'_row').style.background = '#fff'; + } }; These hooks can also be set as "group clear_hook" and "group set_hook" diff --git a/lib/CGI/Ex/validate.js b/lib/CGI/Ex/validate.js index 72cca57..ad80759 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 2007 - Paul Seamons - $Revision: 1.73 $ // 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; @@ -34,9 +35,14 @@ function v_clean_val_hash (val_hash) { order = order.sort(); var f = val_hash['group set_hook']; - if (f && typeof(f) == 'string') val_hash['group set_hook'] = eval(f); + if (f && typeof(f) == 'string') eval("val_hash['group set_hook'] = "+f); f = val_hash['group clear_hook']; - if (f && typeof(f) == 'string') val_hash['group clear_hook'] = eval(f); + if (f && typeof(f) == 'string') eval("val_hash['group clear_hook'] = "+f); + + if (f = val_hash['group validate_if']) { + if (typeof(f) == 'string' || ! f.length) f = [f]; + var deps = v_clean_cond(f); + } var fields = val_hash['group fields']; if (fields) { @@ -72,17 +78,21 @@ function v_clean_val_hash (val_hash) { var field = order[i]; if (found[field] || field.match(/^group\s/)) 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]); + val_hash['group was_checked'] = {}; + val_hash['group was_valid'] = {}; + val_hash['group had_error'] = {}; + return {'fields':fields, 'order':order}; } -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,11 +123,40 @@ 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); if (clean.error) return; @@ -127,13 +166,14 @@ function v_validate (form, val_hash) { 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 +182,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 { @@ -175,47 +222,28 @@ function v_validate (form, val_hash) { 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 +265,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 +280,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 +288,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; @@ -300,7 +331,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 +339,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 +492,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); } } @@ -503,12 +532,11 @@ 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; + if (! (m = value.match(/^(.+\.)([a-z]{2,10})$/))) return 0; + if (m[2] == 'name') { + if (! m[1].match(/^([a-z0-9\-]{1,62}\.){2}$/)) 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[1].match(/^([a-z0-9\-]{1,62}\.)+$/)) return 0; } else if (type == 'URL') { if (! value) return 0; @@ -542,10 +570,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 diff --git a/samples/validate_js_yaml_3.html b/samples/validate_js_yaml_3.html index 02187d1..f4dcaa9 100644 --- a/samples/validate_js_yaml_3.html +++ b/samples/validate_js_yaml_3.html @@ -27,40 +27,46 @@ if (! document.validate) {
- +
- - + + + - +
Enter a date (YYYY/MM/DD) greater than today:
+
Enter a date (YYYY/MM/DD) greater than today:
()
+

- +">
+  
diff --git a/t/1_validate_05_types.t b/t/1_validate_05_types.t index 2247107..d45041f 100644 --- a/t/1_validate_05_types.t +++ b/t/1_validate_05_types.t @@ -7,7 +7,7 @@ =cut use strict; -use Test::More tests => 104; +use Test::More tests => 112; use_ok('CGI::Ex::Validate'); @@ -32,6 +32,27 @@ ok(! $e); $e = validate({bar => 1}, $v); ok($e); +$v = {text1 => {required => 1, validate_if => 'text2 was_valid'}, text2 => {validate_if => 'text3'}}; +$e = validate({}, $v); +ok(! $e, "Got no error on validate_if with was_valid"); +$e = validate({text2 => 1}, $v); +ok(! $e, "Got no error on validate_if with was_valid with non-validated data"); +$e = validate({text3 => 1}, $v); +ok(! $e, "Got no error on validate_if with was_valid with validated - bad data"); +$e = validate({text2 => 1, text3 => 1}, $v); +ok(! $e, "Got error on validate_if with was_valid with validated - good data"); +$e = validate({text1 => 1, text2 => 1, text3 => 1}, $v); +ok(! $e, "No error on validate_if with was_valid with validated - good data"); + +$v = {text1 => {required => 1, validate_if => 'text2 had_error'}, text2 => {required => 1}}; +$e = validate({}, $v); +ok($e, "Got error on validate_if with had_error"); +$e = validate({text2 => 1}, $v); +ok(! $e, "No error on validate_if with had_error and bad_data"); +$e = validate({text1 => 1}, $v); +ok($e && ! $e->as_hash->{text1_error}, "No error on validate_if with had_error and good data"); + + ### required_if $v = {foo => {required_if => 'bar'}}; $e = validate({}, $v); -- 2.43.0