PageSourceSearch

https://www.aishub.net/assets/ff675a50/yii.activeForm.js?v=1770131334

js aishub.net collected 2026-10-02 06:54:42 UTC 32,238 bytes, 791 lines download raw bytes

1/**
2 * Yii form widget.
3 *
4 * This is the JavaScript widget used by the yii\widgets\ActiveForm widget.
5 *
6 * @link http://www.yiiframework.com/
7 * @copyright Copyright (c) 2008 Yii Software LLC
8 * @license http://www.yiiframework.com/license/
9 * @author Qiang Xue <[email protected]>
10 * @since 2.0
11 */
12(function ($) {
13
14    $.fn.yiiActiveForm = function (method) {
15        if (methods[method]) {
16            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
17        } else if (typeof method === 'object' || !method) {
18            return methods.init.apply(this, arguments);
19        } else {
20            $.error('Method ' + method + ' does not exist on jQuery.yiiActiveForm');
21            return false;
22        }
23    };
24
25    var events = {
26        /**
27         * beforeValidate event is triggered before validating the whole form.
28         * The signature of the event handler should be:
29         *     function (event, messages, deferreds)
30         * where
31         *  - event: an Event object.
32         *  - messages: an associative array with keys being attribute IDs and values being error message arrays
33         *    for the corresponding attributes.
34         *  - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.
35         *
36         * If the handler returns a boolean false, it will stop further form validation after this event. And as
37         * a result, afterValidate event will not be triggered.
38         */
39        beforeValidate: 'beforeValidate',
40        /**
41         * afterValidate event is triggered after validating the whole form.
42         * The signature of the event handler should be:
43         *     function (event, messages, errorAttributes)
44         * where
45         *  - event: an Event object.
46         *  - messages: an associative array with keys being attribute IDs and values being error message arrays
47         *    for the corresponding attributes.
48         *  - errorAttributes: an array of attributes that have validation errors. Please refer to attributeDefaults for the structure of this parameter.
49         */
50        afterValidate: 'afterValidate',
51        /**
52         * beforeValidateAttribute event is triggered before validating an attribute.
53         * The signature of the event handler should be:
54         *     function (event, attribute, messages, deferreds)
55         * where
56         *  - event: an Event object.
57         *  - attribute: the attribute to be validated. Please refer to attributeDefaults for the structure of this parameter.
58         *  - messages: an array to which you can add validation error messages for the specified attribute.
59         *  - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.
60         *
61         * If the handler returns a boolean false, it will stop further validation of the specified attribute.
62         * And as a result, afterValidateAttribute event will not be triggered.
63         */
64        beforeValidateAttribute: 'beforeValidateAttribute',
65        /**
66         * afterValidateAttribute event is triggered after validating the whole form and each attribute.
67         * The signature of the event handler should be:
68         *     function (event, attribute, messages)
69         * where
70         *  - event: an Event object.
71         *  - attribute: the attribute being validated. Please refer to attributeDefaults for the structure of this parameter.
72         *  - messages: an array to which you can add additional validation error messages for the specified attribute.
73         */
74        afterValidateAttribute: 'afterValidateAttribute',
75        /**
76         * beforeSubmit event is triggered before submitting the form after all validations have passed.
77         * The signature of the event handler should be:
78         *     function (event)
79         * where event is an Event object.
80         *
81         * If the handler returns a boolean false, it will stop form submission.
82         */
83        beforeSubmit: 'beforeSubmit',
84        /**
85         * ajaxBeforeSend event is triggered before sending an AJAX request for AJAX-based validation.
86         * The signature of the event handler should be:
87         *     function (event, jqXHR, settings)
88         * where
89         *  - event: an Event object.
90         *  - jqXHR: a jqXHR object
91         *  - settings: the settings for the AJAX request
92         */
93        ajaxBeforeSend: 'ajaxBeforeSend',
94        /**
95         * ajaxComplete event is triggered after completing an AJAX request for AJAX-based validation.
96         * The signature of the event handler should be:
97         *     function (event, jqXHR, textStatus)
98         * where
99         *  - event: an Event object.
100         *  - jqXHR: a jqXHR object
101         *  - textStatus: the status of the request ("success", "notmodified", "error", "timeout", "abort", or "parsererror").
102         */
103        ajaxComplete: 'ajaxComplete',
104        /**
105         * afterInit event is triggered after yii activeForm init.
106         * The signature of the event handler should be:
107         *     function (event)
108         * where
109         *  - event: an Event object.
110         */
111        afterInit: 'afterInit'
112    };
113
114    // NOTE: If you change any of these defaults, make sure you update yii\widgets\ActiveForm::getClientOptions() as well
115    var defaults = {
116        // whether to encode the error summary
117        encodeErrorSummary: true,
118        // the jQuery selector for the error summary
119        errorSummary: '.error-summary',
120        // whether to perform validation before submitting the form.
121        validateOnSubmit: true,
122        // the container CSS class representing the corresponding attribute has validation error
123        errorCssClass: 'has-error',
124        // the container CSS class representing the corresponding attribute passes validation
125        successCssClass: 'has-success',
126        // the container CSS class representing the corresponding attribute is being validated
127        validatingCssClass: 'validating',
128        // the GET parameter name indicating an AJAX-based validation
129        ajaxParam: 'ajax',
130        // the type of data that you're expecting back from the server
131        ajaxDataType: 'json',
132        // the URL for performing AJAX-based validation. If not set, it will use the the form's action
133        validationUrl: undefined,
134        // whether to scroll to first visible error after validation.
135        scrollToError: true,
136        // offset in pixels that should be added when scrolling to the first error.
137        scrollToErrorOffset: 0
138    };
139
140    // NOTE: If you change any of these defaults, make sure you update yii\widgets\ActiveField::getClientOptions() as well
141    var attributeDefaults = {
142        // a unique ID identifying an attribute (e.g. "loginform-username") in a form
143        id: undefined,
144        // attribute name or expression (e.g. "[0]content" for tabular input)
145        name: undefined,
146        // the jQuery selector of the container of the input field
147        container: undefined,
148        // the jQuery selector of the input field under the context of the form
149        input: undefined,
150        // the jQuery selector of the error tag under the context of the container
151        error: '.help-block',
152        // whether to encode the error
153        encodeError: true,
154        // whether to perform validation when a change is detected on the input
155        validateOnChange: true,
156        // whether to perform validation when the input loses focus
157        validateOnBlur: true,
158        // whether to perform validation when the user is typing.
159        validateOnType: false,
160        // number of milliseconds that the validation should be delayed when a user is typing in the input field.
161        validationDelay: 500,
162        // whether to enable AJAX-based validation.
163        enableAjaxValidation: false,
164        // function (attribute, value, messages, deferred, $form), the client-side validation function.
165        validate: undefined,
166        // status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
167        status: 0,
168        // whether the validation is cancelled by beforeValidateAttribute event handler
169        cancelled: false,
170        // the value of the input
171        value: undefined,
172        // whether to update aria-invalid attribute after validation
173        updateAriaInvalid: true
174    };
175
176
177    var submitDefer;
178
179    var setSubmitFinalizeDefer = function($form) {
180        submitDefer = $.Deferred();
181        $form.data('yiiSubmitFinalizePromise', submitDefer.promise());
182    };
183
184    // finalize yii.js $form.submit
185    var submitFinalize = function($form) {
186        if(submitDefer) {
187            submitDefer.resolve();
188            submitDefer = undefined;
189            $form.removeData('yiiSubmitFinalizePromise');
190        }
191    };
192
193
194    var methods = {
195        init: function (attributes, options) {
196            return this.each(function () {
197                var $form = $(this);
198                if ($form.data('yiiActiveForm')) {
199                    return;
200                }
201
202                var settings = $.extend({}, defaults, options || {});
203                if (settings.validationUrl === undefined) {
204                    settings.validationUrl = $form.attr('action');
205                }
206
207                $.each(attributes, function (i) {
208                    attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);
209                    watchAttribute($form, attributes[i]);
210                });
211
212                $form.data('yiiActiveForm', {
213                    settings: settings,
214                    attributes: attributes,
215                    submitting: false,
216                    validated: false,
217                    options: getFormOptions($form)
218                });
219
220                /**
221                 * Clean up error status when the form is reset.
222                 * Note that $form.on('reset', ...) does work because the "reset" event does not bubble on IE.
223                 */
224                $form.on('reset.yiiActiveForm', methods.resetForm);
225
226                if (settings.validateOnSubmit) {
227                    $form.on('mouseup.yiiActiveForm keyup.yiiActiveForm', ':submit', function () {
228                        $form.data('yiiActiveForm').submitObject = $(this);
229                    });
230                    $form.on('submit.yiiActiveForm', methods.submitForm);
231                }
232                var event = $.Event(events.afterInit);
233                $form.trigger(event);
234            });
235        },
236
237        // add a new attribute to the form dynamically.
238        // please refer to attributeDefaults for the structure of attribute
239        add: function (attribute) {
240            var $form = $(this);
241            attribute = $.extend({value: getValue($form, attribute)}, attributeDefaults, attribute);
242            $form.data('yiiActiveForm').attributes.push(attribute);
243            watchAttribute($form, attribute);
244        },
245
246        // remove the attribute with the specified ID from the form
247        remove: function (id) {
248            var $form = $(this),
249                attributes = $form.data('yiiActiveForm').attributes,
250                index = -1,
251                attribute = undefined;
252            $.each(attributes, function (i) {
253                if (attributes[i]['id'] == id) {
254                    index = i;
255                    attribute = attributes[i];
256                    return false;
257                }
258            });
259            if (index >= 0) {
260                attributes.splice(index, 1);
261                unwatchAttribute($form, attribute);
262            }
263
264            return attribute;
265        },
266
267        // manually trigger the validation of the attribute with the specified ID
268        validateAttribute: function (id) {
269            var attribute = methods.find.call(this, id);
270            if (attribute != undefined) {
271                validateAttribute($(this), attribute, true);
272            }
273        },
274
275        // find an attribute config based on the specified attribute ID
276        find: function (id) {
277            var attributes = $(this).data('yiiActiveForm').attributes,
278                result = undefined;
279            $.each(attributes, function (i) {
280                if (attributes[i]['id'] == id) {
281                    result = attributes[i];
282                    return false;
283                }
284            });
285            return result;
286        },
287
288        destroy: function () {
289            return this.each(function () {
290                $(this).off('.yiiActiveForm');
291                $(this).removeData('yiiActiveForm');
292            });
293        },
294
295        data: function () {
296            return this.data('yiiActiveForm');
297        },
298
299        // validate all applicable inputs in the form
300        validate: function (forceValidate) {
301            if (forceValidate) {
302                $(this).data('yiiActiveForm').submitting = true;
303            }
304
305            var $form = $(this),
306                data = $form.data('yiiActiveForm'),
307                needAjaxValidation = false,
308                messages = {},
309                deferreds = deferredArray(),
310                submitting = data.submitting;
311
312            if (submitting) {
313                var event = $.Event(events.beforeValidate);
314                $form.trigger(event, [messages, deferreds]);
315
316                if (event.result === false) {
317                    data.submitting = false;
318                    submitFinalize($form);
319                    return;
320                }
321            }
322
323            // client-side validation
324            $.each(data.attributes, function () {
325                this.$form = $form;
326                if (!$(this.input).is(":disabled")) {
327                    this.cancelled = false;
328                    // perform validation only if the form is being submitted or if an attribute is pending validation
329                    if (data.submitting || this.status === 2 || this.status === 3) {
330                        var msg = messages[this.id];
331                        if (msg === undefined) {
332                            msg = [];
333                            messages[this.id] = msg;
334                        }
335                        var event = $.Event(events.beforeValidateAttribute);
336                        $form.trigger(event, [this, msg, deferreds]);
337                        if (event.result !== false) {
338                            if (this.validate) {
339                                this.validate(this, getValue($form, this), msg, deferreds, $form);
340                            }
341                            if (this.enableAjaxValidation) {
342                                needAjaxValidation = true;
343                            }
344                        } else {
345                            this.cancelled = true;
346                        }
347                    }
348                }
349            });
350
351            // ajax validation
352            $.when.apply(this, deferreds).always(function() {
353                // Remove empty message arrays
354                for (var i in messages) {
355                    if (0 === messages[i].length) {
356                        delete messages[i];
357                    }
358                }
359                if (needAjaxValidation && ($.isEmptyObject(messages) || data.submitting)) {
360                    var $button = data.submitObject,
361                        extData = '&' + data.settings.ajaxParam + '=' + $form.attr('id');
362                    if ($button && $button.length && $button.attr('name')) {
363                        extData += '&' + $button.attr('name') + '=' + $button.attr('value');
364                    }
365                    $.ajax({
366                        url: data.settings.validationUrl,
367                        type: $form.attr('method'),
368                        data: $form.serialize() + extData,
369                        dataType: data.settings.ajaxDataType,
370                        complete: function (jqXHR, textStatus) {
371                            $form.trigger(events.ajaxComplete, [jqXHR, textStatus]);
372                        },
373                        beforeSend: function (jqXHR, settings) {
374                            $form.trigger(events.ajaxBeforeSend, [jqXHR, settings]);
375                        },
376                        success: function (msgs) {
377                            if (msgs !== null && typeof msgs === 'object') {
378                                $.each(data.attributes, function () {
379                                    if (!this.enableAjaxValidation || this.cancelled) {
380                                        delete msgs[this.id];
381                                    }
382                                });
383                                updateInputs($form, $.extend(messages, msgs), submitting);
384                            } else {
385                                updateInputs($form, messages, submitting);
386                            }
387                        },
388                        error: function () {
389                            data.submitting = false;
390                            submitFinalize($form);
391                        }
392                    });
393                } else if (data.submitting) {
394                    // delay callback so that the form can be submitted without problem
395                    window.setTimeout(function () {
396                        updateInputs($form, messages, submitting);
397                    }, 200);
398                } else {
399                    updateInputs($form, messages, submitting);
400                }
401            });
402        },
403
404        submitForm: function () {
405            var $form = $(this),
406                data = $form.data('yiiActiveForm');
407
408            if (data.validated) {
409                // Second submit's call (from validate/updateInputs)
410                data.submitting = false;
411                var event = $.Event(events.beforeSubmit);
412                $form.trigger(event);
413                if (event.result === false) {
414                    data.validated = false;
415                    submitFinalize($form);
416                    return false;
417                }
418                updateHiddenButton($form);
419                return true;   // continue submitting the form since validation passes
420            } else {
421                // First submit's call (from yii.js/handleAction) - execute validating
422                setSubmitFinalizeDefer($form);
423
424                if (data.settings.timer !== undefined) {
425                    clearTimeout(data.settings.timer);
426                }
427                data.submitting = true;
428                methods.validate.call($form);
429                return false;
430            }
431        },
432
433        resetForm: function () {
434            var $form = $(this);
435            var data = $form.data('yiiActiveForm');
436            // Because we bind directly to a form reset event instead of a reset button (that may not exist),
437            // when this function is executed form input values have not been reset yet.
438            // Therefore we do the actual reset work through setTimeout.
439            window.setTimeout(function () {
440                $.each(data.attributes, function () {
441                    // Without setTimeout() we would get the input values that are not reset yet.
442                    this.value = getValue($form, this);
443                    this.status = 0;
444                    var $container = $form.find(this.container);
445                    $container.removeClass(
446                        data.settings.validatingCssClass + ' ' +
447                            data.settings.errorCssClass + ' ' +
448                            data.settings.successCssClass
449                    );
450                    $container.find(this.error).html('');
451                });
452                $form.find(data.settings.errorSummary).hide().find('ul').html('');
453            }, 1);
454        },
455
456        /**
457         * Updates error messages, input containers, and optionally summary as well.
458         * If an attribute is missing from messages, it is considered valid.
459         * @param messages array the validation error messages, indexed by attribute IDs
460         * @param summary whether to update summary as well.
461         */
462        updateMessages: function (messages, summary) {
463            var $form = $(this);
464            var data = $form.data('yiiActiveForm');
465            $.each(data.attributes, function () {
466                updateInput($form, this, messages);
467            });
468            if (summary) {
469                updateSummary($form, messages);
470            }
471        },
472
473        /**
474         * Updates error messages and input container of a single attribute.
475         * If messages is empty, the attribute is considered valid.
476         * @param id attribute ID
477         * @param messages array with error messages
478         */
479        updateAttribute: function(id, messages) {
480            var attribute = methods.find.call(this, id);
481            if (attribute != undefined) {
482                var msg = {};
483                msg[id] = messages;
484                updateInput($(this), attribute, msg);
485            }
486        }
487
488    };
489
490    var watchAttribute = function ($form, attribute) {
491        var $input = findInput($form, attribute);
492        if (attribute.validateOnChange) {
493            $input.on('change.yiiActiveForm', function () {
494                validateAttribute($form, attribute, false);
495            });
496        }
497        if (attribute.validateOnBlur) {
498            $input.on('blur.yiiActiveForm', function () {
499                if (attribute.status == 0 || attribute.status == 1) {
500                    validateAttribute($form, attribute, true);
501                }
502            });
503        }
504        if (attribute.validateOnType) {
505            $input.on('keyup.yiiActiveForm', function (e) {
506                if ($.inArray(e.which, [16, 17, 18, 37, 38, 39, 40]) !== -1 ) {
507                    return;
508                }
509                if (attribute.value !== getValue($form, attribute)) {
510                    validateAttribute($form, attribute, false, attribute.validationDelay);
511                }
512            });
513        }
514    };
515
516    var unwatchAttribute = function ($form, attribute) {
517        findInput($form, attribute).off('.yiiActiveForm');
518    };
519
520    var validateAttribute = function ($form, attribute, forceValidate, validationDelay) {
521        var data = $form.data('yiiActiveForm');
522
523        if (forceValidate) {
524            attribute.status = 2;
525        }
526        $.each(data.attributes, function () {
527            if (this.value !== getValue($form, this)) {
528                this.status = 2;
529                forceValidate = true;
530            }
531        });
532        if (!forceValidate) {
533            return;
534        }
535
536        if (data.settings.timer !== undefined) {
537            clearTimeout(data.settings.timer);
538        }
539        data.settings.timer = window.setTimeout(function () {
540            if (data.submitting || $form.is(':hidden')) {
541                return;
542            }
543            $.each(data.attributes, function () {
544                if (this.status === 2) {
545                    this.status = 3;
546                    $form.find(this.container).addClass(data.settings.validatingCssClass);
547                }
548            });
549            methods.validate.call($form);
550        }, validationDelay ? validationDelay : 200);
551    };
552
553    /**
554     * Returns an array prototype with a shortcut method for adding a new deferred.
555     * The context of the callback will be the deferred object so it can be resolved like ```this.resolve()```
556     * @returns Array
557     */
558    var deferredArray = function () {
559        var array = [];
560        array.add = function(callback) {
561            this.push(new $.Deferred(callback));
562        };
563        return array;
564    };
565
566    var buttonOptions = ['action', 'target', 'method', 'enctype'];
567
568    /**
569     * Returns current form options
570     * @param $form
571     * @returns object Object with button of form options
572     */
573    var getFormOptions = function ($form) {
574        var attributes = {};
575        for (var i = 0; i < buttonOptions.length; i++) {
576            attributes[buttonOptions[i]] = $form.attr(buttonOptions[i]);
577        }
578
579        return attributes;
580    };
581
582    /**
583     * Applies temporary form options related to submit button
584     * @param $form the form jQuery object
585     * @param $button the button jQuery object
586     */
587    var applyButtonOptions = function ($form, $button) {
588        for (var i = 0; i < buttonOptions.length; i++) {
589            var value = $button.attr('form' + buttonOptions[i]);
590            if (value) {
591                $form.attr(buttonOptions[i], value);
592            }
593        }
594    };
595
596    /**
597     * Restores original form options
598     * @param $form the form jQuery object
599     */
600    var restoreButtonOptions = function ($form) {
601        var data = $form.data('yiiActiveForm');
602
603        for (var i = 0; i < buttonOptions.length; i++) {
604            $form.attr(buttonOptions[i], data.options[buttonOptions[i]] || null);
605        }
606    };
607
608    /**
609     * Updates the error messages and the input containers for all applicable attributes
610     * @param $form the form jQuery object
611     * @param messages array the validation error messages
612     * @param submitting whether this method is called after validation triggered by form submission
613     */
614    var updateInputs = function ($form, messages, submitting) {
615        var data = $form.data('yiiActiveForm');
616
617        if (data === undefined) {
618            return false;
619        }
620
621        if (submitting) {
622            var errorAttributes = [];
623            $.each(data.attributes, function () {
624                if (!$(this.input).is(":disabled") && !this.cancelled && updateInput($form, this, messages)) {
625                    errorAttributes.push(this);
626                }
627            });
628
629            $form.trigger(events.afterValidate, [messages, errorAttributes]);
630
631            updateSummary($form, messages);
632
633            if (errorAttributes.length) {
634                if (data.settings.scrollToError) {
635                    var top = $form.find($.map(errorAttributes, function(attribute) {
636                        return attribute.input;
637                    }).join(',')).first().closest(':visible').offset().top - data.settings.scrollToErrorOffset;
638                    if (top < 0) {
639                        top = 0;
640                    } else if (top > $(document).height()) {
641                        top = $(document).height();
642                    }
643                    var wtop = $(window).scrollTop();
644                    if (top < wtop || top > wtop + $(window).height()) {
645                        $(window).scrollTop(top);
646                    }
647                }
648                data.submitting = false;
649            } else {
650                data.validated = true;
651                if (data.submitObject) {
652                    applyButtonOptions($form, data.submitObject);
653                }
654                $form.submit();
655                if (data.submitObject) {
656                    restoreButtonOptions($form);
657                }
658            }
659        } else {
660            $.each(data.attributes, function () {
661                if (!this.cancelled && (this.status === 2 || this.status === 3)) {
662                    updateInput($form, this, messages);
663                }
664            });
665        }
666        submitFinalize($form);
667    };
668
669    /**
670     * Updates hidden field that represents clicked submit button.
671     * @param $form the form jQuery object.
672     */
673    var updateHiddenButton = function ($form) {
674        var data = $form.data('yiiActiveForm');
675        var $button = data.submitObject || $form.find(':submit:first');
676        // TODO: if the submission is caused by "change" event, it will not work
677        if ($button.length && $button.attr('type') == 'submit' && $button.attr('name')) {
678            // simulate button input value
679            var $hiddenButton = $('input[type="hidden"][name="' + $button.attr('name') + '"]', $form);
680            if (!$hiddenButton.length) {
681                $('<input>').attr({
682                    type: 'hidden',
683                    name: $button.attr('name'),
684                    value: $button.attr('value')
685                }).appendTo($form);
686            } else {
687                $hiddenButton.attr('value', $button.attr('value'));
688            }
689        }
690    };
691
692    /**
693     * Updates the error message and the input container for a particular attribute.
694     * @param $form the form jQuery object
695     * @param attribute object the configuration for a particular attribute.
696     * @param messages array the validation error messages
697     * @return boolean whether there is a validation error for the specified attribute
698     */
699    var updateInput = function ($form, attribute, messages) {
700        var data = $form.data('yiiActiveForm'),
701            $input = findInput($form, attribute),
702            hasError = false;
703
704        if (!$.isArray(messages[attribute.id])) {
705            messages[attribute.id] = [];
706        }
707
708        attribute.status = 1;
709        if ($input.length) {
710            hasError = messages[attribute.id].length > 0;
711            var $container = $form.find(attribute.container);
712            var $error = $container.find(attribute.error);
713            updateAriaInvalid($form, attribute, hasError);
714            if (hasError) {
715                if (attribute.encodeError) {
716                    $error.text(messages[attribute.id][0]);
717                } else {
718                    $error.html(messages[attribute.id][0]);
719                }
720                $container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)
721                    .addClass(data.settings.errorCssClass);
722            } else {
723                $error.empty();
724                $container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.errorCssClass + ' ')
725                    .addClass(data.settings.successCssClass);
726            }
727            attribute.value = getValue($form, attribute);
728        }
729
730        $form.trigger(events.afterValidateAttribute, [attribute, messages[attribute.id]]);
731
732        return hasError;
733    };
734
735    /**
736     * Updates the error summary.
737     * @param $form the form jQuery object
738     * @param messages array the validation error messages
739     */
740    var updateSummary = function ($form, messages) {
741        var data = $form.data('yiiActiveForm'),
742            $summary = $form.find(data.settings.errorSummary),
743            $ul = $summary.find('ul').empty();
744
745        if ($summary.length && messages) {
746            $.each(data.attributes, function () {
747                if ($.isArray(messages[this.id]) && messages[this.id].length) {
748                    var error = $('<li/>');
749                    if (data.settings.encodeErrorSummary) {
750                        error.text(messages[this.id][0]);
751                    } else {
752                        error.html(messages[this.id][0]);
753                    }
754                    $ul.append(error);
755                }
756            });
757            $summary.toggle($ul.find('li').length > 0);
758        }
759    };
760
761    var getValue = function ($form, attribute) {
762        var $input = findInput($form, attribute);
763        var type = $input.attr('type');
764        if (type === 'checkbox' || type === 'radio') {
765            var $realInput = $input.filter(':checked');
766            if (!$realInput.length) {
767                $realInput = $form.find('input[type=hidden][name="' + $input.attr('name') + '"]');
768            }
769
770            return $realInput.val();
771        } else {
772            return $input.val();
773        }
774    };
775
776    var findInput = function ($form, attribute) {
777        var $input = $form.find(attribute.input);
778        if ($input.length && $input[0].tagName.toLowerCase() === 'div') {
779            // checkbox list or radio list
780            return $input.find('input');
781        } else {
782            return $input;
783        }
784    };
785
786    var updateAriaInvalid = function ($form, attribute, hasError) {
787        if (attribute.updateAriaInvalid) {
788            $form.find(attribute.input).attr('aria-invalid', hasError ? 'true' : 'false');
789        }
790    }
791})(window.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.