dist/formJS.js   B
last analyzed

Complexity

Total Complexity 49
Complexity/F 2.58

Size

Lines of Code 366
Function Count 19

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 49
eloc 193
mnd 30
bc 30
fnc 19
dl 0
loc 366
rs 8.48
bpm 1.5789
cpm 2.5789
noi 2
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like dist/formJS.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 * Copyright (c) 2019.  JAGFx
3
 * @author: SMITH Emmanuel
4
 * @version: 2.1.0
5
 *
6
 */
7
8
(function ( $ ) {
9
	$.fn.formJS = function ( options ) {
10
		var obj = this;
11
12
		var settings = $.extend( true, {
13
			alerts:      {
14
				unexpected: {
15
					title:   'Error',
16
					message: 'Unexpected error occurred'
17
				},
18
				failSend:   {
19
					title:   'Error',
20
					message: 'Unable to send data'
21
				}
22
			},
23
			keys:        {
24
				success: 'success',
25
				info:    'info',
26
				warning: 'warning',
27
				error:   'error'
28
			},
29
			icons:       {
30
				loading: '<span>&#8987;</span>',
31
				success: '<span>&#10003;</span>',
32
				info:    '<span>&#128712;</span>',
33
				warning: '<span>&#65111;</span>',
34
				error:   '<span>&#10799;</span>'
35
			},
36
			form:        {
37
				ajaxSettings:     {
38
					contentType: false
39
				},
40
				alertContainer:   '.formJS',
41
				btnSubmit:        '.btn[type="submit"]',
42
				enableWriteAlert: true
43
			},
44
			redirection: {
45
				message: 'Automatic redirection in a second',
46
				delay:   1100
47
			},
48
			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...
49
			}
50
		}, options );
51
52
		return obj.each( function () {
53
			var $this      = $( this );
54
			var action,
55
				method,
56
				btnSubmit,
57
				currentAlert,
58
				currentResponseType,
59
				ajaxPending,
60
				ajaxSettings,
61
				formdata,
62
				data,
63
				writeError;
64
65
			/**
66
			 * Sending data method
67
			 * @param e
68
			 */
69
			$this.sendData = function ( e ) {
70
				e.preventDefault();
71
72
				// --------------- Check if an ajax request is in precessing
73
				if ( ajaxPending === false )
74
					ajaxPending = true;
75
				else return;
76
77
				try {
78
					// --------------- Check if a submit button is found
79
					if ( btnSubmit.length === 0 )
80
						throw 'Unable to find submit button';
81
82
					// --------------- Check if form method is found
83
					if ( method == "" || method === null )
84
						throw 'Undefined method of form';
85
86
					// --------------- Add loader and disabled submit button
87
					btnSubmit
88
						.append( $( settings.icons.loading ).addClass( 'formJS-loading' ) )
89
						.attr( 'disabled' );
90
91
					btnSubmit.addClass( 'disabled' );
92
93
					// --------------- Prepare ajax setting
94
					writeError   = false;
95
					formdata     = (window.FormData) ? new FormData( $this[ 0 ] ) : null;
96
					data         = (formdata !== null) ? formdata : $this.serialize();
97
					ajaxSettings = $.extend( settings.form.ajaxSettings, {
98
						url:         action,
99
						type:        method,
100
						data:        data,
101
						processData: false
102
					} );
103
104
					$this.trigger( 'formjs:submit', [ ajaxSettings, ajaxPending ] );
105
106
					// --------------- Send ajax request
107
					$.ajax( ajaxSettings )
108
						.done( function ( feedback ) {
109
							try {
110
								// --------------- If no feedback found, write unexpected alert
111
								if ( feedback.length === 0 )
112
									throw 'No data found on response';
113
114
								var feedbackData = (feedback instanceof Object)
115
									? feedback
116
									: $.parseJSON( feedback );
117
								var notif        = '';
118
119
								$this.trigger( 'formjs:ajax-success', [ feedback ] );
120
121
								// --------------- Check feedback structure
122
								$this.checkFeedbackStructure( feedbackData );
123
124
								// --------------- If url field is in feedback,  prepare to redirect to it
125
								if ( feedbackData.type === settings.keys.success && feedbackData.hasOwnProperty( 'url' ) ) {
126
									notif = ' - ' + settings.redirection.message;
127
128
									setTimeout( function () {
129
										window.location.replace( feedbackData.url );
130
									}, settings.redirection.delay );
131
								}
132
133
								if ( $this.responseIsAFeedback() ) {
134
									// --------------- Make alert object with feedback
135
									currentAlert.type    = feedbackData.type;
136
									currentAlert.title   = feedbackData.data.title;
137
									currentAlert.message = feedbackData.data.message + notif;
138
139
								} else if ( $this.responseIsAView() ) {
140
									// --------------- ... Or just replace the current form by the response view
141
142
									$this.updateFormView( feedbackData.view );
143
								}
144
145
							} catch ( error ) {
146
								$this.logError( 'AjaxSuccessCallback', error, feedback );
147
							}
148
						} )
149
						.fail( function ( error ) {
150
							$this.logError( 'AjaxFailCallback', error );
151
						} )
152
						.always( function () {
153
							// --------------- Call after all ajax request
154
							$this.writeAlert();
155
						} );
156
157
				} catch ( error ) {
158
					// --------------- Call if an error thrown before sending ajax request
159
					$this.logError( 'PreSubmit', error );
160
				}
161
			};
162
163
			/**
164
			 * Check the structure of feedback response
165
			 * @param inputData
166
			 */
167
			$this.checkFeedbackStructure = function ( inputData ) {
168
				/**
169
				 * feedbackStructure = {
170
						type: '',
171
						url:  '',
172
						data: {
173
							title:   '',
174
							message: ''
175
						}
176
					};
177
				 */
178
179
				if ( !inputData.hasOwnProperty( 'type' ) )
180
					throw 'Invalid feedback structure: "type" missing';
181
182
				if ( !inputData.hasOwnProperty( 'data' ) && !inputData.hasOwnProperty( 'view' ) )
183
					throw 'Invalid feedback structure: "data" or "view" missing';
184
185
				if ( inputData.hasOwnProperty( 'data' ) ) {
186
					currentResponseType = 'responseFeedback';
187
188
					if ( !inputData.data.hasOwnProperty( 'title' ) )
189
						throw 'Invalid feedback structure: "data.title" missing';
190
191
					if ( !inputData.data.hasOwnProperty( 'message' ) )
192
						throw 'Invalid feedback structure: "data.message" missing';
193
194
					if ( Object.keys( settings.keys ).indexOf( inputData.type ) === -1 )
195
						throw 'Invalid feedback structure: "type" wrong. Accepted values: ' + Object.keys( settings.keys ).toString();
196
				}
197
198
				if ( inputData.hasOwnProperty( 'view' ) )
199
					currentResponseType = 'responseView';
200
			};
201
202
			/**
203
			 * Log and trigger error event when error are occurred during the submit processing
204
			 * @param place Where the error are occurred
205
			 * @param message Error message
206
			 * @param data Additional data about this error
207
			 */
208
			$this.logError = function ( place, message, data ) {
209
				var mess = message.message || message;
210
211
				currentAlert.type = settings.keys.error;
212
				console.error( '[FormJS][' + place + '] ' + mess );
213
214
				$this.trigger( 'formjs:error', [ place, mess, data ] );
215
				writeError = true;
216
			};
217
218
			/**
219
			 * Create DOM alert
220
			 */
221
			$this.writeAlert = function () {
222
				if ( !$this.responseIsAFeedback() && !writeError )
223
					return;
224
225
				$this.trigger( 'formjs:write-alert', [ currentAlert ] );
226
227
				if ( settings.form.enableWriteAlert === true ) {
228
					// --------------- Create alert DOM element
229
					var alert = $( '<div class="alert formjs-' + settings.keys[ currentAlert.type ] + '" role="alert" />' )
230
						.html( '<div class="ico">\n\
231
								' + settings.icons[ currentAlert.type ] + '\n\
232
							</div>\n\
233
							<div class="info">\n\
234
								<h4>' + currentAlert.title + '</h4>\n\
235
								<p>' + currentAlert.message + '</p>\n\
236
							</div>' )
237
						.hide()
238
						.fadeIn( 300 );
239
240
					// --------------- Add alert DOM element to the container
241
					$( settings.form.alertContainer )
242
						.empty()
243
						.html( alert );
244
				}
245
246
				// --------------- Scroll top
247
				$( 'html, body' ).animate( {
248
					scrollTop: $( settings.form.alertContainer ).offset().top - 55
249
				}, 300 );
250
251
				// --------------- Enable formJS process and enabled submit button
252
				var btnSubmit = $( settings.form.btnSubmit );
253
				if ( btnSubmit !== undefined && btnSubmit.length ) {
254
					btnSubmit
255
						.find( '.formJS-loading' )
256
						.remove();
257
258
					btnSubmit.removeClass( 'disabled' );
259
260
					ajaxPending = false;
261
				}
262
263
				// --------------- Print alert into developper console
264
				if ( currentAlert.type === 'success' || currentAlert.type === 'info' )
265
					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...
266
267
				else if ( currentAlert.type === 'error' )
268
					console.error( currentAlert.title + " - " + currentAlert.message );
269
270
				else if ( currentAlert.type === 'warning' )
271
					console.warn( currentAlert.title + " - " + currentAlert.message );
272
273
				else
274
					console.log( currentAlert.title + " - " + currentAlert.message );
275
276
				settings.callback( currentAlert );
277
			};
278
279
			/**
280
			 * Update the HTML DOM with the content returned by the response. Only work with view response type
281
			 * @param viewData HTML of response
282
			 */
283
			$this.updateFormView = function ( viewData ) {
284
				if ( !$this.responseIsAView() )
285
					return;
286
287
				$this.html( viewData );
288
				$this.init();
289
			};
290
291
			/**
292
			 * Return true if the response of sending request is a feedback
293
			 * @returns {boolean}
294
			 */
295
			$this.responseIsAFeedback = function () {
296
				return currentResponseType === 'responseFeedback';
297
			};
298
299
			/**
300
			 * Return true if the response of sending request is a view
301
			 * @returns {boolean}
302
			 */
303
			$this.responseIsAView = function () {
304
				return currentResponseType === 'responseView';
305
			};
306
307
			// ---------------------------- INIT
308
309
			/**
310
			 * Create the container if it not found
311
			 */
312
			$this.init = function () {
313
				$this.initVariables();
314
315
				var container = $this.find( settings.form.alertContainer );
316
317
				if ( container.length === 0 ) {
318
					var $el        = $( '<div/>' );
319
					var props      = settings.form.alertContainer.split( '.' ),
320
						newelement = $el,
321
						id         = '',
322
						className  = '';
323
					$.each( props, function ( i, val ) {
324
						if ( val.indexOf( '#' ) >= 0 ) {
325
							id += val.replace( /^#/, '' );
326
						} else {
327
							className += val + ' ';
328
						}
329
					} );
330
331
					if ( id.length ) newelement.attr( 'id', id );
332
					if ( className.length ) newelement.attr( 'class', className.trim() );
333
334
					$this.prepend( newelement );
335
				}
336
			};
337
338
			/**
339
			 * Init variables with current formJS object
340
			 */
341
			$this.initVariables = function () {
342
				action              = $this.attr( "action" );
343
				method              = $this.attr( "method" );
344
				btnSubmit           = $this.find( settings.form.btnSubmit );
345
				currentAlert        = $.extend( settings.alerts.unexpected, { type: 'error' } );
346
				ajaxPending         = false;
347
				currentResponseType = undefined;
348
349
				$this.initEvent();
350
			};
351
352
			/**
353
			 * Init event with current formJS object
354
			 */
355
			$this.initEvent = function () {
356
				/**
357
				 * Process to sending data from the current form object
358
				 */
359
				$this.submit( $this.sendData );
360
361
				/**
362
				 * Process to sending data from on other way from the formJS plugin
363
				 */
364
				$this.on( 'formjs:send-form', $this.sendData );
365
			};
366
367
			$this.init();
368
369
			return $this;
370
		} );
371
	};
372
373
})( jQuery );
374