1;(function($){ 2 // Constructor. 3 function FormHelper( form, options ) 4 { 5 var defaults = { 6 errorMessages: { 7 required: 'This field is required', 8 minlength: 'This field must have at least {minLength} characters in length', 9 maxlength: 'This field must have maximum {maxLength} characters in length', 10 email: 'This email is not a valid email address', 11 phone: 'This is not a valid US phone number', 12 number: 'This field must contain only numbers', 13 cardNumber: 'This is not a valid card number' 14 }, 15 showAllErrorsInBox: false, 16 enterSubmit: true, 17 liveValidation: true 18 }; 19 this.options = $.extend(true, {}, defaults, options); 20 this.form = $(form); 21 this.useAjaxSubmit = this.form.hasClass('ajaxSubmit'); 22 this.validationErrors = {}; 23 this.record = options.record; 24 this.init(); 25 26 // private variables 27 this._cacheForm = this.form.html(); 28 } 29 30 // Initialize all parts of the plugin. 31 $.extend(FormHelper.prototype, { 32 33 /** 34 * Init formHelper 35 */ 36 init: function() 37 { 38 var me = this; 39 40 if(this.options.liveValidation){ 41 // clear all past validation errors on field focus 42 this.form.find('input,select,textarea').on('focus',function(){ 43 me.clearValidationErrors($(this)); 44 }); 45 // validate fields after every blur, change and keyup event based on the html5 tags and validation css classes 46 this.form.find('input,select,textarea').on('blur',function(){ 47 me.validateField( $(this) ); 48 }); 49 this.form.find('input,select,textarea').on('keyup',function(){ 50 me.validateField( $(this) ); 51 }); 52 this.form.find('input,select,textarea').on('change',function(){ 53 me.validateField( $(this) ); 54 }); 55 } 56 57 // prevent submitting the form if enter key is pressed 58 if (!this.options.enterSubmit) { 59 this.form.find('input,select').on('keypress',function(event){ 60 if (event.which == 13) { 61 event.preventDefault(); 62 } 63 }); 64 } 65 66 // call the submit function on form submit event 67 if( this.useAjaxSubmit ){ 68 this.form.off('submit').on('submit',function( event ){ 69 event.preventDefault(); 70 me.ajaxSubmit(); 71 }); 72 }else{ 73 this.form.off('submit').on('submit',function( event ){ 74 me.validatePanel(); 75 if( !$.isEmptyObject(me.validationErrors) ){ 76 return false; 77 } 78 }); 79 } 80 81 // call the reset function on form reset event 82 this.form.on('reset',function( event ){ 83 event.preventDefault(); 84 me.reset( event ); 85 }); 86 }, 87 88 /** 89 * AjaxSubmit function 90 */ 91 ajaxSubmit: function() 92 { 93 var me = this; 94 this.validatePanel(); 95 96 var beforeSaveEvent = jQuery.Event("beforeFormHelperSubmit"); 97 var fieldValues = me.getAllFieldValues(); 98 me.form.trigger(beforeSaveEvent, [fieldValues]); 99 100 if( $.isEmptyObject(me.validationErrors) && !beforeSaveEvent.isDefaultPrevented()){ 101 102 var submitButton = this.form.find('input[type="submit"]'); 103 var originalValue = submitButton.val(); 104 submitButton.val('Processing... Please wait'); 105 submitButton.attr('disabled', '1'); 106 107 $.ajax({ 108 url: me.form.attr('action'), 109 cache: false, 110 type: 'POST', 111 data: fieldValues, 112 dataType : 'json', 113 abortOnRetry: false, 114 error : function(jqXHR, textStatus, errorThrown){ 115 var event = jQuery.Event("formHelperError"); 116 me.form.trigger(event, [jqXHR.responseText, textStatus, jqXHR, errorThrown]); 117 submitButton.val(originalValue); 118 submitButton.removeAttr('disabled'); 119 }, 120 success : function(responseData, textStatus, jqXHR){ 121 var event = null; 122 if( responseData.success ){ 123 event = jQuery.Event("formHelperSuccess"); 124 me.form.trigger(event, [responseData, textStatus, jqXHR]); 125 }else{ 126 if( !$.isEmptyObject(responseData.errors) ){ 127 me.showModelErrors( responseData.errors ); 128 } 129 event = jQuery.Event("formHelperError"); 130 me.form.trigger(event, [responseData, textStatus, jqXHR, 'CakePhp Model Errors']); 131 } 132 133 submitButton.val(originalValue); 134 submitButton.removeAttr('disabled'); 135 } 136 }); 137 }else{ 138 var event = jQuery.Event("formHelperIsNotValid"); 139 me.form.trigger(event, [me.validationErrors]); 140 } 141 }, 142 143 /** 144 * Function for advanced form reset 145 * Prevent the default browser reset functionality and replace the current form with the cached one 146 * It also fire the `resetFormHelper` event to be used in other JS f
146iles 147 */ 148 reset: function( event ) 149 { 150 if( event ){ 151 event.preventDefault(); 152 } 153 this.form.html(this._cacheForm); 154 this.init(); 155 this.form.trigger('resetFormHelper'); 156 }, 157 158 /** 159 * Function that gets all the form values, excepting the disabled fields 160 * @returns {{}} 161 */ 162 getAllFieldValues: function() 163 { 164 var me = this; 165 var inputs = this.form.find('input,select,textarea').not(':disabled'); 166 var values = {}; 167 168 $.each(inputs,function(index,input){ 169 var $field = $(input); 170 var fieldName = $field.attr('name'); 171 172 if( $field.attr('type') == 'checkbox' || $field.attr('type') == 'radio' ){ 173 if( $field.is(':checked') ){ 174 values[fieldName] = $field.val(); 175 } 176 }else{ 177 values[fieldName] = $field.val(); 178 } 179 }); 180 return values; 181 }, 182 183 /** 184 * Function that will append right after the input a div containing all the error messages. 185 * It will also add `has-error` class to the input. 186 * @param field 187 * @param errors 188 */ 189 displayErrors: function( field, errors ) 190 { 191 if( !$.isEmptyObject(errors) ){ 192 var errorDiv = $('<div />').addClass('form-input-error'); 193 $.each(errors,function( index, error ){ 194 var span = $('<span />').html(error); 195 errorDiv.append(span); 196 }); 197 field.addClass('has-error'); 198 field.closest('.form-input-holder').append(errorDiv); 199 field.closest('.form-input-wrapper').addClass('field-has-error'); 200 } 201 }, 202 203 /** 204 * Display model errors for each field 205 * @param errors - CakeModel validationError object 206 */ 207 showModelErrors: function( errors ) 208 { 209 var me = this; 210 211 $.each(errors,function( index, error ){ 212 var field = me.form.find('input[name="'+index+'"], select[name="'+index+'"], textarea[name="'+index+'"]'); 213 if( field.length > 0){ 214 if($.isArray(error) ){ 215 me.displayErrors( field, error ); 216 }else{ 217 me.displayErrors( field, [error] ); 218 } 219 if (!me.options.showAllErrorsInBox) { 220 delete errors[index]; 221 } 222 } 223 }); 224 //if( !$.isEmptyObject( errors ) ){ 225 this.showOtherErrors( errors ); 226 //} 227 }, 228 229 /** 230 * Display model errors for each field 231 * @param errors - CakeModel validationError object 232 */ 233 showOtherErrors: function( errors ) 234 { 235 var errorsHolder = this.form.find('.form-validation-errors'); 236 errorsHolder.slideUp(200,function(){ 237 $(this).html(''); 238 if( !$.isEmptyObject(errors) ){ 239 $.each(errors,function( index, error ){ 240 var span = $('<span />').html(error); 241 errorsHolder.append(span); 242 }); 243 errorsHolder.slideDown(200); 244 } 245 }); 246 }, 247 248 /** 249 * Run the 'validateField' function on all the enabled fields in the selected panel 250 * If no panel is sent the entire form will be validated 251 * @param panel - a jquery panel object 252 */ 253 validatePanel: function( panel ) 254 { 255 var me = this; 256 if( panel === undefined ){ 257 panel = this.form; 258 } 259 var inputs = panel.find('input[type!="hidden"],select,textarea').not(':disabled'); 260 inputs.each(function(index,field){ 261 me.validateField( $(field) ); 262 }); 263 }, 264 265 /** 266 * Function that will run all the validation methods based on the field properties and classes 267 * @param field - a jquery field object 268 */ 269 validateField: function( field ) 270 { 271 var errors = {}; 272 this.clearValidationErrors( field ); 273 274 if( field.is(':disabled') ){ 275 return; 276 } 277 278 if( field.attr('required') ){ 279 errors['required'] = this.validateBlank( field ); 280 } 281 if( field.attr('minlength') ){
282 errors['minlength'] = this.validateMinLength( field ); 283 } 284 if( field.attr('maxlength') ){ 285 errors['maxlength'] = this.validateMaxLength( field ); 286 } 287 if( field.attr('type') == 'email' ){ 288 errors['email'] = this.validateEmail( field ); 289 } 290 if( field.attr('type') == 'numeric' ){ 291 errors['numeric'] = this.validateNumber( field ); 292 } 293 if( field.attr('type') == 'phone' ){ 294 errors['phone'] = this.validatePhone( field ); 295 } 296 if( field.attr('type') == 'card' ){ 297 errors['phone'] = this.validateCardNumber( field ); 298 } 299 if( field.data('validator')){ 300 errors['validator'] = field.data('validator')( field ); 301 } 302 303 $.each(errors,function( index,error ){ 304 if(!error){ 305 delete errors[index]; 306 } 307 }); 308 309 if( !$.isEmptyObject(errors) ){ 310 this.validationErrors[$(field).attr("name")] = true; 311 } 312 313 this.displayErrors( field, errors ); 314 }, 315 316 /** 317 * Function to validate required fields 318 * @param field - a jquery field object 319 * @returns {*} - false or error message 320 */ 321 validateBlank: function( field ) 322 { 323 var errorMessage = this.options.errorMessages.required; 324 if( this.options.errorMessages.required[field.attr('name')] !== undefined ){ 325 errorMessage = this.options.errorMessages.required[field.attr('name')]; 326 } 327 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'required', errorMessage); 328 if(field.hasClass('checkbox-validation') && !field.is(':checked')){ 329 return errorMessage; 330 } 331 332 if( !field.val() || $.trim(field.val()).length < 1 ){ 333 return errorMessage; 334 } 335 return false; 336 }, 337 338 /** 339 * Function to validate field min length 340 * @param field - a jquery field object 341 * @returns {*} 342 */ 343 validateMinLength: function( field ) 344 { 345 var minLength = field.attr('minlength'); 346 var errorMessage = this.options.errorMessages.minlength.replace('{minLength}',minLength); 347 if( this.options.errorMessages.minlength[field.attr('name')] !== undefined ){ 348 errorMessage = this.options.errorMessages.minlength[field.attr('name')]; 349 } 350 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'minlength', errorMessage); 351 if( field.val().length < minLength ){ 352 return errorMessage; 353 } 354 return false; 355 }, 356 357 /** 358 * Function to validate field max length 359 * @param field - a jquery field object 360 * @returns {*} 361 */ 362 validateMaxLength: function( field ) 363 { 364 var maxLength = field.attr('maxlength'); 365 var errorMessage = this.options.errorMessages.maxlength.replace('{maxLength}',maxLength); 366 if( this.options.errorMessages.maxlength[field.attr('name')] !== undefined ){ 367 errorMessage = this.options.errorMessages.maxlength[field.attr('name')]; 368 } 369 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'maxlength', errorMessage); 370 if( field.val().length > maxLength ){ 371 return errorMessage; 372 } 373 return false; 374 }, 375 376 /** 377 * Function to validate number fields (allowed characters: numbers,space,underscore,dash,dot) 378 * @param field - a jquery field object 379 * @returns {*} 380 */ 381 validateNumber: function( field ) 382 { 383 if( field.val().length > 0 ){ 384 var errorMessage = this.options.errorMessages.number; 385 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'number', errorMessage); 386 if( !field.val().match(/^\d+$/) ){ 387 return errorMessage; 388 } 389 return false;
390 } 391 }, 392 393 /** 394 * Function to validate valid email addresses 395 * @param field - a jquery field object 396 * @returns {*} 397 */ 398 validateEmail: function( field ) 399 { 400 var errorMessage = this.options.errorMessages.email; 401 if( this.options.errorMessages.email[field.attr('name')] !== undefined ){ 402 errorMessage = this.options.errorMessages.email[field.attr('name')]; 403 } 404 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'email', errorMessage); 405 var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i); 406 if( !pattern.test(field.val()) ){ 407 return errorMessage; 408 } 409 return false; 410 }, 411 412 /** 413 * Function to validate us phone number 414 * @param field - a jquery field object 415 * @returns {*} 416 */ 417 validatePhone: function( field ) 418 { 419 var errorMessage = this.options.errorMessages.phone; 420 if( this.options.errorMessages.phone[field.attr('name')] !== undefined ){ 421 errorMessage = this.options.errorMessages.phone[field.attr('name')]; 422 } 423 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'phone', errorMessage); 424 var pattern = new RegExp(/^(\({0,1}[0-9]{3}\){0,1} {0,1}|[0-9]{3}-)[0-9]{3}[\- ]{0,1}[0-9]{4}$/i); 425 if(field.val() && !pattern.test(field.val()) ){ 426 return errorMessage; 427 } 428 return false; 429 }, 430 431 /** 432 * Function to validate card numbers 433 * @param field - a jquery field object 434 * @returns {*} 435 */ 436 validateCardNumber: function( field ) 437 { 438 var errorMessage = this.options.errorMessages.cardNumber; 439 if( this.options.errorMessages.cardNumber[field.attr('name')] !== undefined ){ 440 errorMessage = this.options.errorMessages.cardNumber[field.attr('name')]; 441 } 442 errorMessage = this.getCustomErrorMessage(field.attr('name'), 'cardNumber', errorMessage); 443 444 if ( 445 /^4[0-9]{12}(?:[0-9]{3})?$/.test(field.val()) || // Visa 446 /^(5[1-5]\d{14})|(2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[0-1][0-9]|720)\d{12})$/.test(field.val()) || // Mastercard 447 /^3[47][0-9]{13}$/.test(field.val()) || // American Express 448 /^6(?:011|5[0-9]{2})[0-9]{12}$/.test(field.val()) // Discover 449 ) { 450 return false 451 } 452 return errorMessage; 453 }, 454 455 /** 456 * Remove all the errors from a field 457 * @param field - a jquery field object 458 */ 459 clearValidationErrors: function( field ) 460 { 461 field.removeClass('has-error'); 462 field.closest('.form-input-wrapper').removeClass('field-has-error'); 463 if(field.hasClass('checkbox-validation')){ 464 field.closest('.form-input-holder').find('.form-input-error').remove(); 465 }else{ 466 field.parent().find('.form-input-error').remove(); 467 } 468 delete this.validationErrors[field.attr('name')]; 469 }, 470 471 /** 472 * Show selected panel. Enable all fields in the panel if enableFields is true 473 * @param panel - a jquery dom element object 474 * @param enableFields boolean 475 */ 476 showPanel: function( panel, enableFields ) 477 { 478 var me = this; 479 480 if(panel.length>1){ 481 panel.each(function (index, subPanel) { 482 me.showPanel($(subPanel),enableFields); 483 }) 484 return; 485 } 486 if( enableFields === undefined ){ 487 enableFields = true; 488 } 489 if( enableFields ){ 490 this.enablePanelFields( panel ); 491 } 492 //panel.slideDown(200); 493 panel.fadeIn(200); 494 panel.data('isHidden',false); 495 panel.trigger('panelShown'); 496 }, 497 498 /** 499 * Hide selected panel. Disable all fields in the panel if disabledFields is true 500 * @param panel - a jquery dom element object 501 * @param disableFields boolean 502 */ 503 hidePanel: function( panel, disableFields ) 504 { 505 var me = this; 506 if(panel.length>1){ 507 panel.each(function (index, subPanel) { 508 me.hidePanel($(subPanel),disableFields); 509 }) 510 return; 511 } 512 513 if (disableFields === undefined) { 514 disableFields = true; 515 } 516 //panel.slideUp(200,function(){ 517 panel.fadeOut(200,function(){ 518 if( disableFields ){ 519 me.disablePanelFields( panel ); 520 } 521 }); 522 panel.data('isHidden',true); 523 }, 524 525 hideField:function(mainField){ 526 var me = this; 527 $(mainField).hide(); 528 var inputs = $(mainField).find('input,select,textarea'); 529 inputs.each(function(index,field){ 530 $(field).attr('disabled','disabled'); 531 $(field).closest('.form-input-wrapper').addClass('form-disabled'); 532 $(field).data('force-disable',1); 533 me.validateField($(field)); 534 }); 535 }, 536 showField:function(mainField){ 537 var me = this; 538 $(mainField).show(); 539 var inputs = $(mainField).find('input,select,textarea'); 540 inputs.each(function(index,field){ 541 if($(field).closest('.inputs-wrapper').is(':visible')){ 542 $(field).removeAttr('disabled'); 543 $(field).closest('.form-input-wrapper').removeClass('form-disabled'); 544 } 545 $(field).data('force-disable',null); 546 }); 547 }, 548 549 /** 550 * Enable all the fields in the selected panel 551 * @param panel - a jquery dom element object 552 */ 553 enablePanelFields: function( panel ) 554 { 555 var inputs = panel.find('input,select,textarea'); 556 inputs.each(function(index,field){ 557 if(!$(field).data('force-disable')){ 558 $(field).removeAttr('disabled'); 559 $(field).closest('.form-input-wrapper').removeClass('form-disabled'); 560 } 561 }); 562 }, 563 564 /** 565 * Disable all the fields in the selected panel 566 * @param panel - a jquery dom element object 567 */ 568 disablePanelFields: function( panel ) 569 { 570 var me = this; 571 var inputs = panel.find('input,select,textarea'); 572 inputs.each(function(index,field){ 573 $(field).attr('disabled','disabled'); 574 $(field).closest('.form-input-wrapper').addClass('form-disabled'); 575 me.validateField($(field)); 576 }); 577 }, 578 579 /** 580 * Enable/Disable required property on a form field 581 * @param field - a jquery field object 582 * @param allow - boolean 583 */ 584 allowBlank: function( field, allow ) 585 { 586 if( allow ){ 587 field.removeAttr('required'); 588 field.removeData('required'); 589 }else{ 590 field.attr('required','required'); 591 } 592 }, 593 594 /** 595 * Function that will toggle any field type with a input.text field.
596 * If the field is other than input.text it will be transformed, else it will be restored. 597 * @param field {Object} - a jquery field object 598 */ 599 toggleFieldType: function( field ) 600 { 601 var fieldName = field.attr('name'); 602 var fieldAttributes = field.prop("attributes"); 603 var fieldEvents = field.data('events'); 604 605 if( this.form.find('*[name="' + fieldName + '"]').length > 1 ){ 606 field.siblings('input').not(':disabled').remove(); 607 field.removeAttr('disabled').show(); 608 }else{ 609 var input = $('<input/>'); 610 $.each(fieldAttributes, function() { 611 $(input).attr(this.name, this.value); 612 }); 613 if( fieldEvents ){ 614 for ( var eventType in fieldEvents ) { 615 for ( var idx in fieldEvents[eventType] ) { 616 $(input)[ eventType ]( fieldEvents[eventType][idx].handler ); 617 } 618 } 619 } 620 field.attr('disabled',true).hide(); 621 field.after(input); 622 } 623 }, 624 625 /** 626 * Find a value in the record object based on the field name 627 * @param field - a jQuery field object 628 * @param record (optional) - the record object 629 * @returns {*} 630 */ 631 findValueByFieldName: function( field, record ) 632 { 633 var me = this; 634 var tmp = null; 635 636 if($.type(record) == 'undefined'){ 637 record = me.record; 638 } 639 640 if($.type(field) == 'object' ){ 641 field = field.attr('name').split(/\[|\]+/g); 642 for (var i = 0; i < field.length; i++) { 643 if (field[i] == '') { 644 field.splice(i, 1); 645 i--; 646 } 647 } 648 tmp = me.findValueByFieldName(field, record); 649 } 650 651 if(record[field[0]] && $.type(record[field[0]]) == 'object' ){ 652 var fieldName = field[0]; 653 field.shift(); 654 tmp = me.findValueByFieldName(field, record[fieldName]); 655 }else if(record[field[0]]){ 656 tmp = record[field[0]]; 657 } 658 659 if($.type(tmp) == 'function' ){ 660 tmp = null; 661 } 662 return tmp; 663 }, 664 665 getCustomErrorMessage: function(fieldName, validationType, originalErrorMessage) { 666 if ( 667 this.options.customErrorMessages && 668 this.options.customErrorMessages[validationType] && 669 this.options.customErrorMessages[validationType][fieldName] 670 ) { 671 return this.options.customErrorMessages[validationType][fieldName]; 672 } else { 673 return originalErrorMessage; 674 } 675 } 676 }); 677 678 // A plugin wrapper around the constructor. 679 // Pass `options` with all settings that are different from the default. 680 // The attribute is used to prevent multiple instantiations of the plugin. 681 $.fn.formHelper = function( options ) 682 { 683 return this.each(function(key, value){ 684 var element = $(this); 685 // Return early if this element already has a plugin instance 686 if (element.data('formHelper')) { 687 return element.data('formHelper'); 688 } 689 // Pass options to plugin constructor 690 var formHelper = new FormHelper(this, options); 691 // Store plugin object in this element's data 692 element.data('formHelper', formHelper); 693 }); 694 }; 695 696})( 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.