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>⌛</span>', |
||
31 | success: '<span>✓</span>', |
||
32 | info: '<span>🛈</span>', |
||
33 | warning: '<span>﹗</span>', |
||
34 | error: '<span>⨯</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
|
|||
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
|
|||
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 |
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.