-// Copyright 2007 - Paul Seamons - $Revision: 1.74 $
+// Copyright 2003-2012 - Paul Seamons - ver 2.37
// Distributed under the Perl Artistic License without warranty
// See perldoc CGI::Ex::Validate for usage
function v_error (err) { alert (err); return 1 }
-function v_clean_val_hash (val_hash) {
+function v_get_ordered_fields (val_hash) {
if (typeof(val_hash) != 'object') return {error: v_error("Validation must be an associative array (hash)")};
- var order = [];
+ var ARGS = {};
+ var field_keys = [];
+ var m;
for (var key in val_hash) {
- if (key == 'extend') continue; // Protoype Array()
- if (key.match(/^general\s/)) {
- var new_key = key.replace(/^general\s+/, 'group ');
- val_hash[new_key] = val_hash[key];
- delete(val_hash[key]);
- key = new_key;
+ if (!val_hash.hasOwnProperty(key)) continue;
+ if (m = key.match(/^(general|group)\s+(\w+)/)) {
+ ARGS[m[2]] = val_hash[key];
+ continue;
}
- order.push(key);
+ field_keys.push(key);
}
- order = order.sort();
+ field_keys = field_keys.sort();
- var f = val_hash['group set_hook'];
- if (f && typeof(f) == 'string') eval("val_hash['group set_hook'] = "+f);
- f = val_hash['group clear_hook'];
- if (f && typeof(f) == 'string') eval("val_hash['group clear_hook'] = "+f);
+ var f = ARGS.set_hook; if (f && typeof(f) == 'string') eval("ARGS.set_hook = "+f);
+ f = ARGS.clear_hook; if (f && typeof(f) == 'string') eval("ARGS.clear_hook = "+f);
+ f = ARGS.set_all_hook; if (f && typeof(f) == 'string') eval("ARGS.set_all_hook = "+f);
+ f = ARGS.clear_all_hook; if (f && typeof(f) == 'string') eval("ARGS.clear_all_hook = "+f);
- if (f = val_hash['group validate_if']) {
+ if (f = ARGS.validate_if) {
if (typeof(f) == 'string' || ! f.length) f = [f];
var deps = v_clean_cond(f);
}
- var fields = val_hash['group fields'];
- if (fields) {
- if (typeof(fields) != 'object' || ! fields.length)
+ var fields = [];
+ var ref;
+ if (ref = ARGS.fields || ARGS['order']) {
+ if (typeof(ref) != 'object' || ! ref.length)
return {error:v_error("'group fields' must be a non-empty array")};
- } else {
- fields = [];
- var _order = (val_hash['group order']) ? val_hash['group order'] : order;
- if (typeof(_order) != 'object' || ! _order.length)
- return {error:v_error("'group order' must be a non-empty array")};
- for (var i = 0; i < _order.length; i++) {
- var field = _order[i];
- if (field.match(/^group\s/)) continue;
- var field_val = val_hash[field];
- if (! field_val) {
- if (field == 'OR') field_val = 'OR';
- else return {error:v_error('No element found in group for '+field)};
+ for (var i = 0; i < ref.length; i++) {
+ var field = ref[i];
+ if (typeof(field) == 'object') {
+ if (! field.field) return {error:v_error("Missing field key in validation")};
+ fields.push(field);
+ } else if (field == 'OR') {
+ fields.push('OR');
+ } else {
+ var field_val = val_hash[field];
+ if (! field_val) return {error:v_error('No element found in group for '+field)};
+ if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field;
+ fields.push(field_val);
}
- if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field;
- fields.push(field_val);
}
}
var found = {};
for (var i = 0; i < fields.length; i++) {
var field_val = fields[i];
- var field = field_val.field;
- if (! field) return {error:v_error("Missing field key in validation")};
- found[field] = 1;
+ if (typeof(field_val) != 'object') continue;
+ found[field_val.field] = 1;
}
- for (var i = 0; i < order.length; i++) {
- var field = order[i];
- if (found[field] || field.match(/^group\s/)) continue;
+ for (var i = 0; i < field_keys.length; i++) {
+ var field = field_keys[i];
+ if (found[field]) continue;
var field_val = val_hash[field];
if (typeof(field_val) != 'object' || field_val.length) return {error:v_error('Found a non-hash value on field '+field)};
if (! field_val.field) field_val.field = field;
val_hash['group was_valid'] = {};
val_hash['group had_error'] = {};
- return {'fields':fields, 'order':order};
+ return {'fields':fields, 'args':ARGS};
}
function v_clean_field_val (field_val, N_level) {
var k = field_val.order[i];
var v = field_val[k];
if (typeof(v) == 'undefined') return {error:v_error('No matching validation found on field '+field+' for type '+k)};
- if (k.match(/^(min|max)_in_set(\d*)$/)) {
+ if (k.match(/^(min|max)_in_set_?(\d*)$/)) {
if (typeof(v) == 'string') {
if (! (m = v.match(/^\s*(\d+)(?:\s*[oO][fF])?\s+(.+)\s*$/))) return {error:v_error("Invalid "+k+" check "+v)};
field_val[k] = m[2].split(/[\s,]+/);
field_val[k].unshift(m[1]);
}
for (var j = 1; j < field_val[k].length; j++) if (field_val[k][j] != field_val.field) field_val.deps[field_val[k][j]] = 1;
- } else if (k.match(/^(enum|compare)\d*$/)) {
+ } else if (k.match(/^(enum|compare)_?\d*$/)) {
if (typeof(v) == 'string') field_val[k] = v.split(/\s*\|\|\s*/);
- } else if (k.match(/^match\d*$/)) {
+ } else if (k.match(/^match_?\d*$/)) {
if (typeof(v) == 'string') v = field_val[k] = v.split(/\s*\|\|\s*/);
for (var j = 0; j < v.length; j++) {
if (typeof(v[j]) != 'string' || v[j] == '!') continue;
v[j] = new RegExp(pat, opt);
if (not) v.splice(j, 0, '!');
}
- } else if (k.match(/^custom_js\d*$/)) {
+ } else if (k.match(/^custom_js_?\d*$/)) {
if (typeof(v) == 'string' && v.match(/^\s*function\s*\(/)) eval("field_val[k] = "+v);
- } else if (k.match(/^(validate|required)_if\d*$/)) {
+ } 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_validate (form, val_hash) {
- var clean = v_clean_val_hash(val_hash);
+ var clean = v_get_ordered_fields(val_hash);
if (clean.error) return;
- var order = clean.order;
var fields = clean.fields;
var ERRORS = [];
for (var j = 0; j < errors.length; j++) ERRORS.push(errors[j]);
}
- var m;
- for (var j = 0; j < order.length; j++) {
- var field = order[j];
- if (! (m = field.match(/^group\s+(\w+)$/))) continue;
- if (errors.length == 0 || m[1].match(/^(field|order|title|validate_if)$/)) continue;
- EXTRA[m[1]] = val_hash[field];
+ for (var field in clean.args) {
+ if (errors.length == 0 || field.match(/^(field|order|title|validate_if)$/)) continue;
+ EXTRA[field] = clean.args[field];
}
if (ERRORS.length) return new ValidateError(ERRORS, EXTRA);
return values;
}
-function v_add_error (errors,field,type,field_val,ifs_match,form) {
- errors.push([field, type, field_val, ifs_match]);
+function v_add_error (errors,field,type,field_val,ifs_match,form,custom_err) {
+ errors.push([field, type, field_val, ifs_match, custom_err]);
if (field_val.clear_on_error) {
var el = form[field];
if (el && el.type && el.type.match(/(hidden|password|text|textarea|submit)/)) el.value = '';
function v_field_order (field_val) {
var o = [];
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);
+ if (field_val.hasOwnProperty(k) && ! k.match(/^(field|name|required|was_valid|was_checked|had_error)$/) && ! k.match(/_error$/)) o.push(k);
return o.sort();
}
for (var i = 0; i < values.length; i++) {
if (typeof(values[i]) == 'undefined') continue;
var orig = values[i];
- if (! field_val.do_not_trim) values[i] = values[i].replace(/^\s+/,'').replace(/\s+$/,'');
+ if (! field_val.do_not_trim) {
+ values[i] = values[i].replace(/^\s+/,'');
+ if (v_event != 'change') values[i] = values[i].replace(/\s+$/,'');
+ }
if (field_val.trim_control_chars) values[i] = values[i].replace(/\t/g,' ').replace(/[\x00-\x1F]/g,'');
if (field_val.to_upper_case) values[i] = values[i].toUpperCase();
if (field_val.to_lower_case) values[i] = values[i].toLowerCase();
}
}
- for (var i = 0; i < types.length; i++) {
- var type = types[i];
- var _fv = field_val[type];
- for (var n = 0; n < values.length; n++) {
- var value = values[n];
+ for (var n = 0; n < values.length; n++) {
+ var value = values[n];
+
+ if (typeof field_val['enum'] != 'undefined') {
+ var is_found = 0;
+ for (var j = 0; j < field_val['enum'].length; j++) if (value == field_val['enum'][j]) { is_found = 1; break }
+ if (! is_found) {
+ v_add_error(errors, field, 'enum', field_val, ifs_match, form);
+ continue;
+ }
+ }
- if (type.match(/^enum\d*$/)) {
- var is_found = 0;
- for (var j = 0; j < _fv.length; j++) if (value == _fv[j]) { is_found = 1; break }
- if (! is_found) v_add_error(errors, field, type, field_val, ifs_match, form);
+ if (typeof field_val['type'] != 'undefined')
+ if (! v_check_type(value, field_val['type'], field, form)) {
+ v_add_error(errors, field, 'type', field_val, ifs_match, form);
+ continue;
}
- if (type.match(/^equals\d*$/)) {
+ for (var i = 0; i < types.length; i++) {
+ var type = types[i];
+ var _fv = field_val[type];
+
+ if (type.match(/^equals_?\d*$/)) {
var not = _fv.match(/^!\s*/);
if (not) _fv = _fv.substring(not[0].length);
var success = 0;
if (typeof(value2) == 'undefined') value2 = '';
if (value == value2) success = 1;
}
- if (not && success || ! not && ! success)
+ if (not && success || ! not && ! success) {
v_add_error(errors, field, type, field_val, ifs_match, form);
+ break;
+ }
}
if (type == 'min_len' && value.length < _fv) v_add_error(errors, field, 'min_len', field_val, ifs_match, form);
if (type == 'max_len' && value.length > _fv) v_add_error(errors, field, 'max_len', field_val, ifs_match, form);
- if (type.match(/^match\d*$/)) {
+ if (type.match(/^match_?\d*$/)) {
for (var j = 0; j < _fv.length; j++) {
if (typeof(_fv[j]) == 'string') continue;
var not = (j > 0 && typeof(_fv[j-1]) == 'string' && _fv[j-1] == '!') ? 1 : 0;
}
}
- if (type.match(/^compare\d*$/)) {
+ if (type.match(/^compare_?\d*$/)) {
for (var j = 0; j < _fv.length; j++) {
var comp = _fv[j];
if (! comp) continue;
if (! hold) v_add_error(errors, field, type, field_val, ifs_match, form);
}
}
-
- if (type.match(/^type\d*$/))
- if (! v_check_type(value, _fv, field, form))
- v_add_error(errors, field, type, field_val, ifs_match, form);
}
+ }
+ for (var i = 0; i < types.length; i++) {
+ var type = types[i];
+ var _fv = field_val[type];
// the js is evaluated and should return 1 for success
// or 0 for failure - the variables field, value, and field_val (the hash) are available
- if (type.match(/^custom_js\d*$/)) {
+ 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, 'errors':errors, 'event':v_event})
- : ! eval(_fv)) v_add_error(errors, field, type, field_val, ifs_match, form);
+ var err;
+ var ok;
+ try { ok = (typeof _fv == 'function') ? _fv({'value':value, 'field_val':field_val, 'form':form, 'key':field_val.field, 'errors':errors, 'event':v_event}) : eval(_fv) } catch (e) { err = e }
+ if (!ok) v_add_error(errors, field, type, field_val, ifs_match, form, err);
}
}
} else if (type == 'LOCAL_PART') {
if (typeof(value) == 'undefined' || ! value.length) return 0;
if (typeof(v_local_part) != 'undefined') return (value.match(v_local_part) ? 1 : 0);
- if (value.match(/[^a-z0-9.\-!&+]/)) return 0;
- if (value.match(/^[.\-]/)) return 0;
- if (value.match(/[.\-&]$/)) return 0;
- if (value.match(/(\.-|-\.|\.\.)/)) return 0;
+ // ignoring all valid quoted string local parts
+ if (value.match(/[^\w.~!\#\$%\^&*\-=+?]/)) return 0;
} else if (type == 'IP') {
if (! value) return 0;
if (value.match(/^[.\-]/)) return 0;
if (value.match(/(\.-|-\.|\.\.)/)) return 0;
if (! (m = value.match(/^(.+\.)([a-z]{2,10})$/))) return 0;
- if (! m[1].match(/^([a-z0-9\-]{1,62}\.)+$/)) return 0;
+ if (! m[1].match(/^([a-z0-9\-]{1,63}\.)+$/)) return 0;
} else if (type == 'URL') {
if (! value) return 0;
if (! value) return 0;
if (value.match(/\s/)) return 0;
+ } else if (type == 'INT') {
+ if (!value.match(/^-?(?:0|[1-9]\d*)$/)) return 0;
+ if ((value < 0) ? value < -Math.pow(2,31) : value > Math.pow(2,31)-1) return 0;
+ } else if (type == 'UINT') {
+ if (!value.match(/^(?:0|[1-9]\d*)$/)) return 0;
+ if (value > Math.pow(2,32)-1) return 0;
+ } else if (type == 'NUM') {
+ if (!value.match(/^-?(?:0|[1-9]\d*(?:\.\d+)?|0?\.\d+)$/)) return 0;
+
} else if (type == 'CC') {
if (! value) return 0;
if (value.match(/[^\d\- ]/)) return 0;
var type = err[1];
var field_val = err[2];
var ifs_match = err[3];
- var m;
+ if (err.length == 5 && typeof err[4] != 'undefined' && err[4].length) return err[4]; // custom error from throw in custom_js
+ var m;
var dig = '';
- if (m = type.match(/^(.+?)(\d+)$/)) { type = m[1] ; dig = m[2] }
+ if (m = type.match(/^(.+?)(_?\d+)$/)) { type = m[1] ; dig = m[2] }
var type_lc = type.toLowerCase();
var v = field_val[type + dig];
});
}
- var name = field_val.name || "The field " +field;
+ var name = field_val.name;
+ if (! name && (field.match(/\W/) || (field.match(/\d/) && field.match(/\D/)))) {
+ name = "The field " +field;
+ }
+ if (! name) name = field.replace(/_/g, ' ').replace(/\b(\w)/g, function(all,str){return str.toUpperCase()});
name = name.replace(/\$(\d+)/g, function (all, N) {
if (typeof(ifs_match) != 'object'
|| typeof(ifs_match[N]) == 'undefined') return ''
if (! msg) {
if (dig.length) msg = field_val[type + dig + '_error'];
if (! msg) msg = field_val[type + '_error'];
+ if (! msg) msg = field_val['error'];
}
if (msg) {
msg = msg.replace(/\$(\d+)/g, function (all, N) {
var header = v_find_val('as_hash_header', extra, this.extra, '');
var footer = v_find_val('as_hash_footer', extra, this.extra, '');
for (var key in ret) {
- if (key == 'extend') continue; // Protoype Array()
+ if (!ret.hasOwnProperty(key)) continue;
ret[key] = header + ret[key].join(joiner) + footer;
}
}
if (v_event != 'load') {
for (var key in v_did_inline) {
- if (key == 'extend') continue; // Protoype Array()
+ if (!v_did_inline.hasOwnProperty(key)) continue;
v_inline_error_clear(key, val_hash, form);
}
}
var err_obj = v_validate(form, val_hash);
- if (! err_obj) return true;
+ if (! err_obj) {
+ var f = val_hash['group clear_all_hook'] || document.validate_clear_all_hook;
+ if (f) f();
+ return true;
+ }
+
+ var f = val_hash['group set_all_hook'] || document.validate_set_all_hook;
+ if (f) f(err_obj, val_hash, form);
var field = err_obj.first_field();
if (field && form[field]) {
if (! val_hash['group no_inline']) {
var hash = err_obj.as_hash({as_hash_suffix:""});
for (var key in hash) {
- if (key == 'extend') continue; // Protoype Array()
+ if (!hash.hasOwnProperty(key)) continue;
v_inline_error_set(key, hash[key], val_hash, form);
}
}
val_hash['group onevent'] = types;
if (types.change || types.blur) {
- var clean = v_clean_val_hash(val_hash);
+ var clean = v_get_ordered_fields(val_hash);
if (clean.error) return clean.error;
var h = {};
_add = function (k, v) { if (! h[k]) h[k] = []; h[k].push(v) };
for (var j in clean.fields[i].deps) if (j != clean.fields[i].field) _add(j, clean.fields[i]);
}
for (var k in h) {
- if (k == 'extend') continue; // Protoype Array()
+ if (!h.hasOwnProperty(k)) continue;
var el = form[k];
if (! el) return v_error("No form element by the name "+k);
- v_el_attach(el, h[k], form, val_hash);
+ var _change = !types.change ? 0 : typeof(types.change) == 'object' ? types.change[k] : 1;
+ var _blur = !types.blur ? 0 : typeof(types.blur) == 'object' ? types.blur[k] : 1;
+ v_el_attach(el, h[k], form, val_hash, _change, _blur);
}
}
if (types.load) { v_event = 'load'; document.validate(form) }
}
-function v_el_attach (el, fvs, form, val_hash) {
+function v_el_attach (el, fvs, form, val_hash, _change, _blur) {
+ if (!_change && !_blur) return;
if (! el.type) {
- if (el.length) for (var i = 0; i < el.length; i++) v_el_attach(el[i], fvs, form, val_hash);
+ if (el.length) for (var i = 0; i < el.length; i++) v_el_attach(el[i], fvs, form, val_hash, _change, _blur);
return;
}
var types = val_hash['group onevent'];
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 (k == 'extend') continue; // Protoype Array()
+ if (!e.hasOwnProperty(k)) continue;
v_inline_error_set(k, e[k], val_hash, form);
}
};
- if (types.blur) el.onblur = func;
- if (types.change && ! (''+el).match(/HTMLCollection/)) { // find better way on opera
+ if (_blur) el.onblur = func;
+ if (_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;