Passed
Push — master ( e7ff90...a9a9ce )
by Brian
05:56
created

AyeCode_UI_Settings   F

Complexity

Total Complexity 111

Size/Duplication

Total Lines 1522
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 977
dl 0
loc 1522
rs 1.623
c 1
b 0
f 1
wmc 111

24 Methods

Rating   Name   Duplication   Size   Complexity  
A load_admin_scripts() 0 13 4
A instance() 0 21 4
A html_font_size() 0 3 1
A constants() 0 5 3
B init() 0 28 8
A bs3_compat_css() 0 21 1
A theme_js_settings() 0 6 1
A customizer_settings() 0 33 1
A enqueue_flatpickr() 0 3 1
A get_url() 0 16 3
A inline_script_file_browser() 0 19 1
B get_examples() 0 95 1
A settings_page() 0 97 2
A menu_item() 0 5 1
A get_settings() 0 33 4
F css_secondary() 0 114 23
B enqueue_scripts() 0 39 6
A maybe_show_examples() 0 9 3
F css_primary() 0 138 23
A css_hex_lighten_darken() 0 17 4
A register_settings() 0 2 1
B inline_script() 0 498 1
B enqueue_style() 0 65 7
B custom_css() 0 37 7

How to fix   Complexity   

Complex Class

Complex classes like AyeCode_UI_Settings 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.

While breaking up the class, it is a good idea to analyze how other classes use AyeCode_UI_Settings, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * A class for adjusting AyeCode UI settings on WordPress
4
 *
5
 * This class can be added to any plugin or theme and will add a settings screen to WordPress to control Bootstrap settings.
6
 *
7
 * @link https://github.com/AyeCode/wp-ayecode-ui
8
 *
9
 * @internal This file should not be edited directly but pulled from the github repo above.
10
 */
11
12
/**
13
 * Bail if we are not in WP.
14
 */
15
if ( ! defined( 'ABSPATH' ) ) {
16
	exit;
17
}
18
19
/**
20
 * Only add if the class does not already exist.
21
 */
22
if ( ! class_exists( 'AyeCode_UI_Settings' ) ) {
23
24
	/**
25
	 * A Class to be able to change settings for Font Awesome.
26
	 *
27
	 * Class AyeCode_UI_Settings
28
	 * @ver 1.0.0
29
	 * @todo decide how to implement textdomain
30
	 */
31
	class AyeCode_UI_Settings {
32
33
		/**
34
		 * Class version version.
35
		 *
36
		 * @var string
37
		 */
38
		public $version = '1.0.1';
39
40
		/**
41
		 * Class textdomain.
42
		 *
43
		 * @var string
44
		 */
45
		public $textdomain = 'aui';
46
47
		/**
48
		 * Latest version of Bootstrap at time of publish published.
49
		 *
50
		 * @var string
51
		 */
52
		public $latest = "4.5.3";
53
54
		/**
55
		 * Current version of select2 being used.
56
		 *
57
		 * @var string
58
		 */
59
		public $select2_version = "4.0.11";
60
61
		/**
62
		 * The title.
63
		 *
64
		 * @var string
65
		 */
66
		public $name = 'AyeCode UI';
67
68
		/**
69
		 * The relative url to the assets.
70
		 *
71
		 * @var string
72
		 */
73
		public $url = '';
74
75
		/**
76
		 * Holds the settings values.
77
		 *
78
		 * @var array
79
		 */
80
		private $settings;
81
82
		/**
83
		 * AyeCode_UI_Settings instance.
84
		 *
85
		 * @access private
86
		 * @since  1.0.0
87
		 * @var    AyeCode_UI_Settings There can be only one!
88
		 */
89
		private static $instance = null;
90
91
		/**
92
		 * Main AyeCode_UI_Settings Instance.
93
		 *
94
		 * Ensures only one instance of AyeCode_UI_Settings is loaded or can be loaded.
95
		 *
96
		 * @since 1.0.0
97
		 * @static
98
		 * @return AyeCode_UI_Settings - Main instance.
99
		 */
100
		public static function instance() {
101
			if ( ! isset( self::$instance ) && ! ( self::$instance instanceof AyeCode_UI_Settings ) ) {
102
103
				self::$instance = new AyeCode_UI_Settings;
104
105
				add_action( 'init', array( self::$instance, 'init' ) ); // set settings
106
107
				if ( is_admin() ) {
108
					add_action( 'admin_menu', array( self::$instance, 'menu_item' ) );
109
					add_action( 'admin_init', array( self::$instance, 'register_settings' ) );
110
111
					// Maybe show example page
112
					add_action( 'template_redirect', array( self::$instance,'maybe_show_examples' ) );
113
				}
114
115
				add_action( 'customize_register', array( self::$instance, 'customizer_settings' ));
116
117
				do_action( 'ayecode_ui_settings_loaded' );
118
			}
119
120
			return self::$instance;
121
		}
122
123
		/**
124
		 * Setup some constants.
125
		 */
126
		public function constants(){
127
			define('AUI_PRIMARY_COLOR_ORIGINAL', "#1e73be");
128
			define('AUI_SECONDARY_COLOR_ORIGINAL', '#6c757d');
129
			if (!defined('AUI_PRIMARY_COLOR')) define('AUI_PRIMARY_COLOR', AUI_PRIMARY_COLOR_ORIGINAL);
130
			if (!defined('AUI_SECONDARY_COLOR')) define('AUI_SECONDARY_COLOR', AUI_SECONDARY_COLOR_ORIGINAL);
131
		}
132
133
		/**
134
		 * Initiate the settings and add the required action hooks.
135
		 */
136
		public function init() {
137
			$this->constants();
138
			$this->settings = $this->get_settings();
139
			$this->url = $this->get_url();
140
141
			/**
142
			 * Maybe load CSS
143
			 *
144
			 * We load super early in case there is a theme version that might change the colors
145
			 */
146
			if ( $this->settings['css'] ) {
147
				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ), 1 );
148
			}
149
			if ( $this->settings['css_backend'] && $this->load_admin_scripts() ) {
150
				add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_style' ), 1 );
151
			}
152
153
			// maybe load JS
154
			if ( $this->settings['js'] ) {
155
				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 1 );
156
			}
157
			if ( $this->settings['js_backend'] && $this->load_admin_scripts() ) {
158
				add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 1 );
159
			}
160
161
			// Maybe set the HTML font size
162
			if ( $this->settings['html_font_size'] ) {
163
				add_action( 'wp_footer', array( $this, 'html_font_size' ), 10 );
164
			}
165
166
167
		}
168
169
		/**
170
		 * Check if we should load the admin scripts or not.
171
		 *
172
		 * @return bool
173
		 */
174
		public function load_admin_scripts(){
175
			$result = true;
176
177
			if(!empty($this->settings['disable_admin'])){
178
				$url_parts = explode("\n",$this->settings['disable_admin']);
179
				foreach($url_parts as $part){
180
					if( strpos($_SERVER['REQUEST_URI'], trim($part)) !== false ){
181
						return false; // return early, no point checking further
182
					}
183
				}
184
			}
185
186
			return $result;
187
		}
188
189
		/**
190
		 * Add a html font size to the footer.
191
		 */
192
		public function html_font_size(){
193
			$this->settings = $this->get_settings();
194
			echo "<style>html{font-size:".absint($this->settings['html_font_size'])."px;}</style>";
195
		}
196
197
		/**
198
		 * Adds the styles.
199
		 */
