Passed
Push — master ( 56a07b...463b3a )
by Emmanuel
01:12
created

(   A

Complexity

Conditions 1
Paths 64

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
rs 10
c 0
b 0
f 0
cc 1
nc 64
nop 1
1
/*
2
 * Copyright (c) 2018.  JAGFx
3
 * @author: SMITH Emmanuel
4
 * @version: 2.0.0
5
 */
6
7
(function ( $ ) {
8
	$.fn.formJS = function ( options ) {
9
		var obj = this;
10
11
		var settings = $.extend( {
12
			alerts:      {
13
				unexpected: {
14
					title:   'Error',
15
					message: 'Unexpected error occurred'
16
				},
17
				failSend:   {
18
					title:   'Error',
19
					message: 'Unable to send data'
20
				}
21
			},
22
			keys:        {
23
				success: 'success',
24
				info:    'info',
25
				warning: 'warning',
26
				error:   'error'
27
			},
28
			icons:       {
29
				loading: '<span class="fas fa-circle-notch fa-spin"></span>',
30
				success: '<span class="fas fa-check"></span>',
31
				info:    '<span class="fas fa-info"></span>',
32
				warning: '<span class="fas fa-exclamation-triangle"></span>',
33
				error:   '<span class="fas fa-fire"></span>'
34
			},
35
			form:        {
36
				ajaxSettings:   {
37
					contentType: false
38
				},
39
				alertContainer: '.formJS',
40
				btnSubmit:      '.btn[type="submit"]'
41
			},
42
			redirection: {
43
				message: 'Automatic redirection in a second',
44
				delay:   1100
45
			},
46
			callback:    function ( currentAlert ) {
0 ignored issues
show
Unused Code introduced by
The parameter currentAlert is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
47
			}
48
		}, options );
49
50
		obj.each( function () {
51
			var $this        = $( this );
52
			var action       = $this.attr( "action" );
53
			var method       = $this.attr( "method" );
54
			var btnSubmit    = $this.find( settings.form.btnSubmit );
55
			var currentAlert = $.extend( settings.alerts.unexpected, { type: 'error' } );
56
			var ajaxPending  = false;
57
			var ajaxSettings;
58
			var formdata;
59
			var data;
60
61
			$this.submit( function ( e ) {
62
				e.preventDefault();
63
64
				// --------------- Check if an ajax request is in precessing
65
				if ( ajaxPending === false )
66
					ajaxPending = true;
67
				else return;
68
69
				try {
70
					// --------------- Check if a submit button is found
71
					if ( btnSubmit.length === 0 )
72
						throw 'Unable to find submit button';
73
74
					// --------------- Check if form method is found
75
					if ( method == "" || method === null )
76
						throw 'Undefined method of form';
77
78
					// --------------- Add loader and disabled submit button
79
					btnSubmit
80
						.append( $( settings.icons.loading ).addClass( 'formJS-loading' ) )
81
						.attr( 'disabled' );
82
83
					btnSubmit.addClass( 'disabled' );
84
85
					// --------------- Prepare ajax setting
86
					formdata     = (window.FormData) ? new FormData( $this[ 0 ] ) : null;
87
					data         = (formdata !== null) ? formdata : $this.serialize();
88
					ajaxSettings = $.extend( settings.form.ajaxSettings, {
89
						url:         action,
90
						type:        method,
91
						data:        data,
92
						processData: false
93
					} );
94
95
					// --------------- Send ajax request
96
					$.ajax( ajaxSettings )
97
						.done( function ( feedback ) {
98
							try {
99
								// --------------- If no feedback found, write unexpected alert
100
								if ( feedback.length === 0 )
101
									throw 'No data found on response';
102
103
								var feedbackData = $.parseJSON( feedback );
104
								var notif        = '';
105
106
								// --------------- Check feedback structure
107
								$this.checkFeedbackStructure( feedbackData );
108
109
								// --------------- If url field is in feedback,  prepare to redirect to it
110
								if ( feedbackData.type === settings.keys.success && feedbackData.hasOwnProperty( 'url' ) ) {
111
									notif = ' - ' + settings.redirection.message;
112
113
									setTimeout( function () {
114
										window.location.replace( feedbackData.url );
115
									}, settings.redirection.delay );
116
								}
117
118
								// --------------- Make alert object with feedback
119
								currentAlert.type    = feedbackData.type;
120
								currentAlert.title   = feedbackData.data.title;
121
								currentAlert.message = feedbackData.data.message + notif;
122
123
							} catch ( error ) {
124
								console.error( '[FormJS] ' + error );
125
							}
126
						} )
127
						.fail( function ( error ) {
128
							console.error( error );
129
						} )
130
						.always( function () {
131
							// --------------- Call after all ajax request
132
							$this.writeAlert();
133
						} );
134
135
				} catch ( error ) {
136
					// --------------- Call if an error thrown before sending ajax request
137
					console.error( '[FormJS] ' + error );
138
					$this.writeAlert();
139
				}
140
			} );
141
142
			/**
143
			 * Check the structure of feedback response
144
			 * @param inputData
145
			 */
146
			$this.checkFeedbackStructure = function ( inputData ) {
147
				/**
148
				 * feedbackStructure = {
149
						type: '',
150
						url:  '',
151
						data: {
152
							title:   '',
153
							message: ''
154
						}
155
					};
156
				 */
157
158
				if ( !inputData.hasOwnProperty( 'type' ) )
159
					throw 'Invalid feedback structure: "type" missing';
160
161
				if ( !inputData.hasOwnProperty( 'data' ) )
162
					throw 'Invalid feedback structure: "data" missing';
163
164
				if ( !inputData.data.hasOwnProperty( 'title' ) )
165
					throw 'Invalid feedback structure: "data.title" missing';
166
167
				if ( !inputData.data.hasOwnProperty( 'message' ) )
168
					throw 'Invalid feedback structure: "data.message" missing';
169
170
				if ( Object.keys( settings.keys ).indexOf( inputData.type ) === -1 )
171
					throw 'Invalid feedback structure: "type" wrong. Accepted values: ' + Object.keys( settings.keys ).toString();
172
			};
173
174
			/**
175
			 * Create DOM alert
176
			 */
177
			$this.writeAlert = function () {
178
				// --------------- Create alert DOM element
179
				var alert = $( '<div class="alert formjs-' + settings.keys[ currentAlert.type ] + '" role="alert" />' )
180
					.html( '<div class="ico">\n\
181
								' + settings.icons[ currentAlert.type ] + '\n\
182
							</div>\n\
183
							<div class="info">\n\
184
								<h4>' + currentAlert.title + '</h4>\n\
185
								<p>' + currentAlert.message + '</p>\n\
186
							</div>' )
187
					.hide()
188
					.fadeIn( 300 );
189
190
				// --------------- Add alert DOM element to the container
191
				$( settings.form.alertContainer )
192
					.empty()
193
					.html( alert );
194
195
				// --------------- Scroll top
196
				$( 'html, body' ).animate( {
197
					scrollTop: $( settings.form.alertContainer ).offset().top - 55
198
				}, 300 );
199
200
				// --------------- Enable formJS process and enabled submit button
201
				var btnSubmit = $( settings.form.btnSubmit );
202
				if ( btnSubmit !== undefined && btnSubmit.length ) {
203
					btnSubmit
204
						.find( '.formJS-loading' )
205
						.remove();
206
207
					btnSubmit.removeClass( 'disabled' );
208
209
					ajaxPending = false;
210
				}
211
212
				// --------------- Print alert into developper console
213
				if ( currentAlert.type === 'success' || currentAlert.type === 'info' )
214
					console.log( currentAlert.title + " - " + currentAlert.message );
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
215
216
				else if ( currentAlert.type === 'error' )
217
					console.error( currentAlert.title + " - " + currentAlert.message );
218
219
				else if ( currentAlert.type === 'warning' )
220
					console.warn( currentAlert.title + " - " + currentAlert.message );
221
222
				else
223
					console.log( currentAlert.title + " - " + currentAlert.message );
224
225
				settings.callback( currentAlert );
226
			};
227
228
			/**
229
			 * Create the container if it not found
230
			 */
231
			$this.init = function () {
232
				var container = $this.find( settings.form.alertContainer );
233
234
				if ( container.length === 0 ) {
235
					var $el        = $( '<div/>' );
236
					var props      = settings.form.alertContainer.split( '.' ),
237
						newelement = $el,
238
						id         = '',
239
						className  = '';
240
					$.each( props, function ( i, val ) {
241
						if ( val.indexOf( '#' ) >= 0 ) {
242
							id += val.replace( /^#/, '' );
243
						} else {
244
							className += val + ' ';
245
						}
246
					} );
247
248
					if ( id.length ) newelement.attr( 'id', id );
249
					if ( className.length ) newelement.attr( 'class', className.trim() );
250
251
					$this.prepend( newelement );
252
				}
253
			};
254
255
			$this.init();
256
		} );
257
258
		return obj;
259
	};
260
261
})( jQuery );
262