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