200
		public function enqueue_style() {
201
202
			$css_setting = current_action() == 'wp_enqueue_scripts' ? 'css' : 'css_backend';
203
204
			$rtl = is_rtl() ? '-rtl' : '';
205
206
			if($this->settings[$css_setting]){
207
				$compatibility = $this->settings[$css_setting]=='core' ? false : true;
208
				$url = $this->settings[$css_setting]=='core' ? $this->url.'assets/css/ayecode-ui'.$rtl.'.css' : $this->url.'assets/css/ayecode-ui-compatibility'.$rtl.'.css';
209
				wp_register_style( 'ayecode-ui', $url, array(), $this->latest );
210
				wp_enqueue_style( 'ayecode-ui' );
211
212
				// flatpickr
213
				wp_register_style( 'flatpickr', $this->url.'assets/css/flatpickr.min.css', array(), $this->latest );
214
215
216
				// fix some wp-admin issues
217
				if(is_admin()){
218
					$custom_css = "
219
                body{
220
                    background-color: #f1f1f1;
221
                    font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif;
222
                    font-size:13px;
223
                }
224
                a {
225
				    color: #0073aa;
226
				    text-decoration: underline;
227
				}
228
                label {
229
				    display: initial;
230
				    margin-bottom: 0;
231
				}
232
				input, select {
233
				    margin: 1px;
234
				    line-height: initial;
235
				}
236
				th, td, div, h2 {
237
				    box-sizing: content-box;
238
				}
239
				p {
240
				    font-size: 13px;
241
				    line-height: 1.5;
242
				    margin: 1em 0;
243
				}
244
				h1, h2, h3, h4, h5, h6 {
245
				    display: block;
246
				    font-weight: 600;
247
				}
248
				h2,h3 {
249
				    font-size: 1.3em;
250
				    margin: 1em 0
251
				}
252
                ";
253
254
					// @todo, remove once fixed :: fix for this bug https://github.com/WordPress/gutenberg/issues/14377
255
					$custom_css .= "
256
						.edit-post-sidebar input[type=color].components-text-control__input{
257
						    padding: 0;
258
						}
259
					";
260
					wp_add_inline_style( 'ayecode-ui', $custom_css );
261
				}
262
263
				// custom changes
264
				wp_add_inline_style( 'ayecode-ui', self::custom_css($compatibility) );
265
266
			}
267
		}
268
269
		/**
270
		 * Get inline script used if bootstrap enqueued
271
		 *
272
		 * If this remains small then its best to use this than to add another JS file.
273
		 */
274
		public function inline_script(){
275
			ob_start();
276
			?>
277
			<script>
278
				
279
				/**
280
				 * An AUI bootstrap adaptation of GreedyNav.js ( by Luke Jackson ).
281
				 *
282
				 * Simply add the class `greedy` to any <nav> menu and it will do the rest.
283
				 * Licensed under the MIT license - http://opensource.org/licenses/MIT
284
				 * @ver 0.0.1
285
				 */
286
				function aui_init_greedy_nav(){
287
					jQuery('nav.greedy').each(function(i, obj) {
288
289
						// Check if already initialized, if so continue.
290
						if(jQuery(this).hasClass("being-greedy")){return true;}
291
292
						// Make sure its always expanded
293
						jQuery(this).addClass('navbar-expand');
294
295
						// vars
296
						var $vlinks = '';
297
						var $dDownClass = '';
298
						if(jQuery(this).find('.navbar-nav').length){
299
							if(jQuery(this).find('.navbar-nav').hasClass("being-greedy")){return true;}
300
							$vlinks = jQuery(this).find('.navbar-nav').addClass("being-greedy w-100").removeClass('overflow-hidden');
301
						}else if(jQuery(this).find('.nav').length){
302
							if(jQuery(this).find('.nav').hasClass("being-greedy")){return true;}
303
							$vlinks = jQuery(this).find('.nav').addClass("being-greedy w-100").removeClass('overflow-hidden');
304
							$dDownClass = ' mt-2 ';
305
						}else{
306
							return false;
307
						}
308
309
						jQuery($vlinks).append('<li class="nav-item list-unstyled ml-auto greedy-btn d-none dropdown ">' +
310
							'<a href="javascript:void(0)" data-toggle="dropdown" class="nav-link"><i class="fas fa-ellipsis-h"></i> <span class="greedy-count badge badge-dark badge-pill"></span></a>' +
311
							'<ul class="greedy-links dropdown-menu  dropdown-menu-right '+$dDownClass+'"></ul>' +
312
							'</li>');
313
314
						var $hlinks = jQuery(this).find('.greedy-links');
315
						var $btn = jQuery(this).find('.greedy-btn');
316
317
						var numOfItems = 0;
318
						var totalSpace = 0;
319
						var closingTime = 1000;
320
						var breakWidths = [];
321
322
						// Get initial state
323
						$vlinks.children().outerWidth(function(i, w) {
324
							totalSpace += w;
325
							numOfItems += 1;
326
							breakWidths.push(totalSpace);
327
						});
328
329
						var availableSpace, numOfVisibleItems, requiredSpace, buttonSpace ,timer;
330
331
						/*
332
						 The check function.
333
						 */
334
						function check() {
335
336
							// Get instant state
337
							buttonSpace = $btn.width();
338
							availableSpace = $vlinks.width() - 10;
339
							numOfVisibleItems = $vlinks.children().length;
340
							requiredSpace = breakWidths[numOfVisibleItems - 1];
341
342
							// There is not enough space
343
							if (numOfVisibleItems > 1 && requiredSpace > availableSpace) {
344
								$vlinks.children().last().prev().prependTo($hlinks);
345
								numOfVisibleItems -= 1;
346
								check();
347
								// There is more than enough space
348
							} else if (availableSpace > breakWidths[numOfVisibleItems]) {
349
								$hlinks.children().first().insertBefore($btn);
350
								numOfVisibleItems += 1;
351
								check();
352
							}
353
							// Update the button accordingly
354
							jQuery($btn).find(".greedy-count").html( numOfItems - numOfVisibleItems);
355
							if (numOfVisibleItems === numOfItems) {
356
								$btn.addClass('d-none');
357
							} else $btn.removeClass('d-none');
358
						}
359
360
						// Window listeners
361
						jQuery(window).resize(function() {
362
							check();
363
						});
364
365
						// do initial check
366
						check();
367
					});
368
				}
369
370
				/**
371
				 * Initiate Select2 items.
372
				 */
373
				function aui_init_select2(){
374
					jQuery("select.aui-select2").select2();
375
				}
376
377
				/**
378
				 * A function to convert a time value to a "ago" time text.
379
				 *
380
				 * @param selector string The .class selector
381
				 */
382
				function aui_time_ago(selector) {
383
384
					var templates = {
385
						prefix: "",
386
						suffix: " ago",
387
						seconds: "less than a minute",
388
						minute: "about a minute",
389
						minutes: "%d minutes",
390
						hour: "about an hour",
391
						hours: "about %d hours",
392
						day: "a day",
393
						days: "%d days",
394
						month: "about a month",
395
						months: "%d months",
396
						year: "about a year",
397
						years: "%d years"
398
					};
399
					var template = function (t, n) {
400
						return templates[t] && templates[t].replace(/%d/i, Math.abs(Math.round(n)));
401
					};
402
403
					var timer = function (time) {
404
						if (!time)
405
							return;
406
						time = time.replace(/\.\d+/, ""); // remove milliseconds
407
						time = time.replace(/-/, "/").replace(/-/, "/");
408
						time = time.replace(/T/, " ").replace(/Z/, " UTC");
409
						time = time.replace(/([\+\-]\d\d)\:?(\d\d)/, " $1$2"); // -04:00 -> -0400
410
						time = new Date(time * 1000 || time);
411
412
						var now = new Date();
413
						var seconds = ((now.getTime() - time) * .001) >> 0;
414
						var minutes = seconds / 60;
415
						var hours = minutes / 60;
416
						var days = hours / 24;
417
						var years = days / 365;
418
419
						return templates.prefix + (
420
								seconds < 45 && template('seconds', seconds) ||
421
								seconds < 90 && template('minute', 1) ||
422
								minutes < 45 && template('minutes', minutes) ||
423
								minutes < 90 && template('hour', 1) ||
424
								hours < 24 && template('hours', hours) ||
425
								hours < 42 && template('day', 1) ||
426
								days < 30 && template('days', days) ||
427
								days < 45 && template('month', 1) ||
428
								days < 365 && template('months', days / 30) ||
429
								years < 1.5 && template('year', 1) ||
430
								template('years', years)
431
							) + templates.suffix;
432
					};
433
434
					var elements = document.getElementsByClassName(selector);
435
					for (var i in elements) {
436
						var $this = elements[i];
437
						if (typeof $this === 'object') {
438
							$this.innerHTML = '<i class="far fa-clock"></i> ' + timer($this.getAttribute('title') || $this.getAttribute('datetime'));
439
						}
440
					}
441
					// update time every minute
442
					setTimeout(aui_time_ago, 60000);
443
444
				}
445
446
				/**
447
				 * Initiate tooltips on the page.
448
				 */
449
				function aui_init_tooltips(){
450
					jQuery('[data-toggle="tooltip"]').tooltip();
451
					jQuery('[data-toggle="popover"]').popover();
452
					jQuery('[data-toggle="popover-html"]').popover({
453
						html: true
454
					});
455
456
					// fix popover container compatibility
457
					jQuery('[data-toggle="popover"],[data-toggle="popover-html"]').on('inserted.bs.popover', function () {
458
						jQuery('body > .popover').wrapAll("<div class='bsui' />");
459
					});
460
				}
461
462
				/**
463
				 * Initiate flatpickrs on the page.
464
				 */
465
				$aui_doing_init_flatpickr = false;
466
				function aui_init_flatpickr(){
467
					if ( jQuery.isFunction(jQuery.fn.flatpickr) && !$aui_doing_init_flatpickr) {
468
						$aui_doing_init_flatpickr = true;
469
						jQuery('input[data-aui-init="flatpickr"]:not(.flatpickr-input)').flatpickr();
470
					}
471
					$aui_doing_init_flatpickr = false;
472
				}
473
474
				function aui_modal($title,$body,$footer,$dismissible,$class,$dialog_class) {
475
					if(!$class){$class = '';}
476
					if(!$dialog_class){$dialog_class = '';}
477
					if(!$body){$body = '<div class="text-center"><div class="spinner-border" role="status"></div></div>';}
478
					// remove it first
479
					jQuery('.aui-modal').modal('hide').modal('dispose').remove();
480
					jQuery('.modal-backdrop').remove();
481
482
					var $modal = '';
483
484
					$modal += '<div class="modal aui-modal fade shadow bsui '+$class+'" tabindex="-1">'+
485
						'<div class="modal-dialog modal-dialog-centered '+$dialog_class+'">'+
486
							'<div class="modal-content">';
487
488
					if($title) {
489
						$modal += '<div class="modal-header">' +
490
						'<h5 class="modal-title">' + $title + '</h5>';
491
492
						if ($dismissible) {
493
							$modal += '<button type="button" class="close" data-dismiss="modal" aria-label="Close">' +
494
								'<span aria-hidden="true">&times;</span>' +
495
								'</button>';
496
						}
497
498
						$modal += '</div>';
499
					}
500
					$modal += '<div class="modal-body">'+
501
									$body+
502
								'</div>';
503
504
					if($footer){
505
						$modal += '<div class="modal-footer">'+
506
							$footer +
507
							'</div>';
508
					}
509
510
					$modal +='</div>'+
511
						'</div>'+
512
					'</div>';
513
514
					jQuery('body').append($modal);
515
516
					jQuery('.aui-modal').modal('hide').modal({
517
						//backdrop: 'static'
518
					});
519
				}
520
521
				/**
522
				 * Show / hide fields depending on conditions.
523
				 */
524
				function aui_conditional_fields(form){
525
					jQuery(form).find(".aui-conditional-field").each(function () {
526
527
						var $element_require = jQuery(this).data('element-require');
528
529
						if ($element_require) {
530
531
							$element_require = $element_require.replace("&#039;", "'"); // replace single quotes
532
							$element_require = $element_require.replace("&quot;", '"'); // replace double quotes
533
534
							if (aui_check_form_condition($element_require,form)) {
535
								jQuery(this).removeClass('d-none');
536
							} else {
537
								jQuery(this).addClass('d-none');
538
							}
539
						}
540
					});
541
				}
542
543
				/**
544
				 * Check form condition
545
				 */
546
				function aui_check_form_condition(condition,form) {
547
					if (form) {
548
						condition = condition.replace(/\(form\)/g, "('"+form+"')");
549
					}
550
					return new Function("return " + condition+";")();
551
				}
552
553
				/**
554
				 * A function to determine if a element is on screen.
555
				 */
556
				jQuery.fn.aui_isOnScreen = function(){
557
558
					var win = jQuery(window);
559
560
					var viewport = {
561
						top : win.scrollTop(),
562
						left : win.scrollLeft()
563
					};
564
					viewport.right = viewport.left + win.width();
565
					viewport.bottom = viewport.top + win.height();
566
567
					var bounds = this.offset();
568
					bounds.right = bounds.left + this.outerWidth();
569
					bounds.bottom = bounds.top + this.outerHeight();
570
571
					return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom));
