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