(function(factory){
'use strict';
if(typeof window.define==='function'&&window.define.amd){
window.define([ 'jquery' ], factory);
}else if(typeof window.module==='object'&&window.module.exports){
window.module.exports=factory(window.require('jquery'));
}else{
factory(window.jQuery);
}}(function($){
'use strict';
var pendingRequests={};
var ajax;
var trim;
$.extend($.fn, {
tc_validate: function(options){
var tc_validator;
if(! this.length){
return;
}
tc_validator=$.data(this[ 0 ], 'tc_validator');
if(tc_validator){
return tc_validator;
}
this.attr('novalidate', 'novalidate');
tc_validator=new $.tc_validator(options, this[ 0 ]);
$.data(this[ 0 ], 'tc_validator', tc_validator);
if(tc_validator.settings.onsubmit){
this.on('click.tc_validate', ':submit', function(event){
tc_validator.submitButton=event.currentTarget;
if($(this).hasClass('cancel')){
tc_validator.cancelSubmit=true;
}
if($(this).attr('formnovalidate')!==undefined){
tc_validator.cancelSubmit=true;
}});
this.on('submit.tc_validate', function(event){
if(tc_validator.settings.debug){
event.preventDefault();
}
function handle(){
var hidden;
var result;
if(tc_validator.submitButton&&(tc_validator.settings.submitHandler||tc_validator.formSubmitted)){
hidden=$("<input type='hidden'>").attr('name', tc_validator.submitButton.name).val($(tc_validator.submitButton).val()).appendTo(tc_validator.currentForm);
}
if(tc_validator.settings.submitHandler&&! tc_validator.settings.debug){
result=tc_validator.settings.submitHandler.call(tc_validator, tc_validator.currentForm, event);
if(hidden){
hidden.remove();
}
if(result!==undefined){
return result;
}
return false;
}
return true;
}
if(tc_validator.cancelSubmit){
tc_validator.cancelSubmit=false;
return handle();
}
if(tc_validator.form()){
if(tc_validator.pendingRequest){
tc_validator.formSubmitted=true;
return false;
}
return handle();
}
tc_validator.focusInvalid();
return false;
});
}
return tc_validator;
},
tc_valid: function(){
var valid;
var tc_validator;
var errorList;
if($(this[ 0 ]).is('form')){
valid=this.tc_validate().form();
}else{
errorList=[];
valid=true;
tc_validator=$(this[ 0 ].form).tc_validate();
this.each(function(){
valid=tc_validator.element(this)&&valid;
if(! valid){
errorList=errorList.concat(tc_validator.errorList);
}});
tc_validator.errorList=errorList;
}
return valid;
},
tc_rules: function(command, argument){
var element=this[ 0 ];
var isContentEditable=typeof this.attr('contenteditable')!=='undefined'&&this.attr('contenteditable')!=='false';
var settings;
var staticRules;
var existingRules;
var data;
var param;
var filtered;
if(element===null){
return;
}
if(! element.form&&isContentEditable){
element.form=this.closest('form')[ 0 ];
element.name=this.attr('name');
}
if(element.form===null){
return;
}
if(command){
settings=$.data(element.form, 'tc_validator').settings;
staticRules=settings.rules;
existingRules=$.tc_validator.staticRules(element);
switch(command){
case 'add':
$.extend(existingRules, $.tc_validator.normalizeRule(argument));
delete existingRules.messages;
staticRules[ element.name ]=existingRules;
if(argument.messages){
settings.messages[ element.name ]=$.extend(settings.messages[ element.name ], argument.messages);
}
break;
case 'remove':
if(! argument){
delete staticRules[ element.name ];
return existingRules;
}
filtered={};
$.each(argument.split(/\s/), function(index, method){
filtered[ method ]=existingRules[ method ];
delete existingRules[ method ];
});
return filtered;
}}
data=$.tc_validator.normalizeRules($.extend({}, $.tc_validator.classRules(element), $.tc_validator.attributeRules(element), $.tc_validator.dataRules(element), $.tc_validator.staticRules(element)), element);
if(data.required){
param=data.required;
delete data.required;
data=$.extend({ required: param }, data);
}
if(data.remote){
param=data.remote;
delete data.remote;
data=$.extend(data, { remote: param });
}
return data;
}});
trim=function(str){
return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
$.extend($.expr.pseudos||$.expr[ ':' ], {
blank: function(a){
return ! trim('' + $(a).val());
},
filled: function(a){
var val=$(a).val();
return val!==null&&!! trim('' + val);
},
unchecked: function(a){
return ! $(a).prop('checked');
}});
$.tc_validator=function(options, form){
this.settings=$.extend(true, {}, $.tc_validator.defaults, options);
this.currentForm=form;
this.init();
};
$.tc_validator.format=function(source, params){
if(arguments.length===1){
return function(){
var args=$.makeArray(arguments);
args.unshift(source);
return $.tc_validator.format.apply(this, args);
};}
if(params===undefined){
return source;
}
if(arguments.length > 2&&params.constructor!==Array){
params=$.makeArray(arguments).slice(1);
}
if(params.constructor!==Array){
params=[ params ];
}
$.each(params, function(i, n){
source=source.replace(new RegExp('\\{' + i + '\\}', 'g'), function(){
return n;
});
});
return source;
};
$.extend($.tc_validator, {
defaults: {
messages: {},
groups: {},
rules: {},
errorClass: 'error',
pendingClass: 'pending',
validClass: 'valid',
errorElement: 'label',
focusCleanup: false,
focusInvalid: true,
errorContainer: $([]),
errorLabelContainer: $([]),
onsubmit: true,
ignore: ':hidden',
ignoreTitle: false,
onfocusin: function(element){
this.lastActive=element;
if(this.settings.focusCleanup){
if(this.settings.unhighlight){
this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
}
this.hideThese(this.errorsFor(element));
}},
onfocusout: function(element){
if(! this.checkable(element)&&(element.name in this.submitted||! this.optional(element))){
this.element(element);
}},
onkeyup: function(element, event){
var excludedKeys=[ 16, 17, 18, 20, 35, 36, 37, 38, 39, 40, 45, 144, 225 ];
if(( event.which===9&&this.elementValue(element)==='')||$.inArray(event.keyCode, excludedKeys)!==-1){
}else if(element.name in this.submitted||element.name in this.invalid){
this.element(element);
}},
onclick: function(element){
if(element.name in this.submitted){
this.element(element);
}else if(element.parentNode.name in this.submitted){
this.element(element.parentNode);
}},
highlight: function(element, errorClass, validClass){
if(element.type==='radio'){
this.findByName(element.name).addClass(errorClass).removeClass(validClass);
}else{
$(element).addClass(errorClass).removeClass(validClass);
}},
unhighlight: function(element, errorClass, validClass){
if(element.type==='radio'){
this.findByName(element.name).removeClass(errorClass).addClass(validClass);
}else{
$(element).removeClass(errorClass).addClass(validClass);
}}
},
setDefaults: function(settings){
$.extend($.tc_validator.defaults, settings);
},
messages: {
required: 'This field is required.',
remote: 'Please fix this field.',
email: 'Please enter a valid email address.',
url: 'Please enter a valid URL.',
date: 'Please enter a valid date.',
dateISO: 'Please enter a valid date (ISO).',
number: 'Please enter a valid number.',
digits: 'Please enter only digits.',
equalTo: 'Please enter the same value again.',
maxlength: $.tc_validator.format('Please enter no more than {0} characters.'),
maxlengthsingle: $.tc_validator.format('Please enter no more than {0} character.'),
minlength: $.tc_validator.format('Please enter at least {0} characters.'),
minlengthsingle: $.tc_validator.format('Please enter at least {0} character.'),
rangelength: $.tc_validator.format('Please enter a value between {0} and {1} characters long.'),
range: $.tc_validator.format('Please enter a value between {0} and {1}.'),
max: $.tc_validator.format('Please enter a value less than or equal to {0}.'),
min: $.tc_validator.format('Please enter a value greater than or equal to {0}.'),
step: $.tc_validator.format('Please enter a multiple of {0}.')
},
autoCreateRanges: false,
prototype: {
init: function(){
var currentForm=this.currentForm;
var groups={};
var rules;
this.labelContainer=$(this.settings.errorLabelContainer);
this.errorContext=(this.labelContainer.length&&this.labelContainer)||$(this.currentForm);
this.containers=$(this.settings.errorContainer).add(this.settings.errorLabelContainer);
this.submitted={};
this.valueCache={};
this.pendingRequest=0;
this.pending={};
this.invalid={};
this.reset();
this.groups={};
$.each(this.settings.groups, function(key, value){
if(typeof value==='string'){
value=value.split(/\s/);
}
$.each(value, function(index, name){
groups[ name ]=key;
});
});
rules=this.settings.rules;
$.each(rules, function(key, value){
rules[ key ]=$.tc_validator.normalizeRule(value);
});
function delegate(event){
var isContentEditable=typeof $(event.data.that).attr('contenteditable')!=='undefined'&&$(event.data.that).attr('contenteditable')!=='false';
var tc_validator;
var eventType;
var settings;
if(! event.data.that.form&&isContentEditable){
event.data.that.form=$(event.data.that).closest('form')[ 0 ];
event.data.that.name=$(event.data.that).attr('name');
}
if(currentForm!==event.data.that.form){
return;
}
tc_validator=$.data(event.data.that.form, 'tc_validator');
eventType='on' + event.type.replace(/^validate/, '');
settings=tc_validator.settings;
if(settings[ eventType ]&&! $(event.data.that).is(settings.ignore)){
settings[ eventType ].call(tc_validator, event.data.that, event);
}}
$(this.currentForm)
.on('focusin.tc_validate focusout.tc_validate keyup.tc_validate',
":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
"[type='radio'], [type='checkbox'], [contenteditable], [type='button']",
{ that: this },
delegate
)
.on('click.tc_validate', "select, option, [type='radio'], [type='checkbox']", { that: this }, delegate);
if(this.settings.invalidHandler){
$(this.currentForm).on('invalid-form.tc_validate', this.settings.invalidHandler);
}},
form: function(){
this.checkForm();
$.extend(this.submitted, this.errorMap);
this.invalid=$.extend({}, this.errorMap);
if(! this.tc_valid()){
$(this.currentForm).triggerHandler('invalid-form', [ this ]);
}
this.showErrors();
return this.tc_valid();
},
checkForm: function(){
var i;
var elements;
this.prepareForm();
for(i=0, elements=this.elements(); elements[ i ]; i +=1){
this.currentElements=elements;
this.check(elements[ i ]);
}
return this.tc_valid();
},
element: function(element){
var cleanElement=this.clean(element),
checkElement=this.validationTargetFor(cleanElement),
v=this,
result=true,
rs,
group;
if(checkElement===undefined){
delete this.invalid[ cleanElement.name ];
}else{
this.prepareElement(checkElement);
this.currentElements=$(checkElement);
group=this.groups[ checkElement.name ];
if(group){
$.each(this.groups, function(name, testgroup){
if(testgroup===group&&name!==checkElement.name){
cleanElement=v.validationTargetFor(v.clean(v.findByName(name)));
if(cleanElement&&cleanElement.name in v.invalid){
v.currentElements.push(cleanElement);
result=v.check(cleanElement)&&result;
}}
});
}
rs=this.check(checkElement)!==false;
result=result&&rs;
if(rs){
this.invalid[ checkElement.name ]=false;
}else{
this.invalid[ checkElement.name ]=true;
}
if(! this.numberOfInvalids()){
this.toHide=this.toHide.add(this.containers);
}
this.showErrors();
$(element).attr('aria-invalid', ! rs);
}
return result;
},
showErrors: function(errors){
var tc_validator;
if(errors){
tc_validator=this;
$.extend(this.errorMap, errors);
this.errorList=$.map(this.errorMap, function(message, name){
return {
message: message,
element: tc_validator.findByName(name)[ 0 ]
};});
this.successList=$.grep(this.successList, function(element){
return !(element.name in errors);
});
}
if(this.settings.showErrors){
this.settings.showErrors.call(this, this.errorMap, this.errorList);
}else{
this.defaultShowErrors();
}},
resetForm: function(){
var elements;
if($.fn.resetForm){
$(this.currentForm).resetForm();
}
this.invalid={};
this.submitted={};
this.prepareForm();
this.hideErrors();
elements=this.elements().removeData('previousValue').removeAttr('aria-invalid');
this.resetElements(elements);
},
resetElements: function(elements){
var i;
if(this.settings.unhighlight){
for(i=0; elements[ i ]; i +=1){
this.settings.unhighlight.call(this, elements[ i ], this.settings.errorClass, '');
this.findByName(elements[ i ].name).removeClass(this.settings.validClass);
}}else{
elements.removeClass(this.settings.errorClass).removeClass(this.settings.validClass);
}},
numberOfInvalids: function(){
return this.objectLength(this.invalid);
},
objectLength: function(obj){
var count=0,
i;
for(i in obj){
if(obj[ i ]!==undefined&&obj[ i ]!==null&&obj[ i ]!==false){
count +=1;
}}
return count;
},
hideErrors: function(){
this.hideThese(this.toHide);
},
hideThese: function(errors){
errors.not(this.containers).text('');
this.addWrapper(errors).hide();
},
tc_valid: function(){
return this.size()===0;
},
size: function(){
return this.errorList.length;
},
focusInvalid: function(){
if(this.settings.focusInvalid){
try {
$(this.findLastActive()||(this.errorList.length&&this.errorList[ 0 ].element)||[])
.filter(':visible')
.trigger('focus')
.trigger('focusin');
} catch(e){
}}
},
findLastActive: function(){
var lastActive=this.lastActive;
return (
lastActive &&
$.grep(this.errorList, function(n){
return n.element.name===lastActive.name;
}).length===1 &&
lastActive
);
},
elements: function(){
var tc_validator=this,
rulesCache={};
return $(this.currentForm)
.find('input, select, textarea, [contenteditable]')
.not(':submit, :reset, :image, :disabled')
.not(this.settings.ignore)
.filter(function(){
var name=this.name||$(this).attr('name');
var isContentEditable=typeof $(this).attr('contenteditable')!=='undefined'&&$(this).attr('contenteditable')!=='false';
if(isContentEditable){
this.form=$(this).closest('form')[ 0 ];
this.name=name;
}
if(this.form!==tc_validator.currentForm){
return false;
}
if(name in rulesCache||! tc_validator.objectLength($(this).tc_rules())){
return false;
}
rulesCache[ name ]=true;
return true;
});
},
clean: function(selector){
return $(selector)[ 0 ];
},
errors: function(){
var errorClass=this.settings.errorClass.split(' ').join('.');
return $(this.settings.errorElement + '.' + errorClass, this.errorContext);
},
resetInternals: function(){
this.successList=[];
this.errorList=[];
this.errorMap={};
this.toShow=$([]);
this.toHide=$([]);
},
reset: function(){
this.resetInternals();
this.currentElements=$([]);
},
prepareForm: function(){
this.reset();
this.toHide=this.errors().add(this.containers);
},
prepareElement: function(element){
this.reset();
this.toHide=this.errorsFor(element);
},
elementValue: function(element){
var $element=$(element),
type=element.type,
isContentEditable=typeof $element.attr('contenteditable')!=='undefined'&&$element.attr('contenteditable')!=='false',
val,
idx;
if(type==='radio'||type==='checkbox'){
return this.findByName(element.name).filter(':checked').val();
}else if(type==='number'&&typeof element.validity!=='undefined'){
return element.validity.badInput ? 'NaN':$element.val();
}
if(isContentEditable){
val=$element.text();
}else{
val=$element.val();
}
if(type==='file'){
if(val.substring(0, 12)==='C:\\fakepath\\'){
return val.substring(12);
}
idx=val.lastIndexOf('/');
if(idx >=0){
return val.substring(idx + 1);
}
idx=val.lastIndexOf('\\');
if(idx >=0){
return val.substring(idx + 1);
}
return val;
}
if(typeof val==='string'){
return val.replace(/\r/g, '');
}
return val;
},
check: function(element){
var rules;
var rulesCount;
var dependencyMismatch;
var val;
var result;
var method;
var rule;
var normalizer;
element=this.validationTargetFor(this.clean(element));
rules=$(element).tc_rules();
rulesCount=$.map(rules, function(n, i){
return i;
}).length;
dependencyMismatch=false;
val=this.elementValue(element);
if(typeof rules.normalizer==='function'){
normalizer=rules.normalizer;
}else if(typeof this.settings.normalizer==='function'){
normalizer=this.settings.normalizer;
}
if(normalizer){
val=normalizer.call(element, val);
delete rules.normalizer;
}
for(method in rules){
if(method){
rule={ method: method, parameters: rules[ method ] };
try {
result=$.tc_validator.methods[ method ].call(this, val, element, rule.parameters);
if(result==='dependency-mismatch'&&rulesCount===1){
dependencyMismatch=true;
continue;
}
dependencyMismatch=false;
if(result==='pending'){
this.toHide=this.toHide.not(this.errorsFor(element));
return;
}
if(! result){
this.formatAndAdd(element, rule);
return false;
}} catch(e){
if(e instanceof TypeError){
e.message +='.  Exception occurred when checking element ' + element.id + ", check the '" + rule.method + "' method.";
}
throw e;
}}
}
if(dependencyMismatch){
return;
}
if(this.objectLength(rules)){
this.successList.push(element);
}
return true;
},
customDataMessage: function(element, method){
return $(element).data('msg' + method.charAt(0).toUpperCase() + method.substring(1).toLowerCase())||$(element).data('msg');
},
customMessage: function(name, method){
var m=this.settings.messages[ name ];
return m&&(m.constructor===String ? m:m[ method ]);
},
findDefined: function(){
var i;
for(i=0; i < arguments.length; i +=1){
if(arguments[ i ]!==undefined){
return arguments[ i ];
}}
return undefined;
},
defaultMessage: function(element, rule){
var message;
var theregex;
var singleCheck='';
if(typeof rule==='string'){
rule={ method: rule };}
if(rule.method==='minlength'||rule.method==='maxlength'){
if(rule.parameters===1||rule.parameters==='1'){
singleCheck='single';
}}
message=this.findDefined(this.customMessage(element.name, rule.method + singleCheck),
this.customDataMessage(element, rule.method + singleCheck),
(! this.settings.ignoreTitle&&element.title)||undefined,
$.tc_validator.messages[ rule.method + singleCheck ],
'<strong>Warning: No message defined for ' + element.name + '</strong>'
);
theregex=/\$?\{(\d+)\}/g;
if(typeof message==='function'){
message=message.call(this, rule.parameters, element);
}else if(theregex.test(message)){
message=$.tc_validator.format(message.replace(theregex, '{$1}'), rule.parameters);
}
return message;
},
formatAndAdd: function(element, rule){
var message=this.defaultMessage(element, rule);
this.errorList.push({
message: message,
element: element,
method: rule.method
});
this.errorMap[ element.name ]=message;
this.submitted[ element.name ]=message;
},
addWrapper: function(toToggle){
if(this.settings.wrapper){
toToggle=toToggle.add(toToggle.parent(this.settings.wrapper));
}
return toToggle;
},
defaultShowErrors: function(){
var i, elements, error;
for(i=0; this.errorList[ i ]; i +=1){
error=this.errorList[ i ];
if(this.settings.highlight){
this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
}
this.showLabel(error.element, error.message);
}
if(this.errorList.length){
this.toShow=this.toShow.add(this.containers);
}
if(this.settings.success){
for(i=0; this.successList[ i ]; i +=1){
this.showLabel(this.successList[ i ]);
}}
if(this.settings.unhighlight){
for(i=0, elements=this.validElements(); elements[ i ]; i +=1){
this.settings.unhighlight.call(this, elements[ i ], this.settings.errorClass, this.settings.validClass);
}}
this.toHide=this.toHide.not(this.toShow);
this.hideErrors();
this.addWrapper(this.toShow).show();
},
validElements: function(){
return this.currentElements.not(this.invalidElements());
},
invalidElements: function(){
return $(this.errorList).map(function(){
return this.element;
});
},
showLabel: function(element, message){
var place,
group,
errorID,
v,
error=this.errorsFor(element),
elementID=this.idOrName(element),
describedBy=$(element).attr('aria-describedby');
if(error.length){
error.removeClass(this.settings.validClass).addClass(this.settings.errorClass);
error.html(message);
}else{
error=$('<' + this.settings.errorElement + '>')
.attr('id', elementID + '-error')
.addClass(this.settings.errorClass)
.html(message||'');
place=error;
if(this.settings.wrapper){
place=error
.hide()
.show()
.wrap('<' + this.settings.wrapper + '/>')
.parent();
}
if(this.labelContainer.length){
this.labelContainer.append(place);
}else if(this.settings.errorPlacement){
this.settings.errorPlacement.call(this, place, $(element));
}else{
place.insertAfter(element);
}
if(error.is('label')){
error.attr('for', elementID);
}else if(error.parents("label[for='" + this.escapeCssMeta(elementID) + "']").length===0){
errorID=error.attr('id');
if(! describedBy){
describedBy=errorID;
}else if(! describedBy.match(new RegExp('\\b' + this.escapeCssMeta(errorID) + '\\b'))){
describedBy +=' ' + errorID;
}
$(element).attr('aria-describedby', describedBy);
group=this.groups[ element.name ];
if(group){
v=this;
$.each(v.groups, function(name, testgroup){
if(testgroup===group){
$("[name='" + v.escapeCssMeta(name) + "']", v.currentForm).attr('aria-describedby', error.attr('id'));
}});
}}
}
if(! message&&this.settings.success){
error.text('');
if(typeof this.settings.success==='string'){
error.addClass(this.settings.success);
}else{
this.settings.success(error, element);
}}
this.toShow=this.toShow.add(error);
},
errorsFor: function(element){
var name=this.escapeCssMeta(this.idOrName(element)),
describer=$(element).attr('aria-describedby'),
selector="label[for='" + name + "'], label[for='" + name + "'] *";
if(describer){
selector=selector + ', #' + this.escapeCssMeta(describer).replace(/\s+/g, ', #');
}
return this.errors().filter(selector);
},
escapeCssMeta: function(string){
return string.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]\^`{|}~])/g, '\\$1');
},
idOrName: function(element){
return this.groups[ element.name ]||(this.checkable(element) ? element.name:element.id||element.name);
},
validationTargetFor: function(element){
if(this.checkable(element)){
element=this.findByName(element.name);
}
return $(element).not(this.settings.ignore)[ 0 ];
},
checkable: function(element){
return /radio|checkbox/i.test(element.type);
},
findByName: function(name){
return $(this.currentForm).find("[name='" + this.escapeCssMeta(name) + "']");
},
getLength: function(value, element){
switch(element.nodeName.toLowerCase()){
case 'select':
return $('option:selected', element).length;
case 'input':
if(this.checkable(element)){
return this.findByName(element.name).filter(':checked').length;
}}
return value.length;
},
depend: function(param, element){
return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ](param, element):true;
},
dependTypes: {
boolean: function(param){
return param;
},
string: function(param, element){
return !! $(param, element.form).length;
},
function: function(param, element){
return param(element);
}},
optional: function(element){
var val=this.elementValue(element);
return ! $.tc_validator.methods.required.call(this, val, element)&&'dependency-mismatch';
},
startRequest: function(element){
if(! this.pending[ element.name ]){
this.pendingRequest +=1;
$(element).addClass(this.settings.pendingClass);
this.pending[ element.name ]=true;
}},
stopRequest: function(element, valid){
this.pendingRequest -=1;
if(this.pendingRequest < 0){
this.pendingRequest=0;
}
delete this.pending[ element.name ];
$(element).removeClass(this.settings.pendingClass);
if(valid&&this.pendingRequest===0&&this.formSubmitted&&this.form()){
$(this.currentForm).trigger('submit');
if(this.submitButton){
$("input:hidden[name='" + this.submitButton.name + "']", this.currentForm).remove();
}
this.formSubmitted=false;
}else if(! valid&&this.pendingRequest===0&&this.formSubmitted){
$(this.currentForm).triggerHandler('invalid-form', [ this ]);
this.formSubmitted=false;
}},
previousValue: function(element, method){
method=(typeof method==='string'&&method)||'remote';
return (
$.data(element, 'previousValue') ||
$.data(element, 'previousValue', {
old: null,
valid: true,
message: this.defaultMessage(element, { method: method })
})
);
},
destroy: function(){
this.resetForm();
$(this.currentForm)
.off('.tc_validate')
.removeData('tc_validator')
.find('.tc_validate-equalTo-blur')
.off('.tc_validate-equalTo')
.removeClass('validate-equalTo-blur')
.find('.tc_validate-lessThan-blur')
.off('.tc_validate-lessThan')
.removeClass('validate-lessThan-blur')
.find('.tc_validate-lessThanEqual-blur')
.off('.tc_validate-lessThanEqual')
.removeClass('validate-lessThanEqual-blur')
.find('.tc_validate-greaterThanEqual-blur')
.off('.tc_validate-greaterThanEqual')
.removeClass('validate-greaterThanEqual-blur')
.find('.tc_validate-greaterThan-blur')
.off('.tc_validate-greaterThan')
.removeClass('validate-greaterThan-blur');
}},
classRuleSettings: {
required: { required: true },
email: { email: true },
url: { url: true },
date: { date: true },
dateISO: { dateISO: true },
number: { number: true },
digits: { digits: true },
creditcard: { creditcard: true }},
addClassRules: function(className, rules){
if(className.constructor===String){
this.classRuleSettings[ className ]=rules;
}else{
$.extend(this.classRuleSettings, className);
}},
classRules: function(element){
var rules={},
classes=$(element).attr('class');
if(classes){
$.each(classes.split(' '), function(){
if(this in $.tc_validator.classRuleSettings){
$.extend(rules, $.tc_validator.classRuleSettings[ this ]);
}});
}
return rules;
},
normalizeAttributeRule: function(rules, type, method, value){
if(/min|max|step/.test(method)&&(type===null||/number|range|text/.test(type))){
value=Number(value);
if(isNaN(value)){
value=undefined;
}}
if(value||value===0){
rules[ method ]=value;
}else if(type===method&&type!=='range'){
rules[ method ]=true;
}},
attributeRules: function(element){
var rules={},
$element=$(element),
type=element.getAttribute('type'),
method,
value;
for(method in $.tc_validator.methods){
if(method){
if(method==='required'){
value=element.getAttribute(method);
if(value===''){
value=true;
}
value = !! value;
}else{
value=$element.attr(method);
}
this.normalizeAttributeRule(rules, type, method, value);
}}
if(rules.maxlength&&/-1|2147483647|524288/.test(rules.maxlength)){
delete rules.maxlength;
}
return rules;
},
dataRules: function(element){
var rules={},
$element=$(element),
type=element.getAttribute('type'),
method,
value;
for(method in $.tc_validator.methods){
if(method){
value=$element.data('rule' + method.charAt(0).toUpperCase() + method.substring(1).toLowerCase());
if(value===''){
value=true;
}
this.normalizeAttributeRule(rules, type, method, value);
}}
return rules;
},
staticRules: function(element){
var rules={},
tc_validator=$.data(element.form, 'tc_validator');
if(tc_validator.settings.rules){
rules=$.tc_validator.normalizeRule(tc_validator.settings.rules[ element.name ])||{};}
return rules;
},
normalizeRules: function(rules, element){
var keepRule;
$.each(rules, function(prop, val){
if(val===false){
delete rules[ prop ];
return;
}
if(val.param||val.depends){
keepRule=true;
switch(typeof val.depends){
case 'string':
keepRule = !! $(val.depends, element.form).length;
break;
case 'function':
keepRule=val.depends.call(element, element);
break;
}
if(keepRule){
rules[ prop ]=val.param!==undefined ? val.param:true;
}else{
$.data(element.form, 'tc_validator').resetElements($(element));
delete rules[ prop ];
}}
});
$.each(rules, function(rule, parameter){
rules[ rule ]=typeof parameter==='function'&&rule!=='normalizer' ? parameter(element):parameter;
});
$.each([ 'minlength', 'maxlength' ], function(){
if(rules[ this ]){
rules[ this ]=Number(rules[ this ]);
}});
$.each([ 'rangelength', 'range' ], function(){
var parts;
if(rules[ this ]){
if(Array.isArray(rules[ this ])){
rules[ this ]=[ Number(rules[ this ][ 0 ]), Number(rules[ this ][ 1 ]) ];
}else if(typeof rules[ this ]==='string'){
parts=rules[ this ].replace(/[[\]]/g, '').split(/[\s,]+/);
rules[ this ]=[ Number(parts[ 0 ]), Number(parts[ 1 ]) ];
}}
});
if($.tc_validator.autoCreateRanges){
if(rules.min!==null&&rules.max!==null){
rules.range=[ rules.min, rules.max ];
delete rules.min;
delete rules.max;
}
if(rules.minlength!==null&&rules.maxlength!==null){
rules.rangelength=[ rules.minlength, rules.maxlength ];
delete rules.minlength;
delete rules.maxlength;
}}
return rules;
},
normalizeRule: function(data){
var transformed;
if(typeof data==='string'){
transformed={};
$.each(data.split(/\s/), function(){
transformed[ this ]=true;
});
data=transformed;
}
return data;
},
addMethod: function(name, method, message){
$.tc_validator.methods[ name ]=method;
$.tc_validator.messages[ name ]=message!==undefined ? message:$.tc_validator.messages[ name ];
if(method.length < 3){
$.tc_validator.addClassRules(name, $.tc_validator.normalizeRule(name));
}},
methods: {
required: function(value, element, param){
var val;
if(! this.depend(param, element)){
return 'dependency-mismatch';
}
if(element.nodeName.toLowerCase()==='select'){
val=$(element).val();
return val&&val.length > 0;
}
if(this.checkable(element)){
return this.getLength(value, element) > 0;
}
return value!==undefined&&value!==null&&value.length > 0;
},
email: function(value, element){
return this.optional(element)||/^[a-zA-Z0-9.!#$%&'*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/.test(value);
},
url: function(value, element){
return (
this.optional(element) ||
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value
)
);
},
date: function(value, element){
return this.optional(element)||! /Invalid|NaN/.test(new Date(value).toString());
},
dateISO: function(value, element){
return this.optional(element)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value);
},
number: function(value, element){
return this.optional(element)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
},
digits: function(value, element){
return this.optional(element)||/^\d+$/.test(value);
},
minlength: function(value, element, param){
var length=Array.isArray(value) ? value.length:this.getLength(value, element);
return this.optional(element)||length >=param;
},
maxlength: function(value, element, param){
var length=Array.isArray(value) ? value.length:this.getLength(value, element);
return this.optional(element)||length <=param;
},
rangelength: function(value, element, param){
var length=Array.isArray(value) ? value.length:this.getLength(value, element);
return this.optional(element)||(length >=param[ 0 ]&&length <=param[ 1 ]);
},
min: function(value, element, param){
return this.optional(element)||value >=param;
},
max: function(value, element, param){
return this.optional(element)||value <=param;
},
range: function(value, element, param){
return this.optional(element)||(value >=param[ 0 ]&&value <=param[ 1 ]);
},
step: function(value, element, param){
var type=$(element).attr('type'),
errorMessage='Step attribute on input type ' + type + ' is not supported.',
supportedTypes=[ 'text', 'number', 'range' ],
re=new RegExp('\\b' + type + '\\b'),
notSupported=type&&! re.test(supportedTypes.join()),
decimalPlaces=function(num){
var match=('' + num).match(/(?:\.(\d+))?$/);
if(! match){
return 0;
}
return match[ 1 ] ? match[ 1 ].length:0;
},
decimals,
toInt=function(num){
return Math.round(num * Math.pow(10, decimals));
},
valid=true;
if(notSupported){
throw new Error(errorMessage);
}
decimals=decimalPlaces(param);
if(decimalPlaces(value) > decimals||toInt(value) % toInt(param)!==0){
valid=false;
}
return this.optional(element)||valid;
},
equalTo: function(value, element, param){
var target=$(param);
if(this.settings.onfocusout&&target.not('.tc_validate-equalTo-blur').length){
target.addClass('validate-equalTo-blur').on('blur.tc_validate-equalTo', function(){
$(element).tc_valid();
});
}
return value===target.val();
},
remote: function(value, element, param, method){
var previous;
var tc_validator;
var data;
var optionDataString;
if(this.optional(element)){
return 'dependency-mismatch';
}
method=(typeof method==='string'&&method)||'remote';
previous=this.previousValue(element, method);
if(! this.settings.messages[ element.name ]){
this.settings.messages[ element.name ]={};}
previous.originalMessage=previous.originalMessage||this.settings.messages[ element.name ][ method ];
this.settings.messages[ element.name ][ method ]=previous.message;
param=(typeof param==='string'&&{ url: param })||param;
optionDataString=$.param($.extend({ data: value }, param.data));
if(previous.old===optionDataString){
return previous.valid;
}
previous.old=optionDataString;
tc_validator=this;
this.startRequest(element);
data={};
data[ element.name ]=value;
$.ajax($.extend(true,
{
mode: 'abort',
port: 'validate' + element.name,
dataType: 'json',
data: data,
context: tc_validator.currentForm,
success: function(response){
var valid=response===true||response==='true',
errors,
message,
submitted;
tc_validator.settings.messages[ element.name ][ method ]=previous.originalMessage;
if(valid){
submitted=tc_validator.formSubmitted;
tc_validator.resetInternals();
tc_validator.toHide=tc_validator.errorsFor(element);
tc_validator.formSubmitted=submitted;
tc_validator.successList.push(element);
tc_validator.invalid[ element.name ]=false;
tc_validator.showErrors();
}else{
errors={};
message=response||tc_validator.defaultMessage(element, { method: method, parameters: value });
previous.message=message;
errors[ element.name ]=message;
tc_validator.invalid[ element.name ]=true;
tc_validator.showErrors(errors);
}
previous.valid=valid;
tc_validator.stopRequest(element, valid);
}},
param
)
);
return 'pending';
}}
});
if($.ajaxPrefilter){
$.ajaxPrefilter(function(settings, _, xhr){
var port=settings.port;
if(settings.mode==='abort'){
if(pendingRequests[ port ]){
pendingRequests[ port ].abort();
}
pendingRequests[ port ]=xhr;
}});
}else{
ajax=$.ajax;
$.ajax=function(settings){
var mode=('mode' in settings ? settings:$.ajaxSettings).mode,
port=('port' in settings ? settings:$.ajaxSettings).port;
if(mode==='abort'){
if(pendingRequests[ port ]){
pendingRequests[ port ].abort();
}
pendingRequests[ port ]=ajax.apply(this, arguments);
return pendingRequests[ port ];
}
return ajax.apply(this, arguments);
};}
return $;
}));