572
573
				};
574
575
				/**
576
				 * Maybe show multiple carousel items if set to do so.
577
				 */ 
578
				function aui_carousel_maybe_show_multiple_items($carousel){
579
					var $items = {};
580
					var $item_count = 0;
581
582
					// maybe backup
583
					if(!jQuery($carousel).find('.carousel-inner-original').length){
584
						jQuery($carousel).append('<div class="carousel-inner-original d-none">'+jQuery($carousel).find('.carousel-inner').html()+'</div>');
585
					}
586
587
					// Get the original items html
588
					jQuery($carousel).find('.carousel-inner-original .carousel-item').each(function () {
589
						$items[$item_count] = jQuery(this).html();
590
						$item_count++;
591
					});
592
593
					// bail if no items
594
					if(!$item_count){return;}
595
596
					if(jQuery(window).width() <= 576){
597
						// maybe restore original
598
						if(jQuery($carousel).find('.carousel-inner').hasClass('aui-multiple-items') && jQuery($carousel).find('.carousel-inner-original').length){
599
							jQuery($carousel).find('.carousel-inner').removeClass('aui-multiple-items').html(jQuery($carousel).find('.carousel-inner-original').html());
600
							jQuery($carousel).find(".carousel-indicators li").removeClass("d-none");
601
						}
602
603
					}else{
604
						// new items
605
						var $md_count = jQuery($carousel).data('limit_show');
606
						var $new_items = '';
607
						var $new_items_count = 0;
608
						var $new_item_count = 0;
609
						var $closed = true;
610
						Object.keys($items).forEach(function(key,index) {
611
612
							// close
613
							if(index != 0 && Number.isInteger(index/$md_count) ){
614
								$new_items += '</div></div>';
615
								$closed = true;
616
							}
617
618
							// open
619
							if(index == 0 || Number.isInteger(index/$md_count) ){
620
								$active = index == 0 ? 'active' : '';
621
								$new_items += '<div class="carousel-item '+$active+'"><div class="row m-0">';
622
								$closed = false;
623
								$new_items_count++;
624
								$new_item_count = 0;
625
							}
626
627
							// content
628
							$new_items += '<div class="col pr-1 pl-0">'+$items[index]+'</div>';
629
							$new_item_count++;
630
631
632
						});
633
634
						// close if not closed in the loop
635
						if(!$closed){
636
							// check for spares
637
							if($md_count-$new_item_count > 0){
638
								$placeholder_count = $md_count-$new_item_count;
639
								while($placeholder_count > 0){
640
									$new_items += '<div class="col pr-1 pl-0"></div>';
641
									$placeholder_count--;
642
								}
643
644
							}
645
646
							$new_items += '</div></div>';
647
						}
648
649
						// insert the new items
650
						jQuery($carousel).find('.carousel-inner').addClass('aui-multiple-items').html($new_items);
651
652
						// fix any lazyload images in the active slider
653
						jQuery($carousel).find('.carousel-item.active img').each(function () {
654
							// fix the srcset
655
							if(real_srcset = jQuery(this).attr("data-srcset")){
656
								if(!jQuery(this).attr("srcset")) jQuery(this).attr("srcset",real_srcset);
657
							}
658
							// fix the src
659
							if(real_src = jQuery(this).attr("data-src")){
660
								if(!jQuery(this).attr("srcset"))  jQuery(this).attr("src",real_src);
661
							}
662
						});
663
664
						// maybe fix carousel indicators
665
						$hide_count = $new_items_count-1;
666
						jQuery($carousel).find(".carousel-indicators li:gt("+$hide_count+")").addClass("d-none");
667
					}
668
669
					// trigger a global action to say we have
670
					jQuery( window ).trigger( "aui_carousel_multiple" );
671
				}
