Passed
Push — master ( 57a609...ec0975 )
by Chris
04:27
created

vendor/CMB2/js/cmb2-wysiwyg.js   B

Complexity

Total Complexity 43
Complexity/F 2.26

Size

Lines of Code 342
Function Count 19

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 128
c 0
b 0
f 0
dl 0
loc 342
rs 8.96
wmc 43
mnd 24
bc 24
fnc 19
bpm 1.263
cpm 2.2631
noi 9

How to fix   Complexity   

Complexity

Complex classes like vendor/CMB2/js/cmb2-wysiwyg.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
/**
2
 * Used for WYSIWYG logic
3
 */
4
window.CMB2 = window.CMB2 || {};
5
window.CMB2.wysiwyg = window.CMB2.wysiwyg || {};
6
7
( function(window, document, $, cmb, wysiwyg, undefined ) {
8
	'use strict';
9
10
	// Private variables
11
	var toBeDestroyed = [];
12
	var toBeInitialized = [];
13
	var all = wysiwyg.all = {};
14
15
	// Private functions
16
17
	/**
18
	 * Initializes any editors that weren't initialized because they didn't exist yet.
19
	 *
20
	 * @since  2.2.3
21
	 *
22
	 * @return {void}
23
	 */
24
	function delayedInit() {
25
26
		// Don't initialize until they've all been destroyed.
27
		if ( 0 === toBeDestroyed.length ) {
28
			toBeInitialized.forEach( function ( toInit ) {
29
				toBeInitialized.splice( toBeInitialized.indexOf( toInit ), 1 );
30
				wysiwyg.init.apply( wysiwyg, toInit );
31
			} );
32
		} else {
33
			window.setTimeout( delayedInit, 100 );
34
		}
35
	}
36
37
	/**
38
	 * Destroys any editors that weren't destroyed because they didn't exist yet.
39
	 *
40
	 * @since  2.2.3
41
	 *
42
	 * @return {void}
43
	 */
44
	function delayedDestroy() {
45
		toBeDestroyed.forEach( function( id ) {
46
			toBeDestroyed.splice( toBeDestroyed.indexOf( id ), 1 );
47
			wysiwyg.destroy( id );
48
		} );
49
	}
50
51
	/**
52
	 * Gets the option data for a group (and initializes that data if it doesn't exist).
53
	 *
54
	 * @since  2.2.3
55
	 *
56
	 * @param  {object} data The group/field data.
57
	 *
58
	 * @return {object}      Options data object for a group.
59
	 */
60
	function getGroupData( data ) {
61
		var groupid = data.groupid;
62
		var fieldid = data.fieldid;
63
64
		if ( ! all[ groupid ] || ! all[ groupid ][ fieldid ] ) {
65
			all[ groupid ] = all[ groupid ] || {};
66
			all[ groupid ][ fieldid ] = {
67
				template : wp.template( 'cmb2-wysiwyg-' + groupid + '-' + fieldid ),
68
				defaults : {
69
70
					// Get the data from the template-wysiwyg initiation.
71
					mce : $.extend( {}, tinyMCEPreInit.mceInit[ 'cmb2_i_' + groupid + fieldid ] ),
72
					qt  : $.extend( {}, tinyMCEPreInit.qtInit[ 'cmb2_i_' + groupid + fieldid ] )
73
				}
74
			};
75
			// This is the template-wysiwyg data, and we do not want that to be initiated.
76
			delete tinyMCEPreInit.mceInit[ 'cmb2_i_' + groupid + fieldid ];
77
			delete tinyMCEPreInit.qtInit[ 'cmb2_i_' + groupid + fieldid ];
78
		}
79
80
		return all[ groupid ][ fieldid ];
81
	}
82
83
	/**
84
	 * Initiates the tinyMCEPreInit options for a wysiwyg editor instance.
85
	 *
86
	 * @since  2.2.3
87
	 *
88
	 * @param  {object} options Options data object for the wysiwyg editor instance.
89
	 *
90
	 * @return {void}
91
	 */
92
	function initOptions( options ) {
93
		var nameRegex = new RegExp( 'cmb2_n_' + options.groupid + options.fieldid, 'g' );
94
		var idRegex   = new RegExp( 'cmb2_i_' + options.groupid + options.fieldid, 'g' );
95
		var prop, newSettings, newQTS;
96
97
		// If no settings for this field. Clone from placeholder.
98
		if ( 'undefined' === typeof( tinyMCEPreInit.mceInit[ options.id ] ) ) {
99
			newSettings = $.extend( {}, options.defaults.mce );
100
			for ( prop in newSettings ) {
101
				if ( 'string' === typeof( newSettings[ prop ] ) ) {
102
					newSettings[ prop ] = newSettings[ prop ]
103
						.replace( idRegex, options.id )
104
						.replace( nameRegex, options.name );
105
				}
106
			}
107
			tinyMCEPreInit.mceInit[ options.id ] = newSettings;
108
		}
109
110
		// If no Quicktag settings for this field. Clone from placeholder.
111
		if ( 'undefined' === typeof( tinyMCEPreInit.qtInit[ options.id ] ) ) {
112
			newQTS = $.extend( {}, options.defaults.qt );
113
			for ( prop in newQTS ) {
114
				if ( 'string' === typeof( newQTS[ prop ] ) ) {
115
					newQTS[ prop ] = newQTS[ prop ]
116
						.replace( idRegex, options.id )
117
						.replace( nameRegex, options.name );
118
				}
119
			}
120
			tinyMCEPreInit.qtInit[ options.id ] = newQTS;
121
		}
122
	}
123
124
	/**
125
	 * Initializes all group wysiwyg editors. Hooked to cmb_init.
126
	 *
127
	 * @since  2.2.3
128
	 *
129
	 * @return {void}
130
	 */
131
	wysiwyg.initAll = function() {
132
		var $this,data,initiated;
133
134
		$( '.cmb2-wysiwyg-placeholder' ).each( function() {
135
			$this = $( this );
136
			data  = $this.data();
137
138
			if ( data.groupid ) {
139
140
				data.id    = $this.attr( 'id' );
141
				data.name  = $this.attr( 'name' );
142
				data.value = $this.val();
143
144
				wysiwyg.init( $this, data, false );
145
				initiated = true;
146
			}
147
		} );
148
149
		if ( true === initiated ) {
150
			if ( 'undefined' !== typeof window.QTags ) {
151
				window.QTags._buttonsInit();
152
			}
153
154
			// Hook in our event callbacks.
155
			$( document )
156
				.on( 'cmb2_add_row', wysiwyg.addRow )
157
				.on( 'cmb2_remove_group_row_start', wysiwyg.destroyRowEditors )
158
				.on( 'cmb2_shift_rows_start', wysiwyg.shiftStart )
159
				.on( 'cmb2_shift_rows_complete', wysiwyg.shiftComplete );
160
		}
161
	};
162
163
	/**
164
	 * Initiates wysiwyg editors in a new group row. Hooked to cmb2_add_row.
165
	 *
166
	 * @since  2.2.3
167
	 *
168
	 * @param  {object} evt A jQuery-normalized event object.
169
	 * @param  {object} $row A jQuery dom element object for the group row.
170
	 *
171
	 * @return {void}
172
	 */
173
	wysiwyg.addRow = function( evt, $row ) {
174
		wysiwyg.initRow( $row, evt );
175
	};
176
177
	/**
178
	 * Destroys wysiwyg editors in a group row when that row is removed. Hooked to cmb2_remove_group_row_start.
179
	 *
180
	 * @since  2.2.3
181
	 *
182
	 * @param  {object} evt A jQuery-normalized event object.
183
	 * @param  {object} $btn A jQuery dom element object for the remove-row button.
184
	 *
185
	 * @return {void}
186
	 */
187
	wysiwyg.destroyRowEditors = function( evt, $btn ) {
188
		wysiwyg.destroy( $btn.parents( '.cmb-repeatable-grouping' ).find( '.wp-editor-area' ).attr( 'id' ) );
189
	};
190
191
	/**
192
	 * When a row-shift starts, we need to destroy the wysiwyg editors for the group-rows being shuffled.
193
	 *
194
	 * @since  2.2.3
195
	 *
196
	 * @param  {object} evt   A jQuery-normalized event object.
197
	 * @param  {object} $btn  A jQuery dom element object for the remove-row button.
198
	 * @param  {object} $from A jQuery dom element object for the row being shifted from.
199
	 * @param  {object} $to   A jQuery dom element object for the row being shifted to.
200
	 *
201
	 * @return {void}
202
	 */
203
	wysiwyg.shiftStart = function( evt, $btn, $from, $to ) {
204
		$from.add( $to ).find( '.wp-editor-wrap textarea' ).each( function() {
205
			wysiwyg.destroy( $( this ).attr( 'id' ) );
206
		} );
207
	};
208
209
	/**
210
	 * When a row-shift completes, we need to re-init the wysiwyg editors for the group-rows being shuffled.
211
	 *
212
	 * @since  2.2.3
213
	 *
214
	 * @param  {object} evt   A jQuery-normalized event object.
215
	 * @param  {object} $btn  A jQuery dom element object for the remove-row button.
216
	 * @param  {object} $from A jQuery dom element object for the row being shifted from.
217
	 * @param  {object} $to   A jQuery dom element object for the row being shifted to.
218
	 *
219
	 * @return {void}
220
	 */
221
	wysiwyg.shiftComplete = function( evt, $btn, $from, $to ) {
222
		$from.add( $to ).each( function() {
223
			wysiwyg.initRow( $( this ), evt );
224
		} );
225
	};
226
227
	/**
228
	 * Initializes editors for a new CMB row.
229
	 *
230
	 * @since  2.2.3
231
	 *
232
	 * @param  {object} $row A jQuery dom element object for the group row.
233
	 * @param  {object} evt  A jQuery-normalized event object.
234
	 *
235
	 * @return {void}
236
	 */
237
	wysiwyg.initRow = function( $row, evt ) {
238
		var $toReplace, data, defVal;
239
240
		$row.find( '.cmb2-wysiwyg-inner-wrap' ).each( function() {
241
			$toReplace    = $( this );
242
			data          = $toReplace.data();
243
			defVal        = cmb.getFieldArg( data.hash, 'default', '' );
244
			defVal        = 'undefined' !== typeof defVal && false !== defVal ? defVal : '';
245
246
			data.iterator = $row.data( 'iterator' );
247
			data.fieldid  = data.id;
248
			data.id       = data.groupid + '_' + data.iterator + '_' + data.fieldid;
249
			data.name     = data.groupid + '[' + data.iterator + '][' + data.fieldid + ']';
250
			data.value    = 'cmb2_add_row' !== evt.type && $toReplace.find( '.wp-editor-area' ).length ? $toReplace.find( '.wp-editor-area' ).val() : defVal;
251
252
			// The destroys might not have happened yet.  Don't init until they have.
253
			if ( 0 === toBeDestroyed.length ) {
254
255
				wysiwyg.init( $toReplace, data );
256
257
			} else {
258
				toBeInitialized.push( [$toReplace, data] );
259
				window.setTimeout( delayedInit, 100 );
260
			}
261
		} );
262
263
	};
264
265
	/**
266
	 * Initiates a wysiwyg editor instance and replaces the passed dom element w/ the editor html.
267
	 *
268
	 * @since  2.2.3
269
	 *
270
	 * @param  {object} $toReplace A jQuery dom element which will be replaced with the wysiwyg editor.
271
	 * @param  {object} data        Data used to initate the editor.
272
	 * @param  {bool}   buttonsInit Whether to run QTags._buttonsInit()
273
	 *
274
	 * @return {void}
275
	 */
276
	wysiwyg.init = function( $toReplace, data, buttonsInit ) {
277
		if ( ! data.groupid ) {
278
			return false;
279
		}
280
281
		var mceActive = cmb.canTinyMCE();
282
		var qtActive = 'function' === typeof window.quicktags;
283
		$.extend( data, getGroupData( data ) );
284
285
		initOptions( data );
286
287
		$toReplace.replaceWith( data.template( data ) );
288
289
		if ( mceActive ) {
290
			window.tinyMCE.init( tinyMCEPreInit.mceInit[ data.id ] );
291
		}
292
293
		if ( qtActive ) {
294
			window.quicktags( tinyMCEPreInit.qtInit[ data.id ] );
295
		}
296
297
		if ( mceActive ) {
298
			$( document.getElementById( data.id ) ).parents( '.wp-editor-wrap' ).removeClass( 'html-active' ).addClass( 'tmce-active' );
299
		}
300
301
		if ( false !== buttonsInit && 'undefined' !== typeof window.QTags ) {
302
			window.QTags._buttonsInit();
303
		}
304
305
	};
306
307
	/**
308
	 * Destroys a wysiwyg editor instance.
309
	 *
310
	 * @since  2.2.3
311
	 *
312
	 * @param  {string} id Editor id.
313
	 *
314
	 * @return {void}
315
	 */
316
	wysiwyg.destroy = function( id ) {
317
		if ( ! cmb.canTinyMCE() ) {
318
			// Nothing to see here.
319
			return;
320
		}
321
322
		// The editor might not be initialized yet.  But we need to destroy it once it is.
323
		var editor = tinyMCE.get( id );
324
325
		if ( editor !== null && typeof( editor ) !== 'undefined' ) {
326
			editor.destroy();
327
328
			if ( 'undefined' === typeof( tinyMCEPreInit.mceInit[ id ] ) ) {
329
				delete tinyMCEPreInit.mceInit[ id ];
330
			}
331
332
			if ( 'undefined' === typeof( tinyMCEPreInit.qtInit[ id ] ) ) {
333
				delete tinyMCEPreInit.qtInit[ id ];
334
			}
335
336
		} else if ( -1 === toBeDestroyed.indexOf( id ) ) {
337
			toBeDestroyed.push( id );
338
			window.setTimeout( delayedDestroy, 100 );
339
		}
340
	};
341
342
	// Hook in our event callbacks.
343
	$( document ).on( 'cmb_init', wysiwyg.initAll );
344
345
} )( window, document, jQuery, window.CMB2, window.CMB2.wysiwyg );
346