META-INF.resources.validatorjs.validator.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of validator-web Show documentation
Show all versions of validator-web Show documentation
Jave Web Validator Framework
/*! validator-web 1.0.0
* (c) 2016-2017 Youqian Yue , Apache Licensed
* https://github.com/devefx/validator-web
*/
;(function(factory) {
if ('function' === typeof define && (define.amd || define.cmd)) {
// Register as an anonymous module.
define([], function(){
return factory;
});
} else {
factory(jQuery);
}
}(function($) {
$.extend($.fn, {
validate: function(options) {
// if nothing is selected, return nothing; can't chain anyway
if (!this.length) {
if (options && options.debug && window.console) {
console.warn("Nothing selected, can't validate, returning nothing.");
}
return;
}
// check if a validator for this form was already created
var validator = $.data(this[0], "validator");
if (validator) {
return validator;
}
// Add novalidate tag if HTML5.
this.attr("novalidate", "novalidate");
validator = new $.validator(options, this[0]);
$.data(this[0], "validator", validator);
if (validator.settings.onsubmit) {
this.on("click.validate", ":submit", function(event) {
if (validator.settings.submitHandler) {
validator.submitButton = event.target;
}
// allow suppressing validation by adding a cancel class to the submit button
if ($(this).hasClass("cancel")) {
validator.cancelSubmit = true;
}
// allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
if ($(this).attr("formnovalidate") !== undefined) {
validator.cancelSubmit = true;
}
});
// validate the form on submit
this.on("submit.validate", function(event) {
if (validator.settings.debug) {
// prevent form submit to be able to see console output
event.preventDefault();
}
function handle() {
var hidden, result;
if (validator.settings.submitHandler) {
if (validator.submitButton) {
// insert a hidden input as a replacement for the missing submit button
hidden = $("")
.attr("name", validator.submitButton.name)
.val($(validator.submitButton).val())
.appendTo(validator.currentForm);
}
result = validator.settings.submitHandler.call(validator, validator.currentForm, event);
if (validator.submitButton) {
// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
hidden.remove();
}
if (result !== undefined) {
return result;
}
return false;
}
// jquery ajaxform plugin
var ajaxsubmit = validator.settings.ajaxsubmit;
if (ajaxsubmit) {
var options = $(ajaxsubmit);
options.data = $.extend(options.data || {}, {
_validator_ajaxsubmit: 'true'
});
options.success = function (res) {
if (res && res.status == 101) {
for (var name in res.contents) {
validator.invalid[name] = true;
validator.showErrors(res.contents);
}
return;
}
ajaxsubmit.success && ajaxsubmit.success(res);
};
$(validator.currentForm).ajaxSubmit(options);
return false;
}
return true;
}
// prevent submit for invalid forms or custom submit handlers
if (validator.cancelSubmit) {
validator.cancelSubmit = false;
return handle();
}
if (validator.form()) {
if (validator.pendingRequest) {
validator.formSubmitted = true;
return false;
}
return handle();
} else {
validator.focusInvalid();
return false;
}
});
}
return validator;
},
valid: function () {
var valid, validator, errorList;
if ($(this[0]).is("form")) {
valid = this.validate().form();
} else {
errorList = [];
validator = $(this[0].form).validate();
this.each(function () {
validator.element(this);
errorList = errorList.concat(validator.errorList);
});
validator.errorList = errorList;
valid = validator.valid();
}
return valid;
}
});
$(function () {
$.each($("form[valid]"), function (n, form) {
var params = $(form).attr("valid");
$(form).validate(params ? eval("(" + params + ")") : {});
});
});
}));
$.validationContext = function() {
this.constraints = [];
this.failFast = false;
this.throwException = false;
this.setFailFast = function (failFast) {
this.failFast = failFast;
};
this.setThrowException = function (throwException) {
this.throwException = throwException;
};
this.constraint = function () {
if (arguments.length < 3) {
throw new Error("invalid arguments");
}
var args = $.makeArray(arguments);
var descriptor = {
name: args[0],
message: args[1],
constraintValidator: args[2],
groups: (arguments.length > 3 ? args.slice(3) : ["Default"])
};
this.constraints.push(descriptor);
};
};
$.parseURL = function (url) {
url = url.replace(/\s/g, "");
var a = document.createElement("a");
a.href = url;
if (url.indexOf(a.protocol) != 0) {
throw new Error("no protocol: " + url);
}
return {
source: url,
protocol: a.protocol.replace(":", ""),
host: a.hostname,
port: a.port || -1,
query: a.search.substring(1),
params: (function () {
var ret = {},
seg = a.search.replace(/^\?/, '').split('&'),
len = seg.length, i = 0, s;
for (; i < len; i++) {
if (!seg[i]) {
continue;
}
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
hash: a.hash.replace('#', ''),
path: a.pathname.replace(/^([^\/])/, '/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
segments: a.pathname.replace(/^\//, '').split('/')
};
};
$.validator = function(options, form) {
this.settings = $.extend(true, {}, $.validator.defaults, options);
this.currentForm = form;
this.init();
};
$.validator.format = function(source, params) {
if (arguments.length === 1) {
return function(params) {
var args = $.makeArray(arguments);
args.unshift(source);
return $.validator.format.apply(this, args);
};
}
var valueExpression = {
variables: [],
setVariable: function (name, value) {
this.variables.push(name + "=params."+ name);
},
getValue: function (expression) {
eval("var " + this.variables.join(",") + ";");
return eval(expression);
}
};
$.each(params, function(k, v) {
valueExpression.setVariable(k, v);
source = source.replace(new RegExp("\\{" + k + "\\}", "g"), function() {
return v;
});
});
var patt = new RegExp("\\$\\{([^{}]+)\\}", "g");
var result, str = source;
while ((result = patt.exec(source))) {
var expression = result[1],
resolvedExpression = valueExpression.getValue(expression);
str = str.replace(result[0], resolvedExpression);
}
return str;
};
$.extend($.validator, {
defaults: {
directly: false,
failFast: false,
throwException: false,
groups: ["Default"],
errorClass: "error",
validClass: "valid",
errorElement: "label",
focusCleanup: false,
focusInvalid: true,
errorContainer: $([]),
errorLabelContainer: $([]),
onsubmit: true,
ajaxsubmit: {},
ignore: ":hidden",
onfocusin: function(element) {
this.lastActive = element;
// Hide error label and remove error class on focus if enabled
if (this.settings.focusCleanup) {
if (this.settings.unhighlight) {
this.settings.unhighlight.call(this, element, this.settings.errorClass, "");
}
this.hideThese(this.errorsFor(element));
}
},
onfocusout: function(element) {
if (!this.checkable(element) && (this.settings.directly || element.name in this.submitted || $(element).val().length)) {
this.element(element);
}
},
onkeyup: function(element, event) {
// Avoid revalidate the field when pressing one of the following keys
// Shift => 16
// Ctrl => 17
// Alt => 18
// Caps lock => 20
// End => 35
// Home => 36
// Left arrow => 37
// Up arrow => 38
// Right arrow => 39
// Down arrow => 40
// Insert => 45
// Num lock => 144
// AltGr key => 225
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) {
return;
} else if (element.name in this.submitted || element === this.lastElement) {
this.element(element);
}
},
onclick: function(element) {
// click on selects, radiobuttons and checkboxes
if (element.name in this.submitted) {
this.element(element);
// or option elements, check parent select in that case
} else if (element.parentNode.name in this.submitted) {
this.element(element.parentNode);
}
},
onchange: function (element) {
if (element.type == "file") {
$(element).removeData("savedValue").removeData("previousValue");
this.element(element);
}
},
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($.validator.defaults, settings);
},
prototype: {
init: function() {
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.pendingRequest = 0;
this.pending = {};
this.invalid = {};
this.reset();
this.uncheckedErrorList = [];
this.context = new $.validationContext();
this.initContext();
this.checkElements();
function delegate(event) {
var validator = $.data(this.form, "validator"),
eventType = "on" + event.type.replace(/^validate/, ""),
settings = validator.settings;
if (settings[eventType] && !$(this).is(settings.ignore)) {
settings[eventType].call(validator, this, event);
}
}
$(this.currentForm)
.on("focusin.validate focusout.validate keyup.validate",
":text, [type='password'], 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']", delegate)
// Support: Chrome, oldIE
// "select" is provided as event.target when clicking a option
.on("click.validate", "select, option, [type='radio'], [type='checkbox']", delegate)
.on("change.validate", "[type=file]", delegate);
if (this.settings.invalidHandler) {
$(this.currentForm).on("invalid-form.validate", this.settings.invalidHandler);
}
},
initContext: function () {
this.context.failFast = this.settings.failFast;
this.context.throwException = this.settings.throwException;
var use = this.settings.use;
if (use) {
var validation = $.validator.validations[use];
if (validation) {
validation(this.context);
} else {
throw new Error("not found validation '" + use + "', please check if there is any.");
}
} else {
var results = $.map($.validator.validations, function(v, k) {
return {id: k, validation: v};
});
if (results.length) {
if (results.length > 1) {
throw new Error("the validation is not the only, please specify the name of the validation.");
}
var result = results[0];
this.settings.use = result.id;
result.validation(this.context);
} else {
throw new Error("cannot find any validation.");
}
}
},
checkElements: function () {
var validator = this;
$.each(this.context.constraints, function (n, i) {
var elements = validator.findByName(i.name);
if (elements.length == 0 && (!i.name || !i.constraintValidator.isValid(undefined, validator))) {
validator.uncheckedErrorList.push({
element: elements[0],
message: i.message,
validator: i.constraintValidator
});
if (window.console) {
console.error("Form elements is missing, the name is '%s'.", i.name);
}
}
});
},
hasConstraint: function (element) {
var element = this.validationTargetFor(this.clean(element));
return $.map(this.context.constraints, function(n, i) {
return n.name == element.name || undefined;
}).length > 0;
},
groupsMatch: function (descriptor) {
if (this.settings.groups.length == 0) {
return $.inArray("Default", descriptor.groups) !== -1;
}
return $.map(this.settings.groups, function (n, i) {
return $.inArray(n, descriptor.groups) !== -1 || undefined;
}).length > 0;
},
findConstraints: function (element) {
return $.map(this.context.constraints, function (n, i) {
return (element.name == n.name && n) || undefined;
});
},
form: function () {
this.checkForm();
$.extend(this.submitted, this.errorMap);
this.invalid = $.extend({}, this.errorMap);
if (!this.valid()) {
$(this.currentForm).triggerHandler("invalid-form", [this]);
}
this.showErrors();
return this.valid();
},
checkForm: function () {
this.prepareForm();
for(var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++) {
if (this.check(elements[i]) === false && this.context.failFast) {
break;
}
}
return this.valid();
},
element: function (element) {
var cleanElement = this.clean(element),
checkElement = this.validationTargetFor(cleanElement),
result = true;
this.lastElement = checkElement;
if (checkElement === undefined) {
delete this.invalid[cleanElement.name];
} else {
this.prepareElement(checkElement);
this.currentElements = $(checkElement);
result = this.check(checkElement) !== false;
if (result) {
delete this.invalid[checkElement.name];
} else {
this.invalid[checkElement.name] = true;
}
}
// Add aria-invalid status for screen readers
$(element).attr("aria-invalid", !result);
if (!this.numberOfInvalids()) {
// Hide error containers on last error
this.toHide = this.toHide.add(this.containers);
}
this.showErrors();
return result;
},
showErrors: function (errors) {
if (errors) {
// add items to error list and map
$.extend(this.errorMap, errors);
this.errorList = [];
for (var name in errors) {
this.errorList.push({
message: errors[name],
element: this.findByName(name)[0]
});
}
// remove items from success list
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() {
if ($.fn.resetForm) {
$(this.currentForm).resetForm();
}
this.submitted = {};
this.lastElement = null;
this.prepareForm();
this.hideErrors();
var i, elements = this.elements()
.removeData("previousValue")
.removeAttr("aria-invalid");
if (this.settings.unhighlight) {
for (i = 0; elements[i]; i++) {
this.settings.unhighlight.call(this, elements[i],
this.settings.errorClass, "");
}
} else {
elements.removeClass(this.settings.errorClass);
}
},
numberOfInvalids: function () {
return this.objectLength(this.invalid);
},
objectLength: function(obj) {
var count = 0, i;
for (i in obj) {
count++;
}
return count;
},
hideErrors: function() {
this.hideThese(this.toHide);
},
hideThese: function(elements) {
elements.not(this.containers).text("");
this.addWrapper(elements).hide();
},
valid: function() {
return this.errorList.length === 0 && this.uncheckedErrorList.length == 0;
},
focusInvalid: function() {
if (this.settings.focusInvalid) {
try {
$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
.filter(":visible")
.focus()
// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
.trigger("focusin");
} catch (e) {
// ignore IE throwing errors when focusing hidden elements
}
}
},
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 validator = this,
elesCache = {};
return $(this.currentForm)
.find("input, select, textarea")
.not(":submit, :reset, :image, :disabled")
.not(this.settings.ignore)
.filter(function () {
if (!this.name && validator.settings.debug && window.console) {
console.error("%o has no name assigned", this);
}
if (this.name in elesCache || !validator.hasConstraint($(this))) {
return false;
}
elesCache[this.name] = true;
return true;
});
},
clean: function (selector) {
return $(selector)[0];
},
errorElements: function () {
var errorClass = this.settings.errorClass.split(" ").join(".");
return $(this.settings.errorElement + "." + errorClass, this.errorContext);
},
reset: function () {
this.successList = [];
this.errorList = [];
this.errorMap = {};
this.toShow = $([]);
this.toHide = $([]);
this.currentElements = $([]);
},
prepareForm: function () {
this.reset();
this.toHide = this.errorElements().add(this.containers);
},
prepareElement: function (element) {
this.reset();
this.toHide = this.errorsFor(element);
},
elementValue: function (element) {
var val,
$element = $(element),
type = element.type;
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 ? false : $element.val();
} else if (type === "file") {
var file = $element[0].files[0];
if (this.imageable(file)) {
var savedValue = $.data(element, "savedValue");
return savedValue || function (callback) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
var img = new Image();
img.src = reader.result;
img.onload = function () {
file.width = img.width;
file.height = img.height;
$.data(element, "savedValue", file);
if (callback) {
callback(file);
}
};
};
};
}
return file;
}
val = $element.val();
if (typeof val === "string") {
return val.replace(/\r/g, "");
}
return val;
},
check: function (element) {
element = this.validationTargetFor(this.clean(element));
var constraints = this.findConstraints(element),
value = this.elementValue(element),
self = this, result, constraint, constraintValidator;
if (typeof value == "function") {
this.startRequest(element);
value(function (val) {
self.stopRequest(element, self.element(element));
});
return;
}
for (var i in constraints) {
constraint = constraints[i],
constraintValidator = constraint.constraintValidator;
try {
result = !this.groupsMatch(constraint) || constraintValidator.isValid(value, this, element, constraint);
if (result === "pending") {
this.toHide = this.toHide.not(this.errorsFor(element));
return;
}
if (!result) {
this.formatAndAdd(element, constraint);
return false;
}
} catch (e) {
if (this.settings.debug && window.console) {
console.log("Exception occurred when checking element " + element.id + ", check the '" + constraintValidator.constructor.name + "' constraint.", e);
}
if (e instanceof TypeError) {
e.message += ". Exception occurred when checking element " + element.id + ", check the '" + constraintValidator.constructor.name + "' constraint.";
}
if (this.context.throwException) {
throw e;
}
return false;
}
}
if (this.hasConstraint(element)) {
this.successList.push(element);
}
return true;
},
getMessage: function (element, constraint) {
var message = constraint.message,
theregex = /\$?\{([^{}]+)\}/g;
if (typeof message === "function") {
message = message.call(this, constraint.constraintValidator, element);
} else if (theregex.test(message)) {
var parameters = $.extend({
value: this.elementValue(element)
}, constraint.constraintValidator);
message = $.validator.format(message, parameters);
}
return message;
},
formatAndAdd: function (element, constraint) {
var message = this.getMessage(element, constraint);
this.errorList.push({
message: message,
element: element,
validator: constraint.constraintValidator
});
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++) {
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++) {
this.showLabel(this.successList[i]);
}
}
if (this.settings.unhighlight) {
for (i = 0, elements = this.validElements(); elements[i]; i++) {
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, errorId,
error = this.errorsFor(element),
elementID = this.idOrName(element),
describedby = $(element).data("aria-describedby");
if (error.length) {
// refresh error/success class
error.removeClass(this.settings.validClass).addClass(this.settings.errorClass);
// replace message on existing label
error.html(message);
} else {
// create error element
error = $("<" + this.settings.errorElement + ">")
.attr("id", elementID + "-error")
.addClass(this.settings.errorClass)
.html(message || "");
// Maintain reference to the element to be placed into the DOM
place = error;
if (this.settings.wrapper) {
// make sure the element is visible, even in IE
// actually showing the wrapped element is handled elsewhere
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(place, $(element));
} else {
place.insertAfter(element);
}
// Link error back to the element
if (error.is("label")) {
// If the error is a label, then associate using 'for'
error.attr("for", elementID);
} else if (error.parents("label[for='" + elementID + "']").length === 0) {
// If the element is not a child of an associated label, then it's necessary
// to explicitly apply aria-describedby
errorID = error.attr("id").replace(/(:|\.|\[|\]|\$)/g, "\\$1");
// Respect existing non-error aria-describedby
if (!describedBy) {
describedBy = errorID;
} else if (!describedBy.match(new RegExp("\\b" + errorID + "\\b"))) {
// Add to end of list if not already present
describedBy += " " + errorID;
}
$(element).attr("aria-describedby", describedBy);
}
}
if (message && this.settings.error) {
if (typeof this.settings.error === "string") {
error.addClass(this.settings.error);
} else {
this.settings.error(error, element);
}
}
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.idOrName(element),
describer = $(element).attr("aria-describedby"),
selector = "label[for='" + name + "'], label[for='" + name + "'] *";
// aria-describedby should directly reference the error element
if (describer) {
selector = selector + ", #" + describer.replace(/\s+/g, ", #");
}
return this
.errorElements()
.filter(selector);
},
idOrName: function (element) {
return this.checkable(element) ? element.name : element.id || element.name;
},
validationTargetFor: function (element) {
// If radio/checkbox, validate first element in group instead
if (this.checkable(element)) {
element = this.findByName(element.name);
}
// Always apply ignore filter
return $(element).not(this.settings.ignore)[0];
},
checkable: function(element) {
return (/radio|checkbox/i).test(element.type);
},
imageable: function (file) {
return file && (/image\/(jpeg|png|gif)/).test(file.type);
},
findByName: function(name) {
return $(this.currentForm).find("[name='" + name + "']");
},
startRequest: function(element) {
if (!this.pending[element.name]) {
this.pendingRequest++;
this.pending[element.name] = true;
}
},
stopRequest: function(element, valid) {
this.pendingRequest--;
// sometimes synchronization fails, make sure pendingRequest is never < 0
if (this.pendingRequest < 0) {
this.pendingRequest = 0;
}
delete this.pending[element.name];
if (valid && this.pendingRequest === 0 && this.formSubmitted && this.form()) {
$(this.currentForm).submit();
this.formSubmitted = false;
} else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
$(this.currentForm).triggerHandler("invalid-form", [this]);
this.formSubmitted = false;
}
},
previousValue: function (element, url) {
var previous = $.data(element, "previousValue") || $.data(element, "previousValue", {});
if (!previous[url]) {
previous[url] = {
old: null,
valid: false
}
}
return previous[url];
},
// cleans up all forms and elements, removes validator-specific events
destroy: function() {
this.resetForm();
$(this.currentForm)
.off(".validate")
.removeData("validator");
}
},
validations: {},
});
/** extensions */
function SimpleDateFormat(pattern) {
this.pattern = pattern;
this.regex = new RegExp("^" + pattern.replace("yyyy", "\\d{4}").replace("MM", "(0\\d|1[12])").replace("dd", "([0-2]\\d|3[0-1])")
.replace("HH", "([0-1]\\d|2[0-3])").replace("hh", "(0\\d|1[0-2])").replace("mm", "[0-5]\\d").replace("ss", "[0-5]\\d") + "$");
this.position = {
year: pattern.indexOf("yyyy"), month: pattern.indexOf("MM"), day: pattern.indexOf("dd"),
hour: pattern.toLowerCase().indexOf("hh"), minute: pattern.indexOf("mm"), second: pattern.indexOf("ss")
};
this.parse = function (source) {
if (!this.regex.test(source))
throw new Error("Unparseable date: \"" + source + "\"");
var time = {
year: source.substr(this.position.year, 4),
month: source.substr(this.position.month, 2),
day: source.substr(this.position.day, 2)
};
if (this.position.hour != -1)
time.hour = source.substr(this.position.hour, 2);
if (this.position.minute != -1)
time.minute = source.substr(this.position.minute, 2);
if (this.position.second != -1)
time.second = source.substr(this.position.second, 2);
var day31 = "01,03,05,07,08,10,12";
if (time.day == 31 && day31.indexOf(time.month) == -1)
throw new Error("Unparseable date: \"" + source + "\"");
if (time.month == 2 && time.day == 29 && !(time.year % 4 == 0 && time.year % 100 != 0)
&& !(time.year % 100 == 0 && time.year % 400 == 0)) {
throw new Error("Unparseable date: \"" + source + "\"");
}
var date = new Date();
date.setFullYear(time.year, time.month - 1, time.day);
if (time.hour != undefined) date.setHours(time.hour);
if (time.minute != undefined) date.setMinutes(time.minute);
if (time.second != undefined) date.setSeconds(time.second);
return date;
};
this.format = function (date) {
function fmt(v, n) {
for (var i = n - (v + "").length; i > 0; i--) {
v = "0" + v;
}
return v;
}
var h24 = date.getHours();
return this.pattern.replace("yyyy", fmt(date.getFullYear(), 4)).replace("MM", fmt(date.getMonth() + 1, 2))
.replace("dd", fmt(date.getDate(), 2)).replace("HH", fmt(h24, 2)).replace("hh", fmt((h24 - 1) % 12 + 1, 2))
.replace("mm", fmt(date.getMinutes(), 2)).replace("ss", fmt(date.getSeconds(), 2));
};
}
Number.prototype.scale = function () {
var numStr = this.toString();
var scale = 0;
var index = numStr.indexOf(".");
if (index != -1) {
scale = numStr.length - index - 1;
} else {
var matcher = /0*$/.exec(numStr);
if (matcher.length != 0) {
scale -= matcher[0].length;
}
}
return scale;
};
Number.prototype.precision = function () {
var numStr = this.toString();
var precision = numStr.length;
if (numStr.indexOf(".") != -1) {
precision -= 1;
} else {
var matcher = /0*$/.exec(numStr);
if (matcher.length != 0) {
precision -= matcher[0].length;
}
}
return precision;
};
/** constraints */
$.validator.constraints = {
AssertFalse: function () {
this.isValid = function (value) {
return !value || value != "true";
};
},
AssertTrue: function () {
this.isValid = function (value) {
return !value || value == "true";
};
},
DecimalMax: function (maxValue, inclusive) {
this.maxValue = maxValue;
this.inclusive = inclusive || inclusive == undefined;
this.isValid = function (value) {
if (!value) {
return true;
}
value = Number(value);
if (isNaN(value)) {
return false;
}
return this.inclusive ? value <= this.maxValue : value < this.maxValue;
};
},
DecimalMin: function (minValue, inclusive) {
this.minValue = minValue;
this.inclusive = inclusive || inclusive == undefined;
this.isValid = function (value) {
if (!value) {
return true;
}
value = Number(value);
if (isNaN(value)) {
return false;
}
return this.inclusive ? value >= this.minValue : value > this.minValue;
};
},
Digits: function (maxIntegerLength, maxFractionLength) {
this.maxIntegerLength = maxIntegerLength;
this.maxFractionLength = maxFractionLength;
this.isValid = function (value) {
if (!value) {
return true;
}
var bigNum = Number(value);
var integerPartLength = bigNum.precision() - bigNum.scale();
var fractionPartLength = bigNum.scale() < 0 ? 0 : bigNum.scale();
return (this.maxIntegerLength >= integerPartLength && this.maxFractionLength >= fractionPartLength);
};
},
Email: function () {
this.isValid = function (value) {
if (!value || value.length == 0) {
return true;
}
var splitPosition = value.lastIndexOf("@");
if (splitPosition < 0) {
return false;
}
var name = value.substring(0, splitPosition);
var domain = value.substring(splitPosition + 1);
var valid = false;
if (domain == "163.com" || domain == "126.com" || domain == "yeah.net") {
valid = valid || /^[a-z][a-z0-9_]{5,17}$/i.test(name);
} else if (domain == "qq.com" || domain == "foxmail.com") {
valid = valid || (domain == "qq.com" && /^[1-9][0-9]{4,10}$/.test(name));
valid = valid || (/^[a-z][a-z0-9._-]{2,17}$/i.test(name) && !/([._-]){2,}/.test(name));
} else if (domain == "sina.com" || domain == "sina.cn") {
valid = valid || /^[a-z0-9][a-z0-9_]{2,14}[a-z0-9]$/.test(name);
} else if (domain == "sohu.com") {
valid = valid || /^[a-z][a-zA-Z0-9_]{3,15}$/.test(name);
} else if (domain == "gmail.com") {
valid = valid || (/^[a-z0-9][a-z0-9.]{4,28}[a-z0-9]$/i.test(name) && !/\.{2,}/.test(name) &&
(name.length < 8 || /[a-z]/.test(name)));
} else if (domain == "outlook.com" || domain == "hotmail.com") {
valid = valid || (/^[a-z][a-z0-9._-]{0,63}$/i.test(name) && !/\.{2,}/.test(name));
} else if (domain == "yahoo.com" || domain == "yahoo.com.cn" || domain == "yahoo.cn") {
valid = valid || (/^[a-z][a-z0-9._]{2,30}[a-z0-9]$/i.test(name) && !/_{2,}/.test(name) && name.match(/\./g).length < 2);
} else {
valid = valid || /^[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(name);
}
return valid;
};
},
Future: function (pattern) {
this.pattern = pattern || "yyyy-MM-dd HH:mm:ss";
var sdf = new SimpleDateFormat(this.pattern);
this.isValid = function (value) {
if (!value) {
return true;
}
try {
var date = sdf.parse(value);
return date.getTime() > new Date().getTime();
} catch (e) {
return false;
}
};
},
Past: function (pattern) {
this.pattern = pattern || "yyyy-MM-dd HH:mm:ss";
var sdf = new SimpleDateFormat(this.pattern);
this.isValid = function (value) {
if (!value) {
return true;
}
try {
var date = this.sdf.parse(value);
return date.getTime() < new Date().getTime();
} catch (e) {
return false;
}
};
},
Length: function (min, max) {
this.min = min;
this.max = max;
this.isValid = function (value) {
if (!value) {
return true;
}
var length = value.length;
return length >= this.min && length <= this.max;
};
},
Max: function (maxValue) {
this.maxValue = maxValue;
this.isValid = function (value) {
if (!value) {
return true;
}
value = Number(value);
if (isNaN(value)) {
return false;
}
return value <= this.maxValue;
};
},
Min: function (minValue) {
this.minValue = minValue;
this.isValid = function (value) {
if (!value) {
return true;
}
value = Number(value);
if (isNaN(value)) {
return false;
}
return value >= this.minValue;
};
},
Mobile: function () {
this.isValid = function (value) {
if (!value) {
return true;
}
return /^(0|86|17951)?(13[0-9]|15[012356789]|17[3678]|18[0-9]|14[57])[0-9]{8}$/.test(value);
};
},
NotBlank: function () {
this.isValid = function (value) {
if (!value) {
return false;
}
return $.trim(value).length > 0;
};
},
NotEmpty: function () {
this.isValid = function (value) {
if (value == null) {
return false;
}
return value.length > 0;
};
},
NotNull: function () {
this.isValid = function (value) {
return !!value;
};
},
Null: function() {
this.isValid = function (value) {
return !value;
};
},
Regex: function (regexp, flags) {
this.regexp = regexp;
this.flags = flags || 0;
var attributes = "";
if (this.flags & 0x02) {
attributes += "i";
}
if (this.flags & 0x08) {
attributes += "m";
}
// FIXME Not supported: DOTALL UNICODE_CASE CANON_EQ UNIX_LINES LITERAL UNICODE_CHARACTER_CLASS COMMENTS
var regex = new RegExp(this.regexp, attributes);
this.isValid = function (value) {
if (!value) {
return true;
}
return regex.test(value);
};
},
Size: function (min, max) {
this.min = min;
this.max = max;
this.isValid = function (value, validator, element, constraint) {
var elements = validator.findByName(constraint.name);
var length = elements.length;
return length >= this.min && length <= this.max;
};
},
URL: function (protocol, host, port) {
this.protocol = protocol;
this.host = host;
this.port = port || -1;
this.isValid = function (value) {
if (!value || value.length == 0) {
return true;
}
try {
var url = $.parseURL(value);
} catch (e) {
return false;
}
if (this.protocol && this.protocol.length > 0 && url.protocol != this.protocol) {
return false;
}
if (this.host && this.host.length > 0 && url.host != this.host) {
return false;
}
if (this.port != -1 && url.port != this.port) {
return false;
}
return true;
};
},
EqualTo: function (name, ignoreCase) {
this.name = name;
this.ignoreCase = ignoreCase || false;
this.isValid = function (value, validator) {
var element = validator.findByName(this.name);
var diffValue = validator.elementValue(element);
return ignoreCase ? value.toLowerCase() == diffValue.toLowerCase()
: value == diffValue;
};
},
Remote: function (url, params) {
this.isValid = function (value, validator, element, constraint) {
if (arguments.length < 3)
return false;
var previous = validator.previousValue(element, url),
data;
if (previous.old === value) {
return previous.valid;
}
previous.old = value;
validator.startRequest(element);
data = {};
data["value"] = value;
$.ajax({
mode: "abort",
port: "validate" + element.name,
url: url,
type: "post",
dataType: "json",
data: data,
context: validator.currentForm,
success: function(response) {
var valid = response === true || response === "true",
errors, message, submitted;
previous.valid = valid;
if (valid) {
validator.element(element);
} else {
errors = {};
errors[element.name] = validator.getMessage(element, constraint);
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
validator.stopRequest(element, valid);
}
});
return "pending";
};
var self = this;
params && $.each(params, function (n, i) {
self[n] = i;
});
},
Options: function (validators) {
this.subValidators = validators;
this.isValid = function (value, validator, element) {
for (i in this.subValidators) {
var va = this.subValidators[i];
var valid = va && va.isValid(value, validator, element);
if (valid == "pending" || valid === true) {
return valid;
}
}
return false;
};
},
Separator: function (validator, separator, ignoreLastBlank) {
this.validator = validator;
this.separator = separator;
this.ignoreLastBlank = ignoreLastBlank || true;
this.isValid = function (value, validator, element) {
if (!value) {
return true;
}
if (this.ignoreLastBlank) {
var pos = value.lastIndexOf(this.separator);
var lastString = value.substring(pos + 1);
if (/\s+/.test(lastString)) {
value = value.substring(0, pos);
}
}
var subTexts = value.split(this.separator);
for (var subText in subTexts) {
if (!this.validator.isValid(subText, validator, element)) {
return false;
}
}
return true;
};
},
MultipartSize: function (min, max) {
this.min = min;
this.max = max;
this.isValid = function (file) {
if (!file) {
return true;
}
size = file.size;
return size >= this.min && size <= this.max;
};
},
ImageSize: function (minWidth, maxWidth, minHeight, maxHeight) {
this.minWidth = minWidth;
this.maxWidth = maxWidth;
this.minHeight = minHeight;
this.maxHeight = maxHeight;
this.isValid = function (image) {
if (!image) {
return true;
}
var width = image.width;
var height = image.height;
return width >= this.minWidth && width <= this.maxWidth &&
height >= this.minHeight && height <= this.maxHeight;
};
},
ImageRatio: function (ratio) {
this.ratio = ratio;
this.isValid = function (image) {
if (!image) {
return true;
}
var width = image.width;
var height = image.height;
return this.ratio == (width / height);
};
}
};