]> Dogcows Code - chaz/p5-CGI-Ex/commitdiff
CGI::Ex 2.23 v2.23
authorPaul Seamons <perl@seamons.com>
Sat, 22 Dec 2007 00:00:00 +0000 (00:00 +0000)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 9 May 2014 23:46:43 +0000 (17:46 -0600)
17 files changed:
Changes
META.yml
lib/CGI/Ex.pm
lib/CGI/Ex/App.pm
lib/CGI/Ex/Auth.pm
lib/CGI/Ex/Conf.pm
lib/CGI/Ex/Die.pm
lib/CGI/Ex/Dump.pm
lib/CGI/Ex/Fill.pm
lib/CGI/Ex/JSONDump.pm
lib/CGI/Ex/Template.pm
lib/CGI/Ex/Validate.pm
lib/CGI/Ex/validate.js
samples/validate_js_0_tests.html
samples/validate_js_2_onchange.html
samples/validate_js_yaml_3.html
t/1_validate_05_types.t

diff --git a/Changes b/Changes
index d051859ae9f8885007e239fa989c243f12c06bca..60acdaae09591c15b661f7120412f3024ead810e 100644 (file)
--- 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"
index 91afcc02bee7a818a8b76a9dfed4795ad8973354..b314ee3528af7cd9cd244e5d0ebaaa4a4e42db43 100644 (file)
--- 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:
index f8b666cdf06b42db89cb0a355b25ab88353cf6b5..f1842d69ce5935cc8bef70206282bc1de7f20340 100644 (file)
@@ -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
index d03f90a79c62cdb7d7a14589fad93668bb7e488d..cf97e676ea235a3f0443ea6374aedd689f0c3ab4 100644 (file)
@@ -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";
index 33e2a30e03454a1ad97df80b45f0237fd054481e..5e9cbcbb3fdbc7a1e2dbfca37cd15785d0fe60df 100644 (file)
@@ -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';
 
 ###----------------------------------------------------------------###
 
index 365ece2d057b1fbd4640364a1502aea478e5e6d0..872b113ab81c4c2f3ed6a9219cb47baf2304fc1d 100644 (file)
@@ -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';
 
index afeab9b7af58d976c6304b3c63b03bd75dacb8bc..75c3003c60e6ce055a604f58536156861d35c711 100644 (file)
@@ -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;
index 60652725f79678723e160a2a73c1c1dc25ca93b2..18ecb1e8dd8aafa618847b54d430900e11de17a4 100644 (file)
@@ -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);
index 1bc90f31d5b55a0a08c7b514c71e24162c39d0d2..0d1a2109692df1263ab9931b503a217b6ac0692a 100644 (file)
@@ -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);
 };
