Newer
Older
/*
* web: validate.js
* XNAT http://www.xnat.org
* Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
* All Rights Reserved
*
* Released under the Simplified BSD.
*/
/*!
* Form and value validation functions for XNAT
* Some code adapted from http://rickharrison.github.com/validate.js
*/
var XNAT = getObject(XNAT);
(function(factory){
if (typeof define === 'function' && define.amd) {
define(factory);
}
else if (typeof exports === 'object') {
module.exports = factory();
}
else {
return factory();
}
}(function(){
Mark M. Florida
committed
var undefined, undef, validate;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
XNAT.validation = getObject(XNAT.validation || {});
// HELPERS
function isNull(value){
return !!(value == null || value === '');
}
// copy of jQuery's $.isNumeric() method
function isNumeric( num ) {
return !isArray( num ) && (num - parseFloat( num ) + 1) >= 0;
}
// TODO: implement display of error messages
var message = {
required: 'The __NAME__ field is required.',
matches: 'The __NAME__ field does not match the __VALUE__ field.',
"default": 'The __NAME__ field is still set to default, please change.',
email: 'The __NAME__ field must contain a valid email address.',
emails: 'The __NAME__ field must contain all valid email addresses.',
minLength: 'The __NAME__ field must be at least __VALUE__ characters in length.',
maxLength: 'The __NAME__ field must not exceed __VALUE__ characters in length.',
exactLength: 'The __NAME__ field must be exactly __VALUE__ characters in length.',
greaterThan: 'The __NAME__ field must contain a number greater than __VALUE__.',
lessThan: 'The __NAME__ field must contain a number less than __VALUE__.',
alpha: 'The __NAME__ field must only contain alphabetical characters.',
alphaNumeric: 'The __NAME__ field must only contain alpha-numeric characters.',
alphaDash: 'The __NAME__ field must only contain alpha-numeric characters, underscores, and dashes.',
numeric: 'The __NAME__ field must contain only numbers.',
number: "The __NAME__ value must be of type 'number'.",
integer: 'The __NAME__ field must contain an integer.',
decimal: 'The __NAME__ field must contain a decimal number.',
natural: 'The __NAME__ field must contain only positive numbers.',
naturalNoZero: 'The __NAME__ field must contain a number greater than zero.',
ip: 'The __NAME__ field must contain a valid IP.',
base64: 'The __NAME__ field must contain a base64 string.',
creditCard: 'The __NAME__ field must contain a valid credit card number.',
fileType: 'The __NAME__ field must contain only __VALUE__ files.',
validUrl: 'The __NAME__ field must contain a valid URL.',
greaterThanDate: 'The __NAME__ field must contain a more recent date than __VALUE__.',
lessThanDate: 'The __NAME__ field must contain an older date than __VALUE__.',
greaterThanOrEqualDate: "The __NAME__ field must contain a date that's at least as recent as __VALUE__.",
lessThanOrEqualDate: "The __NAME__ field must contain a date that's __VALUE__ or older."
};
// auto-generate alternate property names from camelCase names
// creates hyphen-ated and under_score aliases
// clutters up the namespace, but... oh, well
forOwn(message, function(name){
message[name.toLowerCase()] = message[name]; // lowercase names
message[toDashed(name)] = message[name]; // hyphen-ated names
message[toUnderscore(name)] = message[name]; // under_score names
});
var regex = {
Mark M. Florida
committed
//required: /[\S\W]+/, // whitespace characters will still validate
notEmpty: /[\S]/, // must contain more than just whitespace characters
rule: /^(.+?)\[(.+)\]$/, // ?
//numeric: /^-?\d*\d{3}[,]*\d[.]*\d+$/,
integer: /^-?[0-9]+$/, // positive or negative whole number
natural: /^[0-9]+$/, // positive whole number
Mark M. Florida
committed
naturalNoZero: /^([1-9]+[0-9]*)$/, // positive whole number, no leading 0s
decimal: /^(-?[0-9]*\.?[0-9])$/,
hexadecimal: /^[0-9a-f]$/i,
email: /^([a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))$/i,
alpha: /^[a-z]$/i, // ONLY letters
alphaSafe: /^[a-z_]$/i, // ONLY letters and underscores
alphaDash: /^[a-z_\-]$/i, // ONLY letters, underscore, and dash
alphaNum: /^[a-z0-9]$/i, // ONLY letters and numbers
alphaNumSafe: /^[a-z0-9_]$/i, // ONLY letters, numbers, and underscore
alphaNumDash: /^[a-z0-9_\-]$/i, // ONLY letters, numbers, underscore, and dash
alphaNumDashSpace: /^[a-z0-9_\- ]$/i, // ONLY letters, numbers, underscore, dash, and space
idSafe: /^([a-z][a-z0-9_\-]*)$/i, // safe to use as an ID - alphasafe and must start with a letter
idStrict: /^([a-z][a-z0-9_]*)$/i, // 'idSafe' without hyphens
ip: /^(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2}))$/i,
base64: /^([^a-zA-Z0-9\/+=])$/i,
numericDash: /^[\d\-\s]$/,
//url: /^(((http|https):\/\/(\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)$/,
//url: /^(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&/=]*))$/i,
url: /^(https?:\/\/[^\/\s]+(\/.*)?)$/i, // keep it simple for less strict url validation
//uri: /^(([\/](\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/])))$/,
uri: /^(\/\w*)/i, // simpler URI check only requires string start with a single '/'
// these date regexes can't check leap years or other incorrect MM/DD combos
Mark M. Florida
committed
dateISO: /^((19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01]))$/,
dateUS: /^((0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d)$/,
dateEU: /^((0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d)$/,
// CRON!!!!! (Say it like "KHAN!!!!!")
cronWords: /^@(reboot|yearly|annually|monthly|weekly|daily|midnight|hourly)$/i,
cronSeconds: /^((\*|\?|0|([1-9]|[1-5][0-9]))(\/\d+)?)$/,
cronMinutes: /^((\*|\?|0|([1-9]|[1-5][0-9]))(\/\d+)?)$/,
cronHours: /^((\*|\?|([0-9]|1[0-9]|2[0-3]))(\/\d+)?)$/,
cronDay: /^((\*|\?|([0-9]|[1-2][0-9]|3[0-1]))(\/\d+)?)$/,
cronMonth: /^((\*|\?|([0-9]|1[0-2]))(\/\d+)?)$/,
cronMonths: /^(((\*|\?|([0-9]|1[0-2]))(\/\d+)?)|(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|DEC))$/i,
Mark M. Florida
committed
cronMonthNames: /^(\*|\?|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|DEC)$/i,
cronWeekday: /^((\*|\?|([0-7]))(\/\d+)?)$/,
cronWeekdays: /^(((\*|\?|([0-7]))(\/\d+)?)|(MON|TUE|WED|THU|FRI|SAT|SUN))$/i,
Mark M. Florida
committed
cronWeekdayNames: /^(\*|\?|MON|TUE|WED|THU|FRI|SAT|SUN)$/i,
// cronAlt: /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$/,
// cron regex lifted from this post: http://stackoverflow.com/questions/235504/validating-crontab-entries-w-php
// cron: /^\s*($|#|\w+\s*=|(\*(?:\/\d+)?|(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?(?:,(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?(?:,(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?(?:,(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?(?:,(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?)*|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+(\*(?:\/\d+)?|(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?(?:,(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?)*|mon|tue|wed|thu|fri|sat|sun)\s+|(@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly)\s+)([^\s]+)\s+(.*)$/,
bogus: /bogus/i // filler
};
// aliases
regex.int = regex.integer;
Mark M. Florida
committed
//regex.number = regex.numeric;
regex.float = regex.decimal;
regex.hex = regex.hexadecimal;
regex.alphaNumeric = regex.alphaNum;
regex.alphaNumericSafe = regex.alphaNumSafe;
regex.ipAddr = regex.ipAddress = regex.ip;
regex.fullUrl = regex.url;
Mark M. Florida
committed
regex.path = regex.uri;
regex.date = regex.dateISO;
// auto-generate alternate property names from camelCase names
// creates hyphen-ated and under_score aliases
// clutters up the namespace, but... oh, well
forOwn(regex, function(name){
regex[name.toLowerCase()] = regex[name]; // lowercase names
regex[toDashed(name)] = regex[name]; // hyphen-ated names
regex[toUnderscore(name)] = regex[name]; // under_score names
});
// export combined 'regex' object back to global namespace
XNAT.validation.regex = extend(regex, XNAT.validation.regex || {});
// define custom test methods for more complex validations
var test = {};
Mark M. Florida
committed
test.required = function(){
// 'this' is the parent Validator instance
return (new Validator(this.element)).required().validated;
};
test.empty = function(value){
return !(value+'');
};
test.not = function(value, not){
return (new Validator()).val(value).not(not).validated;
};
Mark M. Florida
committed
test.numeric = test.number = function(value){
console.log('numeric');
return isNumeric(value);
};
test.interval = function(value){
console.log('interval');
var parts = value.split(/([0-9]+)/);
var units = /\s+(sec|second|min|minute|hour|day|week|month|year)(s)?\s*/;
var num = true;
Mark M. Florida
committed
var valid = value ? true : false;
Mark M. Florida
committed
var i = parts[0] === '' ? 1 : 0; // start i at 1 if parts[0] is an empty string
var part;
while (parts[i] && valid === true) {
part = (parts[i] + '');
if (num) {
valid = /\d+/.test(part);
}
else {
valid = units.test(part);
}
num = !num; // flip for next iteration
i++;
}
return valid;
};
// match to 6-field cron syntax:
// 0 0 * * * *
Mark M. Florida
committed
test.cron = test.cronSyntax = function(value){
Mark M. Florida
committed
value = (value+'').trim();
Mark M. Florida
committed
// easiest test - use words
if (regex.cronWords.test(value)) {
return true;
Mark M. Florida
committed
// split value to test parts
var parts = value.split(/\s+/);
Mark M. Florida
committed
// array of regexes to match 'parts' array
var tests = [
regex.cronSeconds,
regex.cronMinutes,
regex.cronHours,
regex.cronDay,
regex.cronMonths,
regex.cronWeekdays
];
Mark M. Florida
committed
var errors = 6;
Mark M. Florida
committed
parts.forEach(function(part, i){
Mark M. Florida
committed
errors = tests[i].test(part) ? errors - 1 : errors ;
Mark M. Florida
committed
});
Mark M. Florida
committed
return errors === 0;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
};
// check a comma- or space-separated list of multiple email addresses
test.emails = function(value){
var errors = 0;
value.split(/[,\s]+/).forEach(function(email){
if (errors) return false;
email = email.trim();
if (!regex.email.test(email)) {
errors++
}
});
return errors === 0;
};
// make sure there's a minimum number of characters
test.minLength = function(value, length){
if (!regex.naturalNoZero.test(length)) {
return false;
}
return (value.length >= parseInt(length, 10));
};
// don't exceed the maximum number of characters
test.maxLength = function(value, length){
if (!regex.naturalNoZero.test(length)) {
return false;
}
return (value.length <= parseInt(length, 10));
};
test.exactLength = function(value, length){
if (!regex.naturalNoZero.test(length)) {
return false;
}
return (value.length === parseInt(length, 10));
};
test.isLength = test.exactLength;
// XNAT.validate('#concurrent-sessions').is('greaterThan', 0).check();
test.greaterThan = function(value, num){
if (!regex.decimal.test(value)) {
return false;
}
return (parseFloat(value) > parseFloat(num));
};
test.greaterThanOrEqual = function(value, num){
if (!regex.decimal.test(value)) {
return false;
}
return (parseFloat(value) >= parseFloat(num));
};
test.greaterThanOrEqualTo = test.greaterThanOrEqual;
test.gte = test.greaterThanOrEqual;
// XNAT.validate('#session-timeout').is('lessThan', 999).check();
test.lessThan = function(value, num){
if (!regex.decimal.test(value)) {
return false;
}
return (parseFloat(value) < parseFloat(num));
};
test.lessThanOrEqual = function(value, num){
if (!regex.decimal.test(value)) {
return false;
}
return (parseFloat(value) <= parseFloat(num));
};
test.lessThanOrEqualTo = test.lessThanOrEqual;
test.lte = test.lessThanOrEqual;
Mark M. Florida
committed
test.equalTo = function(value, testValue){
if (/^![^!]/.test(testValue)){
return value+'' !== testValue+'';
}
return value+'' === testValue+''
};
test.equals = test.equalTo;
Mark M. Florida
committed
test.eq = test.equalTo;
Mark M. Florida
committed
// date checks
Mark M. Florida
committed
test.greaterThanDate = function(value, date){
var enteredDate = getValidDate(value),
Mark M. Florida
committed
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate > validDate;
};
test.gtDate = test.greaterThanDate;
Mark M. Florida
committed
test.greaterThanOrEqualDate = function(value, date){
var enteredDate = getValidDate(value),
Mark M. Florida
committed
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate >= validDate;
};
test.greaterThanOrEqualToDate = test.greaterThanOrEqualDate;
test.gteDate = test.greaterThanOrEqualDate;
Mark M. Florida
committed
test.lessThanDate = function(value, date){
var enteredDate = getValidDate(value),
Mark M. Florida
committed
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate < validDate;
};
test.ltDate = test.lessThanDate;
Mark M. Florida
committed
test.lessThanOrEqualDate = function(value, date){
var enteredDate = getValidDate(value),
Mark M. Florida
committed
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate <= validDate;
};
test.lessThanOrEqualToDate = test.lessThanOrEqualDate;
test.lteDate = test.lessThanOrEqualDate;
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// XNAT.validate('input.credit-card').is('creditCard').check();
test.creditCard = function(value){
// Luhn Check Code from https://gist.github.com/4075533
// accept only digits, dashes or spaces
if (!regex.numericDash.test(value)) return false;
// The Luhn Algorithm. It's so pretty.
var nCheck = 0, nDigit = 0, bEven = false;
var strippedField = value.replace(/\D/g, "");
for (var n = strippedField.length - 1; n >= 0; n--) {
var cDigit = strippedField.charAt(n);
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) === 0;
};
// Check file extension of submitted file.
//
// XNAT.validate('input.doc[type="file"]').is('fileType', 'doc').check()
//
test.fileType = function(value, type){
//if (type !== 'file') {
// return true;
//}
var ext = value.substr((value.lastIndexOf('.') + 1)).toLowerCase(),
typeArray = type.split(','),
inArray = false,
i = -1,
len = typeArray.length;
while (++i < len) {
if (ext == typeArray[i].toLowerCase()) {
inArray = true;
}
}
return inArray;
};
// auto-generate alternate property names from camelCase names
// creates hyphen-ated and under_score aliases
forOwn(test, function(name){
test[name.toLowerCase()] = test[name]; // lowercase names
test[toDashed(name)] = test[name]; // hyphen-ated names
test[toUnderscore(name)] = test[name]; // under_score names
});
// export combined 'test' object back to global namespace
XNAT.validation.test = extend(test, XNAT.validation.test || {});
// HELPERS
function init(element){
var obj = {
element$: $.spawn('input.tmp|type=hidden'),
len: 0,
regex: '',
value: '',
values: [], // use to check more than one value
Mark M. Florida
committed
validated: true // true until proven false
};
obj.element = obj.element$[0];
if (element) {
obj.element$ = $$(element).removeClass('valid invalid');
obj.len = obj.element$.length;
Mark M. Florida
committed
if (obj.len) {
obj.element = obj.element$[0];
obj.value = obj.element.value || '';
}
}
return obj;
}
Mark M. Florida
committed
// TODO: get this working
function getValidDate(date){
Mark M. Florida
committed
var regexDateMatch = (
date.match(regex['dateISO']) ||
date.match(regex['dateUS']) ||
date.match(regex['dateEU'])
);
if (!date.match('today') && !regexDateMatch) {
return false;
}
var validDate = new Date(),
validDateArray;
if (!date.match('today')) {
Mark M. Florida
committed
validDateArray = date.split(/[\s.-/]+/);
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
validDate.setFullYear(validDateArray[0]);
validDate.setMonth(validDateArray[1] - 1);
validDate.setDate(validDateArray[2]);
}
return validDate;
}
// CONSTRUCTOR
function Validator(element){
extend(this, init(element));
}
Validator.fn = Validator.prototype;
Validator.fn.init = function(element){
if (element){
extend(this, init(element));
}
return this;
};
// reset element so it can be validated again
Validator.fn.reset = function(element){
extend(this, init(element||this.element));
return this;
};
// explicitly set a value to check
// XNAT.validate().val('bar@foo.org').is('email').check();
// -> true
Validator.fn.val = function(value){
this.value = value;
return this;
};
Validator.fn.trim = function(){
this.trimValue = true;
this.element$.each(function(){
this.value = this.value.trim();
});
return this;
};
// set className to valid/invalid
Validator.fn.setClass = function(){
var className = this.validated ? 'valid': 'invalid';
this.element$
.removeClass('valid invalid')
.addClass(className);
};
Validator.fn.is = function(type, args){
Mark M. Florida
committed
var parts = [];
// check all if there's more than
// one element in the selection
if (this.len > 1) {
this.all(type, args);
return this;
}
// return early if the validation is already false
// (this is necessary for working with chained methods)
if (this.validated === false) { return this }
Mark M. Florida
committed
// skip on* types
if (/^on/i.test(type)) {
return this;
}
if (this.trimValue) {
this.value = (this.value+'').trim();
}
Mark M. Florida
committed
// set 'allowEmpty' flag
if (type === 'allow-empty') {
this.allowEmpty = true;
Mark M. Florida
committed
return this;
Mark M. Florida
committed
}
if (typeof type === 'string') {
parts = type.split(':');
type = parts.shift();
if (parts.length) {
this.is(type, parts.join(':'));
return this;
}
}
// start with '!' for negation
// !eq:0
if (/^![^!]/.test(type)){
this.not(type.replace(/^!/, ''), args);
return this;
}
// if there's a test['test'] method, use that
Mark M. Florida
committed
if (typeof test[type] === 'function') {
this.validated = test[type].apply(this, [].concat(this.value, args));
}
// if there's a regex defined (above) for 'type', use that
else if (regex[type]) {
this.pattern(regex[type]);
// this.validated = regex[type].test(this.value);
}
Mark M. Florida
committed
// if 'type' is a string, number or boolean, do a string comparison
else if (/string|number|boolean/i.test(typeof type)) {
this.validated = test.equals.apply(this, [].concat(this.value, type, args));
}
// a 'type' function can also be passed
// (must return boolean true or false)
Mark M. Florida
committed
else if (typeof type === 'function') {
this.validated = type.apply(this, [].concat(args));
}
// otherwise do a regex test
else {
try {
this.pattern(type);
// this.validated = type.test(this.value);
}
catch(e) {
console.log(e);
}
}
Mark M. Florida
committed
// let empty string validate if 'allowEmpty' is true
if (this.allowEmpty && (this.value+'').trim() === ''){
this.validated = true;
}
this.setClass();
return this;
};
Mark M. Florida
committed
Validator.fn.not = function(type, args){
this.is(type, args);
this.validated = !this.validated;
Mark M. Florida
committed
this.setClass();
return this;
};
// validate all elements in a collection
// XNAT.validate('input.float').all('float');
Validator.fn.all = function(type, args){
// var self = this;
var invalid = 0;
this.elements = this.element$.toArray();
if (!type) return this;
// if type is specified, check each element
this.element$.each(function(){
var elValidate = new Validator(this);
elValidate.is(type, args);
//valid = regex[type].test(this.value);
Mark M. Florida
committed
if (!elValidate.isValid(true)) {
invalid++
}
});
this.validated = invalid === 0;
return this;
};
Validator.fn.none = function(type){
// make sure NONE of the elements match the type
};
Validator.fn.pattern = function(regex){
Mark M. Florida
committed
this.regex = (typeof regex === 'string') ? new RegExp(regex) : regex;
this.validated = regex.test(this.value);
Mark M. Florida
committed
this.setClass();
return this;
};
// match the value of another element
// optionally trimming leading and trailing whitespace
Validator.fn.matches = function(target, trim){
var sourceValue = this.value + '';
var targetValue = $$(target).val() + '';
if (trim) {
sourceValue = sourceValue.trim();
targetValue = targetValue.trim();
}
this.validated = sourceValue === targetValue;
Mark M. Florida
committed
this.setClass();
return this;
};
// XNAT.validate('#url').required().check();
Validator.fn.required = function(){
this.all(function(){
if (/checkbox|radio/.test(this.element$.type)) {
return (this.checked === true);
}
return this.value+'' !== '';
});
return this;
};
// set up shortcut methods (uses test[type]() methods)
// example:
// XNAT.validate('#sessions-concurrent-max').lessThan(1000).check();
[ 'minLength', 'maxLength', 'exactLength', 'isLength',
Mark M. Florida
committed
'greaterThan', 'gt', 'greaterThanOrEqual', 'greaterThanOrEqualTo', 'gte',
'lessThan', 'lt', 'lessThanOrEqual', 'lessThanOrEqualTo', 'lte',
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
'equalTo', 'equals', 'fileType' ].forEach(function(method) {
Validator.fn[method] = function (test) {
this.is(method, test);
return this;
}
});
// .valid() must be called LAST
// XNAT.validate('#email').trim().is('email').valid(true);
Validator.fn.valid = function(bool){
bool = (bool === undefined) ? true : bool;
return bool ? this.validated : !this.validated;
};
Validator.fn.isValid = Validator.fn.valid;
//
// call *either* .valid() -OR- .check() last
//
// XNAT.validate('input.email').is('email').valid(true);
//
// --OR--
//
// XNAT.validate().value('foo').check(function(){ return this.value === 'foo' });
//
// .check() must be called last
// XNAT.validate('#email').check('email');
//
// type can be regex['type'] string,
// function (must return true or false),
// or custom regex
Validator.fn.check = function(type){
Mark M. Florida
committed
var self = this,
types = [];
if (type) {
this.is(type);
}
Mark M. Florida
committed
else if (type !== false) {
if (this.element$.dataAttr('validate')) {
types = this.element$.dataAttr('validate').split(/\s+/);
Mark M. Florida
committed
$.each(types, function(idx, item){
// stop if validation has already failed
if (!self.validated) {
return false;
}
// skip on* types
if (!/^on/i.test(type)) {
self.is(item);
}
Mark M. Florida
committed
})
}
}
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
return this.isValid(true);
};
// usage:
// XNAT.validate('#user-email').trim().is('email').check();
validate = function(element){
return new Validator(element);
};
// TODO: move the date methods below to {test} object: test['greaterThanDate']() etc...
validate.check = {
greater_than_date: function(field, date){
var enteredDate = getValidDate(field.value),
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate > validDate;
},
less_than_date: function(field, date){
var enteredDate = getValidDate(field.value),
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate < validDate;
},
greater_than_or_equal_date: function(field, date){
var enteredDate = getValidDate(field.value),
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate >= validDate;
},
less_than_or_equal_date: function(field, date){
var enteredDate = getValidDate(field.value),
validDate = getValidDate(date);
if (!validDate || !enteredDate) {
return false;
}
return enteredDate <= validDate;
}
};
Mark M. Florida
committed
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
// add event listeners for validation
$(function(){
var $body = $('body');
$body.on('focus', ':input[data-validate]', function(){
$(this).removeClass('valid invalid');
});
$body.on('blur', ':input[data-validate].onblur', function(){
validate(this).check();
});
// TODO: enable this after testing validation methods more thoroughly
// $body.on('submit', 'form.validate', function(e){
// e.preventDefault();
// var errors = 0,
// $form = $(this);
// $form.find(':input[data-validate]').not('.ignore').each(function(){
// var valid = validate(this).check();
// if (!valid) { errors++ }
// });
// return errors === 0;
// // if (errors === 0){
// // //$form.removeClass('validate').submit();
// // return true;
// // }
// // return false;
// });
});
// this script has loaded
validate.loaded = true;
return XNAT.validate = validate;
}));