Completed
Pull Request — develop (#1353)
by
unknown
02:53
created

Kirki_Modules_PostMessage::script_var_typography()   C

Complexity

Conditions 14
Paths 216

Size

Total Lines 53
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 35
nc 216
nop 1
dl 0
loc 53
rs 5.3902
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
<?php
2
/**
3
 * Automatic postMessage scripts calculation for Kirki controls.
4
 *
5
 * @package     Kirki
6
 * @category    Modules
7
 * @author      Aristeides Stathopoulos
8
 * @copyright   Copyright (c) 2017, Aristeides Stathopoulos
9
 * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
10
 * @since       3.0.0
11
 */
12
13
// Exit if accessed directly.
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit;
16
}
17
18
/**
19
 * Adds styles to the customizer.
20
 */
21
class Kirki_Modules_PostMessage {
22
	/**
23
	 * The object instance.
24
	 *
25
	 * @static
26
	 * @access private
27
	 * @since 3.0.0
28
	 * @var object
29
	 */
30
	private static $instance;
31
32
	/**
33
	 * The script.
34
	 *
35
	 * @access protected
36
	 * @since 3.0.0
37
	 * @var string
38
	 */
39
	protected $script = '';
40
41
	/**
42
	 * Constructor.
43
	 *
44
	 * @access protected
45
	 * @since 3.0.0
46
	 */
47
	protected function __construct() {
48
		add_action( 'customize_preview_init', array( $this, 'postmessage' ) );
49
	}
50
51
	/**
52
	 * Gets an instance of this object.
53
	 * Prevents duplicate instances which avoid artefacts and improves performance.
54
	 *
55
	 * @static
56
	 * @access public
57
	 * @since 3.0.0
58
	 * @return object
59
	 */
60
	public static function get_instance() {
61
		if ( ! self::$instance ) {
62
			self::$instance = new self();
63
		}
64
		return self::$instance;
65
	}
66
67
	/**
68
	 * Enqueues the postMessage script
69
	 * and adds variables to it using the wp_localize_script function.
70
	 * The rest is handled via JS.
71
	 */
72
	public function postmessage() {
73
74
		wp_enqueue_script( 'kirki_auto_postmessage', trailingslashit( Kirki::$url ) . 'modules/postmessage/postmessage.js', array( 'jquery', 'customize-preview' ), false, true );
75
		$fields = Kirki::$fields;
76
		foreach ( $fields as $field ) {
77
			if ( isset( $field['transport'] ) && 'postMessage' === $field['transport'] && isset( $field['js_vars'] ) && ! empty( $field['js_vars'] ) && is_array( $field['js_vars'] ) && isset( $field['settings'] ) ) {
78
				$this->script .= $this->script( $field );
79
			}
80
		}
81
		$this->script = apply_filters( 'kirki/postmessage/script', $this->script );
82
		wp_add_inline_script( 'kirki_auto_postmessage', $this->script, 'after' );
83
84
	}
85
86
	/**
87
	 * Generates script for a single field.
88
	 *
89
	 * @access protected
90
	 * @since 3.0.0
91
	 * @param array $args The arguments.
92
	 */
93
	protected function script( $args ) {
94
95
		$script = 'wp.customize(\'' . $args['settings'] . '\',function(value){value.bind(function(newval){';
96
		// append unique style tag if not exist
97
		// The style ID.
98
		$style_id = 'kirki-postmessage-' . str_replace( array( '[', ']' ), '', $args['settings'] );
99
		$script .= 'if(null===document.getElementById(\'' . $style_id . '\')||\'undefined\'===typeof document.getElementById(\'' . $style_id . '\')){jQuery(\'head\').append(\'<style id="' . $style_id . '"></style>\');}';
100
101
		// Add anything we need before the main script.
102
		$script .= $this->before_script( $args );
103
104
		$field = array(
105
			'scripts' => array(),
106
		);
107
		// Loop through the js_vars and generate the script.
108
		foreach ( $args['js_vars'] as $key => $js_var ) {
109
			if ( isset( $js_var['function'] ) && 'html' === $js_var['function'] ) {
110
				$script .= $this->script_html_var( $js_var );
111
				continue;
112
			}
113
			$js_var['index_key'] = $key;
114
			$callback = $this->get_callback( $args );
115
			if ( is_callable( $callback ) ) {
116
				$field['scripts'][ $key ] = call_user_func_array( $callback, array( $js_var, $args ) );
117
				continue;
118
			}
119
			$field['scripts'][ $key ] = $this->script_var( $js_var );
120
		}
121
		$combo_extra_script = '';
122
		$combo_css_script   = '';
123
		foreach ( $field['scripts'] as $script_array ) {
124
			$combo_extra_script .= $script_array['script'];
125
			$combo_css_script   .= ( 'css' !== $combo_css_script ) ? $script_array['css'] : '';
126
		}
127
		$text = ( 'css' === $combo_css_script ) ? 'css' : '\'' . $combo_css_script . '\'';
128
		$script .= $combo_extra_script . 'jQuery(\'#' . $style_id . '\').text(' . $text . ');';
129
		$script .= 'jQuery(\'#' . $style_id . '\').appendTo(\'head\');';
130
		$script .= '});});';
131
		return $script;
132
	}
133
134
	/**
135
	 * Generates script for a single js_var when using "html" as function.
136
	 *
137
	 * @access protected
138
	 * @since 3.0.0
139
	 * @param array $args  The arguments for this js_var.
140
	 */
141
	protected function script_html_var( $args ) {
142
143
		$script  = ( isset( $args['choice'] ) ) ? 'newval=newval[\'' . $args['choice'] . '\'];' : '';
144
		$script .= 'jQuery(\'' . $args['element'] . '\').html(newval);';
145
		if ( isset( $args['attr'] ) ) {
146
			$script = 'jQuery(\'' . $args['element'] . '\').attr(\'' . $args['attr'] . '\',newval);';
147
		}
148
		return $script;
149
	}
150
151
	/**
152
	 * Generates script for a single js_var.
153
	 *
154
	 * @access protected
155
	 * @since 3.0.0
156
	 * @param array $args  The arguments for this js_var.
157
	 */
158
	protected function script_var( $args ) {
159
		$script = '';
160
		$property_script = '';
161
162
		$value_key = 'newval' . $args['index_key'];
163
		$property_script .= $value_key . '=newval;';
164
165
		$args = $this->get_args( $args );
166
167
		// Apply callback to the value if a callback is defined.
168
		if ( ! empty( $args['js_callback'][0] ) ) {
169
			$script .= $value_key . '=' . $args['js_callback'][0] . '(' . $value_key . ',' . $args['js_callback'][1] . ');';
170
		}
171
172
		// Apply the value_pattern.
173
		if ( '' !== $args['value_pattern'] ) {
174
			$script .= $this->value_pattern_replacements( $value_key, $args );
175
		}
176
177
		// Tweak to add url() for background-images.
178
		if ( 'background-image' === $args['property'] && ( ! isset( $args['value_pattern'] ) || false === strpos( $args['value_pattern'], 'gradient' ) ) ) {
179
			$script .= 'if(-1===' . $value_key . '.indexOf(\'url(\')){' . $value_key . '=\'url("\'+' . $value_key . '+\'");}';
180
		}
181
182
		// Apply prefix.
183
		$value = $value_key;
184
		if ( '' !== $args['prefix'] ) {
185
			$value = $args['prefix'] . '+' . $value_key;
186
		}
187
		$css = $args['element'] . '{' . $args['property'] . ':\'+' . $value . '+\'' . $args['units'] . $args['suffix'] . ';}';
188
		if ( isset( $args['media_query'] ) ) {
189
			$css = $args['media_query'] . '{' . $css . '}';
190
		}
191
		return array(
192
			'script' => $property_script . $script,
193
			'css'    => $css,
194
		);
195
	}
196
197
	/**
198
	 * Processes script generation for fields that save an array.
199
	 *
200
	 * @access protected
201
	 * @since 3.0.0
202
	 * @param array $args  The arguments for this js_var.
203
	 */
204
	protected function script_var_array( $args ) {
205
206
		$script = ( 0 === $args['index_key'] ) ? 'css=\'\';' : '';
207
		$property_script = '';
208
209
		// Define choice.
210
		$choice  = ( isset( $args['choice'] ) && '' !== $args['choice'] ) ? $args['choice'] : '';
211
212
		$value_key = 'newval' . $args['index_key'];
213
		$property_script .= $value_key . '=newval;';
214
215
		$args = $this->get_args( $args );
216
217
		// Apply callback to the value if a callback is defined.
218
		if ( ! empty( $args['js_callback'][0] ) ) {
219
			$script .= $value_key . '=' . $args['js_callback'][0] . '(' . $value_key . ',' . $args['js_callback'][1] . ');';
220
		}
221
		$script .= '_.each(' . $value_key . ', function(subValue,subKey){';
222
223
		// Apply the value_pattern.
224
		if ( '' !== $args['value_pattern'] ) {
225
			$script .= $this->value_pattern_replacements( 'subValue', $args );
226
		}
227
228
		// Tweak to add url() for background-images.
229
		if ( '' === $choice || 'background-image' === $choice ) {
230
			$script .= 'if(\'background-image\'===\'' . $args['property'] . '\'||\'background-image\'===subKey){';
231
			$script .= 'if(-1===subValue.indexOf(\'url(\')){subValue=\'url("\'+subValue+\'")\';}';
232
			$script .= '}';
233
		}
234
235
		// Apply prefix.
236
		$value = $value_key;
237
		if ( '' !== $args['prefix'] ) {
238
			$value = '\'' . $args['prefix'] . '\'+subValue';
239
		}
240
241
		// Mostly used for padding, margin & position properties.
242
		$direction_script  = 'if(_.contains([\'top\',\'bottom\',\'left\',\'right\'],subKey)){';
243
		$direction_script .= 'css+=\'' . $args['element'] . '{' . $args['property'] . '-\'+subKey+\':\'+subValue+\'' . $args['units'] . $args['suffix'] . ';}\';}';
244
		// Allows us to apply this just for a specific choice in the array of the values.
245
		if ( '' !== $choice ) {
246
			$choice_is_direction = ( false !== strpos( $choice, 'top' ) || false !== strpos( $choice, 'bottom' ) || false !== strpos( $choice, 'left' ) || false !== strpos( $choice, 'right' ) );
247
			$script .= 'if(\'' . $choice . '\'===subKey){';
248
			$script .= ( $choice_is_direction ) ? $direction_script . 'else{' : '';
249
			$script .= 'css+=\'' . $args['element'] . '{' . $args['property'] . ':\'+subValue+\';}\';';
250
			$script .= ( $choice_is_direction ) ? '}' : '';
251
			$script .= '}';
252
		} else {
253
			$script .= $direction_script . 'else{';
254
255
			// This is where most object-based fields will go.
256
			$script .= 'css+=\'' . $args['element'] . '{\'+subKey+\':\'+subValue+\'' . $args['units'] . $args['suffix'] . ';}\';';
257
			$script .= '}';
258
		}
259
		$script .= '});';
260
261
		if ( isset( $args['media_query'] ) ) {
262
			$script .= 'css=\'' . $args['media_query'] . '{\'+css+\'}\';';
263
		}
264
265
		return array(
266
			'script' => $property_script . $script,
267
			'css'    => 'css',
268
		);
269
	}
270
271
	/**
272
	 * Processes script generation for typography fields.
273
	 *
274
	 * @access protected
275
	 * @since 3.0.0
276
	 * @param array $args  The arguments for this js_var.
277
	 */
278
	protected function script_var_typography( $args ) {
279
280
		$script = '';
281
		$css    = '';
282
283
		// Load the font using WenFontloader.
284
		// This is a bit ugly because wp_add_inline_script doesn't allow adding <script> directly.
285
		$webfont_loader = 'sc=\'a\';jQuery(\'head\').append(sc.replace(\'a\',\'<\')+\'script>if(!_.isUndefined(WebFont)){WebFont.load({google:{families:["\'+fontFamily.replace( /\"/g, \'&quot;\' )+\':\'+variant+subsetsString+\'"]}});}\'+sc.replace(\'a\',\'<\')+\'/script>\');';
286
287
		// Add the css.
288
		$css_build_array = array(
289
			'font-family'    => 'fontFamily',
290
			'font-size'      => 'fontSize',
291
			'line-height'    => 'lineHeight',
292
			'letter-spacing' => 'letterSpacing',
293
			'word-spacing'   => 'wordSpacing',
294
			'text-align'     => 'textAlign',
295
			'text-transform' => 'textTransform',
296
			'color'          => 'color',
297
			'font-weight'    => 'fontWeight',
298
			'font-style'     => 'fontStyle',
299
		);
300
		$choice_condition = ( isset( $args['choice'] ) && '' !== $args['choice'] && isset( $css_build_array[ $args['choice'] ] ) );
301
		$script .= ( ! $choice_condition ) ? $webfont_loader : '';
302
		foreach ( $css_build_array as $property => $var ) {
303
			if ( $choice_condition && $property !== $args['choice'] ) {
304
				continue;
305
			}
306
			$script .= ( $choice_condition && 'font-family' === $args['choice'] ) ? $webfont_loader : '';
307
308
			if ( ! isset( $args['choice'] ) || 'font-family' === $args['choice'] ) {
309
				$css .= 'fontFamilyCSS=fontFamily;if(0<fontFamily.indexOf(\' \')&&-1===fontFamily.indexOf(\'"\')){fontFamilyCSS=\'"\'+fontFamily+\'"\';}';
310
				$var = 'fontFamilyCSS';
311
			}
312
			
313
			if ( isset( $args['element'] ) && is_array( $args['element'] ) ) {
314
				$args['element'] = array_unique( $args['element'] );
315
				sort( $args['element'] );
316
				$args['element'] = implode( ',', $args['element'] );
317
			}
318
			
319
			$css .= 'css+=(\'\'!==' . $var . ')?\'' . $args['element'] . '\'+\'{' . $property . ':\'+' . $var . '+\';}\':\'\';';
320
		}
321
322
		$script .= $css;
323
		if ( isset( $args['media_query'] ) ) {
324
			$script .= 'css=\'' . $args['media_query'] . '{\'+css+\'}\';';
325
		}
326
		return array(
327
			'script' => $script,
328
			'css'    => 'css',
329
		);
330
	}
331
332
	/**
333
	 * Adds anything we need before the main script.
334
	 *
335
	 * @access private
336
	 * @since 3.0.0
337
	 * @param array $args The field args.
338
	 * @return string;
0 ignored issues
show
Documentation introduced by
The doc-type string; could not be parsed: Expected "|" or "end of type", but got ";" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
339
	 */
340
	private function before_script( $args ) {
341
342
		$script = '';
343
344
		if ( isset( $args['type'] ) ) {
345
			switch ( $args['type'] ) {
346
				case 'kirki-typography':
347
					$script .= 'fontFamily=(_.isUndefined(newval[\'font-family\']))?\'\':newval[\'font-family\'];';
348
					$script .= 'variant=(_.isUndefined(newval.variant))?\'400\':newval.variant;';
349
					$script .= 'subsets=(_.isUndefined(newval.subsets))?[]:newval.subsets;';
350
					$script .= 'subsetsString=(_.isObject(newval.subsets))?\':\'+newval.subsets.join(\',\'):\'\';';
351
					$script .= 'fontSize=(_.isUndefined(newval[\'font-size\']))?\'\':newval[\'font-size\'];';
352
					$script .= 'lineHeight=(_.isUndefined(newval[\'line-height\']))?\'\':newval[\'line-height\'];';
353
					$script .= 'letterSpacing=(_.isUndefined(newval[\'letter-spacing\']))?\'\':newval[\'letter-spacing\'];';
354
					$script .= 'wordSpacing=(_.isUndefined(newval[\'word-spacing\']))?\'\':newval[\'word-spacing\'];';
355
					$script .= 'textAlign=(_.isUndefined(newval[\'text-align\']))?\'\':newval[\'text-align\'];';
356
					$script .= 'textTransform=(_.isUndefined(newval[\'text-transform\']))?\'\':newval[\'text-transform\'];';
357
					$script .= 'color=(_.isUndefined(newval.color))?\'\':newval.color;';
358
359
					$script .= 'fw=(!_.isString(newval.variant))?\'400\':newval.variant.match(/\d/g);';
360
					$script .= 'fontWeight=(!_.isObject(fw))?400:fw.join(\'\');';
361
					$script .= 'fontStyle=(-1!==variant.indexOf(\'italic\'))?\'italic\':\'normal\';';
362
					$script .= 'css=\'\';';
363
					break;
364
			}
365
		}
366
		return $script;
367
	}
368
369
	/**
370
	 * Sanitizes the arguments and makes sure they are all there.
371
	 *
372
	 * @access private
373
	 * @since 3.0.0
374
	 * @param array $args The arguments.
375
	 * @return array
376
	 */
377
	private function get_args( $args ) {
378
379
		// Make sure everything is defined to avoid "undefined index" errors.
380
		$args = wp_parse_args( $args, array(
381
			'element'       => '',
382
			'property'      => '',
383
			'prefix'        => '',
384
			'suffix'        => '',
385
			'units'         => '',
386
			'js_callback'   => array( '', '' ),
387
			'value_pattern' => '',
388
		));
389
390
		// Element should be a string.
391
		if ( is_array( $args['element'] ) ) {
392
			$args['element'] = implode( ',', $args['element'] );
393
		}
394
395
		// Make sure arguments that are passed-on to callbacks are strings.
396
		if ( is_array( $args['js_callback'] ) && isset( $args['js_callback'][1] ) && is_array( $args['js_callback'][1] ) ) {
397
			$args['js_callback'][1] = wp_json_encode( $args['js_callback'][1] );
398
		}
399
		return $args;
400
401
	}
402
403
	/**
404
	 * Returns script for value_pattern & replacements.
405
	 *
406
	 * @access private
407
	 * @since 3.0.0
408
	 * @param string $value   The value placeholder.
409
	 * @param array  $js_vars The js_vars argument.
410
	 * @return string         The script.
411
	 */
412
	private function value_pattern_replacements( $value, $js_vars ) {
413
		$script = '';
414
		$alias  = $value;
415
		if ( isset( $js_vars['pattern_replace'] ) ) {
416
			$script .= 'settings=window.wp.customize.get();';
417
			foreach ( $js_vars['pattern_replace'] as $search => $replace ) {
418
				$replace = '\'+settings["' . $replace . '"]+\'';
419
				$value = str_replace( $search, $replace, $js_vars['value_pattern'] );
420
				$value = trim( $value, '+' );
421
			}
422
		}
423
		$value_compiled = str_replace( '$', '\'+' . $alias . '+\'', $value );
424
		$value_compiled = trim( $value_compiled, '+' );
425
		return $script . $alias . '=\'' . $value_compiled . '\';';
426
	}
427
428
	/**
429
	 * Get the callback function/method we're going to use for this field.
430
	 *
431
	 * @access private
432
	 * @since 3.0.0
433
	 * @param array $args The field args.
434
	 * @return string|array A callable function or method.
435
	 */
436
	protected function get_callback( $args ) {
437
438
		switch ( $args['type'] ) {
439
			case 'kirki-background':
440
			case 'kirki-dimensions':
441
			case 'kirki-multicolor':
442
			case 'kirki-sortable':
443
				$callback = array( $this, 'script_var_array' );
444
				break;
445
			case 'kirki-typography':
446
				$callback = array( $this, 'script_var_typography' );
447
				break;
448
			default:
449
				$callback = array( $this, 'script_var' );
450
		}
451
		return $callback;
452
	}
453
}
454