672
673
				/**
674
				 * Init Multiple item carousels.
675
				 */ 
676
				function aui_init_carousel_multiple_items(){
677
					jQuery(window).resize(function(){
678
						jQuery('.carousel-multiple-items').each(function () {
679
							aui_carousel_maybe_show_multiple_items(this);
680
						});
681
					});
682
683
					// run now
684
					jQuery('.carousel-multiple-items').each(function () {
685
						aui_carousel_maybe_show_multiple_items(this);
686
					});
687
				}
688
689
				/**
690
				 * Allow navs to use multiple sub menus.
691
				 */
692
				function init_nav_sub_menus(){
693
694
					jQuery('.navbar-multi-sub-menus').each(function(i, obj) {
695
						// Check if already initialized, if so continue.
696
						if(jQuery(this).hasClass("has-sub-sub-menus")){return true;}
697
698
						// Make sure its always expanded
699
						jQuery(this).addClass('has-sub-sub-menus');
700
701
						jQuery(this).find( '.dropdown-menu a.dropdown-toggle' ).on( 'click', function ( e ) {
702
							var $el = jQuery( this );
703
							$el.toggleClass('active-dropdown');
704
							var $parent = jQuery( this ).offsetParent( ".dropdown-menu" );
705
							if ( !jQuery( this ).next().hasClass( 'show' ) ) {
706
								jQuery( this ).parents( '.dropdown-menu' ).first().find( '.show' ).removeClass( "show" );
707
							}
708
							var $subMenu = jQuery( this ).next( ".dropdown-menu" );
709
							$subMenu.toggleClass( 'show' );
710
711
							jQuery( this ).parent( "li" ).toggleClass( 'show' );
712
713
							jQuery( this ).parents( 'li.nav-item.dropdown.show' ).on( 'hidden.bs.dropdown', function ( e ) {
714
								jQuery( '.dropdown-menu .show' ).removeClass( "show" );
715
								$el.removeClass('active-dropdown');
716
							} );
717
718
							if ( !$parent.parent().hasClass( 'navbar-nav' ) ) {
719
								$el.next().addClass('position-relative border-top border-bottom');
720
							}
721
722
							return false;
723
						} );
724
725
					});
726
727
				}
728
				
729
730
				/**
731
				 * Initiate all AUI JS.
732
				 */
733
				function aui_init(){
734
					// nav menu submenus
735
					init_nav_sub_menus();
736
					
737
					// init tooltips
738
					aui_init_tooltips();
739
740
					// init select2
741
					aui_init_select2();
742
743
					// init flatpickr
744
					aui_init_flatpickr();
745
746
					// init Greedy nav
747
					aui_init_greedy_nav();
748
749
					// Set times to time ago
750
					aui_time_ago('timeago');
751
					
752
					// init multiple item carousels
753
					aui_init_carousel_multiple_items();
754
				}
755
756
				// run on window loaded
757
				jQuery(window).on("load",function() {
758
					aui_init();
759
				});
760
761
			</script>
762
			<?php
763
			$output = ob_get_clean();
764
765
			/*
766
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
767
			 */
768
			return str_replace( array(
769
				'<script>',
770
				'</script>'
771
			), '', $output );
772
		}
773
774
		/**
775
		 * Get inline script used if bootstrap file browser enqueued.
776
		 *
777
		 * If this remains small then its best to use this than to add another JS file.
778
		 */
779
		public function inline_script_file_browser(){
780
			ob_start();
781
			?>
782
			<script>
783
				// run on doc ready
784
				jQuery(document).ready(function () {
785
					bsCustomFileInput.init();
786
				});
787
			</script>
788
			<?php
789
			$output = ob_get_clean();
790
791
			/*
792
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
793
			 */
794
			return str_replace( array(
795
				'<script>',
796
				'</script>'
797
			), '', $output );
798
		}
799
800
		/**
801
		 * Adds the Font Awesome JS.
802
		 */
803
		public function enqueue_scripts() {
804
805
			$js_setting = current_action() == 'wp_enqueue_scripts' ? 'js' : 'js_backend';
806
807
			// select2
808
			wp_register_script( 'select2', $this->url.'assets/js/select2.min.js', array('jquery'), $this->select2_version );
809
810
			// flatpickr
811
			wp_register_script( 'flatpickr', $this->url.'assets/js/flatpickr.min.js', array(), $this->latest );
812
813
			// Bootstrap file browser
814
			wp_register_script( 'aui-custom-file-input', $url = $this->url.'assets/js/bs-custom-file-input.min.js', array('jquery'), $this->select2_version );
815
			wp_add_inline_script( 'aui-custom-file-input', $this->inline_script_file_browser() );
816
817
			$load_inline = false;
818
819
			if($this->settings[$js_setting]=='core-popper'){
820
				// Bootstrap bundle
821
				$url = $this->url.'assets/js/bootstrap.bundle.min.js';
822
				wp_register_script( 'bootstrap-js-bundle', $url, array('select2','jquery'), $this->latest );
823
				// if in admin then add to footer for compatibility.
824
				is_admin() ? wp_enqueue_script( 'bootstrap-js-bundle', '', null, null, true ) : wp_enqueue_script( 'bootstrap-js-bundle');
825
				$script = $this->inline_script();
826
				wp_add_inline_script( 'bootstrap-js-bundle', $script );
827
			}elseif($this->settings[$js_setting]=='popper'){
828
				$url = $this->url.'assets/js/popper.min.js';
829
				wp_register_script( 'bootstrap-js-popper', $url, array('select2','jquery'), $this->latest );
830
				wp_enqueue_script( 'bootstrap-js-popper' );
831
				$load_inline = true;
832
			}else{
833
				$load_inline = true;
834
			}
835
836
			// Load needed inline scripts by faking the loading of a script if the main script is not being loaded
837
			if($load_inline){
838
				wp_register_script( 'bootstrap-dummy', '',array('select2','jquery') );
839
				wp_enqueue_script( 'bootstrap-dummy' );
840
				$script = $this->inline_script();
841
				wp_add_inline_script( 'bootstrap-dummy', $script  );
842
			}
843
844
		}
845
846
		/**
847
		 * Enqueue flatpickr if called.
848
		 */
849
		public function enqueue_flatpickr(){
850
			wp_enqueue_style( 'flatpickr' );
851
			wp_enqueue_script( 'flatpickr' );
852
		}
853
854
		/**
855
		 * Get the url path to the current folder.
856
		 *
857
		 * @return string
858
		 */
859
		public function get_url() {
860
861
			$url = '';
862
			// check if we are inside a plugin
863
			$file_dir = str_replace( "/includes","", wp_normalize_path( dirname( __FILE__ ) ) );
864
865
			// add check in-case user has changed wp-content dir name.
866
			$wp_content_folder_name = basename(WP_CONTENT_DIR);
867
			$dir_parts = explode("/$wp_content_folder_name/",$file_dir);
868
			$url_parts = explode("/$wp_content_folder_name/",plugins_url());
869
870
			if(!empty($url_parts[0]) && !empty($dir_parts[1])){
871
				$url = trailingslashit( $url_parts[0]."/$wp_content_folder_name/".$dir_parts[1] );
872
			}
873
874
			return $url;
875
		}
876
877
		/**
878
		 * Register the database settings with WordPress.
879
		 */
880
		public function register_settings() {
881
			register_setting( 'ayecode-ui-settings', 'ayecode-ui-settings' );
882
		}
