Passed
Push — master ( 203c84...bd5512 )
by Paul
04:35
created

+/scripts/admin/shortcode.js   C

Complexity

Total Complexity 54
Complexity/F 2.16

Size

Lines of Code 235
Function Count 25

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
nc 32
dl 0
loc 235
rs 6.8539
c 1
b 0
f 0
wmc 54
mnd 2
bc 39
fnc 25
bpm 1.56
cpm 2.16
noi 0

19 Functions

Rating   Name   Duplication   Size   Complexity  
A GLSR.shortcode.onClose 0 4 2
A GLSR.shortcode.normalize 0 11 3
A GLSR.shortcode.init 0 7 1
A GLSR.shortcode.onToggle 0 4 2
A GLSR.shortcode.initQuicktagsEditor 0 15 2
A GLSR.shortcode.normalizeId 0 5 2
A GLSR.shortcode.responsePopup 0 11 1
A GLSR.shortcode.normalizeHide 0 9 4
A GLSR.shortcode.open 0 4 1
B GLSR.shortcode.validateAttributes 0 15 5
A GLSR.shortcode.responseButtons 0 14 1
A GLSR.shortcode.destroy 0 7 2
A GLSR.shortcode.response 0 16 4
A GLSR.shortcode.normalizeCount 0 5 3
A GLSR.shortcode.close 0 4 1
A GLSR.shortcode.initTinymceEditor 0 3 1
A GLSR.shortcode.request 0 7 1
A GLSR.shortcode.sendToEditor 0 10 4
A GLSR.shortcode.onTrigger 0 14 3

How to fix   Complexity   

Complexity

