PageSourceSearch

https://twr.org/js/ss/frontend/FormHelper.js?1789539203

js twr.org collected 2026-09-24 09:35:35 UTC 27,124 bytes, 696 lines download raw bytes

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.