Passed
Push — master ( b15b71...ab24e8 )
by Paul
04:46
created

+/scripts/public/forms.js   B

Complexity

Total Complexity 43
Complexity/F 1.95

Size

Lines of Code 207
Function Count 22

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
c 2
b 0
f 0
nc 24
dl 0
loc 207
rs 8.3157
wmc 43
mnd 2
bc 33
fnc 22
bpm 1.5
cpm 1.9545
noi 1

20 Functions

Rating   Name   Duplication   Size   Complexity  
A Form.clearFormErrors_ 0 6 2
A Form.onClick_ 0 13 1
A forms.js ➔ Form 0 8 1
A Form.fallbackSubmit_ 0 4 4
A Form.onChange_ 0 3 1
A Form.disableButton_ 0 3 1
A Form.getFieldErrorsEl_ 0 9 2
A Form.init_ 0 6 1
B Form.showFieldErrors_ 0 13 5
B Form.handleResponse_ 0 23 4
A GLSR.Forms 0 9 3
A Form.initStarRatings_ 0 7 1
A Form.enableButton_ 0 3 1
A Form.submitForm_ 0 7 1
A Form.getResultsEl_ 0 9 2
A Form.clearFieldError_ 0 9 3
A Form.getFormData_ 0 6 1
A Form.onSubmit_ 0 7 2
A Form.addRemoveClass_ 0 3 2
A Form.showResults_ 0 5 1

How to fix   Complexity   

Complexity