883
884
		/**
885
		 * Add the WordPress settings menu item.
886
		 * @since 1.0.10 Calling function name direct will fail theme check so we don't.
887
		 */
888
		public function menu_item() {
889
			$menu_function = 'add' . '_' . 'options' . '_' . 'page'; // won't pass theme check if function name present in theme
890
			call_user_func( $menu_function, $this->name, $this->name, 'manage_options', 'ayecode-ui-settings', array(
891
				$this,
892
				'settings_page'
893
			) );
894
		}
895
896
		/**
897
		 * Get a list of themes and their default JS settings.
898
		 *
899
		 * @return array
900
		 */
901
		public function theme_js_settings(){
902
			return array(
903
				'ayetheme' => 'popper',
904
				'listimia' => 'required',
905
				'listimia_backend' => 'core-popper',
906
				'avada'    => 'required',
907
			);
908
		}
909
910
		/**
911
		 * Get the current Font Awesome output settings.
912
		 *
913
		 * @return array The array of settings.
914
		 */
915
		public function get_settings() {
916
917
			$db_settings = get_option( 'ayecode-ui-settings' );
918
			$js_default = 'core-popper';
919
			$js_default_backend = $js_default;
920
921
			// maybe set defaults (if no settings set)
922
			if(empty($db_settings)){
923
				$active_theme = strtolower( get_template() ); // active parent theme.
924
				$theme_js_settings = self::theme_js_settings();
0 ignored issues
show
Bug Best Practice introduced by
The method AyeCode_UI_Settings::theme_js_settings() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

924
				/** @scrutinizer ignore-call */ 
925
    $theme_js_settings = self::theme_js_settings();
Loading history...
925
				if(isset($theme_js_settings[$active_theme])){
926
					$js_default = $theme_js_settings[$active_theme];
927
					$js_default_backend = isset($theme_js_settings[$active_theme."_backend"]) ? $theme_js_settings[$active_theme."_backend"] : $js_default;
928
				}
929
			}
930
931
			$defaults = array(
932
				'css'       => 'compatibility', // core, compatibility
933
				'js'        => $js_default, // js to load, core-popper, popper
934
				'html_font_size'        => '16', // js to load, core-popper, popper
935
				'css_backend'       => 'compatibility', // core, compatibility
936
				'js_backend'        => $js_default_backend, // js to load, core-popper, popper
937
				'disable_admin'     =>  '', // URL snippets to disable loading on admin
938
			);
939
940
			$settings = wp_parse_args( $db_settings, $defaults );
941
942
			/**
943
			 * Filter the Bootstrap settings.
944
			 *
945
			 * @todo if we add this filer people might use it and then it defeates the purpose of this class :/
946
			 */
947
			return $this->settings = apply_filters( 'ayecode-ui-settings', $settings, $db_settings, $defaults );
948
		}
949
950
951
		/**
952
		 * The settings page html output.
953
		 */
954
		public function settings_page() {
955
			if ( ! current_user_can( 'manage_options' ) ) {
956
				wp_die( __( 'You do not have sufficient permissions to access this page.', 'aui' ) );
957
			}
958
			?>
959
			<div class="wrap">
960
				<h1><?php echo $this->name; ?></h1>
961
				<p><?php _e("Here you can adjust settings if you are having compatibility issues.","aui");?></p>
962
				<form method="post" action="options.php">
963
					<?php
964
					settings_fields( 'ayecode-ui-settings' );
965
					do_settings_sections( 'ayecode-ui-settings' );
966
					?>
967
968
					<h2><?php _e( 'Frontend', 'aui' ); ?></h2>
969
					<table class="form-table wpbs-table-settings">
970
						<tr valign="top">
971
							<th scope="row"><label
972
									for="wpbs-css"><?php _e( 'Load CSS', 'aui' ); ?></label></th>
973
							<td>
974
								<select name="ayecode-ui-settings[css]" id="wpbs-css">
975
									<option	value="compatibility" <?php selected( $this->settings['css'], 'compatibility' ); ?>><?php _e( 'Compatibility Mode (default)', 'aui' ); ?></option>
976
									<option value="core" <?php selected( $this->settings['css'], 'core' ); ?>><?php _e( 'Full Mode', 'aui' ); ?></option>
977
									<option	value="" <?php selected( $this->settings['css'], '' ); ?>><?php _e( 'Disabled', 'aui' ); ?></option>
978
								</select>
979
							</td>
980
						</tr>
981
982
						<tr valign="top">
983
							<th scope="row"><label
984
									for="wpbs-js"><?php _e( 'Load JS', 'aui' ); ?></label></th>
985
							<td>
986
								<select name="ayecode-ui-settings[js]" id="wpbs-js">
987
									<option	value="core-popper" <?php selected( $this->settings['js'], 'core-popper' ); ?>><?php _e( 'Core + Popper (default)', 'aui' ); ?></option>
988
									<option value="popper" <?php selected( $this->settings['js'], 'popper' ); ?>><?php _e( 'Popper', 'aui' ); ?></option>
989
									<option value="required" <?php selected( $this->settings['js'], 'required' ); ?>><?php _e( 'Required functions only', 'aui' ); ?></option>
990
									<option	value="" <?php selected( $this->settings['js'], '' ); ?>><?php _e( 'Disabled (not recommended)', 'aui' ); ?></option>
991
								</select>
992
							</td>
993
						</tr>
994
995
						<tr valign="top">
996
							<th scope="row"><label
997
									for="wpbs-font_size"><?php _e( 'HTML Font Size (px)', 'aui' ); ?></label></th>
998
							<td>
999
								<input type="number" name="ayecode-ui-settings[html_font_size]" id="wpbs-font_size" value="<?php echo absint( $this->settings['html_font_size']); ?>" placeholder="16" />
1000
								<p class="description" ><?php _e("Our font sizing is rem (responsive based) here you can set the html font size in-case your theme is setting it too low.","aui");?></p>
1001
							</td>
1002
						</tr>
1003
1004
					</table>
1005
1006
					<h2><?php _e( 'Backend', 'aui' ); ?> (wp-admin)</h2>
1007
					<table class="form-table wpbs-table-settings">
1008
						<tr valign="top">
1009
							<th scope="row"><label
1010
									for="wpbs-css-admin"><?php _e( 'Load CSS', 'aui' ); ?></label></th>
1011
							<td>
1012
								<select name="ayecode-ui-settings[css_backend]" id="wpbs-css-admin">
1013
									<option	value="compatibility" <?php selected( $this->settings['css_backend'], 'compatibility' ); ?>><?php _e( 'Compatibility Mode', 'aui' ); ?></option>
1014
									<option value="core" <?php selected( $this->settings['css_backend'], 'core' ); ?>><?php _e( 'Full Mode', 'aui' ); ?></option>
1015
									<option	value="" <?php selected( $this->settings['css_backend'], '' ); ?>><?php _e( 'Disabled', 'aui' ); ?></option>
1016
								</select>
1017
							</td>
1018
						</tr>
1019
1020
						<tr valign="top">
1021
							<th scope="row"><label
1022
									for="wpbs-js-admin"><?php _e( 'Load JS', 'aui' ); ?></label></th>
1023
							<td>
1024
								<select name="ayecode-ui-settings[js_backend]" id="wpbs-js-admin">
1025
									<option	value="core-popper" <?php selected( $this->settings['js_backend'], 'core-popper' ); ?>><?php _e( 'Core + Popper (default)', 'aui' ); ?></option>
1026
									<option value="popper" <?php selected( $this->settings['js_backend'], 'popper' ); ?>><?php _e( 'Popper', 'aui' ); ?></option>
1027
									<option value="required" <?php selected( $this->settings['js_backend'], 'required' ); ?>><?php _e( 'Required functions only', 'aui' ); ?></option>
1028
									<option	value="" <?php selected( $this->settings['js_backend'], '' ); ?>><?php _e( 'Disabled (not recommended)', 'aui' ); ?></option>
1029
								</select>
1030
							</td>
1031
						</tr>
1032
1033
						<tr valign="top">
1034
							<th scope="row"><label
1035
									for="wpbs-disable-admin"><?php _e( 'Disable load on URL', 'aui' ); ?></label></th>
1036
							<td>
1037
								<p><?php _e( 'If you have backend conflict you can enter a partial URL argument that will disable the loading of AUI on those pages. Add each argument on a new line.', 'aui' ); ?></p>
1038
								<textarea name="ayecode-ui-settings[disable_admin]" rows="10" cols="50" id="wpbs-disable-admin" class="large-text code" spellcheck="false" placeholder="myplugin.php &#10;action=go"><?php echo $this->settings['disable_admin'];?></textarea>
1039
1040
							</td>
1041
						</tr>
1042
1043
					</table>
1044
1045
					<?php
1046
					submit_button();
1047
					?>
1048
				</form>
1049
1050
				<div id="wpbs-version"><?php echo $this->version; ?></div>
1051
			</div>
1052
1053
			<?php
1054
		}
