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