Passed
Push — develop ( c6dd2a...1f8334 )
by Paul
03:38
created

assets/js/form.js   B

Complexity

Conditions 1
Paths > 20000

Size

Total Lines 235

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 73728
nop 3
dl 0
loc 235
rs 8.2857
c 0
b 0
f 0

15 Functions

Rating   Name   Duplication   Size   Complexity  
A Plugin.clearFieldError 0 7 2
A Plugin.postAjax 0 16 3
A Plugin.clearFormErrors 0 6 2
B Plugin._serialize 0 15 5
A Plugin.onChange 0 4 1
A Plugin._isNumeric 0 4 1
A Plugin.init 0 6 1
A Plugin.onSubmit 0 8 2
B Plugin._convertValue 0 16 6
A form.js ➔ Plugin 0 9 3
B Plugin.parseFormData 0 49 6
A Plugin._inArray 0 10 3
A Plugin.showFormMessage 0 11 3
B Plugin.showFormErrors 0 20 6
A Plugin.submitForm 0 19 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
;(function( window, document, undefined ) {
2
	"use strict";
3
4
	var Plugin = function( selector, options )
5
	{
6
		this.el = castor._isString( selector ) ? document.querySelector( selector ) : selector;
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
7
		this.options = castor._extend( this.defaults, options );
8
		if( this.el ) {
9
			this.options = castor._extend( this.options, this.el.getAttribute( 'data-options' ));
10
			this.init();
11
		}
12
	};
13
14
	Plugin.prototype =
15
	{
16
		defaults: {
17
			ajaxUrl: '/ajax.php',
18
			buttonSelector: '[type="submit"]',
19
			fieldClass: 'form-field',
20
			fieldErrorsClass: 'field-errors',
21
			fieldHasErrorClass: 'field-has-error',
22
			formHasErrorsClass: 'form-has-errors',
23
			formMessageClass: 'form-messages',
24
		},
25
26
		clearFieldError: function( el )
27
		{
28
			var fieldEl = el.closest( '.'+this.options.fieldClass );
29
			if( fieldEl === null )return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
30
			castor._removeEl( '.'+this.options.fieldErrorsClass, fieldEl );
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
31
			fieldEl.classList.remove( this.options.fieldHasErrorClass );
32
		},
33
34
		clearFormErrors: function()
35
		{
36
			for( var i = 0; i < this.el.length; i++ ) {
37
				this.clearFieldError( this.el[i] );
38
			}
39
		},
40
41
		init: function()
42
		{
43
			this.button = this.el.querySelector( this.options.buttonSelector );
44
			this.el.addEventListener( 'change', this.onChange.bind( this ));
45
			this.el.addEventListener( 'submit', this.onSubmit.bind( this ));
46
		},
47
48
		onChange: function( ev )
49
		{
50
			this.clearFieldError( ev.target );
51
		},
52
53
		onSubmit: function( ev )
54
		{
55
			ev.preventDefault();
56
			if( !this.button.disabled ) {
57
				this.button.setAttribute( 'disabled', '' );
58
			}
59
			this.submitForm();
60
		},
61
62
		parseFormData: function( convert )
63
		{
64
			convert = !!convert || false;
65
			var keyBreaker = /[^\[\]]+/g; // used to parse bracket notation
66
			var data = {};
67
			var seen = {}; // used to uniquely track seen values
68
			var nestData = function( field, data, parts, seenName )
69
			{
70
				var name = parts.shift();
71
				// Keep track of the dot separated fullname
72
				seenName = seenName ? seenName+'.'+name : name;
73
				if( parts.length ) {
74
					if( !data[name] ) {
75
						data[name] = {};
76
					}
77
					// Recursive call
78
					nestData( field, data[name], parts, seenName );
79
				}
80
				else {
81
					// Convert the value
82
					var value = convert ? this._convertValue( field.value ) : field.value;
83
					// Handle same name case, as well as "last checkbox checked" case
84
					if( seenName in seen && field.type !== "radio" && !data[name].isArray()) {
85
						data[name] = ( name in data ) ? [data[name]] : [];
86
					}
87
					else {
88
						seen[seenName] = true;
89
					}
90
					// Finally, assign data
91
					if( this._inArray( field.type, ['radio','checkbox'] ) && !field.checked )return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
92
					if( !data[name] ) {
93
						data[name] = value;
94
					}
95
					else {
96
						data[name].push( value );
97
					}
98
				}
99
			}.bind( this );
100
			for( var i = 0; i < this.el.length; i++ ) {
101
				var field = this.el[i];
102
				if( !field.name || field.disabled || this._inArray( field.type, ['file','reset','submit','button'] ))continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
103
				var parts = field.name.match( keyBreaker );
104
				if( !parts.length ) {
105
					parts = [field.name];
106
				}
107
				nestData( field, data, parts );
108
			}
109
			return data;
110
		},
111
112
		postAjax: function( data, success )
113
		{
114
			var params = typeof data !== 'string' ? this._serialize( data ) : data;
115
			var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject( "Microsoft.XMLHTTP" );
0 ignored issues
show
Bug introduced by
The variable XMLHttpRequest seems to be never declared. If this is a global, consider adding a /** global: XMLHttpRequest */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable ActiveXObject seems to be never declared. If this is a global, consider adding a /** global: ActiveXObject */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
116
			xhr.open( 'POST', this.options.ajaxUrl ); // asynchronously
117
			xhr.onreadystatechange = function() {
118
				if( xhr.readyState > 3 && xhr.status === 200 ) {
119
					success( JSON.parse( xhr.responseText ));
120
				}
121
			};
122
			xhr.setRequestHeader( 'Accept', 'application/json' );
123
			xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
124
			xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' );
125
			xhr.send( params );
126
			return xhr;
127
		},
128
129
		showFormErrors: function( errors )
130
		{
131
			var fieldEl;
132
			var errorsEl;
133
			for( var error in errors ) {
134
				if( !errors.hasOwnProperty( error ))continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
135
				fieldEl  = this.el.querySelector( '[name="' + error + '"]' ).closest( '.'+this.options.fieldClass );
136
				fieldEl.classList.add( this.options.fieldHasErrorClass );
137
				errorsEl = fieldEl.querySelector( '.'+this.options.fieldErrorsClass );
138
				if( errorsEl === null ) {
139
					errorsEl = castor._appendTo( fieldEl, 'span', {
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
140
						'class': this.options.fieldErrorsClass,
141
					});
142
				}
143
				for( var i = 0; i < errors[error].errors.length; i++ ) {
144
					if( errors[error].errors[i] === null )continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
145
					errorsEl.innerHTML += '<span>' + errors[error].errors[i] + '</span>';
146
				}
147
			}
148
		},
149
150
		showFormMessage: function( response )
151
		{
152
			var messageEl = this.el.querySelector( '.'+this.options.formMessageClass );
153
			if( messageEl === null ) {
154
				messageEl = castor._insertBefore( this.button, 'div', {
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
155
					'class': this.options.formMessageClass,
156
				});
157
			}
158
			messageEl.classList[!!response.errors ? 'add' : 'remove']( this.options.formHasErrorsClass );
159
			messageEl.innerHTML = response.message;
160
		},
161
162
		submitForm: function()
163
		{
164
			var data = {
165
				action: 'submit-' + this.el.id,
166
				request: this.parseFormData(),
167
			};
168
			castor._removeEl( '.'+this.options.formMessageClass, this.el );
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
169
			this.postAjax( data, function( response ) {
170
				this.clearFormErrors();
171
				this.showFormMessage( response );
172
				this.button.removeAttribute( 'disabled' );
173
				if( !!response.errors ) {
174
					this.showFormErrors( response.errors );
175
				}
176
				else {
177
					this.el.reset();
178
				}
179
			}.bind( this ));
180
		},
181
182
		_convertValue: function( value )
183
		{
184
			if( this._isNumeric( value )) {
185
				return parseFloat( value );
186
			}
187
			else if( value === 'true') {
188
				return true;
189
			}
190
			else if( value === 'false' ) {
191
				return false;
192
			}
193
			else if( value === '' || value === null ) {
194
				return undefined;
195
			}
196
			return value;
197
		},
198
199
		_inArray: function( needle, haystack )
200
		{
201
			var length = haystack.length;
202
			while( length-- ) {
203
				if( haystack[ length ] === needle ) {
204
					return true;
205
				}
206
			}
207
			return false;
208
		},
209
210
		_isNumeric: function( value )
211
		{
212
			return !( isNaN( parseFloat( value )) || !isFinite( value ));
213
		},
214
215
		_serialize: function( obj, prefix )
216
		{
217
			var str = [];
218
			for( var property in obj ) {
219
				if( !obj.hasOwnProperty( property ))continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
220
				var key = prefix ? prefix + "[" + property + "]" : property;
221
				var value = obj[ property ];
222
				str.push(
223
					typeof value === "object" ?
224
					this._serialize( value, key ) :
225
					encodeURIComponent( key ) + "=" + encodeURIComponent( value )
226
				);
227
			}
228
			return str.join( "&" );
229
		},
230
	};
231
232
	Plugin.defaults = Plugin.prototype.defaults;
233
	castor.Form = Plugin;
0 ignored issues
show
Bug introduced by
The variable castor seems to be never declared. If this is a global, consider adding a /** global: castor */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
234
235
})( window, document );
236