@UNSUPPORTED_BROWSERS
);
-$VERSION = '2.22';
+$VERSION = '2.23';
$DEFAULT_EXT = 'val';
$QR_EXTRA = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/;
### 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') {
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 {
$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};
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;
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
# 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
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
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
=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.
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.
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
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"
-// 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;
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) {
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++) {
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;
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') {
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 {
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) {
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;
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);
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;
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);
}
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;
}
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);
}
}
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;
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++) {
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 : '';
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];
: 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));
}
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);
}
}
- 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());
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) {
}
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) {
}
};
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 = '';
}
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;
}