Complex classes like +/scripts/admin/shortcode.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: editor, GLSR, site_reviews, tinymce, x */
2
GLSR.shortcode = function( selector ) {
3
	this.el = document.querySelector( selector );
4
	if( !this.el )return;
5
	this.current = null; // GLSR.shortcode.current is used by scForm to trigger the correct popup
6
	this.editor = null;
7
	this.button = this.el.querySelector( 'button' );
8
	this.menuItems = this.el.querySelectorAll( '.mce-menu-item' );
9
	if( !this.button || !this.menuItems.length )return;
10
	this.create = function( editor_id ) {
11
		this.editor = tinymce.get( editor_id );
12
		if( !this.editor )return;
13
		this.request({
14
			action: 'mce-shortcode',
15
			nonce: site_reviews.mce_nonce,
16
			shortcode: this.current,
17
		});
18
	};
19
	this.init();
20
};
21
22
GLSR.shortcode.prototype = {
23
24
	attributes: {},
25
26
	hiddenKeys: [],
27
28
	hiddenFields: {
29
		site_reviews: ['author','avatar','date','excerpt','rating','response','title'],
30
		site_reviews_form: ['email','name','terms','title'],
31
		site_reviews_summary: ['bars','if_empty','rating','stars','summary'],
32
	},
33
34
	/** @return void */
35
	init: function() {
36
		document.addEventListener( 'click', this.onClose.bind( this ));
37
		this.button.addEventListener( 'click', this.onToggle.bind( this ));
38
		this.menuItems.forEach( function( item ) {
39
			item.addEventListener( 'click', this.onTrigger.bind( this ));
40
		}.bind( this ));
41
	},
42
43
	/** @return void */
44
	initTinymceEditor: function() {
45
		tinymce.execCommand( 'GLSR_Shortcode' );
46
	},
47
48
	/** @return void */
49
	initQuicktagsEditor: function() {
50
		if( x( '#scTemp' ).length ) {
51
			this.initTinymceEditor();
52
			return;
53
		}
54
		x( 'body' ).append( '<textarea id="scTemp" style="display:none;"/>' );
55
		tinymce.init({
56
			elements: 'scTemp',
57
			mode: 'exact',
58
			plugins: ['glsr_shortcode', 'wplink'],
59
		});
60
		setTimeout( function() {
61
			this.initTinymceEditor();
62
		}, 200 );
63
	},
64
65
	/** @return void */
66
	close: function() {
67
		x( this.button ).removeClass( 'active' );
68
		x( this.el ).find( '.glsr-mce-menu' ).hide();
69
	},
70
71
	/** @return void */
72
	destroy: function() {
73
		var tmp = x( '#scTemp' );
74
		if( tmp.length ) {
75
			tinymce.get( 'scTemp' ).remove();
76
			tmp.remove();
77
		}
78
	},
79
80
	/** @return void */
81
	normalize: function( attributes ) {
82
		this.attributes = attributes;
83
		this.hiddenKeys = [];
84
		for( var key in attributes ) {
85
			if( !attributes.hasOwnProperty( key ))continue;
86
			this.normalizeCount( key );
87
			this.normalizeHide( key );
88
			this.normalizeId( key );
89
		}
90
		this.attributes.hide = this.hiddenKeys.join( ',' );
91
	},
92
93
	/** @return void */
94
	normalizeCount: function( key ) {
95
		if( key === 'count' && !x.isNumeric( this.attributes[key] )) {
96
			this.attributes[key] = '';
97
		}
98
	},
99
100
	/** @return void */
101
	normalizeHide: function( key ) {
102
		if( !this.hiddenFields.hasOwnProperty( this.current ))return;
103
		var value = key.substring('hide_'.length);
104
		if( this.hiddenFields[this.current].indexOf( value ) === -1 )return;
105
		if( this.attributes[key] ) {
106
			this.hiddenKeys.push( value );
107
		}
108
		delete this.attributes[key];
109
	},
110
111
	/** @return void */
112
	normalizeId: function( key ) {
113
		if( key === 'id' ) {
114
			this.attributes[key] = (+new Date()).toString(36);
115
		}
116
	},
117
118
	/** @return void */
119
	onClose: function( ev ) {
120
		if( x( ev.target ).closest( x( this.el )).length )return;
121
		this.close();
122
	},
123
124
	/** @return void */
125
	onToggle: function( ev ) {
126
		ev.preventDefault();
127
		this[ev.target.classList.contains( 'active' ) ? 'close' : 'open']();
128
	},
129
130
	/** @return void */
131
	onTrigger: function( ev ) {
132
		ev.preventDefault();
133
		this.current = ev.target.dataset.shortcode;
134
		if( !this.current )return;
135
		if( tinymce.get( window.wpActiveEditor )) {
136
			this.initTinymceEditor();
137
		}
138
		else {
139
			this.initQuicktagsEditor();
140
		}
141
		setTimeout( function() {
142
			this.close();
143
		}.bind( this ), 100 );
144
	},
145
146
	/** @return void */
147
	open: function() {
148
		x( this.button ).addClass( 'active' );
149
		x( this.el ).find( '.glsr-mce-menu' ).show();
150
	},
151
152
	/** @return void */
153
	request: function( request ) {
154
		var data = {
155
			action: site_reviews.action,
156
			request: request,
157
		};
158
		x.post( site_reviews.ajaxurl, data, this.response.bind( this ));
159
	},
160
161
	/** @return void */
162
	response: function( response ) {
163
		if( !response.body )return;
164
		if( response.body.length === 0 ) {
165
			window.send_to_editor( '[' + response.shortcode + ']' );
166
			this.destroy();
167
			return;
168
		}
169
		var popup = this.responsePopup( response );
170
		// Change the buttons if server-side validation failed
171
		if( response.ok.constructor === Array ) {
172
			popup.buttons[0].text = response.ok[0];
173
			popup.buttons[0].onclick = 'close';
174
			delete popup.buttons[1];
175
		}
176
		this.editor.windowManager.open( popup );
177
	},
178
179
	/** @return array */
180
	responseButtons: function( response ) {
181
		return [{
182
			text: response.ok,
183
			classes: 'btn glsr-btn primary',
184
			onclick: function() {
185
				var currentWindow = this.editor.windowManager.getWindows()[0];
186
				if( !this.validateAttributes( currentWindow ) )return;
187
				currentWindow.submit();
188
			}.bind( this ),
189
		},{
190
			text: response.close,
191
			onclick: 'close'
192
		}];
193
	},
194
195
	/** @return object */
196
	responsePopup: function( response ) {
197
		return {
198
			title: response.title,
199
			body: response.body,
200
			classes: 'glsr-mce-popup',
201
			minWidth: 320,
202
			buttons: this.responseButtons( response ),
203
			onsubmit: this.sendToEditor.bind( this, response ),
204
			onclose: this.destroy.bind( this ),
205
		};
206
	},
207
208
	/** @return void */
209
	sendToEditor: function( response, ev ) {
210
		var attributes = '';
211
		this.normalize( ev.data );
212
		for( var key in this.attributes ) {
213
			if( this.attributes.hasOwnProperty( key ) && this.attributes[key] !== '' ) {
214
				attributes += ' ' + key + '="' + this.attributes[key] + '"';
215
			}
216
		}
217
		window.send_to_editor( '[' + response.shortcode + attributes + ']' );
218
	},
219
220
	/** @return bool */
221
	validateAttributes: function( currentWindow ) {
222
		var field;
223
		var is_valid = true;
224
		var requiredAttributes = site_reviews.shortcodes[this.current];
225
		for( var id in requiredAttributes ) {
226
			if( !requiredAttributes.hasOwnProperty( id ))continue;
227
			field = currentWindow.find( '#' + id )[0];
228
			if( typeof field !== 'undefined' && field.state.data.value === '' ) {
229
				is_valid = false;
230
				alert( requiredAttributes[id] );
231
				break;
232
			}
233
		}
234
		return is_valid;
235
	},
236
};
237