Passed
Push — master ( ab24e8...554ad1 )
by Paul
04:45
created

Form.showFieldErrors_   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 12
rs 9.2
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 ) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
172
				fieldEl = this.form.querySelector( '[name=' + error + ']' ).closest( this.config.fieldSelector );
173
				fieldEl.classList.add( this.config.hasErrorClass );
174
				errorsEl = this.getFieldErrorsEl_( fieldEl );
175
				for( var i = 0; i < errors[error].errors.length; i++ ) {
176
					errorsEl.innerHTML += errors[error].errors[i];
177
				}
178
			}
179
		},
180
181
		/** @return void */
182
		showResults_: function( response ) { // object
183
			var resultsEl = this.getResultsEl_();
184
			this.addRemoveClass_( resultsEl, 'gslr-has-errors', !!response.errors );
185
			resultsEl.innerHTML = response.message;
186
		},
187
188
		/** @return void */
189
		submitForm_: function( recaptchaToken ) { // string|null
190
			this.disableButton_();
191
			this.fallbackSubmit_();
192
			GLSR.Ajax.post( this.getFormData_( recaptchaToken ), this.handleResponse_.bind( this ), {
193
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
194
			});
195
		},
196
	};
197
198
	GLSR.Forms = function() {
199
		this.nodeList = document.querySelectorAll( 'form.glsr-form' );
200
		this.forms = [];
201
		for( var i = 0; i < this.nodeList.length; i++ ) {
202
			var submitButton = this.nodeList[i].querySelector( '[type=submit]' );
203
			if( !submitButton )continue;
204
			this.forms.push( new Form( this.nodeList[i], submitButton ));
205
		}
206
	};
207
})();
208