1055
1056
		public function customizer_settings($wp_customize){
1057
			$wp_customize->add_section('aui_settings', array(
1058
				'title'    => __('AyeCode UI','aui'),
1059
				'priority' => 120,
1060
			));
1061
1062
			//  =============================
1063
			//  = Color Picker              =
1064
			//  =============================
1065
			$wp_customize->add_setting('aui_options[color_primary]', array(
1066
				'default'           => AUI_PRIMARY_COLOR,
1067
				'sanitize_callback' => 'sanitize_hex_color',
1068
				'capability'        => 'edit_theme_options',
1069
				'type'              => 'option',
1070
				'transport'         => 'refresh',
1071
			));
1072
			$wp_customize->add_control( new WP_Customize_Color_Control($wp_customize, 'color_primary', array(
1073
				'label'    => __('Primary Color','aui'),
1074
				'section'  => 'aui_settings',
1075
				'settings' => 'aui_options[color_primary]',
1076
			)));
1077
1078
			$wp_customize->add_setting('aui_options[color_secondary]', array(
1079
				'default'           => '#6c757d',
1080
				'sanitize_callback' => 'sanitize_hex_color',
1081
				'capability'        => 'edit_theme_options',
1082
				'type'              => 'option',
1083
				'transport'         => 'refresh',
1084
			));
1085
			$wp_customize->add_control( new WP_Customize_Color_Control($wp_customize, 'color_secondary', array(
1086
				'label'    => __('Secondary Color','aui'),
1087
				'section'  => 'aui_settings',
1088
				'settings' => 'aui_options[color_secondary]',
1089
			)));
1090
		}
1091
1092
		public static function bs3_compat_css() {
1093
			ob_start();
1094
			?>
1095
			/* Bootstrap 3 compatibility */
1096
			body.modal-open .modal-backdrop.show:not(.in) {opacity:0.5;}
1097
			body.modal-open .modal.show:not(.in)  {opacity:1;z-index: 99999}
1098
			body.modal-open .modal.show:not(.in) .modal-content  {box-shadow: none;}
1099
			body.modal-open .modal.show:not(.in)  .modal-dialog {transform: initial;}
1100
1101
			.collapse.show:not(.in){display: inherit;}
1102
1103
			/* With Avada builder */
1104
			body.modal-open .modal.in  {opacity:1;z-index: 99999}
1105
			body.modal-open .modal.bsui.in .modal-content  {box-shadow: none;}
1106
			.bsui .collapse.in{display: inherit;}
1107
1108
1109
1110
			body.modal-open .modal.bsui .modal-dialog{left: auto;}
1111
			<?php
1112
			return ob_get_clean();
1113
		}
1114
1115
1116
		public static function custom_css($compatibility = true) {
1117
			$settings = get_option('aui_options');
1118
1119
			ob_start();
1120
1121
			$primary_color = !empty($settings['color_primary']) ? $settings['color_primary'] : AUI_PRIMARY_COLOR;
1122
			$secondary_color = !empty($settings['color_secondary']) ? $settings['color_secondary'] : AUI_SECONDARY_COLOR;
1123
				//AUI_PRIMARY_COLOR_ORIGINAL
1124
			?>
1125
			<style>
1126
1127
1128
1129
				<?php
1130
1131
				// BS compat @todo add option check
1132
				//echo self::bs3_compat_css();
1133
1134
					if(!is_admin() && $primary_color != AUI_PRIMARY_COLOR_ORIGINAL){
1135
						echo self::css_primary($primary_color,$compatibility);
1136
					}
1137
1138
					if(!is_admin() && $secondary_color != AUI_SECONDARY_COLOR_ORIGINAL){
1139
						echo self::css_secondary($settings['color_secondary'],$compatibility);
1140
					}
1141
                ?>
1142
			</style>
1143
			<?php
1144
1145
1146
			/*
1147
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
1148
			 */
1149
			return str_replace( array(
1150
				'<style>',
1151
				'</style>'
1152
			), '', ob_get_clean());
1153
		}
