1/** 2 * jQuery yiiactiveform plugin file. 3 * 4 * @author Qiang Xue <[email protected]> 5 * @link https://www.yiiframework.com/ 6 * @copyright 2008-2010 Yii Software LLC 7 * @license https://www.yiiframework.com/license/ 8 * @since 1.1.1 9 */ 10 11(function ($) { 12 /* 13 * returns the value of the CActiveForm input field 14 * performs additional checks to get proper values for checkbox / radiobutton / checkBoxList / radioButtonList 15 * @param o object the jQuery object of the input element 16 */ 17 var getAFValue = function (o) { 18 var type, 19 c = []; 20 if (!o.length) { 21 return undefined; 22 } 23 if (o[0].tagName.toLowerCase() === 'span') { 24 o.find(':checked').each(function () { 25 c.push(this.value); 26 }); 27 return c.join(','); 28 } 29 type = o.attr('type'); 30 if (type === 'checkbox' || type === 'radio') { 31 return o.filter(':checked').val(); 32 } else { 33 return o.val(); 34 } 35 }; 36 37 /** 38 * yiiactiveform set function. 39 * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for available options. 40 */ 41 $.fn.yiiactiveform = function (options) { 42 return this.each(function () { 43 var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {}), 44 $form = $(this); 45 46 if (settings.validationUrl === undefined) { 47 settings.validationUrl = $form.attr('action'); 48 } 49 $.each(settings.attributes, function (i) { 50 this.value = getAFValue($form.find('#' + this.inputID)); 51 settings.attributes[i] = $.extend({}, { 52 validationDelay: settings.validationDelay, 53 validateOnChange: settings.validateOnChange, 54 validateOnType: settings.validateOnType, 55 hideErrorMessage: settings.hideErrorMessage, 56 inputContainer: settings.inputContainer, 57 errorCssClass: settings.errorCssClass, 58 successCssClass: settings.successCssClass, 59 beforeValidateAttribute: settings.beforeValidateAttribute, 60 afterValidateAttribute: settings.afterValidateAttribute, 61 validatingCssClass: settings.validatingCssClass, 62 errorCallback: settings.errorCallback 63 }, this); 64 }); 65 $form.data('settings', settings); 66 67 settings.submitting = false; // whether it is waiting for ajax submission result 68 var validate = function (attribute, forceValidate) { 69 if (forceValidate) { 70 attribute.status = 2; 71 } 72 $.each(settings.attributes, function () { 73 if (this.value !== getAFValue($form.find('#' + this.inputID))) { 74 this.status = 2; 75 forceValidate = true; 76 } 77 }); 78 if (!forceValidate) { 79 return; 80 } 81 82 if (settings.timer !== undefined) { 83 clearTimeout(settings.timer); 84 } 85 settings.timer = setTimeout(function () { 86 if (settings.submitting || $form.is(':hidden')) { 87 return; 88 } 89 if (attribute.beforeValidateAttribute === undefined || attribute.beforeValidateAttribute($form, attribute)) { 90 $.each(settings.attributes, function () { 91 if (this.status === 2) { 92 this.status = 3; 93 $.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass); 94 } 95 }); 96 $.fn.yiiactiveform.validate($form, function (data) { 97 var hasError = false; 98 $.each(settings.attributes, function () { 99 if (this.status === 2 || this.status === 3) { 100 hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError; 101 } 102 }); 103 if (attribute.afterValidateAttribute !== undefined) { 104 attribute.afterValidateAttribute($form, attribute, data, hasError); 105 } 106 },settings.errorCallback); 107 } 108 }, attribute.validationDelay); 109 }; 110 111 $.each(settings.attributes, function (i, attribute) { 112 if (this.validateOnChange) { 113 $form.find('#' + this.inputID).on('change', function () { 114 validate(attribute, false); 115 }).on('blur', function () { 116 if (attribute.status !== 2 && attribute.status !== 3) { 117 validate(attribute, !attribute.status); 118 } 119 }); 120 } 121 if (this.validateOnType) { 122 $form.find('#' + this.inputID).keyup(function () { 123 if (attribute.value !== getAFValue($(this))) { 124 validate(attribute, false); 125 } 126 }); 127 } 128 }); 129
130 if (settings.validateOnSubmit) { 131 $form.on('mouseup keyup', ':submit', function () { 132 $form.data('submitObject', $(this)); 133 }); 134 var validated = false; 135 $form.on('submit', function () { 136 if (validated) { 137 validated = false; 138 return true; 139 } 140 if (settings.timer !== undefined) { 141 clearTimeout(settings.timer); 142 } 143 settings.submitting = true; 144 if (settings.beforeValidate === undefined || settings.beforeValidate($form)) { 145 $.fn.yiiactiveform.validate($form, function (data) { 146 var hasError = false; 147 $.each(settings.attributes, function () { 148 hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError; 149 }); 150 $.fn.yiiactiveform.updateSummary($form, data); 151 if (settings.afterValidate === undefined || settings.afterValidate($form, data, hasError)) { 152 if (!hasError) { 153 validated = true; 154 var $button = $form.data('submitObject') || $form.find(':submit:first'); 155 // TODO: if the submission is caused by "change" event, it will not work 156 if ($button.length) { 157 $button.trigger('click'); 158 } else { // no submit button in the form 159 $form.trigger('submit'); 160 } 161 return; 162 } 163 } 164 settings.submitting = false; 165 },settings.errorCallback); 166 } else { 167 settings.submitting = false; 168 } 169 return false; 170 }); 171 } 172 173 /* 174 * In case of reseting the form we need to reset error messages 175 * NOTE1: $form.reset - does not exist 176 * NOTE2: $form.on('reset', ...) does not work 177 */ 178 $form.bind('reset', function () { 179 /* 180 * because we bind directly to a form reset event, not to a reset button (that could or could not exist), 181 * when this function is executed form elements values have not been reset yet, 182 * because of that we use the setTimeout 183 */ 184 setTimeout(function () { 185 $.each(settings.attributes, function () { 186 this.status = 0; 187 var $error = $form.find('#' + this.errorID), 188 $container = $.fn.yiiactiveform.getInputContainer(this, $form); 189 190 $container.removeClass( 191 this.validatingCssClass + ' ' + 192 this.errorCssClass + ' ' + 193 this.successCssClass 194 ); 195 196 $error.html('').hide(); 197 198 /* 199 * without the setTimeout() we would get here the current entered value before the reset instead of the reseted value 200 */ 201 this.value = getAFValue($form.find('#' + this.inputID)); 202 }); 203 /* 204 * If the form is submited (non ajax) with errors, labels and input gets the class 'error' 205 */ 206 $form.find('label, :input').each(function () { 207 $(this).removeClass(settings.errorCss); 208 }); 209 $('#' + settings.summaryID).hide().find('ul').html(''); 210 //.. set to initial focus on reset 211 if (settings.focus !== undefined && !window.location.hash) { 212 $form.find(settings.focus).trigger('focus'); 213 } 214 }, 1); 215 }); 216 217 /* 218 * set to initial focus 219 */ 220 if (settings.focus !== undefined && !window.location.hash) { 221 $form.find(settings.focus).trigger('focus'); 222 } 223 }); 224 }; 225 226 /** 227 * Returns the container element of the specified attribute. 228 * @param attribute object the configuration for a particular attribute. 229 * @param form the form jQuery object 230 * @return jQuery the jQuery representation of the container 231 */ 232 $.fn.yiiactiveform.getInputContainer = function (attribute, form) { 233 if (attribute.inputContainer === undefined) { 234 return form.find('#' + attribute.inputID).closest('div'); 235 } else { 236 return form.find(attribute.inputContainer).filter(':has("#' + attribute.inputID + '")'); 237 } 238 }; 239 240 /** 241 * updates the error message and the input container for a particular attribute. 242 * @param attribute object the configuration for a particular attribute. 243 * @param messages array the json data obtained from the ajax validation request 244 * @param form the form jQuery object 245 * @return boolean whether there is a validation error for the specified attribute 246 */ 247 $.fn.yiiactiveform.updateInput = function (attribute, messages, form) { 248 attribute.status = 1; 249 var $error, $container, 250 hasError = false, 251 $el = form.find('#' + attribute.inputID), 252 errorCss = form.data('settings').errorCss; 253 254 if ($el.length) { 255 hasError = messages !== null && $.isArray(messages[attribute.id]) && messages[attribute.id].length > 0; 256 $error = form.find('#' + attribute.errorID); 257 $container = $.fn.yiiactiveform.getInputContainer(attribute, form); 258 259 $container.removeClass( 260 attribute.validatingCssClass + ' ' + 261 attribute.errorCssClass + ' ' + 262 attribute.successCssClass 263 ); 264 $container.find('label, :input').each(function () { 265 $(this).removeClass(errorCss); 266 }); 267 268 if (hasError) { 269 $error.html(messages[attribute.id][0]); 270 $container.addClass(attribute.errorCssClass); 271 } else if (attribute.enableAjaxValidation || attribute.clientValidation) { 272 $container.addClass(attribute.successCssClass); 273 } 274 if (!attribute.hideErrorMessage) { 275 $error.toggle(hasError); 276 } 277 278 attribute.value = getAFValue($el); 279 } 280 return hasError; 281 }; 282 283 /** 284 * updates the error summary, if any. 285 * @param form jquery the jquery representation of the form 286 * @param messages array the json data obtained from the ajax validation request 287 */ 288 $.fn.yiiactiveform.updateSummary = function (form, messages) { 289 var settings = $(form).data('settings'), 290 content = ''; 291 if (settings.summaryID === undefined) { 292 return; 293 } 294 if (messages) { 295 var summaryAttributes = []; 296 for (var i in settings.attributes) { 297 if (settings.attributes[i].summary) { 298 summaryAttributes.push(settings.attributes[i].id); 299 } 300 } 301 $.each(settings.attributes, function () { 302 if ($.inArray(this.id, summaryAttributes) !== -1 && $.isArray(messages[this.id])) { 303 $.each(messages[this.id], function (j, message) { 304 content = content + '<li>' + message + '</li>'; 305 }); 306 } 307 }); 308 } 309 $('#' + settings.summaryID).toggle(content !== '').find('ul').html(content); 310 }; 311 312 /**
313 * Performs the ajax validation request. 314 * This method is invoked internally to trigger the ajax validation. 315 * @param form jquery the jquery representation of the form 316 * @param successCallback function the function to be invoked if the ajax request succeeds 317 * @param errorCallback function the function to be invoked if the ajax request fails 318 */ 319 $.fn.yiiactiveform.validate = function (form, successCallback, errorCallback) { 320 var $form = $(form), 321 settings = $form.data('settings'), 322 needAjaxValidation = false, 323 messages = {}; 324 $.each(settings.attributes, function () { 325 var value, 326 msg = []; 327 if (this.clientValidation !== undefined && (settings.submitting || this.status === 2 || this.status === 3)) { 328 value = getAFValue($form.find('#' + this.inputID)); 329 this.clientValidation(value, msg, this); 330 if (msg.length) { 331 messages[this.id] = msg; 332 } 333 } 334 if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status === 2 || this.status === 3)) { 335 needAjaxValidation = true; 336 } 337 }); 338 339 if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) { 340 if (settings.submitting) { 341 // delay callback so that the form can be submitted without problem 342 setTimeout(function () { 343 successCallback(messages); 344 }, 200); 345 } else { 346 successCallback(messages); 347 } 348 return; 349 } 350 351 var $button = $form.data('submitObject'), 352 extData = '&' + settings.ajaxVar + '=' + $form.attr('id'); 353 if ($button && $button.length) { 354 extData += '&' + $button.attr('name') + '=' + $button.attr('value'); 355 } 356 357 $.ajax({ 358 url: settings.validationUrl, 359 type: $form.attr('method'), 360 data: $form.serialize() + extData, 361 dataType: 'json', 362 success: function (data) { 363 if (data !== null && typeof data === 'object') { 364 $.each(settings.attributes, function () { 365 if (!this.enableAjaxValidation) { 366 delete data[this.id]; 367 } 368 }); 369 successCallback($.extend({}, messages, data)); 370 } else { 371 successCallback(messages); 372 } 373 }, 374 error: function () { 375 if (errorCallback !== undefined) { 376 errorCallback(); 377 } 378 } 379 }); 380 }; 381 382 /** 383 * Returns the configuration for the specified form. 384 * The configuration contains all needed information to perform ajax-based validation. 385 * @param form jquery the jquery representation of the form 386 * @return object the configuration for the specified form. 387 */ 388 $.fn.yiiactiveform.getSettings = function (form) { 389 return $(form).data('settings'); 390 }; 391 392 $.fn.yiiactiveform.defaults = { 393 ajaxVar: 'ajax', 394 validationUrl: undefined, 395 validationDelay: 200, 396 validateOnSubmit: false, 397 validateOnChange: true, 398 validateOnType: false, 399 hideErrorMessage: false, 400 inputContainer: undefined, 401 errorCss: 'error', 402 errorCssClass: 'error', 403 successCssClass: 'success', 404 validatingCssClass: 'validating', 405 summaryID: undefined, 406 timer: undefined, 407 beforeValidateAttribute: undefined, // function (form, attribute) | boolean 408 afterValidateAttribute: undefined, // function (form, attribute, data, hasError) 409 beforeValidate: undefined, // function (form) | boolean 410 afterValidate: undefined, // function (form, data, hasError) | boolean 411 focus: undefined, // jquery selector that indicates which element to receive input focus initially 412 /** 413 * list of attributes to be validated. Each array element is of the following structure: 414 * { 415 * id: 'ModelClass_attribute', // the unique attribute ID 416 * model: 'ModelClass', // the model class name 417 * name: 'name', // attribute name 418 * inputID: 'input-tag-id', 419 * errorID: 'error-tag-id', 420 * value: undefined, 421 * status: 0, // 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating 422 * validationDelay: 200, 423 * validateOnChange: true, 424 * validateOnType: false, 425 * hideErrorMessage: false, 426 * inputContainer: undefined, 427 * errorCssClass: 'error', 428 * successCssClass: 'success', 429 * validatingCssClass: 'validating', 430 * enableAjaxValidation: true, 431 * enableClientValidation: true, 432 * clientValidation: undefined, // function (value, messages, attribute) | client-side validation 433 * beforeValidateAttribute: undefined, // function (form, attribute) | boolean 434 * afterValidateAttribute: undefined, // function (form, attribute, data, hasError) 435 * } 436 */ 437 attributes: [], 438 errorCallback: undefined 439 }; 440})(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.