vendor: 45,550 bytes, lines 1-1217
1(function(exports, module, define) { 2 "use strict"; 3 4 5 var validate = function(attributes, constraints, options) { 6 options = v.extend({}, v.options, options); 7 8 var results = v.runValidations(attributes, constraints, options), 9 attr, validator; 10 11 if (results.some(function(r) { return v.isPromise(r.error); })) { 12 throw new Error("Use validate.async if you want support for promises"); 13 } 14 return validate.processValidationResults(results, options); 15 }; 16 17 var v = validate; 18 19 // Copies over attributes from one or more sources to a single destination. 20 // Very much similar to underscore's extend. 21 // The first argument is the target object and the remaining arguments will be 22 // used as sources. 23 v.extend = function(obj) { 24 [].slice.call(arguments, 1).forEach(function(source) { 25 for (var attr in source) { 26 obj[attr] = source[attr]; 27 } 28 }); 29 return obj; 30 }; 31 32 v.extend(validate, { 33 // This is the version of the library as a semver. 34 // The toString function will allow it to be coerced into a string 35 version: { 36 major: 0, 37 minor: 13, 38 patch: 1, 39 metadata: "development", 40 toString: function() { 41 var version = v.format("%{major}.%{minor}.%{patch}", v.version); 42 if (!v.isEmpty(v.version.metadata)) { 43 version += "+" + v.version.metadata; 44 } 45 return version; 46 } 47 }, 48 49 // Below is the dependencies that are used in validate.js 50 51 // The constructor of the Promise implementation. 52 // If you are using Q.js, RSVP or any other A+ compatible implementation 53 // override this attribute to be the constructor of that promise. 54 // Since jQuery promises aren't A+ compatible they won't work. 55 Promise: typeof Promise !== "undefined" ? Promise : /* istanbul ignore next */ null, 56 57 EMPTY_STRING_REGEXP: /^\s*$/, 58 59 // Runs the validators specified by the constraints object. 60 // Will return an array of the format: 61 // [{attribute: "<attribute name>", error: "<validation result>"}, ...] 62 runValidations: function(attributes, constraints, options) { 63 var results = [], 64 attr, validatorName, value, validators, validator, validatorOptions, error; 65 66 if (v.isDomElement(attributes) || v.isJqueryElement(attributes)) { 67 attributes = v.collectFormValues(attributes); 68 } 69 70 // Loops through each constraints, finds the correct validator and run it. 71 for (attr in constraints) { 72 value = v.getDeepObjectValue(attributes, attr); 73 // This allows the constraints for an attribute to be a function. 74 // The function will be called with the value, attribute name, the complete dict of 75 // attributes as well as the options and constraints passed in. 76 // This is useful when you want to have different 77 // validations depending on the attribute value. 78 validators = v.result(constraints[attr], value, attributes, attr, options, constraints); 79 80 for (validatorName in validators) { 81 validator = v.validators[validatorName]; 82 83 if (!validator) { 84 error = v.format("Unknown validator %{name}", { name: validatorName }); 85 throw new Error(error); 86 } 87 88 validatorOptions = validators[validatorName]; 89 // This allows the options to be a function. The function will be 90 // called with the value, attribute name, the complete dict of 91 // attributes as well as the options and constraints passed in. 92 // This is useful when you want to have different 93 // validations depending on the attribute value. 94 validatorOptions = v.result(validatorOptions, value, attributes, attr, options, constraints); 95 if (!validatorOptions) { 96 continue; 97 } 98 results.push({ 99 attribute: attr, 100 value: value, 101 validator: validatorName, 102 globalOptions: options, 103 attributes: attributes, 104 options: validatorOptions, 105 error: validator.call(validator, 106 value, 107 validatorOptions, 108 attr, 109 attributes, 110 options) 111 }); 112 } 113 } 114 115 return results; 116 }, 117 118 // Takes the output from runValidations and converts it to the correct 119 // output format. 120 processValidationResults: function(errors, options) { 121 errors = v.pruneEmptyErrors(errors, options); 122 errors = v.expandMultipleErrors(errors, options); 123 errors = v.convertErrorMessages(errors, options); 124 125 var format = options.format || "grouped"; 126 127 if (typeof v.formatters[format] === 'function') { 128 errors = v.formatters[format](errors); 129 } else { 130 throw new Error(v.format("Unknown format %{format}", options)); 131 } 132 133 return v.isEmpty(errors) ? undefined : errors; 134 }, 135 136 // Runs the validations with support for promises. 137 // This function will return a promise that is settled when all the 138 // validation promises have been completed. 139 // It can be called even if no validations returned a promise. 140 async: function(attributes, constraints, options) { 141 options = v.extend({}, v.async.options, options); 142 143 var WrapErrors = options.wrapErrors || function(errors) { 144 return errors; 145 }; 146 147 // Removes unknown attributes 148 if (options.cleanAttributes !== false) { 149 attributes = v.cleanAttributes(attributes, constraints); 150 } 151 152 var results = v.runValidations(attributes, constraints, options); 153 154 return new v.Promise(function(resolve, reject) { 155 v.waitForResults(results).then(function() { 156 var errors = v.processValidationResults(results, options); 157 if (errors) { 158 reject(new WrapErrors(errors, options, attributes, constraints)); 159 } else { 160 resolve(attributes); 161 } 162 }, function(err) { 163 reject(err); 164 }); 165 }); 166 }, 167 168 single: function(value, constraints, options) { 169 options = v.extend({}, v.single.options, options, { 170 format: "flat", 171 fullMessages: false 172 }); 173 return v({ single: value }, { single: constraints }, options); 174 }, 175 176 // Returns a promise that is resolved when all promises in the results array 177 // are settled. The promise returned from this function is always resolved, 178 // never rejected. 179 // This function modifies the input argument, it replaces the promises 180 // with the value returned from the promise. 181 waitForResults: function(results) { 182 // Create a sequence of all the results starting with a resolved promise. 183 return results.reduce(function(memo, result) { 184 // If this result isn't a promise skip it in the sequence. 185 if (!v.isPromise(result.error)) { 186 return memo; 187 } 188 189 return memo.then(function() { 190 return result.error.then(function(error) { 191 result.error = error || null; 192 }); 193 }); 194 }, new v.Promise(function(r) { r(); })); // A resolved promise 195 }, 196 197 // If the given argument is a call: function the and: function return the value 198 // otherwise just return the value. Additional arguments will be passed as 199 // arguments to the function. 200 // Example: 201 // ``` 202 // result('foo') // 'foo' 203 // result(Math.max, 1, 2) // 2 204 // ``` 205 result: function(value) { 206 var args = [].slice.call(arguments, 1); 207 if (typeof value === 'function') { 208 value = value.apply(null, args); 209 } 210 return value; 211 }, 212 213 // Checks if the value is a number. This function does not consider NaN a 214 // number like many other `isNumber` functions do. 215 isNumber: function(value) { 216 return typeof value === 'number' && !isNaN(value); 217 }, 218 219 // Returns false if the object is not a function 220 isFunction: function(value) { 221 return typeof value === 'function'; 222 }, 223 224 // A simple check to verify that the value is an integer. Uses `isNumber` 225 // and a simple modulo check. 226 isInteger: function(value) { 227 return v.isNumber(value) && value % 1 === 0; 228 }, 229 230 // Checks if the value is a boolean 231 isBoolean: function(value) { 232 return typeof value === 'boolean'; 233 }, 234 235 // Uses the `Object` function to check if the given argument is an object. 236 isObject: function(obj) { 237 return obj === Object(obj); 238 }, 239 240 // Simply checks if the object is an instance of a date 241 isDate: function(obj) { 242 return obj instanceof Date; 243 }, 244 245 // Returns false if the object is `null` of `undefined` 246 isDefined: function(obj) { 247 return obj !== null && obj !== undefined; 248 }, 249 250 // Checks if the given argument is a promise. Anything with a `then` 251 // function is considered a promise. 252 isPromise: function(p) { 253 return !!p && v.isFunction(p.then); 254 }, 255 256 isJqueryElement: function(o) { 257 return o && v.isString(o.jquery); 258 }, 259 260 isDomElement: function(o) { 261 if (!o) { 262 return false; 263 } 264 265 if (!o.querySelectorAll || !o.querySelector) { 266 return false; 267 } 268 269 if (v.isObject(document) && o === document) { 270 return true; 271 } 272 273 274 /* istanbul ignore else */ 275 if (typeof HTMLElement === "object") { 276 return o instanceof HTMLElement; 277 } else { 278 return o && 279 typeof o === "object" && 280 o !== null && 281 o.nodeType === 1 && 282 typeof o.nodeName === "string"; 283 } 284 }, 285 286 isEmpty: function(value) { 287 var attr; 288 289 // Null and undefined are empty 290 if (!v.isDefined(value)) { 291 return true; 292 } 293 294 // functions are non empty 295 if (v.isFunction(value)) { 296 return false; 297 } 298 299 // Whitespace only strings are empty 300 if (v.isString(value)) { 301 return v.EMPTY_STRING_REGEXP.test(value); 302 } 303 304 // For arrays we use the length property 305 if (v.isArray(value)) { 306 return value.length === 0; 307 } 308 309 // Dates have no attributes but aren't empty 310 if (v.isDate(value)) { 311 return false; 312 } 313 314 // If we find at least one property we consider it non empty 315 if (v.isObject(value)) { 316 for (attr in value) { 317 return false; 318 } 319 return true; 320 } 321 322 return false; 323 }, 324 325 // Formats the specified strings with the given values like so: 326 // ``` 327 // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar" 328 // ``` 329 // If you want to write %{...} without having it replaced simply 330 // prefix it with % like this `Foo: %%{foo}` and it will be returned 331 // as `"Foo: %{foo}"` 332 format: v.extend(function(str, vals) { 333 if (!v.isString(str)) { 334 return str; 335 } 336 return str.replace(v.format.FORMAT_REGEXP, function(m0, m1, m2) { 337 if (m1 === '%') { 338 return "%{" + m2 + "}"; 339 } else { 340 return String(vals[m2]); 341 } 342 }); 343 }, { 344 // Finds %{key} style patterns in the given string 345 FORMAT_REGEXP: /(%?)%\{([^\}]+)\}/g 346 }), 347 348 // "Prettifies" the given string. 349 // Prettifying means replacing [.\_-] with spaces as well as splitting 350 // camel case words. 351 prettify: function(str) { 352 if (v.isNumber(str)) { 353 // If there are more than 2 decimals round it to two 354 if ((str * 100) % 1 === 0) { 355 return "" + str; 356 } else { 357 return parseFloat(Math.round(str * 100) / 100).toFixed(2); 358 } 359 } 360 361 if (v.isArray(str)) { 362 return str.map(function(s) { return v.prettify(s); }).join(", "); 363 } 364 365 if (v.isObject(str)) { 366 if (!v.isDefined(str.toString)) { 367 return JSON.stringify(str); 368 } 369 370 return str.toString(); 371 } 372 373 // Ensure the string is actually a string 374 str = "" + str; 375 376 return str 377 // Splits keys separated by periods 378 .replace(/([^\s])\.([^\s])/g, '$1 $2') 379 // Removes backslashes 380 .replace(/\\+/g, '') 381 // Replaces - and - with space 382 .replace(/[_-]/g, ' ') 383 // Splits camel cased words 384 .replace(/([a-z])([A-Z])/g, function(m0, m1, m2) { 385 return "" + m1 + " " + m2.toLowerCase(); 386 }) 387 .toLowerCase(); 388 }, 389 390 stringifyValue: function(value, options) { 391 var prettify = options && options.prettify || v.prettify; 392 return prettify(value); 393 }, 394 395 isString: function(value) { 396 return typeof value === 'string'; 397 }, 398 399 isArray: function(value) { 400 return {}.toString.call(value) === '[object Array]'; 401 }, 402 403 // Checks if the object is a hash, which is equivalent to an object that 404 // is neither an array nor a function. 405 isHash: function(value) { 406 return v.isObject(value) && !v.isArray(value) && !v.isFunction(value); 407 }, 408 409 contains: function(obj, value) { 410 if (!v.isDefined(obj)) { 411 return false; 412 } 413 if (v.isArray(obj)) { 414 return obj.indexOf(value) !== -1; 415 } 416 return value in obj; 417 }, 418 419 unique: function(array) { 420 if (!v.isArray(array)) { 421 return array; 422 } 423 return array.filter(function(el, index, array) { 424 return array.indexOf(el) == index; 425 }); 426 }, 427 428 forEachKeyInKeypath: function(object, keypath, callback) { 429 if (!v.isString(keypath)) { 430 return undefined; 431 } 432 433 var key = "", 434 i, escape = false; 435 436 for (i = 0; i < keypath.length; ++i) { 437 switch (keypath[i]) { 438 case '.': 439 if (escape) { 440 escape = false; 441 key += '.'; 442 } else { 443 object = callback(object, key, false); 444 key = ""; 445 } 446 break; 447 448 case '\\': 449 if (escape) { 450 escape = false; 451 key += '\\'; 452 } else { 453 escape = true; 454 } 455 break; 456 457 default: 458 escape = false; 459 key += keypath[i]; 460 break; 461 } 462 } 463 464 return callback(object, key, true); 465 }, 466 467 getDeepObjectValue: function(obj, keypath) { 468 if (!v.isObject(obj)) { 469 return undefined; 470 } 471 472 return v.forEachKeyInKeypath(obj, keypath, function(obj, key) { 473 if (v.isObject(obj)) { 474 return obj[key]; 475 } 476 }); 477 }, 478 479 // This returns an object with all the values of the form. 480 // It uses the input name as key and the value as value 481 // So for example this: 482 // <input type="text" name="email" value="[email protected]" /> 483 // would return: 484 // {email: "[email protected]"} 485 collectFormValues: function(form, options) { 486 var values = {}, 487 i, j, input, inputs, option, value; 488 489 if (v.isJqueryElement(form)) { 490 form = form[0]; 491 } 492 493 if (!form) { 494 return values; 495 } 496 497 options = options || {}; 498 499 inputs = form.querySelectorAll("input[name], textarea[name]"); 500 for (i = 0; i < inputs.length; ++i) { 501 input = inputs.item(i); 502 503 if (v.isDefined(input.getAttribute("data-ignored"))) { 504 continue; 505 } 506 507 var name = input.name.replace(/\./g, "\\\\."); 508 value = v.sanitizeFormValue(input.value, options); 509 if (input.type === "number") { 510 value = value ? +value : null; 511 } else if (input.type === "checkbox") { 512 if (input.attributes.value) { 513 if (!input.checked) { 514 value = values[name] || null; 515 } 516 } else { 517 value = input.checked; 518 } 519 } else if (input.type === "radio") { 520 if (!input.checked) { 521 value = values[name] || null; 522 } 523 } 524 values[name] = value; 525 } 526 527 inputs = form.querySelectorAll("select[name]"); 528 for (i = 0; i < inputs.length; ++i) { 529 input = inputs.item(i); 530 if (v.isDefined(input.getAttribute("data-ignored"))) { 531 continue; 532 } 533 534 if (input.multiple) { 535 value = []; 536 for (j in input.options) { 537 option = input.options[j]; 538 if (option && option.selected) { 539 value.push(v.sanitizeFormValue(option.value, options)); 540 } 541 } 542 } else { 543 var _val = typeof input.options[input.selectedIndex] !== 'undefined' ? input.options[input.selectedIndex].value : /* istanbul ignore next */ ''; 544 value = v.sanitizeFormValue(_val, options); 545 } 546 values[input.name] = value; 547 } 548 549 return values; 550 }, 551 552 sanitizeFormValue: function(value, options) { 553 if (options.trim && v.isString(value)) { 554 value = value.trim(); 555 } 556 557 if (options.nullify !== false && value === "") { 558 return null; 559 } 560 return value; 561 }, 562 563 capitalize: function(str) { 564 if (!v.isString(str)) { 565 return str; 566 } 567 return str[0].toUpperCase() + str.slice(1); 568 }, 569 570 // Remove all errors who's error attribute is empty (null or undefined) 571 pruneEmptyErrors: function(errors) { 572 return errors.filter(function(error) { 573 return !v.isEmpty(error.error); 574 }); 575 }, 576 577 // In 578 // [{error: ["err1", "err2"], ...}] 579 // Out 580 // [{error: "err1", ...}, {error: "err2", ...}] 581 // 582 // All attributes in an error with multiple messages are duplicated 583 // when expanding the errors. 584 expandMultipleErrors: function(errors) { 585 var ret = []; 586 errors.forEach(function(error) { 587 // Removes errors without a message 588 if (v.isArray(error.error)) { 589 error.error.forEach(function(msg) { 590 ret.push(v.extend({}, error, { error: msg })); 591 }); 592 } else { 593 ret.push(error); 594 } 595 }); 596 return ret; 597 }, 598 599 // Converts the error mesages by prepending the attribute name unless the 600 // message is prefixed by ^ 601 convertErrorMessages: function(errors, options) { 602 options = options || {}; 603 604 var ret = [], 605 prettify = options.prettify || v.prettify; 606 errors.forEach(function(errorInfo) { 607 var error = v.result(errorInfo.error, 608 errorInfo.value, 609 errorInfo.attribute, 610 errorInfo.options, 611 errorInfo.attributes, 612 errorInfo.globalOptions); 613 614 if (!v.isString(error)) { 615 ret.push(errorInfo); 616 return; 617 } 618 619 if (error[0] === '^') { 620 error = error.slice(1); 621 } else if (options.fullMessages !== false) { 622 error = v.capitalize(prettify(errorInfo.attribute)) + " " + error; 623 } 624 error = error.replace(/\\\^/g, "^"); 625 error = v.format(error, { 626 value: v.stringifyValue(errorInfo.value, options) 627 }); 628 ret.push(v.extend({}, errorInfo, { error: error })); 629 }); 630 return ret; 631 }, 632 633 // In: 634 // [{attribute: "<attributeName>", ...}] 635 // Out: 636 // {"<attributeName>": [{attribute: "<attributeName>", ...}]} 637 groupErrorsByAttribute: function(errors) { 638 var ret = {}; 639 errors.forEach(function(error) { 640 var list = ret[error.attribute]; 641 if (list) { 642 list.push(error); 643 } else { 644 ret[error.attribute] = [error]; 645 } 646 }); 647 return ret; 648 }, 649 650 // In: 651 // [{error: "<message 1>", ...}, {error: "<message 2>", ...}] 652 // Out: 653 // ["<message 1>", "<message 2>"] 654 flattenErrorsToArray: function(errors) { 655 return errors 656 .map(function(error) { return error.error; }) 657 .filter(function(value, index, self) { 658 return self.indexOf(value) === index; 659 }); 660 }, 661 662 cleanAttributes: function(attributes, whitelist) { 663 function whitelistCreator(obj, key, last) { 664 if (v.isObject(obj[key])) { 665 return obj[key]; 666 } 667 return (obj[key] = last ? true : {}); 668 } 669 670 function buildObjectWhitelist(whitelist) { 671 var ow = {}, 672 lastObject, attr; 673 for (attr in whitelist) { 674 if (!whitelist[attr]) { 675 continue; 676 } 677 v.forEachKeyInKeypath(ow, attr, whitelistCreator); 678 } 679 return ow; 680 } 681 682 function cleanRecursive(attributes, whitelist) { 683 if (!v.isObject(attributes)) { 684 return attributes; 685 } 686 687 var ret = v.extend({}, attributes), 688 w, attribute; 689 690 for (attribute in attributes) { 691 w = whitelist[attribute]; 692 693 if (v.isObject(w)) { 694 ret[attribute] = cleanRecursive(ret[attribute], w); 695 } else if (!w) { 696 delete ret[attribute]; 697 } 698 } 699 return ret; 700 } 701 702 if (!v.isObject(whitelist) || !v.isObject(attributes)) { 703 return {}; 704 } 705 706 whitelist = buildObjectWhitelist(whitelist); 707 return cleanRecursive(attributes, whitelist); 708 }, 709 710 exposeModule: function(validate, root, exports, module, define) { 711 if (exports) { 712 if (module && module.exports) { 713 exports = module.exports = validate; 714 } 715 exports.validate = validate; 716 } else { 717 root.validate = validate; 718 if (validate.isFunction(define) && define.amd) { 719 define([], function() { return validate; }); 720 } 721 } 722 }, 723 724 warn: function(msg) { 725 if (typeof console !== "undefined" && console.warn) { 726 console.warn("[validate.js] " + msg); 727 } 728 }, 729 730 error: function(msg) { 731 if (typeof console !== "undefined" && console.error) { 732 console.error("[validate.js] " + msg); 733 } 734 } 735 }); 736 737 validate.validators = { 738 // Presence validates that the value isn't empty 739 presence: function(value, options) { 740 options = v.extend({}, this.options, options); 741 if (options.allowEmpty !== false ? !v.isDefined(value) : v.isEmpty(value)) { 742 return options.message || this.message || "can't be blank"; 743 } 744 }, 745 length: function(value, options, attribute) { 746 // Empty values are allowed 747 if (!v.isDefined(value)) { 748 return; 749 } 750 751 options = v.extend({}, this.options, options); 752 753 var is = options.is, 754 maximum = options.maximum, 755 minimum = options.minimum, 756 tokenizer = options.tokenizer || function(val) { return val; }, 757 err, errors = []; 758 759 value = tokenizer(value); 760 var length = value.length; 761 if (!v.isNumber(length)) { 762 return options.message || this.notValid || "has an incorrect length"; 763 } 764 765 // Is checks 766 if (v.isNumber(is) && length !== is) { 767 err = options.wrongLength || 768 this.wrongLength || 769 "is the wrong length (should be %{count} characters)"; 770 errors.push(v.format(err, { count: is })); 771 } 772 773 if (v.isNumber(minimum) && length < minimum) { 774 err = options.tooShort || 775 this.tooShort || 776 "is too short (minimum is %{count} characters)"; 777 errors.push(v.format(err, { count: minimum })); 778 } 779 780 if (v.isNumber(maximum) && length > maximum) { 781 err = options.tooLong || 782 this.tooLong || 783 "is too long (maximum is %{count} characters)"; 784 errors.push(v.format(err, { count: maximum })); 785 } 786 787 if (errors.length > 0) { 788 return options.message || errors; 789 } 790 }, 791 numericality: function(value, options, attribute, attributes, globalOptions) { 792 // Empty values are fine 793 if (!v.isDefined(value)) { 794 return; 795 } 796 797 options = v.extend({}, this.options, options); 798 799 var errors = [], 800 name, count, checks = { 801 greaterThan: function(v, c) { return v > c; }, 802 greaterThanOrEqualTo: function(v, c) { return v >= c; }, 803 equalTo: function(v, c) { return v === c; }, 804 lessThan: function(v, c) { return v < c; }, 805 lessThanOrEqualTo: function(v, c) { return v <= c; }, 806 divisibleBy: function(v, c) { return v % c === 0; } 807 }, 808 prettify = options.prettify || 809 (globalOptions && globalOptions.prettify) || 810 v.prettify; 811 812 // Strict will check that it is a valid looking number 813 if (v.isString(value) && options.strict) { 814 var pattern = "^-?(0|[1-9]\\d*)"; 815 if (!options.onlyInteger) { 816 pattern += "(\\.\\d+)?"; 817 } 818 pattern += "$"; 819 820 if (!(new RegExp(pattern).test(value))) { 821 return options.message || 822 options.notValid || 823 this.notValid || 824 this.message || 825 "must be a valid number"; 826 } 827 } 828 829 // Coerce the value to a number unless we're being strict. 830 if (options.noStrings !== true && v.isString(value) && !v.isEmpty(value)) { 831 value = +value; 832 } 833 834 // If it's not a number we shouldn't continue since it will compare it. 835 if (!v.isNumber(value)) { 836 return options.message || 837 options.notValid || 838 this.notValid || 839 this.message || 840 "is not a number"; 841 } 842 843 // Same logic as above, sort of. Don't bother with comparisons if this 844 // doesn't pass. 845 if (options.onlyInteger && !v.isInteger(value)) { 846 return options.message || 847 options.notInteger || 848 this.notInteger || 849 this.message || 850 "must be an integer"; 851 } 852 853 for (name in checks) { 854 count = options[name]; 855 if (v.isNumber(count) && !checks[name](value, count)) { 856 // This picks the default message if specified 857 // For example the greaterThan check uses the message from 858 // this.notGreaterThan so we capitalize the name and prepend "not" 859 var key = "not" + v.capitalize(name); 860 var msg = options[key] || 861 this[key] || 862 this.message || 863 "must be %{type} %{count}"; 864 865 errors.push(v.format(msg, { 866 count: count, 867 type: prettify(name) 868 })); 869 } 870 } 871 872 if (options.odd && value % 2 !== 1) { 873 errors.push(options.notOdd || 874 this.notOdd || 875 this.message || 876 "must be odd"); 877 } 878 if (options.even && value % 2 !== 0) { 879 errors.push(options.notEven || 880 this.notEven || 881 this.message || 882 "must be even"); 883 } 884 885 if (errors.length) { 886 return options.message || errors; 887 } 888 }, 889 datetime: v.extend(function(value, options) { 890 if (!v.isFunction(this.parse) || !v.isFunction(this.format)) { 891 throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator"); 892 } 893 894 // Empty values are fine 895 if (!v.isDefined(value)) { 896 return; 897 } 898 899 options = v.extend({}, this.options, options); 900 901 var err, errors = [], 902 earliest = options.earliest ? this.parse(options.earliest, options) : NaN, 903 latest = options.latest ? this.parse(options.latest, options) : NaN; 904 905 value = this.parse(value, options); 906 907 // 86400000 is the number of milliseconds in a day, this is used to remove 908 // the time from the date 909 if (isNaN(value) || options.dateOnly && value % 86400000 !== 0) { 910 err = options.notValid || 911 options.message || 912 this.notValid || 913 "must be a valid date"; 914 return v.format(err, { value: arguments[0] }); 915 } 916 917 if (!isNaN(earliest) && value < earliest) { 918 err = options.tooEarly || 919 options.message || 920 this.tooEarly || 921 "must be no earlier than %{date}"; 922 err = v.format(err, { 923 value: this.format(value, options), 924 date: this.format(earliest, options) 925 }); 926 errors.push(err); 927 } 928 929 if (!isNaN(latest) && value > latest) { 930 err = options.tooLate || 931 options.message || 932 this.tooLate || 933 "must be no later than %{date}"; 934 err = v.format(err, { 935 date: this.format(latest, options), 936 value: this.format(value, options) 937 }); 938 errors.push(err); 939 } 940 941 if (errors.length) { 942 return v.unique(errors); 943 } 944 }, { 945 parse: null, 946 format: null 947 }), 948 date: function(value, options) { 949 options = v.extend({}, options, { dateOnly: true }); 950 return v.validators.datetime.call(v.validators.datetime, value, options); 951 }, 952 format: function(value, options) { 953 if (v.isString(options) || (options instanceof RegExp)) { 954 options = { pattern: options }; 955 } 956 957 options = v.extend({}, this.options, options); 958 959 var message = options.message || this.message || "is invalid", 960 pattern = options.pattern, 961 match; 962 963 // Empty values are allowed 964 if (!v.isDefined(value)) { 965 return; 966 } 967 if (!v.isString(value)) { 968 return message; 969 } 970 971 if (v.isString(pattern)) { 972 pattern = new RegExp(options.pattern, options.flags); 973 } 974 match = pattern.exec(value); 975 if (!match || match[0].length != value.length) { 976 return message; 977 } 978 }, 979 inclusion: function(value, options) { 980 // Empty values are fine 981 if (!v.isDefined(value)) { 982 return; 983 } 984 if (v.isArray(options)) { 985 options = { within: options }; 986 } 987 options = v.extend({}, this.options, options); 988 if (v.contains(options.within, value)) { 989 return; 990 } 991 var message = options.message || 992 this.message || 993 "^%{value} is not included in the list"; 994 return v.format(message, { value: value }); 995 }, 996 exclusion: function(value, options) { 997 // Empty values are fine 998 if (!v.isDefined(value)) { 999 return; 1000 } 1001 if (v.isArray(options)) { 1002 options = { within: options }; 1003 } 1004 options = v.extend({}, this.options, options); 1005 if (!v.contains(options.within, value)) { 1006 return; 1007 } 1008 var message = options.message || this.message || "^%{value} is restricted"; 1009 if (v.isString(options.within[value])) { 1010 value = options.within[value]; 1011 } 1012 return v.format(message, { value: value }); 1013 }, 1014 email: v.extend(function(value, options) { 1015 options = v.extend({}, this.options, options); 1016 var message = options.message || this.message || "is not a valid email"; 1017 // Empty values are fine 1018 if (!v.isDefined(value)) { 1019 return; 1020 } 1021 if (!v.isString(value)) { 1022 return message; 1023 } 1024 if (!this.PATTERN.exec(value)) { 1025 return message; 1026 } 1027 }, { 1028 PATTERN: /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i 1029 }), 1030 equality: function(value, options, attribute, attributes, globalOptions) { 1031 if (!v.isDefined(value)) { 1032 return; 1033 } 1034 1035 if (v.isString(options)) { 1036 options = { attribute: options }; 1037 } 1038 options = v.extend({}, this.options, options); 1039 var message = options.message || 1040 this.message || 1041 "is not equal to %{attribute}"; 1042 1043 if (v.isEmpty(options.attribute) || !v.isString(options.attribute)) { 1044 throw new Error("The attribute must be a non empty string"); 1045 } 1046 1047 var otherValue = v.getDeepObjectValue(attributes, options.attribute), 1048 comparator = options.comparator || function(v1, v2) { 1049 return v1 === v2; 1050 }, 1051 prettify = options.prettify || 1052 (globalOptions && globalOptions.prettify) || 1053 v.prettify; 1054 1055 if (!comparator(value, otherValue, options, attribute, attributes)) { 1056 return v.format(message, { attribute: prettify(options.attribute) }); 1057 } 1058 }, 1059 // A URL validator that is used to validate URLs with the ability to 1060 // restrict schemes and some domains. 1061 url: function(value, options) { 1062 if (!v.isDefined(value)) { 1063 return; 1064 } 1065 1066 options = v.extend({}, this.options, options); 1067 1068 var message = options.message || this.message || "is not a valid url", 1069 schemes = options.schemes || this.schemes || ['http', 'https'], 1070 allowLocal = options.allowLocal || this.allowLocal || false, 1071 allowDataUrl = options.allowDataUrl || this.allowDataUrl || false; 1072 if (!v.isString(value)) { 1073 return message; 1074 } 1075 1076 1077 var regex = 1078 "^" + 1079 // protocol identifier 1080 "(?:(?:" + schemes.join("|") + ")://)" + 1081 // user:pass authentication 1082 "(?:\\S+(?::\\S*)?@)?" + 1083 "(?:"; 1084 1085 var tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))"; 1086 1087 if (allowLocal) { 1088 tld += "?"; 1089 } else { 1090 regex += 1091 // IP address exclusion 1092 // private & local networks 1093 "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + 1094 "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + 1095 "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})"; 1096 } 1097 1098 regex += 1099 // IP address dotted notation octets 1100 // excludes loopback network 0.0.0.0 1101 // excludes reserved space >= 224.0.0.0 1102 // excludes network & broacast addresses 1103 // (first & last IP address of each class) 1104 "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + 1105 "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + 1106 "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + 1107 "|" + 1108 // host name 1109 "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + 1110 // domain name 1111 "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + 1112 tld + 1113 ")" + 1114 // port number 1115 "(?::\\d{2,5})?" + 1116 // resource path 1117 "(?:[/?#]\\S*)?" + 1118 "$"; 1119 1120 if (allowDataUrl) { 1121 // RFC 2397 1122 var mediaType = "\\w+\\/[-+.\\w]+(?:;[\\w=]+)*"; 1123 var urlchar = "[A-Za-z0-9-_.!~\\*'();\\/?:@&=+$,%]*"; 1124 var dataurl = "data:(?:" + mediaType + ")?(?:;base64)?," + urlchar; 1125 regex = "(?:" + regex + ")|(?:^" + dataurl + "$)"; 1126 } 1127 1128 var PATTERN = new RegExp(regex, 'i'); 1129 if (!PATTERN.exec(value)) { 1130 return message; 1131 } 1132 }, 1133 type: v.extend(function(value, originalOptions, attribute, attributes, globalOptions) { 1134 if (v.isString(originalOptions)) { 1135 originalOptions = { type: originalOptions }; 1136 } 1137 1138 if (!v.isDefined(value)) { 1139 return; 1140 } 1141 1142 var options = v.extend({}, this.options, originalOptions); 1143 1144 var type = options.type; 1145 if (!v.isDefined(type)) { 1146 throw new Error("No type was specified"); 1147 } 1148 1149 var check; 1150 if (v.isFunction(type)) { 1151 check = type; 1152 } else { 1153 check = this.types[type]; 1154 } 1155 1156 if (!v.isFunction(check)) { 1157 throw new Error("validate.validators.type.types." + type + " must be a function."); 1158 } 1159 1160 if (!check(value, options, attribute, attributes, globalOptions)) { 1161 var message = originalOptions.message || 1162 this.messages[type] || 1163 this.message || 1164 options.message || 1165 (v.isFunction(type) ? "must be of the correct type" : "must be of type %{type}"); 1166 1167 if (v.isFunction(message)) { 1168 message = message(value, originalOptions, attribute, attributes, globalOptions); 1169 } 1170 1171 return v.format(message, { attribute: v.prettify(attribute), type: type }); 1172 } 1173 }, { 1174 types: { 1175 object: function(value) { 1176 return v.isObject(value) && !v.isArray(value); 1177 }, 1178 array: v.isArray, 1179 integer: v.isInteger, 1180 number: v.isNumber, 1181 string: v.isString, 1182 date: v.isDate, 1183 boolean: v.isBoolean 1184 }, 1185 messages: {} 1186 }) 1187 }; 1188 1189 validate.formatters = { 1190 detailed: function(errors) { return errors; }, 1191 flat: v.flattenErrorsToArray, 1192 grouped: function(errors) { 1193 var attr; 1194 1195 errors = v.groupErrorsByAttribute(errors); 1196 for (attr in errors) { 1197 errors[attr] = v.flattenErrorsToArray(errors[attr]); 1198 } 1199 return errors; 1200 }, 1201 constraint: function(errors) { 1202 var attr; 1203 errors = v.groupErrorsByAttribute(errors); 1204 for (attr in errors) { 1205 errors[attr] = errors[attr].map(function(result) { 1206 return result.validator; 1207 }).sort(); 1208 } 1209 return errors; 1210 } 1211 }; 1212 1213 validate.exposeModule(validate, this, exports, module, define); 1214}).call(this, 1215 typeof exports !== 'undefined' ? /* istanbul ignore next */ exports : null, 1216 typeof module !== 'undefined' ? /* istanbul ignore next */ module : null, 1217 typeof define !== 'undefined' ? /* istanbul ignore next */ define : null);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.