1154
1155
		public static function css_primary($color_code,$compatibility){;
1156
			$color_code = sanitize_hex_color($color_code);
1157
			if(!$color_code){return '';}
1158
			/**
1159
			 * c = color, b = background color, o = border-color, f = fill
1160
			 */
1161
			$selectors = array(
1162
				'a' => array('c'),
1163
				'.btn-primary' => array('b','o'),
1164
				'.btn-primary.disabled' => array('b','o'),
1165
				'.btn-primary:disabled' => array('b','o'),
1166
				'.btn-outline-primary' => array('c','o'),
1167
				'.btn-outline-primary:hover' => array('b','o'),
1168
				'.btn-outline-primary:not(:disabled):not(.disabled).active' => array('b','o'),
1169
				'.btn-outline-primary:not(:disabled):not(.disabled):active' => array('b','o'),
1170
				'.show>.btn-outline-primary.dropdown-toggle' => array('b','o'),
1171
				'.btn-link' => array('c'),
1172
				'.dropdown-item.active' => array('b'),
1173
				'.custom-control-input:checked~.custom-control-label::before' => array('b','o'),
1174
				'.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before' => array('b','o'),
1175
//				'.custom-range::-webkit-slider-thumb' => array('b'), // these break the inline rules...
1176
//				'.custom-range::-moz-range-thumb' => array('b'),
1177
//				'.custom-range::-ms-thumb' => array('b'),
1178
				'.nav-pills .nav-link.active' => array('b'),
1179
				'.nav-pills .show>.nav-link' => array('b'),
1180
				'.page-link' => array('c'),
1181
				'.page-item.active .page-link' => array('b','o'),
1182
				'.badge-primary' => array('b'),
1183
				'.alert-primary' => array('b','o'),
1184
				'.progress-bar' => array('b'),
1185
				'.list-group-item.active' => array('b','o'),
1186
				'.bg-primary' => array('b','f'),
1187
				'.btn-link.btn-primary' => array('c'),
1188
				'.select2-container .select2-results__option--highlighted.select2-results__option[aria-selected=true]' => array('b'),
1189
			);
1190
1191
			$important_selectors = array(
1192
				'.bg-primary' => array('b','f'),
1193
				'.border-primary' => array('o'),
1194
				'.text-primary' => array('c'),
1195
			);
1196
1197
			$color = array();
1198
			$color_i = array();
1199
			$background = array();
1200
			$background_i = array();
1201
			$border = array();
1202
			$border_i = array();
1203
			$fill = array();
1204
			$fill_i = array();
1205
1206
			$output = '';
1207
1208
			// build rules into each type
1209
			foreach($selectors as $selector => $types){
1210
				$selector = $compatibility ? ".bsui ".$selector : $selector;
1211
				$types = array_combine($types,$types);
1212
				if(isset($types['c'])){$color[] = $selector;}
1213
				if(isset($types['b'])){$background[] = $selector;}
1214
				if(isset($types['o'])){$border[] = $selector;}
1215
				if(isset($types['f'])){$fill[] = $selector;}
1216
			}
1217
1218
			// build rules into each type
1219
			foreach($important_selectors as $selector => $types){
1220
				$selector = $compatibility ? ".bsui ".$selector : $selector;
1221
				$types = array_combine($types,$types);
1222
				if(isset($types['c'])){$color_i[] = $selector;}
1223
				if(isset($types['b'])){$background_i[] = $selector;}
1224
				if(isset($types['o'])){$border_i[] = $selector;}
1225
				if(isset($types['f'])){$fill_i[] = $selector;}
1226
			}
1227
1228
			// add any color rules
1229
			if(!empty($color)){
1230
				$output .= implode(",",$color) . "{color: $color_code;} ";
1231
			}
1232
			if(!empty($color_i)){
1233
				$output .= implode(",",$color_i) . "{color: $color_code !important;} ";
1234
			}
1235
1236
			// add any background color rules
1237
			if(!empty($background)){
1238
				$output .= implode(",",$background) . "{background-color: $color_code;} ";
1239
			}
1240
			if(!empty($background_i)){
1241
				$output .= implode(",",$background_i) . "{background-color: $color_code !important;} ";
1242
			}
1243
1244
			// add any border color rules
1245
			if(!empty($border)){
1246
				$output .= implode(",",$border) . "{border-color: $color_code;} ";
1247
			}
1248
			if(!empty($border_i)){
1249
				$output .= implode(",",$border_i) . "{border-color: $color_code !important;} ";
1250
			}
1251
1252
			// add any fill color rules
1253
			if(!empty($fill)){
1254
				$output .= implode(",",$fill) . "{fill: $color_code;} ";
1255
			}
1256
			if(!empty($fill_i)){
1257
				$output .= implode(",",$fill_i) . "{fill: $color_code !important;} ";
1258
			}
1259
1260
1261
			$prefix = $compatibility ? ".bsui " : "";
1262
1263
			// darken
1264
			$darker_075 = self::css_hex_lighten_darken($color_code,"-0.075");
0 ignored issues
show
Bug introduced by
'-0.075' of type string is incompatible with the type double expected by parameter $adjustPercent of AyeCode_UI_Settings::css_hex_lighten_darken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1264
			$darker_075 = self::css_hex_lighten_darken($color_code,/** @scrutinizer ignore-type */ "-0.075");
Loading history...
1265
			$darker_10 = self::css_hex_lighten_darken($color_code,"-0.10");
1266
			$darker_125 = self::css_hex_lighten_darken($color_code,"-0.125");
1267
1268
			// lighten
1269
			$lighten_25 = self::css_hex_lighten_darken($color_code,"0.25");
1270
1271
			// opacity see https://css-tricks.com/8-digit-hex-codes/
1272
			$op_25 = $color_code."40"; // 25% opacity
1273
1274
1275
			// button states
1276
			$output .= $prefix ." .btn-primary:hover{background-color: ".$darker_075.";    border-color: ".$darker_10.";} ";
1277
			$output .= $prefix ." .btn-outline-primary:not(:disabled):not(.disabled):active:focus, $prefix .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show>$prefix .btn-outline-primary.dropdown-toggle:focus{box-shadow: 0 0 0 0.2rem $op_25;} ";
1278
			$output .= $prefix ." .btn-primary:not(:disabled):not(.disabled):active, $prefix .btn-primary:not(:disabled):not(.disabled).active, .show>$prefix .btn-primary.dropdown-toggle{background-color: ".$darker_10.";    border-color: ".$darker_125.";} ";
1279
			$output .= $prefix ." .btn-primary:not(:disabled):not(.disabled):active:focus, $prefix .btn-primary:not(:disabled):not(.disabled).active:focus, .show>$prefix .btn-primary.dropdown-toggle:focus {box-shadow: 0 0 0 0.2rem $op_25;} ";
1280
1281
1282
			// dropdown's
1283
			$output .= $prefix ." .dropdown-item.active, $prefix .dropdown-item:active{background-color: $color_code;} ";
1284
1285
1286
			// input states
1287
			$output .= $prefix ." .form-control:focus{border-color: ".$lighten_25.";box-shadow: 0 0 0 0.2rem $op_25;} ";
1288
1289
			// page link
1290
			$output .= $prefix ." .page-link:focus{box-shadow: 0 0 0 0.2rem $op_25;} ";
1291
1292
			return $output;
1293
		}
1294
1295
		public static function css_secondary($color_code,$compatibility){;
1296
			$color_code = sanitize_hex_color($color_code);
1297
			if(!$color_code){return '';}
1298
			/**
1299
			 * c = color, b = background color, o = border-color, f = fill
1300
			 */
1301
			$selectors = array(
1302
				'.btn-secondary' => array('b','o'),
1303
				'.btn-secondary.disabled' => array('b','o'),
1304
				'.btn-secondary:disabled' => array('b','o'),
1305
				'.btn-outline-secondary' => array('c','o'),
1306
				'.btn-outline-secondary:hover' => array('b','o'),
1307
				'.btn-outline-secondary.disabled' => array('c'),
1308
				'.btn-outline-secondary:disabled' => array('c'),
1309
				'.btn-outline-secondary:not(:disabled):not(.disabled):active' => array('b','o'),
1310
				'.btn-outline-secondary:not(:disabled):not(.disabled).active' => array('b','o'),
1311
				'.btn-outline-secondary.dropdown-toggle' => array('b','o'),
1312
				'.badge-secondary' => array('b'),
1313
				'.alert-secondary' => array('b','o'),
1314
				'.btn-link.btn-secondary' => array('c'),
1315
			);
1316
1317
			$important_selectors = array(
1318
				'.bg-secondary' => array('b','f'),
1319
				'.border-secondary' => array('o'),
1320
				'.text-secondary' => array('c'),
1321
			);
1322
1323
			$color = array();
1324
			$color_i = array();
1325
			$background = array();
1326
			$background_i = array();
1327
			$border = array();
1328
			$border_i = array();
1329
			$fill = array();
1330
			$fill_i = array();
1331
1332
			$output = '';
1333
1334
			// build rules into each type
1335
			foreach($selectors as $selector => $types){
1336
				$selector = $compatibility ? ".bsui ".$selector : $selector;
1337
				$types = array_combine($types,$types);
1338
				if(isset($types['c'])){$color[] = $selector;}
1339
				if(isset($types['b'])){$background[] = $selector;}
1340
				if(isset($types['o'])){$border[] = $selector;}
1341
				if(isset($types['f'])){$fill[] = $selector;}
1342
			}
1343
1344
			// build rules into each type
1345
			foreach($important_selectors as $selector => $types){
1346
				$selector = $compatibility ? ".bsui ".$selector : $selector;
1347
				$types = array_combine($types,$types);
1348
				if(isset($types['c'])){$color_i[] = $selector;}
1349
				if(isset($types['b'])){$background_i[] = $selector;}
1350
				if(isset($types['o'])){$border_i[] = $selector;}
1351
				if(isset($types['f'])){$fill_i[] = $selector;}
1352
			}
1353
1354
			// add any color rules
1355
			if(!empty($color)){
1356
				$output .= implode(",",$color) . "{color: $color_code;} ";
1357
			}
1358
			if(!empty($color_i)){
1359
				$output .= implode(",",$color_i) . "{color: $color_code !important;} ";
1360
			}
1361
1362
			// add any background color rules
1363
			if(!empty($background)){
1364
				$output .= implode(",",$background) . "{background-color: $color_code;} ";
1365
			}
1366
			if(!empty($background_i)){
1367
				$output .= implode(",",$background_i) . "{background-color: $color_code !important;} ";
1368
			}
1369
1370
			// add any border color rules
1371
			if(!empty($border)){
1372
				$output .= implode(",",$border) . "{border-color: $color_code;} ";
1373
			}
1374
			if(!empty($border_i)){
1375
				$output .= implode(",",$border_i) . "{border-color: $color_code !important;} ";
1376
			}
1377
1378
			// add any fill color rules
1379
			if(!empty($fill)){
1380
				$output .= implode(",",$fill) . "{fill: $color_code;} ";
1381
			}
1382
			if(!empty($fill_i)){
1383
				$output .= implode(",",$fill_i) . "{fill: $color_code !important;} ";
1384
			}
1385
1386
1387
			$prefix = $compatibility ? ".bsui " : "";
1388
1389
			// darken
1390
			$darker_075 = self::css_hex_lighten_darken($color_code,"-0.075");
0 ignored issues
show
Bug introduced by
'-0.075' of type string is incompatible with the type double expected by parameter $adjustPercent of AyeCode_UI_Settings::css_hex_lighten_darken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1390
			$darker_075 = self::css_hex_lighten_darken($color_code,/** @scrutinizer ignore-type */ "-0.075");
Loading history...
1391
			$darker_10 = self::css_hex_lighten_darken($color_code,"-0.10");
1392
			$darker_125 = self::css_hex_lighten_darken($color_code,"-0.125");
1393
1394
			// lighten
1395
			$lighten_25 = self::css_hex_lighten_darken($color_code,"0.25");
0 ignored issues
show
Unused Code introduced by
The assignment to $lighten_25 is dead and can be removed.
Loading history...
1396
1397
			// opacity see https://css-tricks.com/8-digit-hex-codes/
1398
			$op_25 = $color_code."40"; // 25% opacity
1399
1400
1401
			// button states
1402
			$output .= $prefix ." .btn-secondary:hover{background-color: ".$darker_075.";    border-color: ".$darker_10.";} ";
1403
			$output .= $prefix ." .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, $prefix .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show>$prefix .btn-outline-secondary.dropdown-toggle:focus{box-shadow: 0 0 0 0.2rem $op_25;} ";
1404
			$output .= $prefix ." .btn-secondary:not(:disabled):not(.disabled):active, $prefix .btn-secondary:not(:disabled):not(.disabled).active, .show>$prefix .btn-secondary.dropdown-toggle{background-color: ".$darker_10.";    border-color: ".$darker_125.";} ";
1405
			$output .= $prefix ." .btn-secondary:not(:disabled):not(.disabled):active:focus, $prefix .btn-secondary:not(:disabled):not(.disabled).active:focus, .show>$prefix .btn-secondary.dropdown-toggle:focus {box-shadow: 0 0 0 0.2rem $op_25;} ";
1406
1407
1408
			return $output;
1409
		}
