1/* 2 * 3 * Dual licensed under MIT and GPL 4 * 5 * Date: 2013-02-10 (Sunday, 10 February 2013) 6 */ 7(function($, undefined) { 8 9 // Default settings: 10 // ///////////////////////////////////////////////////////////////////////////// 11 12 var defaults = { 13 // The default output mode is tooltip because it requires no 14 // dependencies: 15 outputMode : "tooltip", 16 17 // The this property is set to true, validity will scroll the browser 18 // viewport so that the first error is visible when validation fails: 19 scrollTo : false, 20 21 // If this setting is true, modal errors will disappear when they are 22 // clicked on: 23 modalErrorsClickable : true, 24 25 // If a field name cannot be otherwise inferred, this will be used: 26 defaultFieldName : "This field", 27 28 // jQuery selector to filter down to validation-supported elements: 29 elementSupport : ":text, :password, textarea, select, :radio, :checkbox, input[type='hidden'], input[type='tel'], input[type='email']", 30 31 // Function to stringify argments for use when generating error 32 // messages. Primarily, it just generates pretty date strings: 33 argToString : function(val) { 34 return val.getDate ? [ val.getMonth() + 1, val.getDate(), val.getFullYear() ].join("/") : val + ""; 35 }, 36 37 debugPrivates : false 38 }, 39 40 __private; 41 42 // Static functions and properties: 43 // ///////////////////////////////////////////////////////////////////////////// 44 45 $.validity = { 46 47 // Clone the defaults. They can be overridden with the setup function: 48 settings : $.extend(defaults, {}), 49 50 // Built-in library of format-checking tools for use with the match 51 // validator as well as the nonHtml validator: 52 patterns : { 53 intege1 : /^[1-9]\d*$/, // æ£æ´æ° 54 intege2 : /^-[1-9]\d*$/, // è´æ´æ° 55 num : /^([+-]?)\d*\.?\d+$/, // æ°å 56 num1 : /^[0-9]\d*$/, // æ£æ°ï¼æ£æ´æ° + 0ï¼ 57 num2 : /^-[0-9]\d*$/, // è´æ°ï¼è´æ´æ° + 0ï¼ 58 decmal : /^([+-]?)\d*\.\d+$/, // æµ®ç¹æ° 59 decmal1 : /^[1-9]\d*.\d*|0.\d*[1-9]\d*$/,// æ£æµ®ç¹æ° 60 decmal2 : /^-([1-9]\d*.\d*|0.\d*[1-9]\d*)$/,// è´æµ®ç¹æ° 61 decmal3 : /^-?([1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0)$/,// æµ®ç¹æ° 62 decmal4 : /^([0-9]\d*\.\d+|0)$/,// éè´æµ®ç¹æ°ï¼æ£æµ®ç¹æ° + 0ï¼ 63 decmal5 : /^(-([1-9]\d*.\d*|0.\d*[1-9]\d*))|0?.0+|0$/,// éæ£æµ®ç¹æ°ï¼è´æµ®ç¹æ° + 0ï¼ 64 65 email : /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/, // é®ä»¶ 66 color : /^[a-fA-F0-9]{6}$/, // é¢è² 67 chinese : /^[\u4E00-\u9FA5\uF900-\uFA2D]+$/, // ä» ä¸æ 68 ascii : /^[\x00-\xFF]+$/, // ä» ACSIIå符 69 zipcode : /^\d{6}$/, // é®ç¼ 70 mobile : /^[1][0-9]{10}$/, // ææº 71 ip4 : /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, // ipå°å 72 picture : /(.*)\.(jpg|bmp|gif|ico|pcx|jpeg|tif|png|raw|tga)$/, // å¾ç 73 rar : /(.*)\.(rar|zip|7zip|tgz)$/, // å缩æä»¶ 74 zip : /(.*)\.(zip)$/, 75 qq : /^[1-9]*[1-9][0-9]*$/, // QQå·ç 76 tel : /(\d{3}-|\d{4}-)?(\d{8}|\d{7})/, // å½å çµè¯ 77 username : /^\w+$/, // ç¨æ¥ç¨æ·æ³¨åãå¹é ç±æ°åã26ä¸ªè±æåæ¯æè ä¸åçº¿ç»æçå符串 78 username1 : /^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$/,// ç¨æ¥ç¨æ·æ³¨åãå¹é åæ¯ãæ°åãä¸å线ãä¸æç»æï¼ä¸è½ä»¥ä¸å线å¼å¤´åç»å°¾ 79 username2 : /^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5,]+$/,// ç¨æ¥ç¨æ·æ³¨åãå¹é åæ¯ãæ°åãä¸å线ã䏿ãè±æéå·ç»æï¼ä¸è½ä»¥ä¸å线å¼å¤´åç»å°¾ 80 username3 : /^[a-zA-Z]{1}(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$/,// ç¨æ¥ç¨æ·æ³¨åãå¹é åæ¯ãæ°åãä¸å线ãä¸æç»æï¼åªè½ä»¥åæ¯å¼å¤´ 81 username4 : /^[a-zA-Z]{1}(?!.*?_$)[a-zA-Z0-9_]+$/,// ç¨æ¥ç¨æ·æ³¨åãå¹é åæ¯ãæ°åãä¸åçº¿ç»æï¼åªè½ä»¥è±æåæ¯å¼å¤´ä¸è½ä»¥ä¸å线ç»å°¾ 82 username5 : /^(?!_)(?!.*?_$)[a-zA-Z0-9_()ï¼ï¼\u4e00-\u9fa5]+$/,// ç¨æ¥ç¨æ·æ³¨åãå¹é åæ¯ãæ°åãä¸å线ãä¸æãæ¬å·ç»æï¼ä¸è½ä»¥ä¸å线å¼å¤´åç»å°¾ 83 letter : /^[A-Za-z]+$/, // 忝 84 letter_u : /^[A-Z]+$/, // 大å忝 85 letter_l : /^[a-z]+$/, // å°å忝
86 codeid : /^[a-zA-Z0-9]+$/, // æºææ è¯ã大å°å忝忰å 87 idcard : /^[1-9]([0-9]{14}|([0-9]{16}([X]|\d)))$/, // èº«ä»½è¯ 88 date : /^((0?\d)|(1[012]))[\/-]([012]?\d|30|31)[\/-]\d{1,4}$/, 89 url : /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, 90 nonHtml : /^[^<>]*$/ 91 }, 92 93 // Built-in set of default error messages (for use when a message isn't 94 // specified): 95 messages : { 96 97 require : "#{field} is required.", 98 99 // Format validators: 100 match : "#{field} is in an invalid format.", 101 integer : "#{field} must be a positive, whole number.", 102 date : "#{field} must be formatted as a date. (mm/dd/yyyy)", 103 email : "#{field} must be formatted as an email.", 104 usd : "#{field} must be formatted as a US Dollar amount.", 105 url : "#{field} must be formatted as a URL.", 106 number : "#{field} must be formatted as a number.", 107 zip : "#{field} must be formatted as a zipcode ##### or #####-####.", 108 phone : "#{field} must be formatted as a phone number ###-###-####.", 109 guid : "#{field} must be formatted as a guid like {3F2504E0-4F89-11D3-9A0C-0305E82C3301}.", 110 time24 : "#{field} must be formatted as a 24 hour time: 23:00.", 111 time12 : "#{field} must be formatted as a 12 hour time: 12:00 AM/PM", 112 113 // Value range messages: 114 lessThan : "#{field} must be less than #{max}.", 115 lessThanOrEqualTo : "#{field} must be less than or equal to #{max}.", 116 greaterThan : "#{field} must be greater than #{min}.", 117 greaterThanOrEqualTo : "#{field} must be greater than or equal to #{min}.", 118 range : "#{field} must be between #{min} and #{max}.", 119 120 // Value length messages: 121 tooLong : "#{field} cannot be longer than #{max} characters.", 122 tooShort : "#{field} cannot be shorter than #{min} characters.", 123 124 // Composition validators: 125 nonHtml : "#{field} cannot contain HTML characters.", 126 alphabet : "#{field} contains disallowed characters.", 127 128 minCharClass : "#{field} cannot have more than #{min} #{charClass} characters.", 129 maxCharClass : "#{field} cannot have less than #{min} #{charClass} characters.", 130 131 // Aggregate validator messages: 132 equal : "Values don't match.", 133 distinct : "A value was repeated.", 134 sum : "Values don't add to #{sum}.", 135 sumMax : "The sum of the values must be less than #{max}.", 136 sumMin : "The sum of the values must be greater than #{min}.", 137 138 // Radio validator messages: 139 radioChecked : "The selected value is not valid.", 140 141 generic : "Invalid." 142 }, 143 144 // Abstract output: 145 out : { 146 start : function() { 147 this.defer("start"); 148 }, 149 end : function(results) { 150 this.defer("end", results); 151 }, 152 raise : function($obj, msg) { 153 this.defer("raise", $obj, msg); 154 }, 155 raiseAggregate : function($obj, msg) { 156 this.defer("raiseAggregate", $obj, msg); 157 }, 158 defer : function(name) { 159 var v = $.validity, o = v.outputs[v.settings.outputMode]; 160 161 o[name].apply(o, Array.prototype.slice.call(arguments, 1)); 162 } 163 }, 164 165 // Character classes can be used to determine the quantity of a given type 166 // of character in a string: 167 charClasses : { 168 alphabetical : /\w/g,
169 numeric : /\d/g, 170 alphanumeric : /[A-Za-z0-9]/g, 171 symbol : /[^A-Za-z0-9]/g 172 }, 173 174 // Object to contain the output modes. The three built-in output modes are 175 // installed later on in this script. 176 outputs : {}, 177 178 // By default, private members are not exposed. 179 __private : undefined, 180 181 // Override the default settings with user-specified ones. 182 setup : function(options) { 183 this.settings = $.extend(this.settings, options); 184 185 if (this.settings.debugPrivates) { 186 this.__private = __private; 187 } else { 188 this.__private = undefined; 189 } 190 }, 191 192 // Object to store information about ongoing validation. When validation 193 // starts, this will be set to a report object. When validators fail, they 194 // will inform this object. When validation is completed, this object will 195 // contain the information of whether it succeeded: 196 report : null, 197 198 // Determine whether validity is in the middle of validation: 199 isValidating : function() { 200 return !!this.report; 201 }, 202 203 // Function to prepare validity to start validating: 204 start : function() { 205 206 // The output mode should be notified that validation is starting. This 207 // usually means that the output mode will erase errors from the 208 // document in whatever way the mode needs to: 209 this.out.start(); 210 211 // Initialize the report object: 212 this.report = { 213 errors : 0, 214 valid : true 215 }; 216 }, 217 218 // Function called when validation is over to examine the results and 219 // clean-up: 220 end : function() { 221 222 // Null coalescence: fix for Issue 5: 223 var results = this.report || { 224 errors : 0, 225 valid : true 226 }; 227 228 this.report = null; 229 230 // Notify the current output mode that validation is over: 231 this.out.end(results); 232 233 return results; 234 }, 235 236 // Remove validiation errors: 237 clear : function() { 238 this.start(); 239 this.end(); 240 } 241 }; 242 243 // jQuery instance methods: 244 // ///////////////////////////////////////////////////////////////////////////// 245 246 $.fn.extend({ 247 248 // The validity function is how validation can be bound to forms. The user 249 // may pass in a validation function or, as a shortcut, pass in a string of 250 // a CSS selector that grabs all the inputs to require: 251 /** 252 * 253 */ 254 validity : function(arg, setting) { 255 256 return this.each( 257 258 function() { 259 var op = { 260 auto : true, 261 type : 'ajax', 262 resultType : 'json', 263 contentType : 'application/x-www-form-urlencoded', 264 beforeSubmit : function(result) { 265 }, 266 success : function() { 267 }, 268 error : function() { 269 } 270 }; 271 op = $.extend(op, setting); 272 // Only operate on forms: 273 if (this.tagName.toLowerCase() == "form") { 274 var form = $(this); 275 var f = null; 276 277 // If the user entered a string to select the inputs to 278 // require, then make the validation logic ad hoc: 279 if (typeof (arg) == "string") { 280 f = function() { 281 $(arg).require(); 282 }; 283 } 284 285 // If the user entered a validation function then just call 286 // that at the appropriate time: 287 else if ($.isFunction(arg)) { 288 f = arg; 289 } 290 291 if (arg) { 292 if (op.auto == true && op.type == 'iframe') { 293 $('body').append('<iframe frameborder="0" id="hiddenSubmit" name="hiddenSubmit" style="width: 0;height: 0"></iframe>'); 294 $(this).attr('target', 'hiddenSubmit'); 295 } 296 $(this).bind("submit", function() { 297 $.validity.start(); 298 f(); 299 var result = $.validity.end().valid; 300 var before = op.beforeSubmit(result); 301 if(before == false){ 302 return false; 303 } 304 305 if (result == true && op.auto == true) { 306 var submitBtn = $(form.find('[type=submit],.submit')); 307 var data = form.serialize(); 308 submitBtn.attr('disabled', true).removeClass('btn-primary').addClass('disabled'); 309 if(op.type == 'ajax'){ 310 var url = form.attr('action'); 311 $.ajax({
312 type : "post", 313 url : url, 314 data : data, 315 dataType : op.resultType, 316 contentType : op.contentType, 317 success : function(obj) { 318 submitBtn.attr('disabled', false).removeClass('disabled').addClass('btn-primary'); 319 op.success(obj); 320 }, 321 error : function(a, b, msg) { 322 submitBtn.attr('disabled', false).removeClass('disabled').addClass('btn-primary'); 323 alert('æä½å¤±è´¥'); 324 op.error(); 325 } 326 }); 327 result = false; 328 } 329 } 330 return result; 331 }); 332 } 333 } 334 }); 335 }, 336 337 // Validators: 338 // ///////////////////////////////////////////////////////////////////////// 339 340 // Common validators: 341 // ///////////////////////////////////////////////////////////////////////// 342 343 // Validate whether the field has a value. 344 require : function(msg) { 345 return validate(this, function(obj) { 346 return !!$.trim($(obj).val()).length; 347 }, msg || $.validity.messages.require); 348 }, 349 350 // Validate whether the field matches a regex. 351 match : function(rule, msg) { 352 353 // If a default message is to be used: 354 if (!msg) { 355 // First grab the generic one: 356 msg = $.validity.messages.match; 357 358 // If there's a more specific one, use that. 359 if (typeof (rule) === "string" && $.validity.messages[rule]) { 360 msg = $.validity.messages[rule]; 361 } 362 } 363 364 // If the rule is named, rather than specified: 365 if (typeof (rule) == "string") { 366 rule = $.validity.patterns[rule]; 367 } 368 369 return validate(this, 370 371 // Some of the named rules can be functions, such as 'date'. If the 372 // discovered rule is a function use it as such. Otherwise, assume 373 // it's a RegExp. 374 $.isFunction(rule) ? 375 376 function(obj) { 377 return !obj.value.length || rule(obj.value); 378 } : 379 380 function(obj) { 381 // Fix for regexes where the global flag is set. Make sure 382 // to test from the start of the string. 383 if (rule.global) { 384 rule.lastIndex = 0; 385 } 386 387 return !obj.value.length || rule.test(obj.value); 388 }, 389 390 msg); 391 }, 392 393 range : function(min, max, msg) { 394 return validate(this, 395 396 min.getTime && max.getTime ? 397 398 // If both arguments are dates then use them that way. 399 function(obj) { 400 var d = new Date(obj.value); 401 return d >= new Date(min) && d <= new Date(max); 402 } : 403 404 min.substring && max.substring && Big ? 405 406 // If both arguments are strings then parse them using the 407 // Arbitrary-Precision library. 408 function(obj) { 409 var n = new Big(obj.value); 410 return (n.greaterThanOrEqualTo(new Big(min)) && n.lessThanOrEqualTo(new Big(max))); 411 } : 412 413 // Otherwise treat them like floats. 414 function(obj) { 415 var f = parseFloat(obj.value); 416 return f >= min && f <= max; 417 }, 418 419 msg || format($.validity.messages.range, { 420 min : $.validity.settings.argToString(min), 421 max : $.validity.settings.argToString(max) 422 })); 423 }, 424 425 greaterThan : function(min, msg) { 426 return validate(this, 427 428 min.getTime ? function(obj) { 429 return new Date(obj.value) > min; 430 } : 431 432 min.substring && Big ? 433 434 function(obj) { 435 return new Big(obj.value).greaterThan(new Big(min)); 436 } : 437 438 function(obj) { 439 return parseFloat(obj.value) > min; 440 }, 441 442 msg || format($.validity.messages.greaterThan, { 443 min : $.validity.settings.argToString(min) 444 })); 445 }, 446 447 greaterThanOrEqualTo : function(min, msg) { 448 return validate(this, 449 450 min.getTime ? function(obj) { 451 return new Date(obj.value) >= min; 452 } : 453 454 min.substring && Big ? 455 456 function(obj) { 457 return new Big(obj.value).greaterThanOrEqualTo(new Big(min)); 458 } : 459 460 function(obj) { 461 return parseFloat(obj.value) >= min; 462 }, 463 464 msg || format($.validity.messages.greaterThanOrEqualTo, { 465 min : $.validity.settings.argToString(min) 466 })); 467 }, 468 469 lessThan : function(max, msg) { 470 return validate(this, 471 472 max.getTime ? function(obj) { 473 return new Date(obj.value) < max; 474 } : 475 476 max.substring && Big ? 477 478 function(obj) { 479 return new Big(obj.value).lessThan(new Big(max)); 480 } : 481 482 function(obj) { 483 return parseFloat(obj.value) < max; 484 }, 485 486 msg || format($.validity.messages.lessThan, { 487 max : $.validity.settings.argToString(max) 488 })); 489 }, 490 491 lessThanOrEqualTo : function(max, msg) { 492 return validate(this, 493 494 max.getTime ? function(obj) { 495 return new Date(obj.value) <= max; 496 } : 497 498 max.substring && Big ? 499 500 function(obj) { 501 return new Big(obj.value).lessThanOrEqualTo(new Big(max)); 502 } : 503 504 function(obj) { 505 return parseFloat(obj.value) <= max; 506 }, 507 508 msg || format($.validity.messages.lessThanOrEqualTo, { 509 max : $.validity.settings.argToString(max) 510 })); 511 }, 512 513 maxLength : function(max, msg) { 514 return validate(this, function(obj) { 515 return obj.value.length <= max; 516 }, msg || format($.validity.messages.tooLong, { 517 max : max 518 })); 519 }, 520 521 minLength : function(min, msg) { 522 return validate(this, function(obj) { 523 return obj.value.length >= min; 524 }, msg || format($.validity.messages.tooShort, { 525 min : min 526 })); 527 }, 528 529 alphabet : function(alpha, msg) { 530 var chars = []; 531 532 return validate(this, function(obj) { 533 534 // For each character in the string, ensure that it's in the 535 // alphabet definition: 536 for ( var idx = 0; idx < obj.value.length; ++idx) { 537 if (alpha.indexOf(obj.value.charAt(idx)) == -1) { 538 chars.push(obj.value.charAt(idx)); 539 return false; 540 } 541 } 542 543 return true; 544 }, msg || format($.validity.messages.alphabet, { 545 chars : chars.join(", ") 546 })); 547 }, 548
549 minCharClass : function(charClass, min, msg) { 550 if (typeof (charClass) == "string") { 551 charClass = charClass.toLowerCase(); 552 553 if ($.validity.charClasses[charClass]) { 554 charClass = $.validity.charClasses[charClass]; 555 } 556 } 557 558 return validate(this, function(obj) { 559 return (obj.value.match(charClass) || []).length >= min; 560 }, msg || format($.validity.messages.minCharClass, { 561 min : min, 562 charClass : charClass 563 })); 564 }, 565 566 maxCharClass : function(charClass, max, msg) { 567 if (typeof (charClass) == "string") { 568 charClass = charClass.toLowerCase(); 569 570 if ($.validity.charClasses[charClass]) { 571 charClass = $.validity.charClasses[charClass]; 572 } 573 } 574 575 return validate(this, function(obj) { 576 return (obj.value.match(charClass) || []).length <= max; 577 }, msg || format($.validity.messages.maxCharClass, { 578 max : max, 579 charClass : charClass 580 })); 581 }, 582 583 // Validate that the input does not contain potentially dangerous strings. 584 nonHtml : function(msg) { 585 return validate(this, 586 587 function(obj) { 588 return $.validity.patterns.nonHtml.test(obj.value); 589 }, 590 591 msg || $.validity.messages.nonHtml); 592 }, 593 594 // Aggregate validators: 595 // ///////////////////////////////////////////////////////////////////////// 596 597 // Validate that all matched elements bear the same values. Accepts a 598 // function to transform the values for testing. 599 equal : function(arg0, arg1) { 600 var 601 // If a reduced set is attached, use it. Also, remove unsupported 602 // elements. 603 $reduction = (this.reduction || this).filter($.validity.settings.elementSupport), 604 605 transform = function(val) { 606 return val; 607 }, 608 609 msg = $.validity.messages.equal; 610 611 if ($reduction.length) { 612 613 // Figure out what arguments were specified. 614 if ($.isFunction(arg0)) { 615 transform = arg0; 616 617 if (typeof (arg1) == "string") { 618 msg = arg1; 619 } 620 } 621 622 else if (typeof (arg0) == "string") { 623 msg = arg0; 624 } 625 626 var map = $.map($reduction, function(obj) { 627 return transform(obj.value); 628 }), 629 630 // Get the first value. 631 first = map[0], valid = true; 632 633 // If any value is not equal to the first value, then they aren't 634 // all equal, and it's not valid. 635 for ( var i in map) { 636 if (map[i] != first) { 637 valid = false; 638 } 639 } 640 641 if (!valid) { 642 raiseAggregateError($reduction, msg); 643 644 // The set reduces to zero valid elements. 645 this.reduction = $([]); 646 } 647 } 648 649 return this; 650 }, 651 652 // Validate that all matched elements bear distinct values. Accepts an 653 // optional function to transform the values for testing. 654 distinct : function(arg0, arg1) { 655 var 656 // If a reduced set is attached, use it. 657 // Also, remove unsupported elements. 658 $reduction = (this.reduction || this).filter($.validity.settings.elementSupport), 659 660 transform = function(val) { 661 return val; 662 }, 663 664 msg = $.validity.messages.distinct, 665 666 // An empty array to store already-examined values 667 subMap = [], 668 669 // An array with repeated values 670 repeatedVal = [], 671 672 valid = true; 673 674 if ($reduction.length) { 675 676 // Figure out what arguments were specified. 677 if ($.isFunction(arg0)) { 678 transform = arg0; 679 680 if (typeof (arg1) == "string") { 681 msg = arg1; 682 } 683 } 684 685 else if (typeof (arg0) == "string") { 686 msg = arg0; 687 } 688 689 // Map all the matched values into an array. 690 var map = $.map($reduction, function(obj) { 691 return transform(obj.value); 692 }); 693 694 // For each transformed value: 695 for ( var i1 = 0; i1 < map.length; ++i1) { 696 // Unless it's an empty string: 697 if (map[i1].length) { 698 // For each value we've already looked at: 699 for ( var i2 = 0; i2 < subMap.length; ++i2) { 700 // If we've already seen the transformed value: 701 if (subMap[i2] == map[i1]) { 702 valid = false; 703 // Collect repeated values 704 repeatedVal.push(map[i1]); 705 } 706 } 707 708 // We looked at the value. 709 subMap.push(map[i1]); 710 } 711 } 712 713 if (!valid) { 714 // Remove duplicates of duplicates 715 repeatedVal = $.unique(repeatedVal); 716 717 // For all repeated values found... 718 for ( var i = 0, repeatedLength = repeatedVal.length; i < repeatedLength; ++i) { 719 720 // raise the error - aggregate will use last repeated value 721 raiseAggregateError($reduction.filter("[value=\'" + repeatedVal[i] + "\']"), msg); 722 } 723 724 // The set reduces to zero valid elements. 725 this.reduction = $([]); 726 } 727 } 728 729 return this; 730 }, 731 732 // Validate that the numeric sum of all values is equal to a given value. 733 sum : function(sum, msg) { 734 // If a reduced set is attached, use it. Also, remove unsupported 735 // elements. 736 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 737 738 if ($reduction.length && sum != numericSum($reduction)) { 739 raiseAggregateError($reduction, msg || format($.validity.messages.sum, { 740 sum : sum 741 })); 742 743 // The set reduces to zero valid elements. 744 this.reduction = $([]); 745 } 746 747 return this; 748 }, 749 750 // Validates an inclusive upper-bound on the numeric sum of the values of 751 // all matched elements. 752 sumMax : function(max, msg) { 753 // If a reduced set is attached, use it. Also, remove unsupported 754 // elements. 755 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 756 757 if ($reduction.length && max < numericSum($reduction)) { 758 raiseAggregateError($reduction, msg || format($.validity.messages.sumMax, { 759 max : max 760 })); 761 762 // The set reduces to zero valid elements. 763 this.reduction = $([]); 764 } 765 766 return this; 767 }, 768 769 // Validates an inclusive lower-bound on the numeric sum of the values of 770 // all matched elements. 771 sumMin : function(min, msg) { 772 // If a reduced set is attached, use it. Also, remove unsupported 773 // elements. 774 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 775 776 if ($reduction.length && min > numericSum($reduction)) { 777 raiseAggregateError($reduction, msg || format($.validity.messages.sumMin, { 778 min : min 779 })); 780 781 // The set reduces to zero valid elements. 782 this.reduction = $([]); 783 } 784 785 return this; 786 }, 787 788 // Radio group validators: 789 // ///////////////////////////////////////////////////////////////////////// 790 791 radioChecked : function(val, msg) { 792 // If a reduced set is attached, use it. Also, remove unsupported 793 // elements. 794 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 795 796 if ($reduction.is(":radio") && $reduction.find(":checked").val() != val) { 797 raiseAggregateError($reduction, msg || $.validity.messages.radioChecked); 798 } 799 }, 800 801 radioNotChecked : function(val, msg) { 802 // If a reduced set is attached, use it. Also, remove unsupported 803 // elements. 804 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 805 806 if ($reduction.is(":radio") && $reduction.filter(":checked").val() == val) { 807 raiseAggregateError($reduction, msg || $.validity.messages.radioChecked); 808 } 809 }, 810 811 // Checkbox validators: 812 // ///////////////////////////////////////////////////////////////////////// 813 814 checkboxChecked : function(msg) { 815 // If a reduced set is attached, use it. Also, remove unsupported 816 // elements. 817 var $reduction = (this.reduction || this).filter($.validity.settings.elementSupport); 818 819 if ($reduction.is(":checkbox") && !$reduction.is(":checked")) { 820 raiseAggregateError($reduction, msg || $.validity.messages.radioChecked); 821 } 822 }, 823 824 // Specialized validators: 825 // ///////////////////////////////////////////////////////////////////////// 826 827 // If expression is a function, it will be called on each matched element. 828 // Otherwise, it is treated as a boolean, and the determines the validity 829 // of elements in an aggregate fashion. 830 assert : function(expression, msg) { 831 832 // If a reduced set is attached, use it. Do not reduce to supp
832orted 833 // elements. 834 var $reduction = this.reduction || this; 835 836 if ($reduction.length) { 837 838 // In the case that 'expression' is a function, use it as a 839 // regimen on each matched element individually: 840 if ($.isFunction(expression)) { 841 return validate(this, expression, msg || $.validity.messages.generic); 842 } 843 844 // Otherwise map it to a boolean and consider this as an aggregate 845 // validation: 846 else if (!expression) { 847 848 raiseAggregateError($reduction, msg || $.validity.messages.generic); 849 850 // The set reduces to zero valid elements. 851 this.reduction = $([]); 852 } 853 } 854 855 return this; 856 }, 857 858 fail : function(msg) { 859 return this.assert(false, msg); 860 } 861 }); 862 863 // Private utilities: 864 // ///////////////////////////////////////////////////////////////////////////// 865 866 // Do non-aggregate validation. Subject each element in $obj to the regimen. 867 // Raise the specified error on failures. This function is the heart of 868 // validity: 869 function validate($obj, regimen, message) { 870 871 var 872 // If a reduced set is attached, use it Also, remove any unsupported 873 // elements. 874 $reduction = ($obj.reduction || $obj).filter($.validity.settings.elementSupport), 875 876 // Array to store only elements that pass the regimen. 877 elements = []; 878 879 // For each in the reduction. 880 $reduction.each(function() { 881 // If the element passes the regimen, include it in the reduction. 882 if (regimen(this)) { 883 elements.push(this); 884 } 885 886 // Else give the element an error message. 887 else { 888 raiseError(this, format(message, { 889 field : infer(this) 890 })); 891 } 892 }); 893 894 // Attach a (potentially) reduced set of only elements that passed. 895 $obj.reduction = $(elements); 896 897 // Return the full set with attached reduction. 898 return $obj; 899 } 900 901 // Inform the report object that there was a failure. 902 function addToReport() { 903 if ($.validity.isValidating()) { 904 $.validity.report.errors++; 905 $.validity.report.valid = false; 906 } 907 } 908 909 // Inform the report of a failure and display an error according to the idiom 910 // of the current output mode. 911 function raiseError(obj, msg) { 912 addToReport(); 913 914 $.validity.out.raise($(obj), msg); 915 } 916 917 // Inform the report of a failure and display an aggregate error according to 918 // the idiom of the current output mode. 919 function raiseAggregateError($obj, msg) { 920 addToReport(); 921 922 $.validity.out.raiseAggregate($obj, msg); 923 } 924 925 // Yield the sum of the values of all fields matched in obj that can be parsed. 926 function numericSum(obj) { 927 var accumulator = 0; 928 obj.each(function() { 929 var n = parseFloat(this.value); 930 931 accumulator += isNaN(n) ? 0 : n; 932 }); 933 return accumulator; 934 } 935 936 // Using the associative array supplied as the 'obj' argument, replace tokens 937 // of the format #{<key>} in the 'str' argument with that key's value. 938 function format(str, obj) { 939 for ( var p in obj) { 940 if (obj.hasOwnProperty(p)) { 941 str = str.replace(new RegExp("#\\{" + p + "\\}", "g"), obj[p]); 942 } 943 } 944 return capitalize(str); 945 } 946 947 // Infer the field name of the passed DOM element. If a title is specified, its 948 // value is returned. Otherwise, attempt to parse a field name out of the id 949 // attribute. If that doesn't work, return the default field name in the 950 // configuration. 951 function infer(field) { 952 var $f = $(field), id = $f.prop("id"), ret = $.validity.settings.defaultFieldName; 953 954 // Check for title. 955 if ($f.prop("title").length) { 956 ret = $f.prop("title"); 957 } 958 959 // Check for UpperCamelCase. 960 else if (/^([A-Z0-9][a-z]*)+$/.test(id)) { 961 ret = id.replace(/([A-Z0-9])[a-z]*/g, " $&"); 962 } 963 964 // Check for lowercase_separated_by_underscores 965 else if (/^[a-z0-9]+(_[a-z0-9]+)*$/.test(id)) { 966 var arr = id.split("_"); 967 968 for ( var i = 0; i < arr.length; ++i) { 969 arr[i] = capitalize(arr[i]); 970 } 971 972 ret = arr.join(" "); 973 } 974 975 return $.trim(ret); 976 } 977 978 // Capitalize the first character of the string argument. 979 function capitalize(sz) { 980 return sz.substring ? sz.substring(0, 1).toUpperCase() + sz.substring(1, sz.length) : sz; 981 } 982 983 __private = { 984 validate : validate, 985 addToReport : addToReport, 986 raiseError : raiseError, 987 raiseAggregateError : raiseAggregateError, 988 numericSum : numericSum, 989 format : format, 990 infer : infer, 991 capitalize : capitalize 992 }; 993 994})(jQuery);
995// Output modes: 996// ////////////////////////////////////////////////////////////// 997 998// Each output mode gets its own closure, 999// distinct from the validity closure. 1000 1001// Install the tooltip output. 1002(function($) { 1003 $.validity.outputs.tooltip = { 1004 tooltipClass : "validity-tooltip", 1005 1006 start : function() { 1007 $("." + $.validity.outputs.tooltip.tooltipClass).remove(); 1008 }, 1009 1010 end : function(results) { 1011 // If not valid and scrollTo is enabled, scroll the page to the first error. 1012 if (!results.valid && $.validity.settings.scrollTo) { 1013 $(document).scrollTop($("." + $.validity.outputs.tooltip.tooltipClass).offset().top); 1014 } 1015 }, 1016 1017 raise : function($obj, msg) { 1018 var pos = $obj.offset(); 1019 pos.left += $obj.width() + 18; 1020 pos.top += 8; 1021 1022 $("<div class=\"validity-tooltip\">" + msg + "<div class=\"validity-tooltip-outer\">" + "<div class=\"validity-tooltip-inner\"></div>" + "</div>" + "</div>").click(function() { 1023 $obj.focus(); 1024 $(this).fadeOut(); 1025 }).css(pos).hide().appendTo("body").fadeIn(); 1026 }, 1027 1028 raiseAggregate : function($obj, msg) { 1029 // Just raise the error on the last input. 1030 if ($obj.length) { 1031 this.raise($obj.filter(":last"), msg); 1032 } 1033 } 1034 }; 1035})(jQuery); 1036 1037// Install the label output. 1038(function($) { 1039 function getIdentifier($obj) { 1040 return $obj.attr('id').length ? $obj.attr('id') : $obj.attr('name'); 1041 } 1042 1043 $.validity.outputs.label = { 1044 cssClass : "error", 1045 1046 start : function() { 1047 // Remove all the existing error labels. 1048 $("." + $.validity.outputs.label.cssClass).remove(); 1049 }, 1050 1051 end : function(results) { 1052 // If not valid and scrollTo is enabled, scroll the page to the first error. 1053 if (!results.valid && $.validity.settings.scrollTo) { 1054 location.hash = $("." + $.validity.outputs.label.cssClass + ":eq(0)").attr('for'); 1055 } 1056 }, 1057 1058 raise : function($obj, msg) { 1059 var labelSelector = "." + $.validity.outputs.label.cssClass + "[for='" + getIdentifier($obj) + "']"; 1060 1061 // If an error label already exists for the bad input just update its text: 1062 if ($(labelSelector).length) { 1063 $(labelSelector).text(msg); 1064 } 1065 1066 // Otherwize create a new one and stick it after the input: 1067 else { 1068 $("<label/>").attr("for", getIdentifier($obj)).addClass($.validity.outputs.label.cssClass).text(msg) 1069 1070 // In the case that the element does not have an id 1071 // then the for attribute in the label will not cause 1072 // clicking the label to focus the element. This line 1073 // will make that happen. 1074 .click(function() { 1075 if ($obj.length) { 1076 $obj[0].select(); 1077 } 1078 }) 1079 1080 .insertAfter($obj); 1081 } 1082 }, 1083 1084 raiseAggregate : function($obj, msg) { 1085 // Just raise the error on the last input. 1086 if ($obj.length) { 1087 this.raise($($obj.get($obj.length - 1)), msg); 1088 } 1089 } 1090 }; 1091})(jQuery); 1092 1093// Install the modal output. 1094(function($) { 1095 var 1096 // Class to apply to modal errors. 1097 errorClass = "validity-modal-msg", 1098 1099 // The selector for the element where modal errors will me injected semantically. 1100 container = "body"; 1101 1102 $.validity.outputs.modal = { 1103 start : function() { 1104 // Remove all the existing errors. 1105 $("." + errorClass).remove(); 1106 }, 1107 1108 end : function(results) { 1109 // If not valid and scrollTo is enabled, scroll the page to the first error. 1110 if (!results.valid && $.validity.settings.scrollTo) { 1111 location.hash = $("." + errorClass + ":eq(0)").attr('id'); 1112 } 1113 }, 1114 1115 raise : function($obj, msg) { 1116 if ($obj.length) { 1117 var off = $obj.offset(), obj = $obj.get(0), 1118 1119 // Design a style object based off of the input's location. 1120 errorStyle = { 1121 left : parseInt(off.left + $obj.width() + 4, 10) + "px", 1122 top : parseInt(off.top - 10, 10) + "px" 1123 }; 1124 1125 // Create one and position it next to the input. 1126 $("<div/>").addClass(errorClass).css(errorStyle).text(msg).click($.validity.settings.modalErrorsClickable ? function() { 1127 $(this).remove(); 1128 } : null).appendTo(container); 1129 } 1130 }, 1131 1132 raiseAggregate : function($obj, msg) { 1133 // Just raise the error on the last input. 1134 if ($obj.length) { 1135 this.raise($($obj.get($obj.length - 1)), msg); 1136 } 1137 } 1138 }; 1139})(jQuery); 1140 1141// Install the summary output 1142(function($) { 1143 var 1144 // Container contains the summary. This is the element that is shown or hidden. 1145 container = ".validity-summary-container", 1146
1147 // Erroneous refers to an input with an invalid value, 1148 // not the error message itself. 1149 erroneous = "validity-erroneous", 1150 1151 // Selector for erroneous inputs. 1152 errors = "." + erroneous, 1153 1154 // The wrapper for entries in the summary. 1155 wrapper = "<li/>", 1156 1157 // Buffer to contain all the error messages that build up during validation. 1158 // When validation ends, it'll be flushed into the summary. 1159 // This way, the summary doesn't flicker empty then fill up. 1160 buffer = []; 1161 1162 $.validity.outputs.summary = { 1163 start : function() { 1164 $(errors).removeClass(erroneous); 1165 buffer = []; 1166 }, 1167 1168 end : function(results) { 1169 // Hide the container and empty its summary. 1170 $(container).hide().find("ul").html(''); 1171 1172 // If there are any errors at all: 1173 // (Otherwise the container shouldn't be shown): 1174 if (buffer.length) { 1175 // Use integer based iteration for solution to Issue 7. 1176 for ( var i = 0; i < buffer.length; ++i) { 1177 $(wrapper).text(buffer[i]).appendTo(container + " ul"); 1178 } 1179 1180 $(container).show(); 1181 1182 // If scrollTo is enabled, scroll the page to the first error. 1183 if ($.validity.settings.scrollTo) { 1184 location.hash = $(errors + ":eq(0)").attr("id"); 1185 } 1186 } 1187 }, 1188 1189 raise : function($obj, msg) { 1190 buffer.push(msg); 1191 $obj.addClass(erroneous); 1192 }, 1193 1194 raiseAggregate : function($obj, msg) { 1195 this.raise($obj, msg); 1196 }, 1197 1198 container : function() { 1199 document.write("<div class=\"validity-summary-container\">" + "The form didn't submit for the following reason(s):" + "<ul></ul>" + "</div>"); 1200 } 1201 }; 1202})(jQuery);
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.