X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fp5-CGI-Ex;a=blobdiff_plain;f=lib%2FCGI%2FEx%2Fvalidate.js;h=72cca57da30c5634834bb5c5f64cf90731a366e3;hp=40b4c04d6d7f92d615df5c69fe65c9e5e7c9849e;hb=490b94ab4051adf93abf16a4ed34efb923d6e8fc;hpb=ad1be93dffb2b25223fb93cbe2a7d349c6b5c127 diff --git a/lib/CGI/Ex/validate.js b/lib/CGI/Ex/validate.js index 40b4c04..72cca57 100644 --- a/lib/CGI/Ex/validate.js +++ b/lib/CGI/Ex/validate.js @@ -1,1066 +1,933 @@ -/**----------------------------------------------------------------*** -* Copyright 2007 - Paul Seamons * -* Distributed under the Perl Artistic License without warranty * -* Based upon CGI/Ex/Validate.pm v1.14 from Perl * -* For instructions on usage, see perldoc of CGI::Ex::Validate * -***----------------------------------------------------------------**/ -// $Revision: 1.42 $ - -function Validate () { - this.error = vob_error; - this.validate = vob_validate; - this.check_conditional = vob_check_conditional; - this.filter_types = vob_filter_types; - this.add_error = vob_add_error; - this.validate_buddy = vob_validate_buddy; - this.check_type = vob_check_type; - this.get_form_value = vob_get_form_value; -} +// Copyright 2007 - Paul Seamons - $Revision: 1.62 $ +// Distributed under the Perl Artistic License without warranty +// See perldoc CGI::Ex::Validate for usage + +var v_did_inline = {}; function ValidateError (errors, extra) { this.errors = errors; this.extra = extra; - this.as_string = eob_as_string; this.as_array = eob_as_array; this.as_hash = eob_as_hash; - this.get_error_text = eob_get_error_text; - this.first_field = eob_first_field; + this.first_field = eob_first_field; } -///----------------------------------------------------------------/// +// -function vob_error (err) { - alert (err); -} - -function vob_validate (form, val_hash) { - if (typeof(val_hash) == 'string') { - if (! document.yaml_load) - return this.error("Cannot parse yaml string - document.yaml_load is not loaded"); - val_hash = document.yaml_load(val_hash); - } +function v_error (err) { alert (err); return 1 } - var ERRORS = new Array (); - var EXTRA = new Array (); - // var USED_GROUPS = new Array(); - - // distinguishing between associative and index based arrays is harder than in perl - if (! val_hash.length) val_hash = new Array(val_hash); - for (var i = 0; i < val_hash.length; i ++) { - var group_val = val_hash[i]; - if (typeof(group_val) != 'object' || group_val.length) return this.error("Validation groups must be a hash"); - var title = group_val['group title']; - var validate_if = group_val['group validate_if']; - - if (validate_if && ! this.check_conditional(form, validate_if)) continue; - // USED_GROUPS.push(group_val); - - /// if the validation items were not passed as an arrayref - /// look for a group order and then fail back to the keys of the group - var fields = group_val['group fields']; - var order = new Array(); - for (var key in group_val) { - if (key == 'extend') continue; // Protoype Array() fix - order[order.length] = key; - } - order = order.sort(); - if (fields) { - if (typeof(fields) != 'object' || ! fields.length) - return this.error("'group fields' must be a non-empty array"); - } else { - fields = new Array(); - var _order = (group_val['group order']) ? group_val['group order'] : order; - if (typeof(_order) != 'object' || ! _order.length) - return this.error("'group order' must be a non-empty array"); - for (var j = 0; j < _order.length; j ++) { - var field = _order[j]; - if (field.match('^(group|general)\\s')) continue; - var field_val = group_val[field]; - if (! field_val) { - if (field == 'OR') field_val = 'OR'; - else return this.error('No element found in group for '+field); - } - if (typeof(field_val) == 'object' && ! field_val['field']) field_val['field'] = field; - fields[fields.length] = field_val; - } - } +function v_clean_val_hash (val_hash) { + if (typeof(val_hash) != 'object') return {error: v_error("Validation must be an associative array (hash)")}; - /// check which fields have been used - var found = new Array(); - for (var j = 0; j < fields.length; j ++) { - var field_val = fields[j]; - var field = field_val['field']; - if (! field) return this.error("Missing field key in validation"); - // if (found[field]) return this.error('Duplicate order found for '+field+' in group order or fields'); - found[field] = 1; - } + var order = []; + 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; + } + order.push(key); + } + order = order.sort(); - /// add any remaining fields from the order - for (var j = 0; j < order.length; j ++) { - var field = order[j]; - if (found[field] || field.match('^(group|general)\\s')) continue; - var field_val = group_val[field]; - if (typeof(field_val) != 'object' || field_val.length) return this.error('Found a non-hash value on field '+field); - if (! field_val['field']) field_val['field'] = field; - fields[fields.length] = field_val; - } + var f = val_hash['group set_hook']; + if (f && typeof(f) == 'string') val_hash['group set_hook'] = eval(f); + f = val_hash['group clear_hook']; + if (f && typeof(f) == 'string') val_hash['group clear_hook'] = eval(f); - /// now lets do the validation - var is_found = 1; - var errors = new Array(); - var hold_error; - - for (var j = 0; j < fields.length; j ++) { - var ref = fields[j]; - if (typeof(ref) != 'object' && ref == 'OR') { - if (is_found) j ++; - is_found = 1; - continue; - } - is_found = 1; - if (! ref['field']) return this.error("Missing field key during normal validation"); - var err = this.validate_buddy(form, ref['field'], ref); - - /// test the error - if errors occur allow for OR - if OR fails use errors from first fail - if (err.length) { - if (j <= fields.length && typeof(fields[j + 1] != 'object') && fields[j + 1] == 'OR') { - hold_error = err; - } else { - if (hold_error) err = hold_error; - for (var k = 0; k < err.length; k ++) errors[errors.length] = err[k]; - hold_error = ''; - } - } else { - hold_error = ''; - } - } + var fields = val_hash['group fields']; + if (fields) { + if (typeof(fields) != 'object' || ! fields.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)}; + } + 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; + } + + for (var i = 0; i < order.length; i++) { + 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 (! 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]); + + return {'fields':fields, 'order':order}; +} - /// add on errors as requested - if (errors.length) { - if (title) ERRORS[ERRORS.length] = title; - for (var j = 0; j < errors.length; j ++) ERRORS[ERRORS.length] = errors[j]; - } +function v_clean_field_val (field_val) { + 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++) { + 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 (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*$/)) { + if (typeof(v) == 'string') field_val[k] = v.split(/\s*\|\|\s*/); + } 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; + var m = v[j].match(/^\s*(!\s*|)m([^\s\w])(.*)\2([eigsmx]*)\s*$/); + if (! m) return {error:v_error("Not sure how to parse that match ("+v[j]+")")}; + var not = m[1]; + var pat = m[3]; + var opt = m[4]; + if (opt.indexOf('e') != -1) return {error:v_error("The e option cannot be used on field "+field_val.field+", test "+k)}; + opt = opt.replace(/[sg]/g,''); + v[j] = new RegExp(pat, opt); + 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); + } + } +} - /// add on general options, and group options if errors in group occurred - var m; - for (var j = 0; j < order.length; j ++) { - var field = order[j]; - if (! (m = field.match('^(general|group)\\s+(\\w+)$'))) continue; - if (m[1] == 'group' && (errors.length == 0 || m[2].match('^(field|order|title)$'))) continue; - EXTRA[m[2]] = group_val[field]; +function v_validate (form, val_hash) { + var clean = v_clean_val_hash(val_hash); + if (clean.error) return; + var order = clean.order; + var fields = clean.fields; + + 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 is_found = 1; + var errors = []; + var hold_error; + + for (var j = 0; j < fields.length; j++) { + var ref = fields[j]; + if (typeof(ref) != 'object' && ref == 'OR') { + if (is_found) j++; + is_found = 1; + 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); + + if (err.length) { + if (j <= fields.length && typeof(fields[j + 1] != 'object') && fields[j + 1] == 'OR') { + hold_error = err; + } else { + if (hold_error) err = hold_error; + for (var k = 0; k < err.length; k++) errors.push(err[k]); + hold_error = ''; } + } else { + hold_error = ''; + } } - /// store any extra items from self - for (var key in this) { - if (key == 'extend') continue; // Protoype Array() fix - if (! key.match('_error$') - && ! key.match('^(raise_error|as_hash_\\w+|as_array_\\w+|as_string_\\w+)$')) continue; - EXTRA[key] = this[key]; + if (errors.length) { + if (title) ERRORS.push(title); + for (var j = 0; j < errors.length; j++) ERRORS.push(errors[j]); } - /// allow for checking for unused keys - // if (EXTRA['no_extra_fields']) - // won't do anything about this for now - let the server handle it + 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]; + } - /// return what they want if (ERRORS.length) return new ValidateError(ERRORS, EXTRA); return; } - -/// allow for optional validation on groups and on individual items -function vob_check_conditional (form, ifs, N_level, ifs_match) { - +function v_check_conditional (form, ifs, N_level, ifs_match) { if (! N_level) N_level = 0; - N_level ++; - - /// can pass a single hash - or an array ref of hashes - if (! ifs) { - return this.error("Need reference passed to check_conditional"); - } else if (typeof(ifs) != 'object') { - ifs = new Array(ifs); - } else if (! ifs.length) { // turn hash into array of hash - ifs = new Array(ifs); - } + N_level++; + + if (! ifs) return v_error("Need reference passed to check_conditional"); + if (typeof(ifs) != 'object' || ! ifs.length) ifs = [ifs]; - /// run the if options here - /// multiple items can be passed - all are required unless OR is used to separate var is_found = 1; var m; - 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 = new Array(); - if (m = field.match('^(\\s*!\\s*)')) { - field = field.substring(m[1].length); - ref['max_in_set'] = '0 of ' + field; - } else { - ref['required'] = 1; - } - ref['field'] = field; - } - } - if (! is_found) break; - - /// get the field - allow for custom variables based upon a match - var field = ref['field']; - if (! field) return this.error("Missing field key during validate_if"); - field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { - if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' - return ifs_match[N]; - }); - - var err = this.validate_buddy(form, field, ref, N_level); - if (err.length) is_found = 0; + 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 (! 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; } -function vob_filter_types (type, types) { - var values = new Array(); +function v_filter_types (type, types) { + var values = []; var regexp = new RegExp('^'+type+'_?\\d*$'); for (var i = 0; i < types.length; i++) - if (types[i].match(regexp)) values[values.length] = types[i]; + if (types[i].match(regexp)) values.push(types[i]); return values; } -function vob_add_error (errors,field,type,field_val,ifs_match,form) { - errors[errors.length] = new Array(field, type, field_val, ifs_match); - if (field_val['clear_on_error']) { - var el = form[field]; - if (el) { - var type = el.type; - if (type && (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit')) - el.value = ''; - } +function v_add_error (errors,field,type,field_val,ifs_match,form) { + errors.push([field, type, field_val, ifs_match]); + if (field_val.clear_on_error) { + var el = form[field]; + if (el && el.type && el.type.match(/(hidden|password|text|textarea|submit)/)) el.value = ''; } + return errors; } -/// this is where the main checking goes on -function vob_validate_buddy (form, field, field_val, N_level, ifs_match) { - if (! N_level) N_level = 0; - if (++ N_level > 10) return this.error("Max dependency level reached " + N_level); - if (! form.elements) return; - - var errors = new Array(); - var types = new Array(); - for (var key in field_val) { - if (key == 'extend') continue; // Protoype Array() fix - types[types.length] = key; - } - types = types.sort(); +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); + return o.sort(); +} - /// allow for not running some tests in the cgi - if (this.filter_types('exclude_js', types).length) return errors; +function v_validate_buddy (form, field, field_val, N_level, 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; + var types = field_val.order || v_field_order(field_val); - /// allow for field names that contain regular expressions var m; - if (m = field.match('^(!\\s*|)m([^\\s\\w])(.*)\\2([eigsmx]*)$')) { - var not = m[1]; - var pat = m[3]; - var opt = m[4]; - if (opt.indexOf('e') != -1) return this.error("The e option cannot be used on field "+field); - opt = opt.replace(new RegExp('[sg]','g'),''); - var reg = new RegExp(pat, opt); - - var keys = new Array(); - for (var i = 0; i < form.elements.length; i ++) { - var _field = form.elements[i].name; - if (! _field) continue; - if ( (not && ! (m = _field.match(reg))) || (m = _field.match(reg))) { - var err = this.validate_buddy(form, _field, field_val, N_level, m); - for (var j = 0; j < err.length; j ++) errors[errors.length] = err[j]; - } - } - return errors; - } + if (m = field.match(/^(!\s*|)m([^\s\w])(.*)\2([eigsmx]*)$/)) { + 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 } + opt = opt.replace(/[sg]/g,''); + var reg = new RegExp(pat, opt); + + for (var i = 0; i < form.elements.length; i++) { + 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); + for (var j = 0; j < err.length; j++) errors.push(err[j]); + } + } + return errors; + } + + var _value = v_get_form_value(form[field]); + var modified = 0; - var _value = this.get_form_value(form[field]); - var values; - if (typeof(_value) == 'object') { - values = _value; - } else { - values = new Array(); - values[values.length] = _value; + if (typeof(field_val['default']) != 'undefined' + && (typeof(_value) == 'undefined' + || (typeof(_value) == 'object' && _value.length == 0) + || ! _value.length)) { + _value = field_val['default']; + modified = 1; } - var n_values = (typeof(_value) == 'undefined') ? 0 : values.length; - /// allow for default value - var tests = this.filter_types('default', types); - if (n_values == 0 || (n_values == 1 && values[0].length == 0)) { - for (var i = 0; i < tests.length; i ++) { - var el = form[field]; - if (! el) continue; - var type = el.type; - if (type && (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit')) - el.value = values[0] = '' + field_val[tests[i]]; - } - } + var values = (typeof(_value) == 'object') ? _value : [_value]; + var n_values = (typeof(_value) == 'undefined') ? 0 : values.length; - /// allow for a few form modifiers - var modified = 0; - for (var i = 0; i < values.length; i ++) { - if (typeof(values[i]) == 'undefined') continue; - if (! this.filter_types('do_not_trim',types).length) - values[i] = values[i].replace('^\\s+','').replace(new RegExp('\\s+$',''),''); - if (this.filter_types('trim_control_chars',types).length) - values[i] = values[i].replace(new RegExp('\t', 'g'),' ').replace(new RegExp('[\\x00-\\x1F]+','g'),''); - if (this.filter_types('to_upper_case',types).length) { - values[i] = values[i].toUpperCase(); - } else if (this.filter_types('to_lower_case',types).length) { - values[i] = values[i].toLowerCase(); - } - } - var tests = this.filter_types('replace', types); - for (var i = 0; i < tests.length; i ++) { - var ref = field_val[tests[i]]; - ref = (typeof(ref) == 'object') ? ref : ref.split(new RegExp('\\s*\\|\\|\\s*')); - for (var j = 0; j < ref.length; j ++) { - if (! (m = ref[j].match('^\\s*s([^\\s\\w])(.+)\\1(.*)\\1([eigmx]*)$'))) - return this.error("Not sure how to parse that replace "+ref[j]); - var pat = m[2]; - var swap = m[3]; - var opt = m[4]; - if (opt.indexOf('e') != -1) - return this.error("The e option cannot be used on field "+field+", replace "+tests[i]); - var regexp = new RegExp(pat, opt); - for (var k = 0; k < values.length; k ++) { - if (values[k].match(regexp)) modified = 1; - values[k] = values[k].replace(regexp,swap); - } - } + 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.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(); + + var tests = v_filter_types('replace', types); + for (var k = 0; k < tests.length; k++) { + var ref = field_val[tests[k]]; + ref = (typeof(ref) == 'object') ? ref : ref.split(/\s*\|\|\s*/); + for (var j = 0; j < ref.length; j++) { + if (! (m = ref[j].match(/^\s*s([^\s\w])(.+)\1(.*)\1([eigmx]*)$/))) + return v_error("Not sure how to parse that replace "+ref[j]); + 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 } + var regexp = new RegExp(pat, opt); + values[i] = values[i].replace(regexp, swap); + } + } + + if (orig != values[i]) modified = 1; } if (modified && n_values == 1) { - var el = form[field]; - var type = el.type; - if (! type) return ''; - if (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit') - el.value = values[0]; + var el = form[field]; + if (el && el.type && el.type.match(/(hidden|password|text|textarea|submit)/)) el.value = values[0]; } - /// only continue if a validate_if is not present or passes test var needs_val = 0; var n_vif = 0; - var tests = this.filter_types('validate_if', types); - for (var i = 0; i < tests.length; i ++) { - n_vif ++; - var ifs = field_val[tests[i]]; - var ret = this.check_conditional(form, ifs, N_level, ifs_match); - if (ret) needs_val ++; + 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); + if (ret) needs_val++; } if (! needs_val && n_vif) return errors; - - /// check for simple existence - /// optionally check only if another condition is met var is_required = ''; - var tests = this.filter_types('required', types); - for (var i = 0; i < tests.length; i ++) { - if (! field_val[tests[i]] || field_val[tests[i]] == 0) continue; + 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 (! 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; is_required = tests[i]; break; + } } - if (! is_required) { - var tests = this.filter_types('required_if', types); - for (var i = 0; i < tests.length; i ++) { - var ifs = field_val[tests[i]]; - if (! this.check_conditional(form, ifs, N_level, ifs_match)) continue; - is_required = tests[i]; - break; + if (is_required) { + var found; + for (var i = 0; i < values.length; i++) { + if (values[i].length) { + found = 1; + break; } - } - if (is_required && (typeof(_value) == 'undefined' - || ((typeof(_value) == 'object' && _value.length == 0) - || ! _value.length))) { - this.add_error(errors, field, is_required, field_val, ifs_match, form); - return errors; + } + if (! found) return v_add_error(errors, field, is_required, field_val, ifs_match, form); } - /// min values check - var tests = this.filter_types('min_values', types); - for (var i = 0; i < tests.length; i ++) { - var n = field_val[tests[i]]; - if (n_values < n) { - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - return errors; - } - } + if (field_val.min_values && n_values < field_val.min_values) + return v_add_error(errors, field, 'min_values', field_val, ifs_match, form); - /// max values check - var tests = this.filter_types('max_values', types); - if (! tests.length) { - tests[tests.length] = 'max_values'; - field_val['max_values'] = 1; - } - for (var i = 0; i < tests.length; i ++) { - var n = field_val[tests[i]]; - if (n_values > n) { - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - return errors; - } - } + if (typeof(field_val.max_values) == 'undefined') field_val.max_values = 1; + if (field_val.max_values && n_values > field_val.max_values) + return v_add_error(errors, field, 'max_values', field_val, ifs_match, form); - /// min_in_set and max_in_set check for (var h = 0; h < 2 ; h++) { - var minmax = (h == 0) ? 'min' : 'max'; - var tests = this.filter_types(minmax+'_in_set', types); - for (var i = 0; i < tests.length; i ++) { - if (! (m = field_val[tests[i]].match('^\\s*(\\d+)(?:\\s*[oO][fF])?\\s+(.+)\\s*$'))) - return this.error("Invalid in_set check "+field_val[tests[i]]); - var n = m[1]; - var _fields = m[2].split(new RegExp('[\\s,]+')); - for (var k = 0; k < _fields.length; k ++) { - var _value = this.get_form_value(form[_fields[k]]); - var _values; - if (typeof(_value) == 'undefined') continue; - if (typeof(_value) == 'object') { - _values = _value; - } else { - _values = new Array(); - _values[_values.length] = _value; - } - for (var l = 0; l < _values.length; l ++) { - var _value = _values[l]; - if (typeof(_value) != 'undefined' && _value.length) n --; - } - } - if ( (minmax == 'min' && n > 0) - || (minmax == 'max' && n < 0)) { - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - return errors; - } - } - } - - // the remaining tests operate on each value of a field - for (var n = 0; n < values.length; n ++) { + var minmax = (h == 0) ? 'min' : 'max'; + var tests = v_filter_types(minmax+'_in_set', types); + for (var i = 0; i < tests.length; i++) { + var a = field_val[tests[i]]; + var n = a[0]; + for (var k = 1; k < a.length; k++) { + var _value = v_get_form_value(form[a[k]]); + var _values; + if (typeof(_value) == 'undefined') continue; + _values = (typeof(_value) == 'object') ? _value : [_value]; + for (var l = 0; l < _values.length; l++) { + var _value = _values[l]; + if (typeof(_value) != 'undefined' && _value.length) n--; + } + } + if ( (minmax == 'min' && n > 0) + || (minmax == 'max' && n < 0)) { + v_add_error(errors, field, tests[i], field_val, ifs_match, form); + return errors; + } + } + } + + 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]; - /// allow for enum types - var tests = this.filter_types('enum', types); - for (var i = 0; i < tests.length; i ++) { - var hold = field_val[tests[i]]; - var _enum = (typeof(hold) == 'object') ? hold : hold.split(new RegExp('\\s*\\|\\|\\s*')); - var is_found = 0; - for (var j = 0; j < _enum.length; j ++) { - if (value != _enum[j]) continue; - is_found = 1; - break; - } - if (! is_found) this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } - - /// field equality test - var tests = this.filter_types('equals', types); - for (var i = 0; i < tests.length; i ++) { - var field2 = field_val[tests[i]]; - var not = field2.match('^!\\s*'); - if (not) field2 = field2.substring(not[0].length); - var success = 0; - if (m = field2.match('^(["\'])(.*)\\1$')) { - if (value == m[2]) success = 1; + 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 (type.match(/^equals\d*$/)) { + var not = _fv.match(/^!\s*/); + if (not) _fv = _fv.substring(not[0].length); + var success = 0; + if (m = _fv.match(/^([\"\'])(.*)\1$/)) { + if (value == m[2]) success = 1; + } else { + var value2 = v_get_form_value(form[_fv]); + if (typeof(value2) == 'undefined') value2 = ''; + if (value == value2) success = 1; + } + if (not && success || ! not && ! success) + v_add_error(errors, field, type, field_val, ifs_match, form); + } + + 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*$/)) { + 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 ( ( not && value.match(_fv[j])) + || (! not && ! value.match(_fv[j]))) v_add_error(errors, field, type, field_val, ifs_match, form); + } + } + + if (type.match(/^compare\d*$/)) { + for (var j = 0; j < _fv.length; j++) { + var comp = _fv[j]; + if (! comp) continue; + var hold = false; + var copy = value; + if (m = comp.match(/^\s*(>|<|[>' ) hold = (copy > m[2]) + else if (m[1] == '<' ) hold = (copy < m[2]) + else if (m[1] == '>=') hold = (copy >= m[2]) + else if (m[1] == '<=') hold = (copy <= m[2]) + else if (m[1] == '!=') hold = (copy != m[2]) + else if (m[1] == '==') hold = (copy == m[2]) + } else if (m = comp.match(/^\s*(eq|ne|gt|ge|lt|le)\s+(.+?)\s*$/)) { + if ( m[2].match(/^\"/)) m[2] = m[2].replace(/^"(.*)"$/,'$1'); + else if (m[2].match(/^\'/)) m[2] = m[2].replace(/^'(.*)'$/,'$1'); + if (m[1] == 'gt') hold = (copy > m[2]) + else if (m[1] == 'lt') hold = (copy < m[2]) + else if (m[1] == 'ge') hold = (copy >= m[2]) + else if (m[1] == 'le') hold = (copy <= m[2]) + else if (m[1] == 'ne') hold = (copy != m[2]) + else if (m[1] == 'eq') hold = (copy == m[2]) } else { - var value2 = this.get_form_value(form[field2]); - if (typeof(value2) == 'undefined') value2 = ''; - if (value == value2) success = 1; - } - if (not && success || ! not && ! success) - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } - - /// length min check - var tests = this.filter_types('min_len', types); - for (var i = 0; i < tests.length; i ++) { - var n = field_val[tests[i]]; - if (value.length < n) this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } - - /// length max check - var tests = this.filter_types('max_len', types); - for (var i = 0; i < tests.length; i ++) { - var n = field_val[tests[i]]; - if (value.length > n) this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } - - /// now do match types - var tests = this.filter_types('match', types); - for (var i = 0; i < tests.length; i ++) { - var ref = field_val[tests[i]]; - ref = (typeof(ref) == 'object') ? ref - : (typeof(ref) == 'function') ? new Array(ref) - : ref.split(new RegExp('\\s*\\|\\|\\s*')); - for (var j = 0; j < ref.length; j ++) { - if (typeof(ref[j]) == 'function') { - if (! value.match(ref[j])) this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } else { - if (! (m = ref[j].match('^\\s*(!\\s*|)m([^\\s\\w])(.*)\\2([eigsmx]*)\\s*$'))) - return this.error("Not sure how to parse that match ("+ref[j]+")"); - var not = m[1]; - var pat = m[3]; - var opt = m[4]; - if (opt.indexOf('e') != -1) - return this.error("The e option cannot be used on field "+field+", test "+tests[i]); - opt = opt.replace(new RegExp('[sg]','g'),''); - var regexp = new RegExp(pat, opt); - if ( ( not && value.match(regexp)) - || (! not && ! value.match(regexp))) { - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - } - } + v_error("Not sure how to compare \""+comp+"\""); + return errors; } + if (! hold) v_add_error(errors, field, type, field_val, ifs_match, form); + } } - /// allow for comparison checks - var tests = this.filter_types('compare', types); - for (var i = 0; i < tests.length; i ++) { - var ref = field_val[tests[i]]; - ref = (typeof(ref) == 'object') ? ref : ref.split(new RegExp('\\s*\\|\\|\\s*')); - for (var j = 0; j < ref.length; j ++) { - var comp = ref[j]; - if (! comp) continue; - var hold = false; - var copy = value; - if (m = comp.match('^\\s*(>|<|[>' ) hold = (copy > m[2]) - else if (m[1] == '<' ) hold = (copy < m[2]) - else if (m[1] == '>=') hold = (copy >= m[2]) - else if (m[1] == '<=') hold = (copy <= m[2]) - else if (m[1] == '!=') hold = (copy != m[2]) - else if (m[1] == '==') hold = (copy == m[2]) - } else if (m = comp.match('^\\s*(eq|ne|gt|ge|lt|le)\\s+(.+?)\\s*$')) { - m[2] = m[2].replace('^(["\'])(.*)\\1$','$1'); - if (m[1] == 'gt') hold = (copy > m[2]) - else if (m[1] == 'lt') hold = (copy < m[2]) - else if (m[1] == 'ge') hold = (copy >= m[2]) - else if (m[1] == 'le') hold = (copy <= m[2]) - else if (m[1] == 'ne') hold = (copy != m[2]) - else if (m[1] == 'eq') hold = (copy == m[2]) - } else { - return this.error("Not sure how to compare \""+comp+"\""); - } - if (! hold) this.add_error(errors, field, tests[i], 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); + } - /// do specific type checks - var tests = this.filter_types('type',types); - for (var i = 0; i < tests.length; i ++) - if (! this.check_type(value, field_val[tests[i]], field, form)) - this.add_error(errors, field, tests[i], field_val, ifs_match, form); - - /// do custom_js type checks - // this will allow for a custom piece of javascript - // 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 - var tests = this.filter_types('custom_js',types); - for (var i = 0; i < tests.length; i ++) - if (! eval(field_val[tests[i]])) - this.add_error(errors, field, tests[i], field_val, ifs_match, form); + // 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*$/)) { + 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}) + : ! eval(_fv)) v_add_error(errors, field, type, field_val, ifs_match, form); + } } - /// all done - time to return return errors; } -/// used to validate specific types -function vob_check_type (value, type, field, form) { +function v_check_type (value, type, field, form) { var m; + type = type.toUpperCase(); - /// do valid email address for our system if (type == 'EMAIL') { - if (! value) return 0; - if (! (m = value.match('^(.+)\@(.+?)$'))) return 0; - if (m[1].length > 60) return 0; - if (m[2].length > 100) return 0; - if (! this.check_type(m[2],'DOMAIN') && ! this.check_type(m[2],'IP')) return 0; - if (! this.check_type(m[1],'LOCAL_PART')) return 0; - - /// the "username" portion of an email address + if (! value) return 0; + if (! (m = value.match(/^(.+)@(.+?)$/))) return 0; + if (m[1].length > 60) return 0; + if (m[2].length > 100) return 0; + if (! v_check_type(m[2],'DOMAIN') && ! v_check_type(m[2],'IP')) return 0; + if (! v_check_type(m[1],'LOCAL_PART')) return 0; + } else if (type == 'LOCAL_PART') { - if (typeof(value) == 'undefined' || ! value.length) return 0; - if (value.match('[^a-z0-9.\\-!&+]')) return 0; - if (value.match('^[.\\-]')) return 0; - if (value.match('[.\\-&]$')) return 0; - if (value.match('(\\.-|-\\.|\\.\\.)')) return 0; + 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; - /// standard IP address } else if (type == 'IP') { - if (! value) return 0; - var dig = value.split(new RegExp('\\.')); - if (dig.length != 4) return 0; - for (var i = 0; i < 4; i ++) - if (typeof(dig[i]) == 'undefined' || dig[i].match('\\D') || dig[i] > 255) return 0; + if (! value) return 0; + var dig = value.split(/\./); + if (dig.length != 4) return 0; + for (var i = 0; i < 4; i++) + if (typeof(dig[i]) == 'undefined' || dig[i].match(/\D/) || dig[i] > 255) return 0; - /// domain name - including tld and subdomains (which are all domains) } else if (type == 'DOMAIN') { - if (! value) return 0; - 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; - } else - if (! value.match('^([a-z0-9][a-z0-9\\-]{0,62}\\.)*[a-z0-9][a-z0-9\\-]{0,62}$')) return 0; - - /// validate a url + if (! value) return 0; + 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; + } else + if (! value.match(/^([a-z0-9][a-z0-9\-]{0,62}\.)*[a-z0-9][a-z0-9\-]{0,62}$/)) return 0; + } else if (type == 'URL') { - if (! value) return 0; - if (! (m = value.match(new RegExp('^https?://([^/]+)','i'),''))) return 0; - value = value.substring(m[0].length); - if (! this.check_type(m[1],'DOMAIN') && ! this.check_type(m[1],'IP')) return 0; - if (value && ! this.check_type(value,'URI')) return 0; + if (! value) return 0; + if (! (m = value.match(/^https?:\/\/([^\/]+)/i))) return 0; + value = value.substring(m[0].length); + var dom = m[1].replace(/:\d+$/).replace(/\.$/); + if (! v_check_type(dom,'DOMAIN') && ! v_check_type(m[1],'IP')) return 0; + if (value && ! v_check_type(value,'URI')) return 0; - /// validate a uri - the path portion of a request } else if (type == 'URI') { - if (! value) return 0; - if (value.match('\\s')) return 0; + if (! value) return 0; + if (value.match(/\s/)) return 0; } else if (type == 'CC') { - if (! value) return 0; - if (value.match('[^\\d\\- ]') || value.length > 16 || value.length < 13) return; - /// simple mod10 check - value = value.replace(new RegExp('[\\- ]','g'), ''); - var sum = 0; - var swc = 0; - - for (var i = value.length - 1; i >= 0; i --) { - if (++ swc > 2) swc = 1; - var y = value.charAt(i) * swc; - if (y > 9) y -= 9; - sum += y; - } - if (sum % 10) return 0; - + if (! value) return 0; + if (value.match(/[^\d\- ]/)) return 0; + value = value.replace(/[\- ]/g, ''); + if (value.length > 16 || value.length < 13) return 0; + // mod10 + var sum = 0; + var swc = 0; + for (var i = value.length - 1; i >= 0; i--) { + if (++swc > 2) swc = 1; + var y = value.charAt(i) * swc; + if (y > 9) y -= 9; + sum += y; + } + if (sum % 10) return 0; } return 1; } -// little routine that will get the values from the form -// it will return multiple values as an array -function vob_get_form_value (el) { +function v_get_form_value (el) { if (! el) return ''; if (el.disabled) return ''; var type = el.type ? el.type.toLowerCase() : ''; if (el.length && type != 'select-one') { - var a = new Array(); - for (var j=0;j'); - - /// now add to the hash - var found = new Array(); - var ret = new Array(); - for (var i = 0; i < errors.length; i ++) { - if (typeof(errors[i]) == 'string') continue; - if (! errors[i].length) continue; - - var field = errors[i][0]; - var type = errors[i][1]; - var field_val = errors[i][2]; - var ifs_match = errors[i][3]; - - if (! field) return alert("Missing field name"); - if (field_val['delegate_error']) { - field = field_val['delegate_error']; - field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { - if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' - return ifs_match[N]; - }); - } - - var text = this.get_error_text(errors[i]); - if (! found[field]) found[field] = new Array(); - if (found[field][text]) continue; - found[field][text] = 1; - - field += suffix; - if (! ret[field]) ret[field] = new Array(); - ret[field].push(text); - } - - /// allow for elements returned as - if (joiner) { - var header = eob_get_val('as_hash_header', extra2, extra1, ''); - var footer = eob_get_val('as_hash_footer', extra2, extra1, ''); - for (var key in ret) { - if (key == 'extend') continue; // Protoype Array() fix - ret[key] = header + ret[key].join(joiner) + footer; - } +function v_find_val () { + var key = arguments[0]; + for (var i = 1; i < arguments.length; i++) { + if (typeof(arguments[i]) == 'string') return arguments[i]; + if (typeof(arguments[i]) == 'undefined') continue; + if (typeof(arguments[i][key]) != 'undefined') return arguments[i][key]; } - - return ret; + return ''; } -/// return a user friendly error message -function eob_get_error_text (err) { - var extra = this.extra; +function v_get_error_text (err, extra1, extra2) { var field = err[0]; var type = err[1]; var field_val = err[2]; var ifs_match = err[3]; var m; - var dig = (m = type.match('(_?\\d+)$')) ? m[1] : ''; + var dig = (m = type.match(/(_?\d+)$/)) ? m[1] : ''; var type_lc = type.toLowerCase(); + var v = field_val[type + dig]; - /// allow for delegated field names - only used for defaults - if (field_val['delegate_error']) { - field = field_val['delegate_error']; - field = field.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { - if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' - return ifs_match[N]; - }); - } - - /// the the name of this thing - var name = (field_val['name']) ? field_val['name'] : "The field " +field; - name = name.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { + if (field_val.delegate_error) { + field = field_val.delegate_error; + field = field.replace(/\$(\d+)/g, function (all, N) { if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' + || typeof(ifs_match[N]) == 'undefined') return '' return ifs_match[N]; + }); + } + + var name = field_val.name || "The field " +field; + name = name.replace(/\$(\d+)/g, function (all, N) { + if (typeof(ifs_match) != 'object' + || typeof(ifs_match[N]) == 'undefined') return '' + return ifs_match[N]; }); + var msg = v_find_val(type + '_error', extra1, extra2); + if (! msg) { + if (dig.length) msg = field_val[type + dig + '_error']; + if (! msg) msg = field_val[type + '_error']; + } + if (msg) { + msg = msg.replace(/\$(\d+)/g, function (all, N) { + if (typeof(ifs_match) != 'object' || typeof(ifs_match[N]) == 'undefined') return ''; + return ifs_match[N]; + }); + msg = msg.replace(/\$field/g, field); + msg = msg.replace(/\$name/g, name); + if (v && typeof(v) == 'string') msg = msg.replace(/\$value/g, v); + return msg; + } + + if (type == 'equals') { + var field2 = field_val["equals" + dig]; + var name2 = field_val["equals" +dig+ "_name"]; + if (! name2) name2 = "the field " +field2; + name2 = name2.replace(/\$(\d+)/g, function (all, N) { + return (typeof(ifs_match) != 'object' || typeof(ifs_match[N]) == 'undefined') ? '' : ifs_match[N]; + }); + return name + " did not equal " + name2 +"."; + } + if (type == 'min_in_set') return "Not enough fields were chosen from the set ("+v[0]+' of '+v.join(", ").replace(/^\d+,\s*/,'')+")"; + if (type == 'max_in_set') return "Too many fields were chosen from the set (" +v[0]+' of '+v.join(", ").replace(/^\d+,\s*/,'')+")"; + + return name + ( + (type == 'required' || type == 'required_if') ? " is required." + : type == 'match' ? " contains invalid characters." + : type == 'compare' ? " did not fit comparison." + : type == 'custom_js' ? " did not match custom_js"+dig+" check." + : type == 'enum' ? " is not in the given list." + : type == 'min_values' ? " had less than "+v+" value"+(v == 1 ? '' : 's')+"." + : type == 'max_values' ? " had more than "+v+" value"+(v == 1 ? '' : 's')+"." + : 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+"." + : alert("Missing error on field "+field+" for type "+type+dig)); +} - /// type can look like "required" or "required2" or "required100023" - /// allow for fallback from required100023_error through required_error - var possible_keys = new Array(type + '_error'); - if (dig.length) possible_keys.unshift(type + dig + '_error'); +// - /// look in the passed hash or self first - for (var i = 0; i < possible_keys.length; i ++) { - var key = possible_keys[i]; - var ret = field_val[key]; - if (! ret) { - if (extra[key]) ret = extra[key]; - else continue; - } - ret = ret.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { - if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' - return ifs_match[N]; - }); - ret = ret.replace(new RegExp('\\$field','g'), field); - ret = ret.replace(new RegExp('\\$name' ,'g'), name); - if (field_val[type + dig] && typeof(field_val[type + dig]) == 'string') - ret = ret.replace(new RegExp('\\$value' ,'g'), field_val[type + dig]); - return ret; - } +function eob_as_string (extra) { + var joiner = v_find_val('as_string_join', extra, this.extra, '\n'); + var header = v_find_val('as_string_header', extra, this.extra, ''); + var footer = v_find_val('as_string_footer', extra, this.extra, ''); + return header + this.as_array(extra).join(joiner) + footer; +} - /// set default messages - if (type == 'required' || type == 'required_if') { - return name + " is required."; - - } else if (type == 'min_values') { - var n = field_val["min_values" + dig]; - var values = (n == 1) ? 'value' : 'values'; - return name + " had less than "+n+" "+values+"."; - - } else if (type == 'max_values') { - var n = field_val["max_values" + dig]; - var values = (n == 1) ? 'value' : 'values'; - return name + " had more than "+n+" "+values+"."; - - } else if (type == 'min_in_set') { - var set = field_val["min_in_set" + dig]; - return "Not enough fields were chosen from the set ("+set+")"; - return "Too many fields were chosen from the set ("+set+")"; - - } else if (type == 'max_in_set') { - var set = field_val["max_in_set" + dig]; - return "Too many fields were chosen from the set ("+set+")"; - - } else if (type == 'enum') { - return name + " is not in the given list."; - - } else if (type == 'equals') { - var field2 = field_val["equals" + dig]; - var name2 = field_val["equals" +dig+ "_name"]; - if (! name2) name2 = "the field " +field2; - name2 = name2.replace(new RegExp('\\$(\\d+)','g'), function (all, N) { - if (typeof(ifs_match) != 'object' - || typeof(ifs_match[N]) == 'undefined') return '' - return ifs_match[N]; - }); - return name + " did not equal " + name2 +"."; +function eob_as_array (extra) { + var errors = this.errors; + var title = v_find_val('as_array_title', extra, this.extra, 'Please correct the following items:'); + + var has_headings; + if (title) has_headings = 1; + else for (var i = 0; i < errors.length; i++) if (typeof(errors[i]) == 'string') has_headings = 1; - } else if (type == 'min_len') { - var n = field_val["min_len"+dig]; - var chars = (n == 1) ? 'character' : 'characters'; - return name + " was less than "+n+" "+chars+"."; + var prefix = v_find_val('as_array_prefix', extra, this.extra, (has_headings ? ' ' : '')); - } else if (type == 'max_len') { - var n = field_val["max_len"+dig]; - var chars = (n == 1) ? 'character' : 'characters'; - return name + " was more than "+n+" "+chars+"."; + var arr = []; + if (title && title.length) arr.push(title); - } else if (type == 'match') { - return name + " contains invalid characters."; + var found = {}; + for (var i = 0; i < errors.length; i++) { + if (typeof(errors[i]) == 'string') { + arr.push(errors[i]); + found = {}; + } else { + var text = v_get_error_text(errors[i], extra, this.extra); + if (found[text]) continue; + found[text] = 1; + arr.push(prefix + text); + } + } - } else if (type == 'compare') { - return name + " did not fit comparison."; + return arr; +} - } else if (type == 'type') { - var _type = field_val["type"+dig]; - return name + " did not match type "+_type+"."; +function eob_as_hash (extra) { + var errors = this.errors; + var suffix = v_find_val('as_hash_suffix', extra, this.extra, '_error'); + var joiner = v_find_val('as_hash_join', extra, this.extra, '
'); + + var found = {}; + var ret = {}; + for (var i = 0; i < errors.length; i++) { + if (typeof(errors[i]) == 'string') continue; + if (! errors[i].length) continue; + + var field = errors[i][0]; + var type = errors[i][1]; + var field_val = errors[i][2]; + var ifs_match = errors[i][3]; + + if (! field) return alert("Missing field name"); + if (field_val['delegate_error']) { + field = field_val['delegate_error']; + field = field.replace(/\$(\d+)/g, function (all, N) { + if (typeof(ifs_match) != 'object' + || typeof(ifs_match[N]) == 'undefined') return '' + return ifs_match[N]; + }); + } - } else if (type == 'custom_js') { - return name + " did not match custom_js"+dig+" check."; + var text = v_get_error_text(errors[i], extra, this.extra); + if (! found[field]) found[field] = {}; + if (found[field][text]) continue; + found[field][text] = 1; + field += suffix; + if (! ret[field]) ret[field] = []; + ret[field].push(text); } - return alert("Missing error on field "+field+" for type "+type+dig); + if (joiner) { + 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() + ret[key] = header + ret[key].join(joiner) + footer; + } + } + + return ret; } function eob_first_field () { for (var i = 0; i < this.errors.length; i++) { - if (typeof(this.errors[i]) != 'object') continue; - if (! this.errors[i][0]) continue; - return this.errors[i][0]; + if (typeof(this.errors[i]) != 'object') continue; + if (! this.errors[i][0]) continue; + return this.errors[i][0]; } return; } -///----------------------------------------------------------------/// +// document.validate = function (form, val_hash) { - // undo previous inline - if (document.did_inline) { - for (var key in document.did_inline) { - if (key == 'extend') continue; // Protoype Array() fix - var el = document.getElementById(key); - if (el) el.innerHTML = ''; - } - document.did_inline = undefined; - } - - // do the validate val_hash = document.load_val_hash(form, val_hash); if (typeof(val_hash) == 'undefined') return true; - if (! document.val_obj) document.val_obj = new Validate(); - var err_obj = document.val_obj.validate(form, val_hash); - // return success + 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 (! err_obj) return true; - // focus var field = err_obj.first_field(); - if (field && form[field] && form[field].focus) form[field].focus(); - - // inline - if (! err_obj.extra.no_inline) { - var d = document.did_inline = new Array(); - var hash = err_obj.as_hash(); - for (var key in hash) { - if (key == 'extend') continue; // Protoype Array() fix - var el = document.getElementById(key); - if (el) el.innerHTML = hash[key]; - d[key] = 1; - } - } - - // alert - if (! err_obj.extra.no_confirm) { - return confirm(err_obj.as_string()) ? false : true; - } else if (! err_obj.extra.no_alert) { - alert(err_obj.as_string()); - return false; - } else if (! err_obj.extra.no_inline) { - return false; + if (field && form[field]) { + if (form[field].focus) form[field].focus(); + else if (form[field].length && form[field][0].focus) form[field][0].focus(); + } + + 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() + v_inline_error_set(key, hash[key], val_hash, form); + } + } + + 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()); + return false; + } else if (! val_hash['group no_inline']) { + return false; } else { - return true; + return true; } } document.load_val_hash = function (form, val_hash) { - // check the form we are using if (! form) return alert('Missing form or form name'); if (typeof(form) == 'string') { - if (! document[form]) return alert('No form by name '+form); - form = document[form]; + if (! document[form]) return alert('No form by name '+form); + form = document[form]; } - // if we already have validation - use it if (form.val_hash) return form.val_hash; - // load in the validation and save it for future use if (typeof(val_hash) != 'object') { - // get the hash from a javascript function - if (typeof(val_hash) == 'function') { - val_hash = val_hash(formname); - } else if (typeof(val_hash) == 'undefined') { - var el; - // get hash from a global js variable - if (typeof(document.validation) != 'undefined') { - val_hash = document.validation; - // get hash from a element by if of validation - } else if (el = document.getElementById('validation')) { - val_hash = el.innerHTML; - val_hash = val_hash.replace(new RegExp('<', 'ig'),'<'); - val_hash = val_hash.replace(new RegExp('>', 'ig'),'>'); - val_hash = val_hash.replace(new RegExp('&','ig'),'&'); - // read hash from - } else { - var order = new Array(); - var str = ''; - var yaml = form.getAttribute('validation'); - if (yaml) { - if (m = yaml.match('^( +)')) yaml = yaml.replace(new RegExp('^'+m[1], 'g'), ''); //unindent - yaml = yaml.replace(new RegExp('\\s*$',''),'\n'); // add trailing - str += yaml; - } - var m; - for (var i = 0; i < form.elements.length; i ++) { - var name = form.elements[i].name; - var yaml = form.elements[i].getAttribute('validation'); - if (! name || ! yaml) continue; - yaml = yaml.replace(new RegExp('\\s*$',''),'\n'); // add trailing - yaml = yaml.replace(new RegExp('^(.)','mg'),' $1'); // indent all - yaml = yaml.replace(new RegExp('^( *[^\\s&*\\[\\{])',''),'\n$1'); // add newline - str += name +':' + yaml; - order[order.length] = name; - } - if (str) val_hash = str + "group order: [" + order.join(', ') + "]\n"; - } - } - if (typeof(val_hash) == 'string') { - if (! document.yaml_load) return; - document.hide_yaml_errors = (! document.show_yaml_errors); - if (location.search && location.search.indexOf('show_yaml_errors') != -1) - document.hide_yaml_errors = 0; - val_hash = document.yaml_load(val_hash); - if (document.yaml_error_occured) return; - } + if (typeof(val_hash) == 'function') { + val_hash = val_hash(formname); + } else if (typeof(val_hash) == 'undefined') { + var el; + if (typeof(document.validation) != 'undefined') { + val_hash = document.validation; + } else if (el = document.getElementById('validation')) { + val_hash = el.innerHTML.replace(/</ig,'<').replace(/>/ig,'>').replace(/&/ig,'&'); + } else { + var order = []; + var str = ''; + var yaml = form.getAttribute('validation'); + if (yaml) { + if (m = yaml.match(/^( +)/)) yaml = yaml.replace(new RegExp('^'+m[1], 'g'), ''); + yaml = yaml.replace(/\s*$/,'\n'); + str += yaml; + } + var m; + for (var i = 0; i < form.elements.length; i++) { + var name = form.elements[i].name; + var yaml = form.elements[i].getAttribute('validation'); + if (! name || ! yaml) continue; + yaml = yaml.replace(/\s*$/,'\n').replace(/^(.)/mg,' $1').replace(/^( *[^\s&*\[\{])/,'\n$1'); + str += name +':' + yaml; + order.push(name); + } + if (str) val_hash = str + "group order: [" + order.join(', ') + "]\n"; + } + } + if (typeof(val_hash) == 'string') { + if (! document.yaml_load) return; + document.hide_yaml_errors = (! document.show_yaml_errors); + if (location.search && location.search.indexOf('show_yaml_errors') != -1) + document.hide_yaml_errors = 0; + val_hash = document.yaml_load(val_hash); + if (document.yaml_error_occured) return; + val_hash = val_hash[0]; + } } - // attach to the form form.val_hash = val_hash; return form.val_hash; } - document.check_form = function (form, val_hash) { - // check the form we are using if (! form) return alert('Missing form or form name'); if (typeof(form) == 'string') { - if (! document[form]) return alert('No form by name '+form); - form = document[form]; + if (! document[form]) return alert('No form by name '+form); + form = document[form]; } - // void call - allow for getting it at run time rather than later - document.load_val_hash(form, val_hash); + val_hash = document.load_val_hash(form, val_hash); + if (! val_hash) return; + + var types = val_hash['group onevent'] || {submit:1}; + if (typeof(types) == 'string') types = types.split(/\s*,\s*/); + if (typeof(types.length) != 'undefined') { + var t = {}; + for (var i = 0; i < types.length; i++) t[types[i]] = 1; + types = t; + } + val_hash['group onevent'] = types; + + if (types.change || types.blur) { + var clean = v_clean_val_hash(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 i = 0; i < clean.fields.length; i++) { + _add(clean.fields[i].field, clean.fields[i]); + 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() + 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); + } + } + + if (types.submit) { + var orig_submit = form.onsubmit || function () { return true }; + form.onsubmit = function (e) { return document.validate(this) && orig_submit(e, this) }; + } +} - // attach handler - var orig_submit = form.onsubmit || function () { return true }; - form.onsubmit = function (e) { return document.validate(this) && orig_submit(e, this) }; +function v_el_attach (el, fvs, form, val_hash) { + if (! el.type) { + if (el.length) for (var i = 0; i < el.length; i++) v_el_attach(el[i], fvs, form, val_hash); + return; + } + var types = val_hash['group onevent']; + var func = function () { + var e = []; + var f = {}; + 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; + } + 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() + v_inline_error_set(k, e[k], val_hash, form); + } + }; + 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; + } } -// the end // +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 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; + var el = document.getElementById(key + v_find_val('as_hash_suffix', val_hash, '_error')); + if (el) el.innerHTML = val; +}