-// 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;
}