PageSourceSearch

https://qvw.be/js/jQv/jquery.validity.js

js qvw.be collected 2026-10-03 00:29:46 UTC 43,439 bytes, 1,320 lines download raw bytes

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