index 6fdd2d015be65e1eaf4ede87edba8da57420c8df..c2bafee3e56cbd73bd7ec5fe45565be2b9311689 100644 (file)
@@ -17,7 +17,7 @@ use strict;
 use base qw(Exporter);
 
 BEGIN {
-    $VERSION  = '2.22';
+    $VERSION  = '2.23';
 
     @EXPORT = qw(JSONDump);
     @EXPORT_OK = @EXPORT;
index c3acbbc7f9485bd7bb23d14c0dfab8e4b3a7833e..51d644457b94da1e77eaa530bd60518e7b082f0c 100644 (file)
@@ -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;
index 37201955b59166afc36f67fd0f73b5556eed579c..f67a4baa368215838f97aa19d03111c2eab242c6 100644 (file)
@@ -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<had_error>
+
+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<match>
 
 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<was_valid>
+
+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<vif_disable>
+
+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<onevent>
 
 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<no_inline>
 
 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
         = '<span style="font-weight:bold;color:red">!</span>';
-      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
-        = '<span style="font-weight:bold;color:green">+</span>';
-      document.getElementById(key+'_row').style.background
-        = '#ddffdd';
+    document.validate_clear_hook = function (args) {
+      if (args.was_valid) {
+       document.getElementById(args.key+'_img').innerHTML
+         = '<span style="font-weight:bold;color:green">+</span>';
+       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"
index 72cca57da30c5634834bb5c5f64cf90731a366e3..ad80759bdce2a6f822ed4c2f2662aae775852ad2 100644 (file)
@@ -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<el.length;j++) el[i].disabled = disable;
+ } else el.disabled = disable;
+}
+
+function v_get_form_value (el, form) {
+ if (typeof(el) == 'string') el = form[el];
+ if (! el) return '';
+ var type = (el.type && ! (''+el).match(/HTMLCollection/)) ? el.type.toLowerCase() : '';
  if (el.length && type != 'select-one') {
   var a = [];
   for (var j=0;j<el.length;j++) {
@@ -563,6 +627,7 @@ function v_get_form_value (el) {
  if (type.match(/(hidden|password|text|textarea|submit)/)) return el.value;
  if (type.indexOf('select') != -1) {
   if (! el.length) return '';
+  if (el.selectedIndex == -1) return '';
   return el[el.selectedIndex].value;
  }
  if (type == 'checkbox' || type == 'radio') return el.checked ? el.value : '';
@@ -589,7 +654,8 @@ function v_get_error_text (err, extra1, extra2) {
  var ifs_match = err[3];
  var m;
 
- var dig = (m = type.match(/(_?\d+)$/)) ? m[1] : '';
+ var dig = '';
+ if (m = type.match(/^(.+?)(\d+)$/)) { type = m[1] ; dig = m[2] }
  var type_lc = type.toLowerCase();
  var v = field_val[type + dig];
 
@@ -648,6 +714,9 @@ function v_get_error_text (err, extra1, extra2) {
   : type == 'min_len'    ? " was less than "+v+" character"+(v == 1 ? '' : 's')+"."
   : type == 'max_len'    ? " was more than "+v+" character"+(v == 1 ? '' : 's')+"."
   : type == 'type'       ? " did not match type "+v+"."
+  : type == 'had_error'  ? " had no error (but should have had)."
+  : type == 'was_valid'  ? " was not valid."
+  : type == 'was_checked'? " was not checked."
   : alert("Missing error on field "+field+" for type "+type+dig));
 }
 
@@ -752,9 +821,11 @@ document.validate = function (form, val_hash) {
  val_hash = document.load_val_hash(form, val_hash);
  if (typeof(val_hash) == 'undefined') return true;
 
- for (var key in v_did_inline) {
-  if (key == 'extend') continue; // Protoype Array()
-  v_inline_error_clear(key, val_hash, form);
+ if (v_event != 'load') {
+  for (var key in v_did_inline) {
+   if (key == 'extend') continue; // Protoype Array()
+   v_inline_error_clear(key, val_hash, form);
+  }
  }
 
  var err_obj = v_validate(form, val_hash);
@@ -774,7 +845,9 @@ document.validate = function (form, val_hash) {
   }
  }
 
- if (! val_hash['group no_confirm']) {
+ if (v_event == 'load') {
+   return false;
+ } else if (! val_hash['group no_confirm']) {
   return confirm(err_obj.as_string()) ? false : true;
  } else if (! val_hash['group no_alert']) {
   alert(err_obj.as_string());
@@ -878,8 +951,10 @@ document.check_form = function (form, val_hash) {
 
  if (types.submit) {
   var orig_submit = form.onsubmit || function () { return true };
-  form.onsubmit = function (e) { return document.validate(this) && orig_submit(e, this) };
+  form.onsubmit = function (e) { v_event = 'submit'; return document.validate(this) && orig_submit(e, this) };
  }
+
+ if (types.load) { v_event = 'load'; document.validate(form) }
 }
 
 function v_el_attach (el, fvs, form, val_hash) {
@@ -889,18 +964,29 @@ function v_el_attach (el, fvs, form, val_hash) {
  }
  var types = val_hash['group onevent'];
  var func = function () {
+  v_event = 'change';
   var e = [];
   var f = {};
+  var chk = {};
   for (var i = 0; i < fvs.length; i++) {
    var field_val = fvs[i];
-   var _e = v_validate_buddy(form, field_val.field, field_val);
-   for (var j = 0; j < _e.length; j++) e.push(_e[j]);
-   f[field_val.delegate_error || field_val.field] = 1;
-  }
-  if (! e.length) {
-    for (var k in f) v_inline_error_clear(k, val_hash, form);
-    return;
+   var k = field_val.field;
+   if (! chk[k]) {
+    chk[k] = 1;
+    val_hash['group was_checked'][k] = 1;
+    val_hash['group was_valid'][k]   = 1;
+    val_hash['group had_error'][k]   = 0;
+   }
+   var _e = v_validate_buddy(form, k, field_val, val_hash);
+   if (_e.length) {
+    val_hash['group had_error'][k] = 1;
+    val_hash['group was_valid'][k] = 0;
+    for (var j = 0; j < _e.length; j++) e.push(_e[j]);
+   }
+   f[field_val.delegate_error || field_val.field] = _e.length ? 0 : 1;
   }
+  for (var k in f) if (f[k]) v_inline_error_clear(k, val_hash, form);
+  if (! e.length) return;
   e = new ValidateError(e, {});
   e = e.as_hash({as_hash_suffix:"", first_only:(val_hash['group first_only']?1:0)});
   for (var k in e) {
@@ -909,17 +995,19 @@ function v_el_attach (el, fvs, form, val_hash) {
   }
  };
  if (types.blur) el.onblur = func;
- if (types.change) {
-  if (el.type.match(/(password|text|textarea)/)) el.onkeyup = func;
-  else if (el.type.match(/(checkbox|radio)/)) el.onclick = func;
-  else if (el.type.match(/(select)/)) el.onchange = func;
+ if (types.change && ! (''+el).match(/HTMLCollection/)) { // find better way on opera
+  var type = el.type ? el.type.toLowerCase() : '';
+  if (type.match(/(password|text|textarea)/)) el.onkeyup = func;
+  else if (type.match(/(checkbox|radio)/)) el.onclick = func;
+  else if (type.match(/(select)/)) el.onchange = func;
  }
 }
 
 function v_inline_error_clear (key, val_hash, form) {
    delete(v_did_inline[key]);
    var f = val_hash['group clear_hook'] || document.validate_clear_hook;
-   if (typeof(f) == 'function') if (f(key, val_hash, form)) return 1;
+   var g = val_hash['group was_valid'] || {};
+   if (typeof(f) == 'function') if (f({'key':key, 'val_hash':val_hash, 'form':form, was_valid:g[key], 'event':v_event})) return 1;
    var el = document.getElementById(key + v_find_val('as_hash_suffix', val_hash, '_error'));
    if (el) el.innerHTML = '';
 }
@@ -927,7 +1015,7 @@ function v_inline_error_clear (key, val_hash, form) {
 function v_inline_error_set (key, val, val_hash, form) {
    v_did_inline[key] = 1;
    var f = val_hash['group set_hook'] || document.validate_set_hook;
-   if (typeof(f) == 'function') if(f(key, val, val_hash, form)) return 1;
+   if (typeof(f) == 'function') if (f({'key':key, 'value':val, 'val_hash':val_hash, 'form':form, 'event':v_event})) return 1;
    var el = document.getElementById(key + v_find_val('as_hash_suffix', val_hash, '_error'));
    if (el) el.innerHTML = val;
 }
index e9c6604895c56960194ae6aae16c2b92b6e0b30a..5471ec8edc6fefefab6857a9672dbed780374101 100644 (file)
@@ -85,6 +85,26 @@ function run_tests () {
   ok(e, "Got validate_if error");
   ok(e.text1_error == "The field text1 is required.", "Got the right required error");
 
+  var 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.text1_error, "No error on validate_if with had_error and good data");
+
   // required_if
   e = validate({}, {text1:{required_if:'text2'}});
   ok(! e, "No required_if error");
@@ -109,7 +129,7 @@ function run_tests () {
   ok(! e, "No min_values error");
 
   // enum
-  var v = {text1:{'enum':[1, 2, 3]}};
+  v = {text1:{'enum':[1, 2, 3]}};
   e = validate({}, v);
   ok(e, "Got enum error");
   ok(e.text1_error == "The field text1 is not in the given list.", "Got the right enum error");
index 68b8fc1d1a67188764553541e45d54a4e09cd2aa..95a405e7370c183290c6f7532156467e37164eb0 100644 (file)
@@ -115,16 +115,21 @@ if (! document.validate) {
 
 <script src="../lib/CGI/Ex/validate.js"></script>
 <script>
-document.validate_set_hook = function (key) {
-  document.getElementById(key+'_img').innerHTML = '<span style="font-weight:bold;color:red">!</span>';
-  document.getElementById(key+'_row').style.background = '#ffdddd';
+document.validate_set_hook = function (args) {
+  document.getElementById(args.key+'_img').innerHTML = '<span style="font-weight:bold;color:red">!</span>';
+  document.getElementById(args.key+'_row').style.background = '#ffdddd';
 };
-document.validate_clear_hook = function (key) {
-  document.getElementById(key+'_img').innerHTML = '<span style="font-weight:bold;color:green">+</span>';
-  document.getElementById(key+'_row').style.background = '#ddffdd';
+document.validate_clear_hook = function (args) {
+  if (args.was_valid) {
+   document.getElementById(args.key+'_img').innerHTML = '<span style="font-weight:bold;color:green">+</span>';
+   document.getElementById(args.key+'_row').style.background = '#ddffdd';
+  } else {
+   document.getElementById(args.key+'_img').innerHTML = '';
+   document.getElementById(args.key+'_row').style.background = '#fff';
+  }
 };
 document.validation = {
-  "group onevent": 'blur,change,submit',
+  "group onevent": 'change,blur,submit',
   "group no_confirm": 1,
   "group no_alert": 1,
   "group order": ["username", "password", "password2", "email", "email2", "state", "region", "s_r_combo", "enum", "compare", "checkone", "checktwo", "foo"],
@@ -143,6 +148,8 @@ document.validation = {
     match_error: "$name must contain both a letter and a number."
   },
   password2: {
+    validate_if: 'password was_valid',
+    vif_disable: 1,
     name: "Verify password",
     equals: "password",
     equals_name: "password"
@@ -155,6 +162,8 @@ document.validation = {
     type_error: "$name must be a valid email address."
   },
   email2: {
+    validate_if: 'email was_valid',
+    vif_disable: 1,
     name: "Verify email",
     equals: "email",
     equals_name: "email"
@@ -226,6 +235,9 @@ document.validation = {
   }
 };
 if (document.check_form) document.check_form('a');
+// do this in javascript to let the real form through without js
+document.a.password2.disabled = true;
+document.a.email2.disabled = true;
 </script>
 
 </html>
index 02187d1f5ecc673ae8d7effd48db75288016d02c..f4dcaa912cc608fc834f39cb3281836642118fe8 100644 (file)
@@ -27,40 +27,46 @@ if (! document.validate) {
 
 
 <form name=a validation="
-general no_confirm: 1
-general no_alert: 1
-general as_array_prefix: ' -- '
+group onevent: 'submit,change'
+group no_confirm: 1
+group no_alert: 1
+group as_array_prefix: ' -- '
 ">
-<table>
+<table border=0>
 <tr>
-  <td valign=top>Enter a date (YYYY/MM/DD) greater than today:<br>
+  <td colspan=2 valign=top>Enter a date (YYYY/MM/DD) greater than today:<br>
     (<script>var t=new Date();document.writeln(t.toGMTString())</script>)
   </td>
-  <td>
+</tr>
+<tr>
+  <td width=100>
     <input type=text size=20 name=date validation="
  name: Date
  required: 1
+ order: ['match','custom_js']
  match: 'm|^\d\d\d\d/\d\d/\d\d$|'
  match_error: 'Please enter date in YYYY/MM/DD format'
  custom_js: |
   function (args) {
+   if (args.errors.length) return 1;
    var t=new Date();
    var y=t.getYear()+1900;
    var m=t.getMonth() + 1;
    var d=t.getDate();
    if (m<10) m = '0'+m;
    if (d<10) d = '0'+d;
-   (args.value > ''+y+'/'+m+'/'+d) ? 1 : 0;
+   return (args.value > ''+y+'/'+m+'/'+d) ? 1 : 0;
   }
  custom_js_error: The date was not greater than today.
-"><br>
-    <span id=date_error class=error></span>
+">
   </td>
+  <td><span id=date_error class=error></span></td>
 </tr>
 <tr>
-  <td colspan=2 align=right>
+  <td align=right>
     <input type=submit>
   </td>
+  <td>&nbsp;</td>
 </tr>
 </table>
 </form>
index 224710759c4246bdfb3dfe11562ee9884b98f76c..d45041f4ff891b3b465def7f109d7ca6869b3366 100644 (file)
@@ -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);
This page took 0.055984 seconds and 4 git commands to generate.