/*
* jQuery.validity v1.3.1
* http://validity.thatscaptaintoyou.com/
* http://github.com/whatgoodisaroad/validity
* * Dual licensed under MIT and GPL
*
* Date: 2013-02-10 (Sunday, 10 February 2013)
*/
(function($, undefined) {
// Default settings:
// /////////////////////////////////////////////////////////////////////////////
var defaults = {
// The default output mode is tooltip because it requires no
// dependencies:
outputMode : "tooltip",
// The this property is set to true, validity will scroll the browser
// viewport so that the first error is visible when validation fails:
scrollTo : false,
// If this setting is true, modal errors will disappear when they are
// clicked on:
modalErrorsClickable : true,
// If a field name cannot be otherwise inferred, this will be used:
defaultFieldName : "This field",
// jQuery selector to filter down to validation-supported elements:
elementSupport : ":text, :password, textarea, select, :radio, :checkbox, input[type='hidden'], input[type='tel'], input[type='email']",
// Function to stringify argments for use when generating error
// messages. Primarily, it just generates pretty date strings:
argToString : function(val) {
return val.getDate ? [ val.getMonth() + 1, val.getDate(), val.getFullYear() ].join("/") : val + "";
},
debugPrivates : false
},
__private;
// Static functions and properties:
// /////////////////////////////////////////////////////////////////////////////
$.validity = {
// Clone the defaults. They can be overridden with the setup function:
settings : $.extend(defaults, {}),
// Built-in library of format-checking tools for use with the match
// validator as well as the nonHtml validator:
patterns : {
intege1 : /^[1-9]\d*$/, // 正整数
intege2 : /^-[1-9]\d*$/, // 负整数
num : /^([+-]?)\d*\.?\d+$/, // 数字
num1 : /^[0-9]\d*$/, // 正数(正整数 + 0)
num2 : /^-[0-9]\d*$/, // 负数(负整数 + 0)
decmal : /^([+-]?)\d*\.\d+$/, // 浮点数
decmal1 : /^[1-9]\d*.\d*|0.\d*[1-9]\d*$/,// 正浮点数
decmal2 : /^-([1-9]\d*.\d*|0.\d*[1-9]\d*)$/,// 负浮点数
decmal3 : /^-?([1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0)$/,// 浮点数
decmal4 : /^([0-9]\d*\.\d+|0)$/,// 非负浮点数(正浮点数 + 0)
decmal5 : /^(-([1-9]\d*.\d*|0.\d*[1-9]\d*))|0?.0+|0$/,// 非正浮点数(负浮点数 + 0)
email : /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/, // 邮件
color : /^[a-fA-F0-9]{6}$/, // 颜色
chinese : /^[\u4E00-\u9FA5\uF900-\uFA2D]+$/, // 仅中文
ascii : /^[\x00-\xFF]+$/, // 仅ACSII字符
zipcode : /^\d{6}$/, // 邮编
mobile : /^(13|14|15|17|18)[0-9]{9}$/, // 手机
ip4 : /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, // ip地址
picture : /(.*)\.(jpg|bmp|gif|ico|pcx|jpeg|tif|png|raw|tga)$/, // 图片
rar : /(.*)\.(rar|zip|7zip|tgz)$/, // 压缩文件
zip : /(.*)\.(zip)$/,
qq : /^[1-9]*[1-9][0-9]*$/, // QQ号码
tel : /^(\d{3}-|\d{4}-)(\d{8}|\d{7})$/, // 国内电话
compfax : /^(\d{3,4}-)?\d{7,8}$/, // 传真
username : /^\w+$/, // 用来用户注册。匹配由数字、26个英文字母或者下划线组成的字符串
username1 : /^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$/,// 用来用户注册。匹配字母、数字、下划线、中文组成,不能以下划线开头和结尾
username2 : /^(?!_)(?!.*?_$)[a-zA-Z0-9_\.\u4e00-\u9fa5]+$/,// 用来用户注册。匹配字母、数字、下划线、点、中文组成,不能以下划线开头和结尾
letter : /^[A-Za-z]+$/, // 字母
letter_u : /^[A-Z]+$/, // 大写字母
letter_l : /^[a-z]+$/, // 小写字母
codeid : /^[a-zA-Z0-9]+$/, // 机构标识。大小写字母及数字
idcard : /^[1-9]{1}([0-9]{14}$|[0-9]{16}([0-9]|[xX]))$/, // 身份证
date : /^((0?\d)|(1[012]))[\/-]([012]?\d|30|31)[\/-]\d{1,4}$/,
url : /^(http?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,
nonHtml : /^[^<>]*$/
},
// Built-in set of default error messages (for use when a message isn't
// specified):
messages : {
require : "#{field} is required.",
// Format validators:
match : "#{field} is in an invalid format.",
integer : "#{field} must be a positive, whole number.",
date : "#{field} must be formatted as a date. (mm/dd/yyyy)",
email : "#{field} must be formatted as an email.",
usd : "#{field} must be formatted as a US Dollar amount.",
url : "#{field} must be formatted as a URL.",
number : "#{field} must be formatted as a number.",
zip : "#{field} must be formatted as a zipcode ##### or #####-####.",
phone : "#{field} must be formatted as a phone number ###-###-####.",
guid : "#{field} must be formatted as a guid like {3F2504E0-4F89-11D3-9A0C-0305E82C3301}.",
time24 : "#{field} must be formatted as a 24 hour time: 23:00.",
time12 : "#{field} must be formatted as a 12 hour time: 12:00 AM/PM",
// Value range messages:
lessThan : "#{field} must be less than #{max}.",
lessThanOrEqualTo : "#{field} must be less than or equal to #{max}.",
greaterThan : "#{field} must be greater than #{min}.",
greaterThanOrEqualTo : "#{field} must be greater than or equal to #{min}.",
range : "#{field} must be between #{min} and #{max}.",
// Value length messages:
tooLong : "#{field} cannot be longer than #{max} characters.",
tooShort : "#{field} cannot be shorter than #{min} characters.",
// Composition validators:
nonHtml : "#{field} cannot contain HTML characters.",
alphabet : "#{field} contains disallowed characters.",
minCharClass : "#{field} cannot have more than #{min} #{charClass} characters.",
maxCharClass : "#{field} cannot have less than #{min} #{charClass} characters.",
// Aggregate validator messages:
equal : "Values don't match.",
distinct : "A value was repeated.",
sum : "Values don't add to #{sum}.",
sumMax : "The sum of the values must be less than #{max}.",
sumMin : "The sum of the values must be greater than #{min}.",
// Radio validator messages:
radioChecked : "The selected value is not valid.",
generic : "Invalid."
},
// Abstract output:
out : {
start : function() {
this.defer("start");
},
end : function(results) {
this.defer("end", results);
},
raise : function($obj, msg) {
this.defer("raise", $obj, msg);
},
raiseAggregate : function($obj, msg) {
this.defer("raiseAggregate", $obj, msg);
},
defer : function(name) {
var v = $.validity, o = v.outputs[v.settings.outputMode];
o[name].apply(o, Array.prototype.slice.call(arguments, 1));
}
},
// Character classes can be used to determine the quantity of a given type
// of character in a string:
charClasses : {
alphabetical : /\w/g,
numeric : /\d/g,
alphanumeric : /[A-Za-z0-9]/g,
symbol : /[^A-Za-z0-9]/g
},
// Object to contain the output modes. The three built-in output modes are
// installed later on in this script.
outputs : {},
// By default, private members are not exposed.
__private : undefined,
// Override the default settings with user-specified ones.
setup : function(options) {
this.settings = $.extend(this.settings, options);
if (this.settings.debugPrivates) {
this.__private = __private;
} else {
this.__private = undefined;
}
},
// Object to store information about ongoing validation. When validation
// starts, this will be set to a report object. When validators fail, they
// will inform this object. When validation is completed, this object will
// contain the information of whether it succeeded:
report : null,
// Determine whether validity is in the middle of validation:
isValidating : function() {
return !!this.report;
},
// Function to prepare validity to start validating:
start : function() {
// The output mode should be notified that validation is starting. This
// usually means that the output mode will erase errors from the
// document in whatever way the mode needs to:
this.out.start();
// Initialize the report object:
this.report = {
errors : 0,
valid : true
};
},
// Function called when validation is over to examine the results and
// clean-up:
end : function() {
// Null coalescence: fix for Issue 5:
var results = this.report || {
errors : 0,
valid : true
};
this.report = null;
// Notify the current output mode that validation is over:
this.out.end(results);
return results;
},
// Remove validiation errors:
clear : function() {
this.start();
this.end();
}
};
// jQuery instance methods:
// /////////////////////////////////////////////////////////////////////////////
$.fn.extend({
// The validity function is how validation can be bound to forms. The user
// may pass in a validation function or, as a shortcut, pass in a string of
// a CSS selector that grabs all the inputs to require:
/**
* */
validity : function(arg, setting) {
function error(msg){
if (window["console"]){
console.error(msg);
}
}
return this.each(
function() {
var op = {
auto : true,
type : 'ajax',
beforeSubmit : function(result) {
},
success : function() {
},
error : function() {
}
};
op = $.extend(op, setting);
// Only operate on forms:
if (this.tagName.toLowerCase() == "form") {
var form = $(this);
var f = null;
// If the user entered a string to select the inputs to
// require, then make the validation logic ad hoc:
if (typeof (arg) == "string") {
f = function() {
$(arg).require();
};
}
// If the user entered a validation function then just call
// that at the appropriate time:
else if ($.isFunction(arg)) {
f = arg;
}
if (arg) {
if (op.auto == true && op.type == 'iframe') {
$('body').append('');
$(this).attr('target', 'hiddenSubmit');
}
$(this).bind("submit", function() {
$.validity.start();
f();
var result = $.validity.end().valid;
var before = op.beforeSubmit(result);
if(before == false){
return false;
}
if (result == true && op.auto == true) {
var submitBtn = $(form.find('[type=submit],.submit'));
var data = form.serialize();
submitBtn.attr('disabled', true).removeClass('btn-primary').addClass('disabled');
if(op.type == 'ajax'){
var url = form.attr('action');
$.ajax({
type : "post",
url : url,
data : data,
dataType : "json",
success : function(obj) {
submitBtn.attr('disabled', false).removeClass('disabled').addClass('btn-primary');
op.success(obj);
},
error : function(a, b, msg) {
submitBtn.attr('disabled', false).removeClass('disabled').addClass('btn-primary');
//if(console){
error('操作失败');
//}
op.error();
}
});
result = false;
}
}
return result;
});
}
}
});
},
// Validators:
// /////////////////////////////////////////////////////////////////////////
// Common validators:
// /////////////////////////////////////////////////////////////////////////
// Validate whether the field has a value.
require : function(msg) {
return validate(this, function(obj) {
return !!$.trim($(obj).val()).length;
}, msg || $.validity.messages.require);
},
// Validate whether the field matches a regex.
match : function(rule, msg) {
// If a default message is to be used:
if (!msg) {
// First grab the generic one:
msg = $.validity.messages.match;
// If there's a more specific one, use that.
if (typeof (rule) === "string" && $.validity.messages[rule]) {
msg = $.validity.messages[rule];
}
}
// If the rule is named, rather than specified:
if (typeof (rule) == "string") {
rule = $.validity.patterns[rule];
}
return validate(this,
// Some of the named rules can be functions, such as 'date'. If the
// discovered rule is a function use it as such. Otherwise, assume
// it's a RegExp.
$.isFunction(rule) ?
function(obj) {
return !obj.value.length || rule(obj.value);
} :
function(obj) {
// Fix for regexes where the global flag is set. Make sure
// to test from the start of the string.
if (rule.global) {
rule.lastIndex = 0;
}
return !obj.value.length || rule.test(obj.value);
},
msg);
},
range : function(min, max, msg) {
return validate(this,
min.getTime && max.getTime ?
// If both arguments are dates then use them that way.
function(obj) {
var d = new Date(obj.value);
return d >= new Date(min) && d <= new Date(max);
} :
min.substring && max.substring && Big ?
// If both arguments are strings then parse them using the
// Arbitrary-Precision library.
function(obj) {
var n = new Big(obj.value);
return (n.greaterThanOrEqualTo(new Big(min)) && n.lessThanOrEqualTo(new Big(max)));
} :
// Otherwise treat them like floats.
function(obj) {
var f = parseFloat(obj.value);
return f >= min && f <= max;
},
msg || format($.validity.messages.range, {
min : $.validity.settings.argToString(min),
max : $.validity.settings.argToString(max)
}));
},
greaterThan : function(min, msg) {
return validate(this,
min.getTime ? function(obj) {
return new Date(obj.value) > min;
} :
min.substring && Big ?
function(obj) {
return new Big(obj.value).greaterThan(new Big(min));
} :
function(obj) {
return parseFloat(obj.value) > min;
},
msg || format($.validity.messages.greaterThan, {
min : $.validity.settings.argToString(min)
}));
},
greaterThanOrEqualTo : function(min, msg) {
return validate(this,
min.getTime ? function(obj) {
return new Date(obj.value) >= min;
} :
min.substring && Big ?
function(obj) {
return new Big(obj.value).greaterThanOrEqualTo(new Big(min));
} :
function(obj) {
return parseFloat(obj.value) >= min;
},
msg || format($.validity.messages.greaterThanOrEqualTo, {
min : $.validity.settings.argToString(min)
}));
},
lessThan : function(max, msg) {
return validate(this,
max.getTime ? function(obj) {
return new Date(obj.value) < max;
} :
max.substring && Big ?
function(obj) {
return new Big(obj.value).lessThan(new Big(max));
} :
function(obj) {
return parseFloat(obj.value) < max;
},
msg || format($.validity.messages.lessThan, {
max : $.validity.settings.argToString(max)
}));
},
lessThanOrEqualTo : function(max, msg) {
return validate(this,
max.getTime ? function(obj) {
return new Date(obj.value) <= max;
} :
max.substring && Big ?
function(obj) {
return new Big(obj.value).lessThanOrEqualTo(new Big(max));
} :
function(obj) {
return parseFloat(obj.value) <= max;
},
msg || format($.validity.messages.lessThanOrEqualTo, {
max : $.validity.settings.argToString(max)
}));
},
maxLength : function(max, msg) {
return validate(this, function(obj) {
return obj.value.length <= max;
}, msg || format($.validity.messages.tooLong, {
max : max
}));
},
minLength : function(min, msg) {
return validate(this, function(obj) {
return obj.value.length >= min;
}, msg || format($.validity.messages.tooShort, {
min : min
}));
},
alphabet : function(alpha, msg) {
var chars = [];
return validate(this, function(obj) {
// For each character in the string, ensure that it's in the
// alphabet definition:
for ( var idx = 0; idx < obj.value.length; ++idx) {
if (alpha.indexOf(obj.value.charAt(idx)) == -1) {
chars.push(obj.value.charAt(idx));
return false;
}
}
return true;
}, msg || format($.validity.messages.alphabet, {
chars : chars.join(", ")
}));
},
minCharClass : function(charClass, min, msg) {
if (typeof (charClass) == "string") {
charClass = charClass.toLowerCase();
if ($.validity.charClasses[charClass]) {
charClass = $.validity.charClasses[charClass];
}
}
return validate(this, function(obj) {
return (obj.value.match(charClass) || []).length >= min;
}, msg || format($.validity.messages.minCharClass, {
min : min,
charClass : charClass
}));
},
maxCharClass : function(charClass, max, msg) {
if (typeof (charClass) == "string") {
charClass = charClass.toLowerCase();
if ($.validity.charClasses[charClass]) {
charClass = $.validity.charClasses[charClass];
}
}
return validate(this, function(obj) {
return (obj.value.match(charClass) || []).length <= max;
}, msg || format($.validity.messages.maxCharClass, {
max : max,
charClass : charClass
}));
},
// Validate that the input does not contain potentially dangerous strings.
nonHtml : function(msg) {
return validate(this,
function(obj) {
return $.validity.patterns.nonHtml.test(obj.value);
},
msg || $.validity.messages.nonHtml);
},
// Aggregate validators:
// /////////////////////////////////////////////////////////////////////////
// Validate that all matched elements bear the same values. Accepts a
// function to transform the values for testing.
equal : function(arg0, arg1) {
var
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
$reduction = (this.reduction || this).filter($.validity.settings.elementSupport),
transform = function(val) {
return val;
},
msg = $.validity.messages.equal;
if ($reduction.length) {
// Figure out what arguments were specified.
if ($.isFunction(arg0)) {
transform = arg0;
if (typeof (arg1) == "string") {
msg = arg1;
}
}
else if (typeof (arg0) == "string") {
msg = arg0;
}
var map = $.map($reduction, function(obj) {
return transform(obj.value);
}),
// Get the first value.
first = map[0], valid = true;
// If any value is not equal to the first value, then they aren't
// all equal, and it's not valid.
for ( var i in map) {
if (map[i] != first) {
valid = false;
}
}
if (!valid) {
raiseAggregateError($reduction, msg);
// The set reduces to zero valid elements.
this.reduction = $([]);
}
}
return this;
},
// Validate that all matched elements bear distinct values. Accepts an
// optional function to transform the values for testing.
distinct : function(arg0, arg1) {
var
// If a reduced set is attached, use it.
// Also, remove unsupported elements.
$reduction = (this.reduction || this).filter($.validity.settings.elementSupport),
transform = function(val) {
return val;
},
msg = $.validity.messages.distinct,
// An empty array to store already-examined values
subMap = [],
// An array with repeated values
repeatedVal = [],
valid = true;
if ($reduction.length) {
// Figure out what arguments were specified.
if ($.isFunction(arg0)) {
transform = arg0;
if (typeof (arg1) == "string") {
msg = arg1;
}
}
else if (typeof (arg0) == "string") {
msg = arg0;
}
// Map all the matched values into an array.
var map = $.map($reduction, function(obj) {
return transform(obj.value);
});
// For each transformed value:
for ( var i1 = 0; i1 < map.length; ++i1) {
// Unless it's an empty string:
if (map[i1].length) {
// For each value we've already looked at:
for ( var i2 = 0; i2 < subMap.length; ++i2) {
// If we've already seen the transformed value:
if (subMap[i2] == map[i1]) {
valid = false;
// Collect repeated values
repeatedVal.push(map[i1]);
}
}
// We looked at the value.
subMap.push(map[i1]);
}
}
if (!valid) {
// Remove duplicates of duplicates
repeatedVal = $.unique(repeatedVal);
// For all repeated values found...
for ( var i = 0, repeatedLength = repeatedVal.length; i < repeatedLength; ++i) {
// raise the error - aggregate will use last repeated value
raiseAggregateError($reduction.filter("[value=\'" + repeatedVal[i] + "\']"), msg);
}
// The set reduces to zero valid elements.
this.reduction = $([]);
}
}
return this;
},
// Validate that the numeric sum of all values is equal to a given value.
sum : function(sum, msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.length && sum != numericSum($reduction)) {
raiseAggregateError($reduction, msg || format($.validity.messages.sum, {
sum : sum
}));
// The set reduces to zero valid elements.
this.reduction = $([]);
}
return this;
},
// Validates an inclusive upper-bound on the numeric sum of the values of
// all matched elements.
sumMax : function(max, msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.length && max < numericSum($reduction)) {
raiseAggregateError($reduction, msg || format($.validity.messages.sumMax, {
max : max
}));
// The set reduces to zero valid elements.
this.reduction = $([]);
}
return this;
},
// Validates an inclusive lower-bound on the numeric sum of the values of
// all matched elements.
sumMin : function(min, msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.length && min > numericSum($reduction)) {
raiseAggregateError($reduction, msg || format($.validity.messages.sumMin, {
min : min
}));
// The set reduces to zero valid elements.
this.reduction = $([]);
}
return this;
},
// Radio group validators:
// /////////////////////////////////////////////////////////////////////////
radioChecked : function(val, msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.is(":radio") && $reduction.find(":checked").val() != val) {
raiseAggregateError($reduction, msg || $.validity.messages.radioChecked);
}
},
radioNotChecked : function(val, msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.is(":radio") && $reduction.filter(":checked").val() == val) {
raiseAggregateError($reduction, msg || $.validity.messages.radioChecked);
}
},
// Checkbox validators:
// /////////////////////////////////////////////////////////////////////////
checkboxChecked : function(msg) {
// If a reduced set is attached, use it. Also, remove unsupported
// elements.
var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport);
if ($reduction.is(":checkbox") && !$reduction.is(":checked")) {
raiseAggregateError($reduction, msg || $.validity.messages.radioChecked);
}
},
// Specialized validators:
// /////////////////////////////////////////////////////////////////////////
// If expression is a function, it will be called on each matched element.
// Otherwise, it is treated as a boolean, and the determines the validity
// of elements in an aggregate fashion.
assert : function(expression, msg) {
// If a reduced set is attached, use it. Do not reduce to supported
// elements.
var $reduction = this.reduction || this;
if ($reduction.length) {
// In the case that 'expression' is a function, use it as a
// regimen on each matched element individually:
if ($.isFunction(expression)) {
return validate(this, expression, msg || $.validity.messages.generic);
}
// Otherwise map it to a boolean and consider this as an aggregate
// validation:
else if (!expression) {
raiseAggregateError($reduction, msg || $.validity.messages.generic);
// The set reduces to zero valid elements.
this.reduction = $([]);
}
}
return this;
},
fail : function(msg) {
return this.assert(false, msg);
}
});
// Private utilities:
// /////////////////////////////////////////////////////////////////////////////
// Do non-aggregate validation. Subject each element in $obj to the regimen.
// Raise the specified error on failures. This function is the heart of
// validity:
function validate($obj, regimen, message) {
var
// If a reduced set is attached, use it Also, remove any unsupported
// elements.
$reduction = ($obj.reduction || $obj).filter($.validity.settings.elementSupport),
// Array to store only elements that pass the regimen.
elements = [];
// For each in the reduction.
$reduction.each(function() {
// If the element passes the regimen, include it in the reduction.
if (regimen(this)) {
elements.push(this);
}
// Else give the element an error message.
else {
raiseError(this, format(message, {
field : infer(this)
}));
}
});
// Attach a (potentially) reduced set of only elements that passed.
$obj.reduction = $(elements);
// Return the full set with attached reduction.
return $obj;
}
// Inform the report object that there was a failure.
function addToReport() {
if ($.validity.isValidating()) {
$.validity.report.errors++;
$.validity.report.valid = false;
}
}
// Inform the report of a failure and display an error according to the idiom
// of the current output mode.
function raiseError(obj, msg) {
addToReport();
$.validity.out.raise($(obj), msg);
}
// Inform the report of a failure and display an aggregate error according to
// the idiom of the current output mode.
function raiseAggregateError($obj, msg) {
addToReport();
$.validity.out.raiseAggregate($obj, msg);
}
// Yield the sum of the values of all fields matched in obj that can be parsed.
function numericSum(obj) {
var accumulator = 0;
obj.each(function() {
var n = parseFloat(this.value);
accumulator += isNaN(n) ? 0 : n;
});
return accumulator;
}
// Using the associative array supplied as the 'obj' argument, replace tokens
// of the format #{} in the 'str' argument with that key's value.
function format(str, obj) {
for ( var p in obj) {
if (obj.hasOwnProperty(p)) {
str = str.replace(new RegExp("#\\{" + p + "\\}", "g"), obj[p]);
}
}
return capitalize(str);
}
// Infer the field name of the passed DOM element. If a title is specified, its
// value is returned. Otherwise, attempt to parse a field name out of the id
// attribute. If that doesn't work, return the default field name in the
// configuration.
function infer(field) {
var $f = $(field), id = $f.prop("id"), ret = $.validity.settings.defaultFieldName;
// Check for title.
if ($f.prop("title").length) {
ret = $f.prop("title");
}
// Check for UpperCamelCase.
else if (/^([A-Z0-9][a-z]*)+$/.test(id)) {
ret = id.replace(/([A-Z0-9])[a-z]*/g, " $&");
}
// Check for lowercase_separated_by_underscores
else if (/^[a-z0-9]+(_[a-z0-9]+)*$/.test(id)) {
var arr = id.split("_");
for ( var i = 0; i < arr.length; ++i) {
arr[i] = capitalize(arr[i]);
}
ret = arr.join(" ");
}
return $.trim(ret);
}
// Capitalize the first character of the string argument.
function capitalize(sz) {
return sz.substring ? sz.substring(0, 1).toUpperCase() + sz.substring(1, sz.length) : sz;
}
__private = {
validate : validate,
addToReport : addToReport,
raiseError : raiseError,
raiseAggregateError : raiseAggregateError,
numericSum : numericSum,
format : format,
infer : infer,
capitalize : capitalize
};
})(jQuery);
// Output modes:
// //////////////////////////////////////////////////////////////
// Each output mode gets its own closure,
// distinct from the validity closure.
// Install the tooltip output.
(function($) {
$.validity.outputs.tooltip = {
tooltipClass : "validity-tooltip",
start : function() {
$("." + $.validity.outputs.tooltip.tooltipClass).remove();
},
end : function(results) {
// If not valid and scrollTo is enabled, scroll the page to the first error.
if (!results.valid && $.validity.settings.scrollTo) {
$(document).scrollTop($("." + $.validity.outputs.tooltip.tooltipClass).offset().top);
}
},
raise : function($obj, msg) {
var pos = $obj.offset();
pos.left += $obj.width() + 18;
pos.top += 8;
$("
" + msg + "
" + "" + "
" + "
").click(function() {
$obj.focus();
$(this).fadeOut();
}).css(pos).hide().appendTo("body").fadeIn();
},
raiseAggregate : function($obj, msg) {
// Just raise the error on the last input.
if ($obj.length) {
this.raise($obj.filter(":last"), msg);
}
}
};
})(jQuery);
// Install the label output.
(function($) {
function getIdentifier($obj) {
return $obj.attr('id').length ? $obj.attr('id') : $obj.attr('name');
}
$.validity.outputs.label = {
cssClass : "error",
start : function() {
// Remove all the existing error labels.
$("." + $.validity.outputs.label.cssClass).remove();
},
end : function(results) {
// If not valid and scrollTo is enabled, scroll the page to the first error.
if (!results.valid && $.validity.settings.scrollTo) {
location.hash = $("." + $.validity.outputs.label.cssClass + ":eq(0)").attr('for');
}
},
raise : function($obj, msg) {
var labelSelector = "." + $.validity.outputs.label.cssClass + "[for='" + getIdentifier($obj) + "']";
// If an error label already exists for the bad input just update its text:
if ($(labelSelector).length) {
$(labelSelector).text(msg);
}
// Otherwize create a new one and stick it after the input:
else {
$("").attr("for", getIdentifier($obj)).addClass($.validity.outputs.label.cssClass).text(msg)
// In the case that the element does not have an id
// then the for attribute in the label will not cause
// clicking the label to focus the element. This line
// will make that happen.
.click(function() {
if ($obj.length) {
$obj[0].select();
}
})
.insertAfter($obj);
}
},
raiseAggregate : function($obj, msg) {
// Just raise the error on the last input.
if ($obj.length) {
this.raise($($obj.get($obj.length - 1)), msg);
}
}
};
})(jQuery);
// Install the modal output.
(function($) {
var
// Class to apply to modal errors.
errorClass = "validity-modal-msg",
// The selector for the element where modal errors will me injected semantically.
container = "body";
$.validity.outputs.modal = {
start : function() {
// Remove all the existing errors.
$("." + errorClass).remove();
},
end : function(results) {
// If not valid and scrollTo is enabled, scroll the page to the first error.
if (!results.valid && $.validity.settings.scrollTo) {
location.hash = $("." + errorClass + ":eq(0)").attr('id');
}
},
raise : function($obj, msg) {
if ($obj.length) {
var off = $obj.offset(), obj = $obj.get(0),
// Design a style object based off of the input's location.
errorStyle = {
left : parseInt(off.left + $obj.width() + 4, 10) + "px",
top : parseInt(off.top - 10, 10) + "px"
};
// Create one and position it next to the input.
$("").addClass(errorClass).css(errorStyle).text(msg).click($.validity.settings.modalErrorsClickable ? function() {
$(this).remove();
} : null).appendTo(container);
}
},
raiseAggregate : function($obj, msg) {
// Just raise the error on the last input.
if ($obj.length) {
this.raise($($obj.get($obj.length - 1)), msg);
}
}
};
})(jQuery);
// Install the summary output
(function($) {
var
// Container contains the summary. This is the element that is shown or hidden.
container = ".validity-summary-container",
// Erroneous refers to an input with an invalid value,
// not the error message itself.
erroneous = "validity-erroneous",
// Selector for erroneous inputs.
errors = "." + erroneous,
// The wrapper for entries in the summary.
wrapper = "",
// Buffer to contain all the error messages that build up during validation.
// When validation ends, it'll be flushed into the summary.
// This way, the summary doesn't flicker empty then fill up.
buffer = [];
$.validity.outputs.summary = {
start : function() {
$(errors).removeClass(erroneous);
buffer = [];
},
end : function(results) {
// Hide the container and empty its summary.
$(container).hide().find("ul").html('');
// If there are any errors at all:
// (Otherwise the container shouldn't be shown):
if (buffer.length) {
// Use integer based iteration for solution to Issue 7.
for ( var i = 0; i < buffer.length; ++i) {
$(wrapper).text(buffer[i]).appendTo(container + " ul");
}
$(container).show();
// If scrollTo is enabled, scroll the page to the first error.
if ($.validity.settings.scrollTo) {
location.hash = $(errors + ":eq(0)").attr("id");
}
}
},
raise : function($obj, msg) {
buffer.push(msg);
$obj.addClass(erroneous);
},
raiseAggregate : function($obj, msg) {
this.raise($obj, msg);
},
container : function() {
document.write("
" + "The form didn't submit for the following reason(s):" + "