Complex classes like +/scripts/public/forms.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
/** global: CustomEvent, GLSR, HTMLFormElement, StarRating */
2
;(function() {
3
4
	'use strict';
5
6
	var Form = function( formEl, buttonEl ) { // HTMLElement, HTMLElement
7
		this.button = buttonEl;
8
		this.enableButton = this.enableButton_.bind( this );
9
		this.form = formEl;
10
		this.recaptcha = new GLSR.Recaptcha( this );
11
		this.submitForm = this.submitForm_.bind( this );
12
		this.init_();
13
	};
14
15
	Form.prototype = {
16
		config: {
17
			fieldErrorsClass: 'glsr-field-errors',
18
			fieldSelector: '.glsr-field',
19
			formMessagesClass: 'glsr-form-messages',
20
			hasErrorClass: 'glsr-has-error',
21
		},
22
23
		/** @return void */
24
		addRemoveClass_: function( el, classValue, bool ) { // HTMLElement, string, bool
25
			el.classList[bool ? 'add' : 'remove']( classValue );
26
		},
27
28
		/** @return void */
29
		clearFieldError_: function( el ) { // HTMLElement
30
			var fieldEl = el.closest( this.config.fieldSelector );
31
			if( fieldEl === null )return;
32
			fieldEl.classList.remove( this.config.hasErrorClass );
33
			var errorEl = fieldEl.querySelector( this.config.fieldErrorsSelector );
34
			if( errorEl !== null ) {
35
				errorEl.parentNode.removeChild( errorEl );
36
			}
37
		},
38
39
		/** @return void */
40
		clearFormErrors_: function() {
41
			this.getResultsEl_().innerHTML = '';
42
			for( var i = 0; i < this.form.length; i++ ) {
43
				this.clearFieldError_( this.form[i] );
44
			}
45
		},
46
47
		/** @return void */
48
		disableButton_: function() {
49
			this.button.setAttribute( 'disabled', '' );
50
		},
51
52
		/** @return void */
53
		enableButton_: function() {
54
			this.button.removeAttribute( 'disabled' );
55
		},
56
57
		/** @return void */
58
		fallbackSubmit_: function() {
59
			if( GLSR.Ajax.isFileAPISupported() && GLSR.Ajax.isFormDataSupported() && GLSR.Ajax.isUploadSupported() )return;
60
			this.form.submit();
61
		},
62
63
		/** @return void */
64
		handleResponse_: function( response ) { // object
65
			console.log( response );
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...
66
			if( response.recaptcha === true ) {
67
				console.log( 'executing recaptcha' );
68
				this.recaptcha.execute();
69
				return;
70
			}
71
			if( response.recaptcha === 'reset' ) {
72
				console.log( 'reseting failed recaptcha' );
73
				this.recaptcha.reset();
74
			}
75
			if( response.errors === false ) {
76
				console.log( 'reseting recaptcha' );
77
				this.recaptcha.reset();
78
				this.form.reset();
79
			}
80
			console.log( 'submission finished' );
81
			this.showFieldErrors_( response.errors );
82
			this.showResults_( response );
83
			this.enableButton_();
84
			response.form = this.form;
85
			document.dispatchEvent( new CustomEvent( 'site-reviews/after/submission', { detail: response }));
86
		},
87
88
		/** @return HTMLDivElement */
89
		getFieldErrorsEl_: function( fieldEl ) { // HTMLElement
90
			var errorsEl = fieldEl.querySelector( '.' + this.config.fieldErrorsClass );
91
			if( errorsEl === null ) {
92
				errorsEl = document.createElement( 'div' );
93
				errorsEl.setAttribute( 'class', this.config.fieldErrorsClass );
94
				fieldEl.appendChild( errorsEl );
95
			}
96
			return errorsEl;
97
		},
98
99
		/** @return object */
100
		getFormData_: function( recaptchaToken ) { // string|null
101
			recaptchaToken = recaptchaToken || '';
102
			var formData = new FormData( this.form );
103
			formData.append( 'g-recaptcha-response', recaptchaToken );
104
			return formData;
105
		},
106
107
		/** @return HTMLDivElement */
108
		getResultsEl_: function() {
109
			var resultsEl = this.form.querySelector( '.' + this.config.formMessagesClass );
110
			if( resultsEl === null ) {
111
				resultsEl = document.createElement( 'div' );
112
				resultsEl.setAttribute( 'class', this.config.formMessagesClass );
113
				this.button.parentNode.insertBefore( resultsEl, this.button.nextSibling );
114
			}
115
			return resultsEl;
116
		},
117
118
		/** @return void */
119
		init_: function() {
120
			this.button.addEventListener( 'click', this.onClick_.bind( this ));
121
			this.form.addEventListener( 'change', this.onChange_.bind( this ));
122
			this.form.addEventListener( 'submit', this.onSubmit_.bind( this ));
123
			this.initStarRatings_();
124
		},
125
126
		/** @return void */
127
		initStarRatings_: function() {
128
			new StarRating( 'select.glsr-star-rating', {
129
				clearable: false,
130
				showText: false,
131
				onClick: this.clearFieldError_.bind( this ),
132
			});
133
		},
134
135
		/** @return void */
136
		onChange_: function( ev ) { // Event
137
			this.clearFieldError_( ev.target );
138
		},
139
140
		/**
141
		 * This event method handles the mayhem caused by the invisible-recaptcha plugin
142
		 * and is triggered on the invisible-recaptcha callback
143
		 * @return void */
144
		onClick_: function() {
145
			var form = this;
146
			this.form.onsubmit = null;
147
			HTMLFormElement.prototype._submit = HTMLFormElement.prototype.submit;
148
			HTMLFormElement.prototype.submit = function() {
149
				var token = this.querySelector( '#g-recaptcha-response' );
150
				if( null !== token && this.querySelector( form.config.fieldSelector )) {
151
					form.submitForm_( token.value );
152
					return;
153
				}
154
				this._submit();
155
			};
156
		},
157
158
		/** @return void */
159
		onSubmit_: function( ev ) { // HTMLEvent
160
			if( this.form.classList.contains( 'no-ajax' ))return;
161
			ev.preventDefault();
162
			this.recaptcha.addListeners();
163
			this.clearFormErrors_();
164
			this.submitForm_();
165
		},
166
167
		/** @return void */
168
		showFieldErrors_: function( errors ) { // object
169
			if( !errors )return;
170
			var fieldEl, errorsEl;
171
			for( var error in errors ) {
172
				if( !errors.hasOwnProperty( error ))continue;
173
				fieldEl = this.form.querySelector( '[name=' + error + ']' ).closest( this.config.fieldSelector );
174
				fieldEl.classList.add( this.config.hasErrorClass );
175
				errorsEl = this.getFieldErrorsEl_( fieldEl );
176
				for( var i = 0; i < errors[error].errors.length; i++ ) {
177
					errorsEl.innerHTML += errors[error].errors[i];
178
				}
179
			}
180
		},
181
182
		/** @return void */
183
		showResults_: function( response ) { // object
184
			var resultsEl = this.getResultsEl_();
185
			this.addRemoveClass_( resultsEl, 'gslr-has-errors', !!response.errors );
186
			resultsEl.innerHTML = response.message;
187
		},
188
189
		/** @return void */
190
		submitForm_: function( recaptchaToken ) { // string|null
191
			this.disableButton_();
192
			this.fallbackSubmit_();
193
			GLSR.Ajax.post( this.getFormData_( recaptchaToken ), this.handleResponse_.bind( this ), {
194
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
195
			});
196
		},
197
	};
198
199
	GLSR.Forms = function() {
200
		this.nodeList = document.querySelectorAll( 'form.glsr-form' );
201
		this.forms = [];
202
		for( var i = 0; i < this.nodeList.length; i++ ) {
203
			var submitButton = this.nodeList[i].querySelector( '[type=submit]' );
204
			if( !submitButton )continue;
205
			this.forms.push( new Form( this.nodeList[i], submitButton ));
206
		}
207
	};
208
})();
209