1410
1411
		/**
1412
		 * Increases or decreases the brightness of a color by a percentage of the current brightness.
1413
		 *
1414
		 * @param   string  $hexCode        Supported formats: `#FFF`, `#FFFFFF`, `FFF`, `FFFFFF`
1415
		 * @param   float   $adjustPercent  A number between -1 and 1. E.g. 0.3 = 30% lighter; -0.4 = 40% darker.
1416
		 *
1417
		 * @return  string
1418
		 */
1419
		public static function css_hex_lighten_darken($hexCode, $adjustPercent) {
1420
			$hexCode = ltrim($hexCode, '#');
1421
1422
			if (strlen($hexCode) == 3) {
1423
				$hexCode = $hexCode[0] . $hexCode[0] . $hexCode[1] . $hexCode[1] . $hexCode[2] . $hexCode[2];
1424
			}
1425
1426
			$hexCode = array_map('hexdec', str_split($hexCode, 2));
1427
1428
			foreach ($hexCode as & $color) {
1429
				$adjustableLimit = $adjustPercent < 0 ? $color : 255 - $color;
1430
				$adjustAmount = ceil($adjustableLimit * $adjustPercent);
1431
1432
				$color = str_pad(dechex($color + $adjustAmount), 2, '0', STR_PAD_LEFT);
0 ignored issues
show
Bug introduced by
$color + $adjustAmount of type double is incompatible with the type integer expected by parameter $number of dechex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1432
				$color = str_pad(dechex(/** @scrutinizer ignore-type */ $color + $adjustAmount), 2, '0', STR_PAD_LEFT);
Loading history...
1433
			}
1434
1435
			return '#' . implode($hexCode);
1436
		}
1437
1438
		/**
1439
		 * Check if we should display examples.
1440
		 */
1441
		public function maybe_show_examples(){
1442
			if(current_user_can('manage_options') && isset($_REQUEST['preview-aui'])){
1443
				echo "<head>";
1444
				wp_head();
1445
				echo "</head>";
1446
				echo "<body>";
1447
				echo $this->get_examples();
1448
				echo "</body>";
1449
				exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1450
			}
1451
		}
1452
1453
		/**
1454
		 * Get developer examples.
1455
		 *
1456
		 * @return string
1457
		 */
1458
		public function get_examples(){
1459
			$output = '';
1460
1461
1462
			// open form
1463
			$output .= "<form class='p-5 m-5 border rounded'>";
1464
1465
			// input example
1466
			$output .= aui()->input(array(
1467
				'type'  =>  'text',
1468
				'id'    =>  'text-example',
1469
				'name'    =>  'text-example',
1470
				'placeholder'   => 'text placeholder',
1471
				'title'   => 'Text input example',
1472
				'value' =>  '',
1473
				'required'  => false,
1474
				'help_text' => 'help text',
1475
				'label' => 'Text input example label'
1476
			));
1477
1478
			// input example
1479
			$output .= aui()->input(array(
1480
				'type'  =>  'url',
1481
				'id'    =>  'text-example2',
1482
				'name'    =>  'text-example',
1483
				'placeholder'   => 'url placeholder',
1484
				'title'   => 'Text input example',
1485
				'value' =>  '',
1486
				'required'  => false,
1487
				'help_text' => 'help text',
1488
				'label' => 'Text input example label'
1489
			));
1490
1491
			// checkbox example
1492
			$output .= aui()->input(array(
1493
				'type'  =>  'checkbox',
1494
				'id'    =>  'checkbox-example',
1495
				'name'    =>  'checkbox-example',
1496
				'placeholder'   => 'checkbox-example',
1497
				'title'   => 'Checkbox example',
1498
				'value' =>  '1',
1499
				'checked'   => true,
1500
				'required'  => false,
1501
				'help_text' => 'help text',
1502
				'label' => 'Checkbox checked'
1503
			));
1504
1505
			// checkbox example
1506
			$output .= aui()->input(array(
1507
				'type'  =>  'checkbox',
1508
				'id'    =>  'checkbox-example2',
1509
				'name'    =>  'checkbox-example2',
1510
				'placeholder'   => 'checkbox-example',
1511
				'title'   => 'Checkbox example',
1512
				'value' =>  '1',
1513
				'checked'   => false,
1514
				'required'  => false,
1515
				'help_text' => 'help text',
1516
				'label' => 'Checkbox un-checked'
1517
			));
1518
1519
			// switch example
1520
			$output .= aui()->input(array(
1521
				'type'  =>  'checkbox',
1522
				'id'    =>  'switch-example',
1523
				'name'    =>  'switch-example',
1524
				'placeholder'   => 'checkbox-example',
1525
				'title'   => 'Switch example',
1526
				'value' =>  '1',
1527
				'checked'   => true,
1528
				'switch'    => true,
1529
				'required'  => false,
1530
				'help_text' => 'help text',
1531
				'label' => 'Switch on'
1532
			));
1533
1534
			// switch example
1535
			$output .= aui()->input(array(
1536
				'type'  =>  'checkbox',
1537
				'id'    =>  'switch-example2',
1538
				'name'    =>  'switch-example2',
1539
				'placeholder'   => 'checkbox-example',
1540
				'title'   => 'Switch example',
1541
				'value' =>  '1',
1542
				'checked'   => false,
1543
				'switch'    => true,
1544
				'required'  => false,
1545
				'help_text' => 'help text',
1546
				'label' => 'Switch off'
1547
			));
1548
1549
			// close form
1550
			$output .= "</form>";
1551
1552
			return $output;
1553
		}
1554
1555
	}
1556
1557
	/**
1558
	 * Run the class if found.
1559
	 */
1560
	AyeCode_UI_Settings::instance();
1561
}