Customify – A Theme Customizer Booster - Version 2.3.0

Version Description

  • Improved support for the new Gutenberg block editor. Compatible with the latest WordPress 5.0 beta version.
  • Big performance improvements both in the frontend and also in the Customizer.
  • Cleanup regarding old and deprecated features.
Download this release

Release Info

Developer vlad.olaru
Plugin Icon Customify – A Theme Customizer Booster
Version 2.3.0
Comparing to
See all releases

Code changes from version 2.2.0 to 2.3.0

class-pixcustomify.php CHANGED
@@ -158,6 +158,11 @@ class PixCustomifyPlugin {
158
  * Initialize plugin
159
  */
160
  private function init() {
 
 
 
 
 
161
  // Load the config file
162
  $this->config = $this->get_config();
163
  // Load the plugin's settings from the DB
@@ -183,11 +188,6 @@ class PixCustomifyPlugin {
183
  * Register our actions and filters
184
  */
185
  function register_hooks() {
186
- /*
187
- * Register hooks that are fired when the plugin is activated, deactivated, and uninstalled, respectively.
188
- */
189
- register_activation_hook( $this->file, array( $this, 'activate' ) );
190
- register_deactivation_hook( $this->file, array( $this, 'deactivate' ) );
191
 
192
  /*
193
  * Load plugin text domain
@@ -209,6 +209,11 @@ class PixCustomifyPlugin {
209
  // We need to be able to load things like components configs depending on those firing up or not
210
  // DO NOT TRY to use the Customify values before this!
211
  add_action( 'init', array( $this, 'load_plugin_configs' ), 15 );
 
 
 
 
 
212
 
213
  /*
214
  * Now setup the admin side of things
@@ -245,6 +250,7 @@ class PixCustomifyPlugin {
245
  $load_location = $this->get_plugin_setting( 'style_resources_location', 'wp_head' );
246
 
247
  add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
 
248
  add_action( 'wp_head', array( $this, 'output_typography_dynamic_style' ), 10 );
249
 
250
  add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
@@ -253,7 +259,7 @@ class PixCustomifyPlugin {
253
  add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
254
 
255
  if ( $this->get_plugin_setting( 'enable_editor_style', true ) ) {
256
- add_action( 'admin_head', array( $this, 'add_customizer_settings_into_wp_editor' ) );
257
  }
258
 
259
  add_action( 'rest_api_init', array( $this, 'add_rest_routes_api' ) );
@@ -310,10 +316,7 @@ class PixCustomifyPlugin {
310
  */
311
  function load_plugin_configs() {
312
 
313
- // Allow themes or other plugins to filter the config.
314
- $this->customizer_config = apply_filters( 'customify_filter_fields', $this->customizer_config );
315
- // We apply a second filter for those that wish to work with the final config and not rely on a a huge priority number.
316
- $this->customizer_config = apply_filters( 'customify_final_config', $this->customizer_config );
317
 
318
  $this->opt_name = $this->localized['options_name'] = $this->customizer_config['opt-name'];
319
  $this->options_list = $this->get_options();
@@ -335,32 +338,75 @@ class PixCustomifyPlugin {
335
  $this->localized['style_manager_user_feedback_provided'] = get_option( 'style_manager_user_feedback_provided', false );
336
  }
337
 
338
- public function get_version() {
339
- return $this->_version;
340
- }
 
 
 
 
 
 
 
341
 
342
- public function get_slug() {
343
- return $this->plugin_slug;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
345
 
346
  /**
347
- * Fired when the plugin is activated.
348
- * @since 1.0.0
349
  *
350
- * @param boolean $network_wide True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog.
351
  */
352
- public static function activate( $network_wide ) {
353
- //@todo Define activation functionality here
 
354
  }
355
 
356
  /**
357
- * Fired when the plugin is deactivated.
358
- * @since 1.0.0
 
359
  *
360
- * @param boolean $network_wide True if WPMU superadmin uses "Network Deactivate" action, false if WPMU is disabled or plugin is deactivated on an individual blog.
361
  */
362
- static function deactivate( $network_wide ) {
363
- //@todo Define deactivation functionality here
 
 
 
 
 
 
 
 
364
  }
365
 
366
  /**
@@ -533,9 +579,71 @@ class PixCustomifyPlugin {
533
  }
534
 
535
  /**
536
- * Public style generated by customizer
537
  */
538
- function output_dynamic_style() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  $custom_css = '';
540
 
541
  foreach ( $this->options_list as $option_id => $option ) {
@@ -571,7 +679,7 @@ class PixCustomifyPlugin {
571
  }
572
 
573
  if ( ! empty( $media_query_custom_css ) ) {
574
- $media_query_custom_css = PHP_EOL . '@media ' . $media_query . " { " . PHP_EOL . PHP_EOL . $custom_css . "}" . PHP_EOL;
575
  }
576
 
577
  if ( ! empty( $media_query_custom_css ) ) {
@@ -580,87 +688,169 @@ class PixCustomifyPlugin {
580
 
581
  }
582
  }
583
- ?>
584
 
585
- <style id="customify_output_style">
586
- <?php echo apply_filters( 'customify_dynamic_style', $custom_css ); ?>
587
- </style>
588
- <?php
589
 
590
- /**
591
- * from now on we output only style tags only for the preview purpose
592
- * so don't cry if you see 30+ style tags for each section
593
- */
594
- if ( ! isset( $GLOBALS['wp_customize'] ) ) {
595
- return;
596
  }
597
 
598
- foreach ( $this->options_list as $option_id => $options ) {
 
 
599
 
600
- if ( isset( $options['type'] ) && $options['type'] === 'custom_background' ) {
601
- $options['value'] = $this->get_option( $option_id );
602
- $custom_background_output = $this->process_custom_background_field_output( $option_id, $options ); ?>
603
 
604
- <style id="custom_background_output_for_<?php echo sanitize_html_class( $option_id ); ?>">
605
- <?php
606
- if ( ! empty( $custom_background_output )) {
607
- echo $custom_background_output;
608
- } ?>
609
- </style>
610
- <?php }
611
 
612
- if ( ! isset( $options['live'] ) || $options['live'] !== true ) {
613
- continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  }
615
 
616
- $this_value = $this->get_option( $option_id );
617
- if ( ! empty( $options['css'] ) ) {
618
- foreach ( $options['css'] as $key => $properties_set ) {
619
- // We need to use a class because we may have multiple <style>s with the same "ID" for example
620
- // when targeting the same property but with different selectors.
621
- ?>
622
- <style class="dynamic_setting_<?php echo sanitize_html_class( $option_id ) . '_property_' . str_replace( '-', '_', $properties_set['property'] ) . '_' . $key; ?>"
623
- type="text/css"><?php
624
 
625
- if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
626
- echo '@media '. $properties_set['media'] . " {" . PHP_EOL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  }
 
628
 
629
- if ( isset( $properties_set['selector'] ) && isset( $properties_set['property'] ) ) {
630
- $css_output = $this->process_css_property($properties_set, $this_value);
631
- if ( ! empty( $css_output ) ) {
632
- echo $css_output . PHP_EOL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
  }
634
  }
635
 
636
- if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
637
- echo "}" . PHP_EOL;
638
- } ?>
639
- </style>
640
- <?php }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  }
642
  }
643
- }
644
 
645
- protected function load_google_fonts() {
646
- $fonts_path = plugin_dir_path( $this->file ) . 'features/customizer/controls/resources/google.fonts.php';
647
 
648
- if ( file_exists( $fonts_path ) ) {
649
- $this->google_fonts = require( $fonts_path );
650
- }
651
 
652
- if ( ! empty( $this->google_fonts ) ) {
653
- return $this->google_fonts;
654
- }
655
 
656
- return false;
 
 
 
 
 
657
  }
658
 
659
- function output_typography_dynamic_style() {
 
 
660
  $this->get_typography_fields( $this->options_list, 'type', 'typography', $this->typo_settings );
661
 
662
  if ( empty( $this->typo_settings ) ) {
663
- return;
664
  }
665
 
666
  $families = '';
@@ -743,140 +933,38 @@ class PixCustomifyPlugin {
743
  }
744
  }
745
 
746
- if ( ! empty ( $families ) && $this->get_plugin_setting( 'typography', '1' ) && $this->get_plugin_setting( 'typography_google_fonts', 1 ) ) { ?>
747
- <script type="text/javascript">
748
- if (typeof WebFont !== 'undefined') {<?php // if there is a WebFont object, use it ?>
749
- WebFont.load({
750
- google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
751
- classes: false,
752
- events: false
753
- });
754
- } else {<?php // basically when we don't have the WebFont object we create the google script dynamically ?>
755
-
756
- var tk = document.createElement('script');
757
- tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
758
- tk.type = 'text/javascript';
759
-
760
- tk.onload = tk.onreadystatechange = function () {
761
- WebFont.load({
762
- google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
763
- classes: false,
764
- events: false
765
- });
766
- };
767
-
768
- var s = document.getElementsByTagName('script')[0];
769
- s.parentNode.insertBefore(tk, s);
770
- }
771
- </script>
772
- <?php } ?>
773
- <style id="customify_typography_output_style">
774
- <?php
775
-
776
- foreach ( $this->typo_settings as $font ) {
777
- $selector = apply_filters( 'customify_typography_css_selector', $font['selector'], $font );
778
-
779
- $load_all_weights = false;
780
- if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
781
- $load_all_weights = true;
782
- }
783
-
784
- if ( isset( $selector ) && isset( $font['value'] ) && ! empty( $font['value'] ) ) {
785
- // Make sure that the value is in the proper format
786
- $value = PixCustomifyPlugin::decodeURIComponent( $font['value'] );
787
- if ( is_string( $value ) ) {
788
- $value = json_decode( $value, true );
789
- }
790
-
791
- // In case the value is null (most probably because the json_decode failed),
792
- // try the default value (mostly for google fonts)
793
- if ( $value === null ) {
794
- $value = $this->get_font_defaults_value( $font['value'] );
795
- }
796
-
797
- // Shim the old case when the default was only the font name
798
- if ( ! empty( $value ) && is_string( $value ) ) {
799
- $value = array( 'font_family' => $value );
800
- }
801
-
802
- // Handle special logic for when the $value array is not an associative array
803
- if ( ! $this->is_assoc( $value ) ) {
804
- $value = $this->standardize_non_associative_font_default( $value );
805
- }
806
-
807
- // Bail if empty or we don't have an array
808
- if ( empty( $value ) || ! is_array( $value ) ) {
809
- continue;
810
- }
811
-
812
- $selected_variant = '';
813
- if ( ! empty( $value['selected_variants'] ) ) {
814
- if ( is_array( $value['selected_variants'] ) ) {
815
- $selected_variant = $value['selected_variants'][0];
816
- } else {
817
- $selected_variant = $value['selected_variants'];
818
- }
819
- }
820
-
821
- // First handle the case where we have the font-family in the selected variant (usually this means a custom font from our Fonto plugin)
822
- if ( ! empty( $selected_variant ) && is_array( $selected_variant ) && ! empty( $selected_variant['font-family'] ) ) {
823
- // The variant's font-family
824
- echo $selector . " {\nfont-family: " . $selected_variant['font-family'] . ";\n";
825
-
826
- if ( ! $load_all_weights ) {
827
- // If this is a custom font (like from our plugin Fonto) with individual styles & weights - i.e. the font-family says it all
828
- // we need to "force" the font-weight and font-style
829
- if ( ! empty( $value['type'] ) && 'custom_individual' == $value['type'] ) {
830
- $selected_variant['font-weight'] = '400 !important';
831
- $selected_variant['font-style'] = 'normal !important';
832
- }
833
-
834
- // Output the font weight, if available
835
- if ( ! empty( $selected_variant['font-weight'] ) ) {
836
- echo "font-weight: " . $selected_variant['font-weight'] . ";\n";
837
- }
838
-
839
- // Output the font style, if available
840
- if ( ! empty( $selected_variant['font-style'] ) ) {
841
- echo "font-style: " . $selected_variant['font-style'] . ";\n";
842
- }
843
- }
844
-
845
- echo "}\n";
846
- } elseif ( isset( $value['font_family'] ) ) {
847
- // The selected font family
848
- echo $selector . " {\n font-family: " . $value['font_family'] . ";\n";
849
-
850
- if ( ! empty( $selected_variant ) && ! $load_all_weights ) {
851
- $weight_and_style = strtolower( $selected_variant );
852
-
853
- $italic_font = false;
854
-
855
- //determine if this is an italic font (the $weight_and_style is usually like '400' or '400italic' )
856
- if ( strpos( $weight_and_style, 'italic' ) !== false ) {
857
- $weight_and_style = str_replace( 'italic', '', $weight_and_style);
858
- $italic_font = true;
859
- }
860
-
861
- if ( ! empty( $weight_and_style ) ) {
862
- //a little bit of sanity check - in case it's not a number
863
- if( $weight_and_style === 'regular' ) {
864
- $weight_and_style = 'normal';
865
- }
866
- echo "font-weight: " . $weight_and_style . ";\n";
867
- }
868
-
869
- if ( $italic_font ) {
870
- echo "font-style: italic;\n";
871
- }
872
- }
873
 
874
- echo "}\n";
875
- }
876
- }
877
- } ?>
878
- </style>
879
- <?php }
880
 
881
  /**
882
  * Handle special logic for when the $value array is not an associative array
@@ -993,7 +1081,7 @@ class PixCustomifyPlugin {
993
  }
994
 
995
  // lose the tons of tabs
996
- $css_property['selector'] = trim( $css_property['selector'] );
997
 
998
  $css_property['selector'] = apply_filters( 'customify_css_selector', $css_property['selector'], $css_property );
999
 
@@ -1080,6 +1168,8 @@ class PixCustomifyPlugin {
1080
  $selector = implode( ' ', $options['output'] );
1081
  }
1082
 
 
 
1083
 
1084
  $output .= $selector . " {";
1085
  if ( isset( $value['background-image'] ) && ! empty( $value['background-image'] ) ) {
@@ -1111,71 +1201,74 @@ class PixCustomifyPlugin {
1111
  /**
1112
  * add our customizer styling edits into the wp_editor
1113
  */
1114
- function add_customizer_settings_into_wp_editor() {
1115
 
1116
  ob_start();
 
1117
  $this->output_typography_dynamic_style();
1118
  $this->output_dynamic_style();
1119
 
1120
- $custom_css = ob_get_clean(); ?>
1121
- <script type="text/javascript">
1122
- /* <![CDATA[ */
1123
- (function ($) {
1124
- $(window).load(function () {
1125
- /**
1126
- * @param iframe_id the id of the frame you want to append the style
1127
- * @param style_element the style element you want to append
1128
- */
1129
- var append_script_to_iframe = function (ifrm_id, scriptEl) {
1130
- var myIframe = document.getElementById(ifrm_id);
1131
-
1132
- var script = myIframe.contentWindow.document.createElement("script");
1133
- script.type = "text/javascript";
1134
- script.innerHTML = scriptEl.innerHTML;
1135
-
1136
- myIframe.contentWindow.document.head.appendChild(script);
1137
- };
1138
-
1139
- var append_style_to_iframe = function (ifrm_id, styleElment) {
1140
- var ifrm = window.frames[ifrm_id];
1141
- if ( typeof ifrm === "undefined" ) {
1142
- return;
1143
- }
1144
- ifrm = ( ifrm.contentDocument || ifrm.contentDocument || ifrm.document );
1145
- var head = ifrm.getElementsByTagName('head')[0];
1146
-
1147
- if (typeof styleElment !== "undefined") {
1148
- head.appendChild(styleElment);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1149
  }
1150
- };
1151
-
1152
- var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
1153
- parser = new DOMParser(),
1154
- doc = parser.parseFromString(xmlString, "text/html");
1155
-
1156
- if (typeof window.frames['content_ifr'] !== 'undefined') {
1157
-
1158
- $.each(doc.head.childNodes, function (key, el) {
1159
- if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
1160
-
1161
- switch (el.tagName) {
1162
- case 'STYLE' :
1163
- append_style_to_iframe('content_ifr', el);
1164
- break;
1165
- case 'SCRIPT' :
1166
- append_script_to_iframe('content_ifr', el);
1167
- break;
1168
- default:
1169
- break;
1170
- }
1171
- }
1172
- });
1173
  }
1174
  });
1175
- })(jQuery);
1176
- /* ]]> */
1177
- </script>
1178
- <?php }
 
 
 
 
1179
 
1180
  /**
1181
  * Register the administration menu for this plugin into the WordPress Dashboard menu.
158
  * Initialize plugin
159
  */
160
  private function init() {
161
+ // We don't want to put extra load on the heartbeat AJAX request.
162
+ if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && 'heartbeat' === $_REQUEST['action'] ) {
163
+ return;
164
+ }
165
+
166
  // Load the config file
167
  $this->config = $this->get_config();
168
  // Load the plugin's settings from the DB
188
  * Register our actions and filters
189
  */
190
  function register_hooks() {
 
 
 
 
 
191
 
192
  /*
193
  * Load plugin text domain
209
  // We need to be able to load things like components configs depending on those firing up or not
210
  // DO NOT TRY to use the Customify values before this!
211
  add_action( 'init', array( $this, 'load_plugin_configs' ), 15 );
212
+ // Also handle the force clearing of the cached config. Since we can't know wo can influence it, we need to be proactive.
213
+ add_action( 'activated_plugin', array( $this, 'clear_customizer_config_cache' ), 10 );
214
+ add_action( 'deactivated_plugin', array( $this, 'clear_customizer_config_cache' ), 10 );
215
+ add_action( 'switch_theme', array( $this, 'clear_customizer_config_cache' ), 10 );
216
+ add_action( 'upgrader_process_complete', array( $this, 'clear_customizer_config_cache' ), 10 );
217
 
218
  /*
219
  * Now setup the admin side of things
250
  $load_location = $this->get_plugin_setting( 'style_resources_location', 'wp_head' );
251
 
252
  add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
253
+ add_action( 'wp_head', array( $this, 'output_typography_dynamic_script' ), 10 );
254
  add_action( 'wp_head', array( $this, 'output_typography_dynamic_style' ), 10 );
255
 
256
  add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
259
  add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
260
 
261
  if ( $this->get_plugin_setting( 'enable_editor_style', true ) ) {
262
+ add_action( 'admin_enqueue_scripts', array( $this, 'script_to_add_customizer_settings_into_wp_editor' ), 10, 1 );
263
  }
264
 
265
  add_action( 'rest_api_init', array( $this, 'add_rest_routes_api' ) );
316
  */
317
  function load_plugin_configs() {
318
 
319
+ $this->load_customizer_config();
 
 
 
320
 
321
  $this->opt_name = $this->localized['options_name'] = $this->customizer_config['opt-name'];
322
  $this->options_list = $this->get_options();
338
  $this->localized['style_manager_user_feedback_provided'] = get_option( 'style_manager_user_feedback_provided', false );
339
  }
340
 
341
+ /**
342
+ * Set the customizer configuration.
343
+ *
344
+ * @since 2.2.1
345
+ *
346
+ * @param bool $skip_cache Optional. Whether to use the cached config or generate a new one.
347
+ */
348
+ protected function load_customizer_config( $skip_cache = false ) {
349
+ // First try and get the cached data
350
+ $data = get_option( $this->_get_customizer_config_cache_key() );
351
 
352
+ // We don't force skip the cache for AJAX requests for performance reasons.
353
+ if ( ! wp_doing_ajax() && defined('CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG' ) && true === CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG ) {
354
+ $skip_cache = true;
355
+ }
356
+
357
+ // For performance reasons, we will ONLY regenerate when in the WP ADMIN area or via an ADMIN AJAX call.
358
+ if ( false !== $data && false === $skip_cache && ! is_admin() && ! is_customize_preview() ) {
359
+ $this->customizer_config = $data;
360
+ return;
361
+ }
362
+
363
+ // Get the cache data expiration timestamp.
364
+ $expire_timestamp = get_option( $this->_get_customizer_config_cache_key() . '_timestamp' );
365
+
366
+ // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
367
+ if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
368
+ // Allow themes or other plugins to filter the config.
369
+ // We use $this->customizer_config so we can start from whatever default configuration it may be.
370
+ $data = apply_filters( 'customify_filter_fields', $this->customizer_config );
371
+ // We apply a second filter for those that wish to work with the final config and not rely on a a huge priority number.
372
+ $data = apply_filters( 'customify_final_config', $data );
373
+
374
+ // Cache the data in an option for 6 hours
375
+ update_option( $this->_get_customizer_config_cache_key() , $data, true );
376
+ update_option( $this->_get_customizer_config_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
377
+ }
378
+
379
+ $this->customizer_config = $data;
380
+ return;
381
  }
382
 
383
  /**
384
+ * Clear the customizer config cache.
 
385
  *
386
+ * @since 2.2.1
387
  */
388
+ public function clear_customizer_config_cache() {
389
+ delete_option( $this->_get_customizer_config_cache_key() );
390
+ delete_option( $this->_get_customizer_config_cache_key() . '_timestamp' );
391
  }
392
 
393
  /**
394
+ * Get the customizer config cache key.
395
+ *
396
+ * @since 2.2.1
397
  *
398
+ * @return string
399
  */
400
+ private function _get_customizer_config_cache_key() {
401
+ return 'customify_customizer_config';
402
+ }
403
+
404
+ public function get_version() {
405
+ return $this->_version;
406
+ }
407
+
408
+ public function get_slug() {
409
+ return $this->plugin_slug;
410
  }
411
 
412
  /**
579
  }
580
 
581
  /**
582
+ * Output CSS style generated by customizer
583
  */
584
+ function output_dynamic_style() { ?>
585
+ <style id="customify_output_style">
586
+ <?php echo $this->get_dynamic_style(); ?>
587
+ </style>
588
+ <?php
589
+
590
+ /**
591
+ * from now on we output only style tags only for the preview purpose
592
+ * so don't cry if you see 30+ style tags for each section
593
+ */
594
+ if ( ! isset( $GLOBALS['wp_customize'] ) ) {
595
+ return;
596
+ }
597
+
598
+ foreach ( $this->options_list as $option_id => $options ) {
599
+
600
+ if ( isset( $options['type'] ) && $options['type'] === 'custom_background' ) {
601
+ $options['value'] = $this->get_option( $option_id );
602
+ $custom_background_output = $this->process_custom_background_field_output( $option_id, $options ); ?>
603
+
604
+ <style id="custom_background_output_for_<?php echo sanitize_html_class( $option_id ); ?>">
605
+ <?php
606
+ if ( ! empty( $custom_background_output )) {
607
+ echo $custom_background_output;
608
+ } ?>
609
+ </style>
610
+ <?php }
611
+
612
+ if ( ! isset( $options['live'] ) || $options['live'] !== true ) {
613
+ continue;
614
+ }
615
+
616
+ $this_value = $this->get_option( $option_id );
617
+ if ( ! empty( $options['css'] ) ) {
618
+ foreach ( $options['css'] as $key => $properties_set ) {
619
+ // We need to use a class because we may have multiple <style>s with the same "ID" for example
620
+ // when targeting the same property but with different selectors.
621
+
622
+ $inline_style = '<style class="dynamic_setting_ ' . sanitize_html_class( $option_id ) . '_property_' . str_replace( '-', '_', $properties_set['property'] ) . '_' . $key .'" type="text/css">';
623
+
624
+ if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
625
+ $inline_style .= '@media '. $properties_set['media'] . ' {';
626
+ }
627
+
628
+ if ( isset( $properties_set['selector'] ) && isset( $properties_set['property'] ) ) {
629
+ $css_output = $this->process_css_property($properties_set, $this_value);
630
+ if ( ! empty( $css_output ) ) {
631
+ $inline_style .= $css_output;
632
+ }
633
+ }
634
+
635
+ if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
636
+ $inline_style .= '}';
637
+ }
638
+ $inline_style .= '</style>';
639
+
640
+ echo $inline_style;
641
+ }
642
+ }
643
+ }
644
+ }
645
+
646
+ function get_dynamic_style() {
647
  $custom_css = '';
648
 
649
  foreach ( $this->options_list as $option_id => $option ) {
679
  }
680
 
681
  if ( ! empty( $media_query_custom_css ) ) {
682
+ $media_query_custom_css = PHP_EOL . '@media ' . $media_query . " { " . PHP_EOL . PHP_EOL . $media_query_custom_css . "}" . PHP_EOL;
683
  }
684
 
685
  if ( ! empty( $media_query_custom_css ) ) {
688
 
689
  }
690
  }
 
691
 
692
+ return apply_filters( 'customify_dynamic_style', $custom_css );
693
+ }
 
 
694
 
695
+ protected function load_google_fonts() {
696
+ $fonts_path = plugin_dir_path( $this->file ) . 'features/customizer/controls/resources/google.fonts.php';
697
+
698
+ if ( file_exists( $fonts_path ) ) {
699
+ $this->google_fonts = require( $fonts_path );
 
700
  }
701
 
702
+ if ( ! empty( $this->google_fonts ) ) {
703
+ return $this->google_fonts;
704
+ }
705
 
706
+ return false;
707
+ }
 
708
 
709
+ function output_typography_dynamic_style() {
710
+ $style = $this->get_typography_dynamic_style();
 
 
 
 
 
711
 
712
+ if ( ! empty( $style ) ) { ?>
713
+ <style id="customify_typography_output_style">
714
+ <?php echo $style; ?>
715
+ </style>
716
+ <?php }
717
+ }
718
+
719
+ function get_typography_dynamic_style() {
720
+ $output = '';
721
+
722
+ $this->get_typography_fields( $this->options_list, 'type', 'typography', $this->typo_settings );
723
+
724
+ if ( empty( $this->typo_settings ) ) {
725
+ return $output;
726
+ }
727
+
728
+ ob_start();
729
+ foreach ( $this->typo_settings as $font ) {
730
+ $selector = apply_filters( 'customify_typography_css_selector', $font['selector'], $font );
731
+
732
+ $load_all_weights = false;
733
+ if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
734
+ $load_all_weights = true;
735
  }
736
 
737
+ if ( isset( $selector ) && isset( $font['value'] ) && ! empty( $font['value'] ) ) {
738
+ // Make sure that the value is in the proper format
739
+ $value = PixCustomifyPlugin::decodeURIComponent( $font['value'] );
740
+ if ( is_string( $value ) ) {
741
+ $value = json_decode( $value, true );
742
+ }
 
 
743
 
744
+ // In case the value is null (most probably because the json_decode failed),
745
+ // try the default value (mostly for google fonts)
746
+ if ( $value === null ) {
747
+ $value = $this->get_font_defaults_value( $font['value'] );
748
+ }
749
+
750
+ // Shim the old case when the default was only the font name
751
+ if ( ! empty( $value ) && is_string( $value ) ) {
752
+ $value = array( 'font_family' => $value );
753
+ }
754
+
755
+ // Handle special logic for when the $value array is not an associative array
756
+ if ( ! $this->is_assoc( $value ) ) {
757
+ $value = $this->standardize_non_associative_font_default( $value );
758
+ }
759
+
760
+ // Bail if empty or we don't have an array
761
+ if ( empty( $value ) || ! is_array( $value ) ) {
762
+ continue;
763
+ }
764
+
765
+ $selected_variant = '';
766
+ if ( ! empty( $value['selected_variants'] ) ) {
767
+ if ( is_array( $value['selected_variants'] ) ) {
768
+ $selected_variant = $value['selected_variants'][0];
769
+ } else {
770
+ $selected_variant = $value['selected_variants'];
771
  }
772
+ }
773
 
774
+ // First handle the case where we have the font-family in the selected variant (usually this means a custom font from our Fonto plugin)
775
+ if ( ! empty( $selected_variant ) && is_array( $selected_variant ) && ! empty( $selected_variant['font-family'] ) ) {
776
+ // The variant's font-family
777
+ echo $selector . " {\nfont-family: " . $selected_variant['font-family'] . ";\n";
778
+
779
+ if ( ! $load_all_weights ) {
780
+ // If this is a custom font (like from our plugin Fonto) with individual styles & weights - i.e. the font-family says it all
781
+ // we need to "force" the font-weight and font-style
782
+ if ( ! empty( $value['type'] ) && 'custom_individual' == $value['type'] ) {
783
+ $selected_variant['font-weight'] = '400 !important';
784
+ $selected_variant['font-style'] = 'normal !important';
785
+ }
786
+
787
+ // Output the font weight, if available
788
+ if ( ! empty( $selected_variant['font-weight'] ) ) {
789
+ echo "font-weight: " . $selected_variant['font-weight'] . ";\n";
790
+ }
791
+
792
+ // Output the font style, if available
793
+ if ( ! empty( $selected_variant['font-style'] ) ) {
794
+ echo "font-style: " . $selected_variant['font-style'] . ";\n";
795
  }
796
  }
797
 
798
+ echo "}\n";
799
+ } elseif ( isset( $value['font_family'] ) ) {
800
+ // The selected font family
801
+ echo $selector . " {\n font-family: " . $value['font_family'] . ";\n";
802
+
803
+ if ( ! empty( $selected_variant ) && ! $load_all_weights ) {
804
+ $weight_and_style = strtolower( $selected_variant );
805
+
806
+ $italic_font = false;
807
+
808
+ //determine if this is an italic font (the $weight_and_style is usually like '400' or '400italic' )
809
+ if ( strpos( $weight_and_style, 'italic' ) !== false ) {
810
+ $weight_and_style = str_replace( 'italic', '', $weight_and_style);
811
+ $italic_font = true;
812
+ }
813
+
814
+ if ( ! empty( $weight_and_style ) ) {
815
+ //a little bit of sanity check - in case it's not a number
816
+ if( $weight_and_style === 'regular' ) {
817
+ $weight_and_style = 'normal';
818
+ }
819
+ echo "font-weight: " . $weight_and_style . ";\n";
820
+ }
821
+
822
+ if ( $italic_font ) {
823
+ echo "font-style: italic;\n";
824
+ }
825
+ }
826
+
827
+ echo "}\n";
828
+ }
829
  }
830
  }
 
831
 
832
+ $output = ob_get_clean();
 
833
 
834
+ return $output;
835
+ }
 
836
 
837
+ function output_typography_dynamic_script() {
 
 
838
 
839
+ $script = $this->get_typography_dynamic_script();
840
+ if ( ! empty ( $script ) ) { ?>
841
+ <script type="text/javascript">
842
+ <?php echo $script; ?>
843
+ </script>
844
+ <?php }
845
  }
846
 
847
+ function get_typography_dynamic_script() {
848
+ $output = '';
849
+
850
  $this->get_typography_fields( $this->options_list, 'type', 'typography', $this->typo_settings );
851
 
852
  if ( empty( $this->typo_settings ) ) {
853
+ return $output;
854
  }
855
 
856
  $families = '';
933
  }
934
  }
935
 
936
+ if ( ! empty ( $families ) && $this->get_plugin_setting( 'typography', '1' ) && $this->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
937
+ ob_start();
938
+ ?>
939
+ if (typeof WebFont !== 'undefined') {<?php // if there is a WebFont object, use it ?>
940
+ WebFont.load({
941
+ google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
942
+ classes: false,
943
+ events: false
944
+ });
945
+ } else {<?php // basically when we don't have the WebFont object we create the google script dynamically ?>
946
+
947
+ var tk = document.createElement('script');
948
+ tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
949
+ tk.type = 'text/javascript';
950
+
951
+ tk.onload = tk.onreadystatechange = function () {
952
+ WebFont.load({
953
+ google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
954
+ classes: false,
955
+ events: false
956
+ });
957
+ };
958
+
959
+ var s = document.getElementsByTagName('script')[0];
960
+ s.parentNode.insertBefore(tk, s);
961
+ }
962
+ <?php
963
+ $output = ob_get_clean();
964
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
965
 
966
+ return $output;
967
+ }
 
 
 
 
968
 
969
  /**
970
  * Handle special logic for when the $value array is not an associative array
1081
  }
1082
 
1083
  // lose the tons of tabs
1084
+ $css_property['selector'] = trim( preg_replace( '/\t+/', '', $css_property['selector'] ) );
1085
 
1086
  $css_property['selector'] = apply_filters( 'customify_css_selector', $css_property['selector'], $css_property );
1087
 
1168
  $selector = implode( ' ', $options['output'] );
1169
  }
1170
 
1171
+ // Loose the ton of tabs.
1172
+ $selector = trim( preg_replace( '/\t+/', '', $selector ) );
1173
 
1174
  $output .= $selector . " {";
1175
  if ( isset( $value['background-image'] ) && ! empty( $value['background-image'] ) ) {
1201
  /**
1202
  * add our customizer styling edits into the wp_editor
1203
  */
1204
+ function script_to_add_customizer_settings_into_wp_editor() {
1205
 
1206
  ob_start();
1207
+ $this->output_typography_dynamic_script();
1208
  $this->output_typography_dynamic_style();
1209
  $this->output_dynamic_style();
1210
 
1211
+ $custom_css = ob_get_clean();
1212
+
1213
+ ob_start(); ?>
1214
+ (function ($) {
1215
+ $(window).load(function () {
1216
+ /**
1217
+ * @param iframe_id the id of the frame you want to append the style
1218
+ * @param style_element the style element you want to append - boooom
1219
+ */
1220
+ var append_script_to_iframe = function (ifrm_id, scriptEl) {
1221
+ var myIframe = document.getElementById(ifrm_id);
1222
+
1223
+ var script = myIframe.contentWindow.document.createElement("script");
1224
+ script.type = "text/javascript";
1225
+ script.innerHTML = scriptEl.innerHTML;
1226
+
1227
+ myIframe.contentWindow.document.head.appendChild(script);
1228
+ };
1229
+
1230
+ var append_style_to_iframe = function (ifrm_id, styleElment) {
1231
+ var ifrm = window.frames[ifrm_id];
1232
+ if ( typeof ifrm === "undefined" ) {
1233
+ return;
1234
+ }
1235
+ ifrm = ( ifrm.contentDocument || ifrm.contentDocument || ifrm.document );
1236
+ var head = ifrm.getElementsByTagName('head')[0];
1237
+
1238
+ if (typeof styleElment !== "undefined") {
1239
+ head.appendChild(styleElment);
1240
+ }
1241
+ };
1242
+
1243
+ var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
1244
+ parser = new DOMParser(),
1245
+ doc = parser.parseFromString(xmlString, "text/html");
1246
+
1247
+ if (typeof window.frames['content_ifr'] !== 'undefined') {
1248
+
1249
+ $.each(doc.head.childNodes, function (key, el) {
1250
+ if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
1251
+
1252
+ switch (el.tagName) {
1253
+ case 'STYLE' :
1254
+ append_style_to_iframe('content_ifr', el);
1255
+ break;
1256
+ case 'SCRIPT' :
1257
+ append_script_to_iframe('content_ifr', el);
1258
+ break;
1259
+ default:
1260
+ break;
1261
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1262
  }
1263
  });
1264
+ }
1265
+ });
1266
+ })(jQuery);
1267
+ <?php
1268
+ $script = ob_get_clean();
1269
+ wp_add_inline_script( 'editor', $script );
1270
+
1271
+ }
1272
 
1273
  /**
1274
  * Register the administration menu for this plugin into the WordPress Dashboard menu.
customify.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
  Description: A Theme Customizer Booster
6
- Version: 2.2.0
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
@@ -61,7 +61,7 @@ function PixCustomifyPlugin() {
61
  */
62
  require_once plugin_dir_path( __FILE__ ) . 'class-pixcustomify.php';
63
 
64
- $instance = PixCustomifyPlugin::instance( __FILE__, '2.2.0' );
65
 
66
  return $instance;
67
  }
@@ -71,5 +71,4 @@ function PixCustomifyPlugin() {
71
  $pixcustomify_plugin = PixCustomifyPlugin();
72
 
73
  // Load custom modules
74
- require_once( 'features/class-CSS_Editor.php' );
75
  require_once( 'features/class-Font_Selector.php' );
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
  Description: A Theme Customizer Booster
6
+ Version: 2.3.0
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
61
  */
62
  require_once plugin_dir_path( __FILE__ ) . 'class-pixcustomify.php';
63
 
64
+ $instance = PixCustomifyPlugin::instance( __FILE__, '2.3.0' );
65
 
66
  return $instance;
67
  }
71
  $pixcustomify_plugin = PixCustomifyPlugin();
72
 
73
  // Load custom modules
 
74
  require_once( 'features/class-Font_Selector.php' );
features/class-CSS_Editor.php DELETED
@@ -1,205 +0,0 @@
1
- <?php
2
-
3
- class Customify_CSS_Live_Editor {
4
-
5
- /**
6
- * Instance of this class.
7
- * @since 1.0.0
8
- * @var object
9
- */
10
- protected static $instance = null;
11
-
12
- protected function __construct() {
13
-
14
- add_action( 'customify_create_custom_control', array( $this, 'cle_create_custom_control' ), 10, 1 );
15
- add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_styles' ), 10 );
16
- $load_location = PixCustomifyPlugin()->get_plugin_setting( 'style_resources_location', 'wp_head' );
17
-
18
- if ( function_exists( 'wp_custom_css_cb' ) ) {
19
- // remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
20
- // add_action( $load_location, 'wp_custom_css_cb', 999999999 );
21
- } else {
22
- // keep this for wordpress versions lower than 4.7
23
- add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
24
- }
25
-
26
- //Check the WordPress version and if there are known problems disable it
27
- add_filter( 'customify_css_live_editor_enabled', array( $this, 'disable_if_wp_incompatible' ), 10, 1 );
28
- }
29
-
30
- /**
31
- * Return an instance of this class.
32
- * @since 1.0.0
33
- * @return object A single instance of this class.
34
- */
35
- public static function get_instance() {
36
-
37
- // If the single instance hasn't been set, set it now.
38
- if ( is_null( self::$instance ) ) {
39
- self::$instance = new self;
40
- }
41
-
42
- return self::$instance;
43
- }
44
-
45
- function enqueue_admin_customizer_styles() {
46
-
47
- $dir = plugin_dir_url( __FILE__ );
48
- $dir = rtrim( $dir, 'features/' );
49
-
50
- wp_enqueue_script( 'customify-ace-editor', $dir . '/js/ace/ace.js', array( 'jquery' ), false, true );
51
-
52
- if ( ! apply_filters( 'customify_css_live_editor_enabled', true ) ) {
53
- return ;
54
- }
55
-
56
- wp_enqueue_script( 'live-css-editor', $dir . '/js/live_css_editor.js', array( 'customify-ace-editor' ), false, true );
57
- }
58
-
59
- function cle_create_custom_control( $wp_customize ) {
60
- // Allow others to short-circuit us
61
- if ( ! apply_filters( 'customify_css_live_editor_enabled', true ) ) {
62
-
63
- // port CSS if
64
- if ( function_exists( 'wp_update_custom_css_post' ) ) {
65
- // Migrate any existing theme CSS to the core option added in WordPress 4.7.
66
- $store_type = PixCustomifyPlugin()->get_plugin_setting( 'values_store_mod', 'option' );
67
-
68
- $default_css = __( "/*
69
- * Welcome to the Custom CSS Editor
70
- *
71
- * CSS (Cascading Style Sheets) is a language that helps
72
- * the browser render your website. You may remove these
73
- * lines and get started with your own customizations.
74
- *
75
- * The generated code will be placed after the theme
76
- * stylesheets, which means that your rules can take
77
- * precedence and override the theme CSS rules. Just
78
- * write here what you want to change, you don't need
79
- * to copy all your theme's stylesheet content.
80
- *
81
- * Getting started with CSS (tutorial):
82
- * http://bit.ly/css-getting-started
83
- */
84
-
85
- /* An example of a Custom CSS Snippet */
86
- selector {
87
- color: green;
88
- }", 'customify' );
89
-
90
- if ( $store_type === 'option' ) {
91
- $CSS = get_option( 'live_css_edit' );
92
- } elseif ( $store_type === 'theme_mod' ) {
93
- $CSS = get_theme_mod( 'live_css_edit' );
94
- }
95
-
96
- if ( ! empty( $CSS ) && $default_css !== $CSS ) {
97
-
98
- $CSS = str_replace( $default_css, '', $CSS );
99
-
100
- $core_css = wp_get_custom_css(); // Preserve any CSS already added to the core option.
101
- $return = wp_update_custom_css_post( $core_css . $CSS );
102
- if ( ! is_wp_error( $return ) ) {
103
- // Remove the old theme_mod, so that the CSS is stored in only one place moving forward.
104
- remove_theme_mod( 'custom_theme_css' );
105
- if ( $store_type === 'option' ) {
106
- $CSS = delete_option( 'live_css_edit' );
107
- } elseif ( $store_type === 'theme_mod' ) {
108
- $CSS = remove_theme_mod( 'live_css_edit' );
109
- }
110
- }
111
- }
112
- }
113
-
114
- return;
115
- }
116
-
117
- $wp_customize->add_section( 'live_css_edit_section', array(
118
- 'priority' => 11,
119
- 'capability' => 'edit_theme_options',
120
- 'title' => __( 'CSS Editor', 'customify' ),
121
- ) );
122
-
123
- $saving_type = PixCustomifyPlugin()->get_plugin_setting( 'values_store_mod', 'option' );
124
-
125
- $wp_customize->add_setting( 'live_css_edit', array(
126
- 'type' => $saving_type,
127
- 'label' => __( 'CSS Editor', 'customify' ),
128
- 'capability' => 'edit_theme_options',
129
- 'transport' => 'postMessage',
130
- 'default' => __( "/*
131
- * Welcome to the Custom CSS Editor
132
- *
133
- * CSS (Cascading Style Sheets) is a language that helps
134
- * the browser render your website. You may remove these
135
- * lines and get started with your own customizations.
136
- *
137
- * The generated code will be placed after the theme
138
- * stylesheets, which means that your rules can take
139
- * precedence and override the theme CSS rules. Just
140
- * write here what you want to change, you don't need
141
- * to copy all your theme's stylesheet content.
142
- *
143
- * Getting started with CSS (tutorial):
144
- * http://bit.ly/css-getting-started
145
- */
146
-
147
- /* An example of a Custom CSS Snippet */
148
- selector {
149
- color: green;
150
- }", 'customify' )
151
- ) );
152
-
153
- $this_control = new Pix_Customize_CSS_Editor_Control(
154
- $wp_customize,
155
- 'live_css_edit_control',
156
- array(
157
- 'label' => __( 'Edit Live Css', 'customify' ),
158
- 'section' => 'live_css_edit_section',
159
- 'settings' => 'live_css_edit',
160
- )
161
- );
162
- $wp_customize->add_control( $this_control );
163
- }
164
-
165
- function output_dynamic_style() {
166
- // Allow others to short-circuit us
167
- if ( ! apply_filters( 'customify_css_live_editor_enabled', true ) ) {
168
- return;
169
- }
170
-
171
- $store_type = PixCustomifyPlugin()->get_plugin_setting( 'values_store_mod', 'option' );
172
- if ( $store_type === 'option' ) {
173
- $output = get_option( 'live_css_edit' );
174
- } elseif ( $store_type === 'theme_mod' ) {
175
- $output = get_theme_mod( 'live_css_edit' );
176
- }
177
-
178
- if ( empty( $output ) ) {
179
- return;
180
- } ?>
181
- <style id="customify_css_editor_output">
182
- <?php echo $output; ?>
183
- </style>
184
- <?php
185
- }
186
-
187
- function disable_if_wp_incompatible( $enabled ) {
188
- global $wp_version;
189
-
190
- // WordPress 4.7 introduced a Customizer CSS editor that conflicts with our own
191
- // It's best to leave only the one in core
192
- // So only load our CSS editor for WP version smaller than 4.7
193
- // We use 4.6.9 to catch the release candidates also
194
- if ( version_compare( $wp_version, '4.6.9', '>=' ) ) {
195
- return false;
196
- }
197
-
198
- return $enabled;
199
- }
200
- }
201
-
202
- $customify_CSS_Editor = Customify_CSS_Live_Editor::get_instance();
203
-
204
-
205
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
features/class-Font_Selector.php CHANGED
@@ -19,79 +19,81 @@ class Customify_Font_Selector {
19
  $this->theme_fonts = apply_filters( 'customify_theme_fonts', array() );
20
 
21
  $load_location = PixCustomifyPlugin()->get_plugin_setting( 'style_resources_location', 'wp_head' );
22
- add_action( $load_location, array( $this, 'output_font_dynamic_style' ), 100 );
 
23
  add_action( 'customify_font_family_before_options', array( $this, 'add_customify_theme_fonts' ), 11, 2 );
24
 
25
  if ( PixCustomifyPlugin()->get_plugin_setting( 'enable_editor_style', true ) ) {
26
- add_action( 'admin_head', array( $this, 'add_customizer_settings_into_wp_editor' ) );
27
  }
28
 
29
  }
30
 
31
- function add_customizer_settings_into_wp_editor() {
32
 
33
  ob_start();
34
- $this->output_font_dynamic_style();
35
 
36
- $custom_css = ob_get_clean(); ?>
37
- <script type="text/javascript">
38
- /* <![CDATA[ */
39
- (function ($) {
40
- $(window).load(function () {
41
- /**
42
- * @param iframe_id the id of the frame you want to append the style
43
- * @param style_element the style element you want to append
44
- */
45
- var append_script_to_iframe = function (ifrm_id, scriptEl) {
46
- var myIframe = document.getElementById(ifrm_id);
47
-
48
- var script = myIframe.contentWindow.document.createElement("script");
49
- script.type = "text/javascript";
50
- script.innerHTML = scriptEl.innerHTML;
51
-
52
- myIframe.contentWindow.document.head.appendChild(script);
53
- };
54
-
55
- var append_style_to_iframe = function (ifrm_id, styleElment) {
56
- var ifrm = window.frames[ifrm_id];
57
- if ( typeof ifrm === "undefined" ) {
58
- return;
59
- }
60
- ifrm = ( ifrm.contentDocument || ifrm.document );
61
- var head = ifrm.getElementsByTagName('head')[0];
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- if (typeof styleElment !== "undefined") {
64
- head.appendChild(styleElment);
 
 
 
 
 
 
 
 
 
 
65
  }
66
- };
67
-
68
- var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
69
- parser = new DOMParser(),
70
- doc = parser.parseFromString(xmlString, "text/html");
71
-
72
- if (typeof window.frames['content_ifr'] !== 'undefined') {
73
-
74
- $.each(doc.head.childNodes, function (key, el) {
75
- if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
76
-
77
- switch (el.tagName) {
78
- case 'STYLE' :
79
- append_style_to_iframe('content_ifr', el);
80
- break;
81
- case 'SCRIPT' :
82
- append_script_to_iframe('content_ifr', el);
83
- break;
84
- default:
85
- break;
86
- }
87
- }
88
- });
89
  }
90
  });
91
- })(jQuery);
92
- /* ]]> */
93
- </script>
94
- <?php }
 
 
 
95
 
96
  public function get_theme_fonts() {
97
  return $this->theme_fonts;
@@ -123,7 +125,13 @@ class Customify_Font_Selector {
123
  return $to_return;
124
  }
125
 
126
- function output_font_dynamic_style( $editor = false ) {
 
 
 
 
 
 
127
 
128
  /** @var PixCustomifyPlugin $local_plugin */
129
  $local_plugin = PixCustomifyPlugin();
@@ -133,17 +141,9 @@ class Customify_Font_Selector {
133
  $local_plugin->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
134
 
135
  if ( empty( self::$typo_settings ) ) {
136
- return;
137
  }
138
 
139
- $google_families = $local_families = '';
140
-
141
- $args = array(
142
- 'google_families' => '',
143
- 'local_families' => '',
144
- 'local_srcs' => '',
145
- );
146
-
147
  foreach ( self::$typo_settings as $id => $font ) {
148
  if ( isset ( $font['value'] ) ) {
149
 
@@ -156,7 +156,7 @@ class Customify_Font_Selector {
156
 
157
  $value = $this->validate_font_values( $value );
158
 
159
- // in case the value is still null, try default value(mostly for google fonts)
160
  if ( ! is_array( $value ) || $value === null ) {
161
  $value = $local_plugin->get_font_defaults_value( str_replace( '"', '', $font['value'] ) );
162
  }
@@ -166,45 +166,52 @@ class Customify_Font_Selector {
166
  continue;
167
  }
168
 
169
- //Handle special logic for when the $value array is not an associative array
170
  if ( ! $local_plugin->is_assoc( $value ) ) {
171
  $value = $local_plugin->standardize_non_associative_font_default( $value );
172
  }
173
 
174
- if ( isset( $value['font_family'] ) && isset( $value['type'] ) && $value['type'] == 'google' ) {
175
- $args['google_families'] .= "'" . $value['font_family'];
 
 
 
 
 
176
 
177
- if ( $load_all_weights && is_array( $value['variants'] ) ) {
178
- $args['google_families'] .= ":" . implode( ',', $value['variants'] );
179
- } elseif ( isset( $value['selected_variants'] ) && ! empty( $value['selected_variants'] ) ) {
180
  if ( is_array( $value['selected_variants'] ) ) {
181
- $args['google_families'] .= ":" . implode( ',', $value['selected_variants'] );
182
  } elseif ( is_string( $value['selected_variants'] ) || is_numeric( $value['selected_variants'] ) ) {
183
- $args['google_families'] .= ":" . $value['selected_variants'];
184
  }
185
- } elseif ( isset( $value['variants'] ) && ! empty( $value['variants'] ) ) {
186
  if ( is_array( $value['variants'] ) ) {
187
- $args['google_families'] .= ":" . implode( ',', $value['variants'] );
188
  } else {
189
- $args['google_families'] .= ":" . $value['variants'];
190
  }
191
  }
192
 
193
- if ( isset( $value['selected_subsets'] ) && ! empty( $value['selected_subsets'] ) ) {
194
  if ( is_array( $value['selected_subsets'] ) ) {
195
- $args['google_families'] .= ":" . implode( ',', $value['selected_subsets'] );
196
  } else {
197
- $args['google_families'] .= ":" . $value['selected_subsets'];
198
  }
199
- } elseif ( isset( $value['subsets'] ) && ! empty( $value['subsets'] ) ) {
200
  if ( is_array( $value['subsets'] ) ) {
201
- $args['google_families'] .= ":" . implode( ',', $value['subsets'] );
202
  } else {
203
- $args['google_families'] .= ":" . $value['subsets'];
204
  }
205
  }
206
 
207
- $args['google_families'] .= '\',';
 
 
208
  } elseif ( isset( $this->theme_fonts[ $value['font_family'] ] ) ) {
209
 
210
  // $value['type'] = 'theme_font';
@@ -212,96 +219,121 @@ class Customify_Font_Selector {
212
  // $value['variants'] = $this->theme_fonts[ $value['font_family'] ]['variants'];
213
 
214
  if ( false === strpos( $args['local_families'], $value['font_family'] ) ) {
215
- $args['local_families'] .= "'" . $value['font_family'] . "',";
216
  }
217
 
218
  if ( false === strpos( $args['local_srcs'], $this->theme_fonts[ $value['font_family'] ]['src'] ) ) {
219
- $args['local_srcs'] .= "'" . $this->theme_fonts[ $value['font_family'] ]['src'] . "',";
220
  }
221
  }
222
  }
223
  }
224
 
225
- if ( ( ! empty ( $args['local_families'] ) || ! empty ( $args['google_families'] ) ) && PixCustomifyPlugin()->get_plugin_setting( 'typography', '1' ) && PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
226
- $this->display_webfont_script( $args );
227
- }
 
 
228
 
229
- foreach ( self::$typo_settings as $key => $font ) {
230
- if ( ! isset( $font['selector'] ) ) {
231
- continue;
232
- }
233
 
234
- $font['selector'] = apply_filters( 'customify_font_css_selector', $font['selector'], $font );
235
 
236
- if ( empty( $font['selector'] ) || empty( $font['value'] ) ) {
237
- continue;
238
- }
239
 
240
- $value = $this->maybe_decode_value( $font['value'] );
 
241
 
242
- if ( $value === null ) {
243
- $value = $local_plugin->get_font_defaults_value( $font['value'] );
244
- }
245
 
246
- // shim the old case when the default was only the font name
247
- if ( is_string( $value ) && ! empty( $value ) ) {
248
- $value = array( 'font_family' => $value );
249
- }
250
 
251
- // Handle special logic for when the $value array is not an associative array
252
- if ( ! $local_plugin->is_assoc( $value ) ) {
253
- $value = $local_plugin->standardize_non_associative_font_default( $value );
 
254
  }
255
 
256
- $this->output_font_style( $key, $font, $value, $editor );
 
 
 
 
 
 
 
 
257
  }
258
 
259
  // in customizer the CSS is printed per option, in front-end we need to print them in bulk
260
  if ( ! isset( $GLOBALS['wp_customize'] ) ) { ?>
261
- <style id="customify_fonts_output">
262
- <?php echo join( "\n", $this->customify_CSS_output ); ?>
263
- </style><?php
264
- return;
265
  }
266
  }
267
 
268
- function display_webfont_script( $args ) { ?>
269
- <script type="text/javascript">
270
- var customify_font_loader = function () {
271
- var webfontargs = {
272
- classes: false,
273
- events: false
274
- };
275
- <?php if ( ! empty( $args['google_families'] ) ) { ?>
276
- webfontargs.google = {families: [<?php echo( rtrim( $args['google_families'], ',' ) ); ?>]};
277
- <?php }
278
- if ( ! empty( $args['local_families'] ) && ! empty( $args['local_srcs'] ) ) { ?>
279
- webfontargs.custom = {
280
- families: [<?php echo( rtrim( $args['local_families'], ',' ) ); ?>],
281
- urls: [<?php echo rtrim( $args['local_srcs'], ',' ) ?>]
282
- };
283
- <?php } ?>
284
- WebFont.load(webfontargs);
285
- };
286
 
287
- if (typeof WebFont !== 'undefined') { <?php // if there is a WebFont object, use it ?>
288
- customify_font_loader();
289
- } else { <?php // basically when we don't have the WebFont object we create the google script dynamically ?>
290
- var tk = document.createElement('script');
291
- tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
292
- tk.type = 'text/javascript';
293
-
294
- tk.onload = tk.onreadystatechange = function () {
295
- customify_font_loader();
296
- };
297
- var s = document.getElementsByTagName('script')[0];
298
- s.parentNode.insertBefore(tk, s);
299
  }
300
- </script>
301
- <?php
 
 
 
302
  }
303
 
304
- function output_font_style( $field, $font, $value, $editor ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
  $value = $this->validate_font_values( $value );
307
  // some sanitizing
@@ -463,14 +495,61 @@ class Customify_Font_Selector {
463
 
464
  $CSS = ob_get_clean();
465
 
466
- if ( isset( $GLOBALS['wp_customize'] ) ) { ?>
467
- <style id="customify_font_output_for_<?php echo sanitize_html_class( $field ); ?>">
468
- <?php echo $CSS ?>
469
- </style><?php
470
- return;
471
- } else {
472
- $this->customify_CSS_output[] = $CSS;
 
 
 
 
 
 
 
 
 
 
473
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  }
475
 
476
  function get_field_unit( $font, $field ) {
19
  $this->theme_fonts = apply_filters( 'customify_theme_fonts', array() );
20
 
21
  $load_location = PixCustomifyPlugin()->get_plugin_setting( 'style_resources_location', 'wp_head' );
22
+ add_action( $load_location, array( $this, 'output_webfont_script' ), 99 );
23
+ add_action( $load_location, array( $this, 'output_fonts_dynamic_style' ), 100 );
24
  add_action( 'customify_font_family_before_options', array( $this, 'add_customify_theme_fonts' ), 11, 2 );
25
 
26
  if ( PixCustomifyPlugin()->get_plugin_setting( 'enable_editor_style', true ) ) {
27
+ add_action( 'admin_head', array( $this, 'script_to_add_customizer_settings_into_wp_editor' ) );
28
  }
29
 
30
  }
31
 
32
+ function script_to_add_customizer_settings_into_wp_editor() {
33
 
34
  ob_start();
35
+ $this->output_fonts_dynamic_style();
36
 
37
+ $custom_css = ob_get_clean();
38
+
39
+ ob_start(); ?>
40
+ (function ($) {
41
+ $(window).load(function () {
42
+ /**
43
+ * @param iframe_id the id of the frame you want to append the style
44
+ * @param style_element the style element you want to append
45
+ */
46
+ var append_script_to_iframe = function (ifrm_id, scriptEl) {
47
+ var myIframe = document.getElementById(ifrm_id);
48
+
49
+ var script = myIframe.contentWindow.document.createElement("script");
50
+ script.type = "text/javascript";
51
+ script.innerHTML = scriptEl.innerHTML;
52
+
53
+ myIframe.contentWindow.document.head.appendChild(script);
54
+ };
55
+
56
+ var append_style_to_iframe = function (ifrm_id, styleElment) {
57
+ var ifrm = window.frames[ifrm_id];
58
+ if ( typeof ifrm === "undefined" ) {
59
+ return;
60
+ }
61
+ ifrm = ( ifrm.contentDocument || ifrm.document );
62
+ var head = ifrm.getElementsByTagName('head')[0];
63
+
64
+ if (typeof styleElment !== "undefined") {
65
+ head.appendChild(styleElment);
66
+ }
67
+ };
68
+
69
+ var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
70
+ parser = new DOMParser(),
71
+ doc = parser.parseFromString(xmlString, "text/html");
72
+
73
+ if (typeof window.frames['content_ifr'] !== 'undefined') {
74
 
75
+ $.each(doc.head.childNodes, function (key, el) {
76
+ if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
77
+
78
+ switch (el.tagName) {
79
+ case 'STYLE' :
80
+ append_style_to_iframe('content_ifr', el);
81
+ break;
82
+ case 'SCRIPT' :
83
+ append_script_to_iframe('content_ifr', el);
84
+ break;
85
+ default:
86
+ break;
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
  });
90
+ }
91
+ });
92
+ })(jQuery);
93
+ <?php
94
+ $script = ob_get_clean();
95
+ wp_add_inline_script( 'editor', $script );
96
+ }
97
 
98
  public function get_theme_fonts() {
99
  return $this->theme_fonts;
125
  return $to_return;
126
  }
127
 
128
+ protected function get_fonts_args() {
129
+
130
+ $args = array(
131
+ 'google_families' => array(),
132
+ 'local_families' => array(),
133
+ 'local_srcs' => array(),
134
+ );
135
 
136
  /** @var PixCustomifyPlugin $local_plugin */
137
  $local_plugin = PixCustomifyPlugin();
141
  $local_plugin->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
142
 
143
  if ( empty( self::$typo_settings ) ) {
144
+ return $args;
145
  }
146
 
 
 
 
 
 
 
 
 
147
  foreach ( self::$typo_settings as $id => $font ) {
148
  if ( isset ( $font['value'] ) ) {
149
 
156
 
157
  $value = $this->validate_font_values( $value );
158
 
159
+ // in case the value is still null, try default value (mostly for google fonts)
160
  if ( ! is_array( $value ) || $value === null ) {
161
  $value = $local_plugin->get_font_defaults_value( str_replace( '"', '', $font['value'] ) );
162
  }
166
  continue;
167
  }
168
 
169
+ // Handle special logic for when the $value array is not an associative array
170
  if ( ! $local_plugin->is_assoc( $value ) ) {
171
  $value = $local_plugin->standardize_non_associative_font_default( $value );
172
  }
173
 
174
+ // If we have reached this far and we don't have a type, we will assume it's a google font.
175
+ if ( ! isset( $value['type'] ) ) {
176
+ $value['type'] = 'google';
177
+ }
178
+
179
+ if ( isset( $value['font_family'] ) && isset( $value['type'] ) && $value['type'] === 'google' ) {
180
+ $family = "'" . $value['font_family'];
181
 
182
+ if ( $load_all_weights && ! empty( $value['variants'] ) && is_array( $value['variants'] ) ) {
183
+ $family .= ":" . implode( ',', $value['variants'] );
184
+ } elseif ( ! empty( $value['selected_variants'] ) ) {
185
  if ( is_array( $value['selected_variants'] ) ) {
186
+ $family .= ":" . implode( ',', $value['selected_variants'] );
187
  } elseif ( is_string( $value['selected_variants'] ) || is_numeric( $value['selected_variants'] ) ) {
188
+ $family .= ":" . $value['selected_variants'];
189
  }
190
+ } elseif ( ! empty( $value['variants'] ) ) {
191
  if ( is_array( $value['variants'] ) ) {
192
+ $family .= ":" . implode( ',', $value['variants'] );
193
  } else {
194
+ $family .= ":" . $value['variants'];
195
  }
196
  }
197
 
198
+ if ( ! empty( $value['selected_subsets'] ) ) {
199
  if ( is_array( $value['selected_subsets'] ) ) {
200
+ $family .= ":" . implode( ',', $value['selected_subsets'] );
201
  } else {
202
+ $family .= ":" . $value['selected_subsets'];
203
  }
204
+ } elseif ( ! empty( $value['subsets'] ) ) {
205
  if ( is_array( $value['subsets'] ) ) {
206
+ $family .= ":" . implode( ',', $value['subsets'] );
207
  } else {
208
+ $family .= ":" . $value['subsets'];
209
  }
210
  }
211
 
212
+ $family .= "'";
213
+
214
+ $args['google_families'][] = $family;
215
  } elseif ( isset( $this->theme_fonts[ $value['font_family'] ] ) ) {
216
 
217
  // $value['type'] = 'theme_font';
219
  // $value['variants'] = $this->theme_fonts[ $value['font_family'] ]['variants'];
220
 
221
  if ( false === strpos( $args['local_families'], $value['font_family'] ) ) {
222
+ $args['local_families'][] = "'" . $value['font_family'] . "'";
223
  }
224
 
225
  if ( false === strpos( $args['local_srcs'], $this->theme_fonts[ $value['font_family'] ]['src'] ) ) {
226
+ $args['local_srcs'][] = "'" . $this->theme_fonts[ $value['font_family'] ]['src'] . "'";
227
  }
228
  }
229
  }
230
  }
231
 
232
+ $args = array(
233
+ 'google_families' => array_unique( $args['google_families'] ),
234
+ 'local_families' => array_unique( $args['local_families'] ),
235
+ 'local_srcs' => array_unique( $args['local_srcs'] ),
236
+ );
237
 
238
+ return $args;
239
+ }
 
 
240
 
241
+ function output_fonts_dynamic_style() {
242
 
243
+ /** @var PixCustomifyPlugin $local_plugin */
244
+ $local_plugin = PixCustomifyPlugin();
 
245
 
246
+ self::$options_list = $local_plugin->get_options();
247
+ $local_plugin->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
248
 
249
+ if ( empty( self::$typo_settings ) ) {
250
+ return;
251
+ }
252
 
253
+ $output = '';
 
 
 
254
 
255
+ foreach ( self::$typo_settings as $key => $font ) {
256
+ $font_output = $this->get_font_style( $font );
257
+ if ( empty( $font_output ) ) {
258
+ continue;
259
  }
260
 
261
+ $output .= $font_output . PHP_EOL;
262
+
263
+ // If we are in a Customizer context we will output CSS rules grouped so we can target them.
264
+ // In the frontend we want a whole bulk.
265
+ if ( isset( $GLOBALS['wp_customize'] ) ) { ?>
266
+ <style id="customify_font_output_for_<?php echo sanitize_html_class( $key ); ?>">
267
+ <?php echo $font_output; ?>
268
+ </style><?php
269
+ }
270
  }
271
 
272
  // in customizer the CSS is printed per option, in front-end we need to print them in bulk
273
  if ( ! isset( $GLOBALS['wp_customize'] ) ) { ?>
274
+ <style id="customify_fonts_output">
275
+ <?php echo $output; ?>
276
+ </style><?php
 
277
  }
278
  }
279
 
280
+ function get_fonts_dynamic_style() {
281
+
282
+ $output = '';
283
+
284
+ /** @var PixCustomifyPlugin $local_plugin */
285
+ $local_plugin = PixCustomifyPlugin();
286
+
287
+ self::$options_list = $local_plugin->get_options();
288
+ $local_plugin->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
289
+
290
+ if ( empty( self::$typo_settings ) ) {
291
+ return $output;
292
+ }
 
 
 
 
 
293
 
294
+ foreach ( self::$typo_settings as $key => $font ) {
295
+
296
+ $font_output = $this->get_font_style( $font );
297
+ if ( empty( $font_output ) ) {
298
+ continue;
 
 
 
 
 
 
 
299
  }
300
+
301
+ $output .= $font_output . PHP_EOL;
302
+ }
303
+
304
+ return $output;
305
  }
306
 
307
+ function get_font_style( $font ) {
308
+
309
+ if ( ! isset( $font['selector'] ) ) {
310
+ return '';
311
+ }
312
+
313
+ $font['selector'] = apply_filters( 'customify_font_css_selector', $font['selector'], $font );
314
+
315
+ if ( empty( $font['selector'] ) || empty( $font['value'] ) ) {
316
+ return '';
317
+ }
318
+
319
+ /** @var PixCustomifyPlugin $local_plugin */
320
+ $local_plugin = PixCustomifyPlugin();
321
+
322
+ $value = $this->maybe_decode_value( $font['value'] );
323
+
324
+ if ( $value === null ) {
325
+ $value = $local_plugin->get_font_defaults_value( $font['value'] );
326
+ }
327
+
328
+ // shim the old case when the default was only the font name
329
+ if ( is_string( $value ) && ! empty( $value ) ) {
330
+ $value = array( 'font_family' => $value );
331
+ }
332
+
333
+ // Handle special logic for when the $value array is not an associative array
334
+ if ( ! $local_plugin->is_assoc( $value ) ) {
335
+ $value = $local_plugin->standardize_non_associative_font_default( $value );
336
+ }
337
 
338
  $value = $this->validate_font_values( $value );
339
  // some sanitizing
495
 
496
  $CSS = ob_get_clean();
497
 
498
+ return $CSS;
499
+ }
500
+
501
+ function output_webfont_script() {
502
+ $script = $this->get_fonts_dynamic_script();
503
+ if ( ! empty( $script ) ) { ?>
504
+ <script type="text/javascript">
505
+ <?php echo $script; ?>
506
+ </script>
507
+ <?php }
508
+ }
509
+
510
+ function get_fonts_dynamic_script() {
511
+ $args = $this->get_fonts_args();
512
+
513
+ if ( ( empty ( $args['local_families'] ) && empty ( $args['google_families'] ) ) || ! PixCustomifyPlugin()->get_plugin_setting( 'typography', '1' ) || ! PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
514
+ return '';
515
  }
516
+
517
+ ob_start();
518
+ ?>
519
+ var customify_font_loader = function () {
520
+ var webfontargs = {
521
+ classes: false,
522
+ events: false
523
+ };
524
+ <?php if ( ! empty( $args['google_families'] ) ) { ?>
525
+ webfontargs.google = {families: [<?php echo join( ',', $args['google_families'] ); ?>]};
526
+ <?php }
527
+ if ( ! empty( $args['local_families'] ) && ! empty( $args['local_srcs'] ) ) { ?>
528
+ webfontargs.custom = {
529
+ families: [<?php echo join( ',', $args['local_families'] ); ?>],
530
+ urls: [<?php echo join( ',', $args['local_srcs'] ) ?>]
531
+ };
532
+ <?php } ?>
533
+ WebFont.load(webfontargs);
534
+ };
535
+
536
+ if (typeof WebFont !== 'undefined') { <?php // if there is a WebFont object, use it ?>
537
+ customify_font_loader();
538
+ } else { <?php // basically when we don't have the WebFont object we create the google script dynamically ?>
539
+ var tk = document.createElement('script');
540
+ tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
541
+ tk.type = 'text/javascript';
542
+
543
+ tk.onload = tk.onreadystatechange = function () {
544
+ customify_font_loader();
545
+ };
546
+ var s = document.getElementsByTagName('script')[0];
547
+ s.parentNode.insertBefore(tk, s);
548
+ }
549
+ <?php
550
+ $output = ob_get_clean();
551
+
552
+ return apply_filters( 'customify_fonts_webfont_script', $output );
553
  }
554
 
555
  function get_field_unit( $font, $field ) {
features/customizer/controls/class-Pix_Customize_Ace_Editor_Control.php CHANGED
@@ -24,4 +24,8 @@ class Pix_Customize_Ace_Editor_Control extends Pix_Customize_Control {
24
  <?php
25
 
26
  }
 
 
 
 
27
  }
24
  <?php
25
 
26
  }
27
+
28
+ public function enqueue() {
29
+ wp_enqueue_script( 'customify-ace-editor', plugins_url( '/js/ace/ace.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), false, true );
30
+ }
31
  }
features/customizer/controls/class-Pix_Customize_Font_Control.php CHANGED
@@ -21,6 +21,8 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
21
 
22
  private static $std_fonts = null;
23
 
 
 
24
  /**
25
  * Constructor.
26
  *
@@ -60,7 +62,11 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
60
  ) );
61
 
62
  $this->CSSID = $this->get_CSS_ID();
63
- $this->load_google_fonts();
 
 
 
 
64
 
65
  // This is intentionally commented as it is only used in development to refresh the Google Fonts list
66
  // $this->generate_google_fonts_json();
@@ -79,6 +85,16 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
79
  $this->current_value = $this->value();
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
82
  /**
83
  * Render the control's content.
84
  *
@@ -111,12 +127,12 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
111
  //make sure it is an object from here going forward
112
  $current_value = (object) $current_value;
113
 
114
- $font_family = '';
115
  if ( isset( $current_value->font_family ) ) {
116
- $font_family = $current_value->font_family;
117
  }
118
 
119
- $select_data = '';
120
  if ( isset( $current_value->load_all_weights ) ) {
121
  $this->load_all_weights = $current_value->font_load_all_weights;
122
 
@@ -125,7 +141,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
125
  <div class="font-options__wrapper">
126
  <?php
127
  $this->display_value_holder( $current_value );
128
- $this->display_field_title( $font_family, esc_attr( $this->CSSID ) ); ?>
129
 
130
  <input type="checkbox" class="customify_font_tooltip"
131
  id="tooltip_toogle_<?php echo esc_attr( $this->CSSID ); ?>">
@@ -135,40 +151,17 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
135
  <select id="select_font_font_family_<?php echo esc_attr( $this->CSSID ); ?>" class="customify_font_family"<?php echo $select_data; ?> data-field="font_family">
136
  <?php
137
  // Allow others to add options here
138
- do_action( 'customify_font_family_before_options', $font_family, $current_value );
139
 
140
- $this->display_recommended_options_group( $font_family, $current_value );
141
 
142
- $this->display_standard_options_group( $font_family, $current_value );
143
 
144
  do_action( 'customify_font_family_before_google_fonts_options' );
145
 
146
  if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
147
 
148
- if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_group_google_fonts' ) ) {
149
-
150
- $grouped_google_fonts = array();
151
- foreach ( self::$google_fonts as $key => $font ) {
152
- if ( isset( $font['category'] ) ) {
153
- $grouped_google_fonts[ $font['category'] ][] = $font;
154
- }
155
- }
156
-
157
- foreach ( $grouped_google_fonts as $group_name => $group ) {
158
- echo '<optgroup label="' . __( 'Google fonts', 'customify' ) . ' ' . $group_name . '">';
159
- foreach ( $group as $key => $font ) {
160
- self::output_font_option( $key, $font_family, $font );
161
- }
162
- echo "</optgroup>";
163
- }
164
-
165
- } else {
166
- echo '<optgroup label="' . __( 'Google fonts', 'customify' ) . '">';
167
- foreach ( self::$google_fonts as $key => $font ) {
168
- self::output_font_option( $key, $font_family, $font );
169
- }
170
- echo "</optgroup>";
171
- }
172
  } ?>
173
  </select>
174
  </li>
@@ -205,6 +198,67 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
205
  ?>
206
  <?php }
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  /**
209
  * This input will hold the values of this typography field
210
  */
@@ -241,7 +295,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
241
  $font = $key;
242
  }
243
 
244
- self::output_font_option( $key, $font_family, $font, $font_type );
245
  }
246
  echo "</optgroup>";
247
  }
@@ -255,7 +309,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
255
 
256
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
257
  foreach ( self::$std_fonts as $key => $font ) {
258
- self::output_font_option( $key, $font_family, $font, 'std' );
259
  }
260
  echo "</optgroup>";
261
  }
@@ -505,49 +559,61 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
505
  /**
506
  * This method displays an <option> tag from the given params
507
  *
508
- * @param string $key
509
- * @param string $active_font_family
510
  * @param string|array $font
511
- * @param string string $type
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  */
513
- public static function output_font_option( $key, $active_font_family, $font, $type = 'google' ) {
514
- //initialize data attributes
515
- $data = '';
516
 
517
- $data .= ' data-type="' . esc_attr( $type ) . '"';
518
 
519
- //we will handle Google Fonts separately
 
 
520
  if ( $type === 'google' ) {
521
  // Handle the font variants markup, if available
522
  if ( isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
523
- $data .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
524
  }
525
 
526
  if ( isset( $font['subsets'] ) && ! empty( $font['subsets'] ) ) {
527
- $data .= ' data-subsets="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['subsets'] ) ) . '"';
528
  }
529
 
530
  //determine if it's selected
531
- $selected = ( $active_font_family === $font['family'] ) ? ' selected="selected" ' : '';
532
 
533
- //output the markup
534
- echo '<option value="' . $font['family'] . '"' . $selected . $data . '>' . $font['family'] . '</option>';
535
  } elseif ( $type === 'theme_font' ) {
536
- $data = '';
537
 
538
  // Handle the font variants markup, if available
539
  if ( isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
540
- $data .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
541
  }
542
 
543
- $selected = ( $active_font_family === $font['family'] ) ? ' selected="selected" ' : '';
544
- $data .= ' data-src="' . $font['src'] . '" data-type="theme_font"';
545
- //output the markup
546
- echo '<option value="' . $font['family'] . '"' . $selected . $data . '>' . $font['family'] . '</option>';
547
  } else {
548
  // Handle the font variants markup, if available
549
  if ( is_array( $font ) && isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
550
- $data .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
551
  }
552
 
553
  // by default, we assume we only get a font family string
@@ -558,7 +624,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
558
  }
559
 
560
  //determine if it's selected
561
- $selected = ( $active_font_family === $font_family ) ? ' selected="selected" ' : '';
562
 
563
  //now determine if we have a "pretty" display for this font family
564
  $font_family_display = $font_family;
@@ -572,27 +638,31 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
572
  }
573
  $option_class = $type . '_font';
574
 
575
- //output the markup
576
- echo '<option class="' . esc_attr( $option_class ) . '" value="' . esc_attr( $font_family ) . '" ' . $selected . $data . '>' . $font_family_display . '</option>';
577
  }
 
 
578
  }
579
 
580
  /** ==== Helpers ==== */
581
 
582
  /**
583
- * Load the google fonts list from the local file
 
584
  * @return bool|mixed|null
585
  */
586
- protected function load_google_fonts() {
587
 
588
- $fonts_path = plugin_dir_path( __FILE__ ) . 'resources/google.fonts.php';
 
589
 
590
- if ( file_exists( $fonts_path ) ) {
591
- self::$google_fonts = require( $fonts_path );
 
592
  }
593
 
594
  if ( ! empty( self::$google_fonts ) ) {
595
- return apply_filters( 'customify_filter_google_fonts_list', self::$google_fonts );
596
  }
597
 
598
  return false;
21
 
22
  private static $std_fonts = null;
23
 
24
+ protected static $font_control_instance_count = 0;
25
+
26
  /**
27
  * Constructor.
28
  *
62
  ) );
63
 
64
  $this->CSSID = $this->get_CSS_ID();
65
+ $this->maybe_load_google_fonts();
66
+
67
+ self::$font_control_instance_count += 1;
68
+
69
+ $this->add_hooks();
70
 
71
  // This is intentionally commented as it is only used in development to refresh the Google Fonts list
72
  // $this->generate_google_fonts_json();
85
  $this->current_value = $this->value();
86
  }
87
 
88
+ protected function add_hooks() {
89
+ // We will only add the google fonts select options only once as they will be reused for all controls.
90
+ if ( self::$font_control_instance_count === 1 ) {
91
+ add_action( 'customize_controls_print_footer_scripts', array(
92
+ $this,
93
+ 'customize_pane_settings_google_fonts_options'
94
+ ), 10000 );
95
+ }
96
+ }
97
+
98
  /**
99
  * Render the control's content.
100
  *
127
  //make sure it is an object from here going forward
128
  $current_value = (object) $current_value;
129
 
130
+ $active_font_family = '';
131
  if ( isset( $current_value->font_family ) ) {
132
+ $active_font_family = $current_value->font_family;
133
  }
134
 
135
+ $select_data = 'data-active_font_family="' . esc_attr( $active_font_family ) . '"';
136
  if ( isset( $current_value->load_all_weights ) ) {
137
  $this->load_all_weights = $current_value->font_load_all_weights;
138
 
141
  <div class="font-options__wrapper">
142
  <?php
143
  $this->display_value_holder( $current_value );
144
+ $this->display_field_title( $active_font_family, esc_attr( $this->CSSID ) ); ?>
145
 
146
  <input type="checkbox" class="customify_font_tooltip"
147
  id="tooltip_toogle_<?php echo esc_attr( $this->CSSID ); ?>">
151
  <select id="select_font_font_family_<?php echo esc_attr( $this->CSSID ); ?>" class="customify_font_family"<?php echo $select_data; ?> data-field="font_family">
152
  <?php
153
  // Allow others to add options here
154
+ do_action( 'customify_font_family_before_options', $active_font_family, $current_value );
155
 
156
+ $this->display_recommended_options_group( $active_font_family, $current_value );
157
 
158
+ $this->display_standard_options_group( $active_font_family, $current_value );
159
 
160
  do_action( 'customify_font_family_before_google_fonts_options' );
161
 
162
  if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
163
 
164
+ echo '<optgroup class="google-fonts-opts-placeholder" label="' . __( 'Google fonts', 'customify' ) . '"></optgroup>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  } ?>
166
  </select>
167
  </li>
198
  ?>
199
  <?php }
200
 
201
+ public function get_google_fonts_opts_html() {
202
+ $html = '';
203
+ if ( ! PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
204
+ return $html;
205
+ }
206
+
207
+ ob_start();
208
+ if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_group_google_fonts' ) ) {
209
+
210
+ $grouped_google_fonts = array();
211
+ foreach ( self::$google_fonts as $key => $font ) {
212
+ if ( isset( $font['category'] ) ) {
213
+ $grouped_google_fonts[ $font['category'] ][] = $font;
214
+ }
215
+ }
216
+
217
+ foreach ( $grouped_google_fonts as $group_name => $group ) {
218
+ echo '<optgroup label="' . __( 'Google fonts', 'customify' ) . ' ' . $group_name . '">';
219
+ foreach ( $group as $key => $font ) {
220
+ self::output_font_option( $font );
221
+ }
222
+ echo "</optgroup>";
223
+ }
224
+
225
+ } else {
226
+ echo '<optgroup label="' . __( 'Google fonts', 'customify' ) . '">';
227
+ foreach ( self::$google_fonts as $key => $font ) {
228
+ self::output_font_option( $font );
229
+ }
230
+ echo "</optgroup>";
231
+ }
232
+
233
+ $html = ob_get_clean();
234
+
235
+ return $html;
236
+ }
237
+
238
+ public function customize_pane_settings_google_fonts_options() {
239
+ if ( ! PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
240
+ return;
241
+ }
242
+
243
+ ?>
244
+ <script type="text/javascript">
245
+ if ( 'undefined' === typeof _wpCustomizeSettings.settings ) {
246
+ _wpCustomizeSettings.settings = {};
247
+ }
248
+
249
+ <?php
250
+ echo "(function ( sAdditional ){\n";
251
+
252
+ printf(
253
+ "sAdditional['google_fonts_opts'] = %s;\n",
254
+ wp_json_encode( $this->get_google_fonts_opts_html() )
255
+ );
256
+ echo "})( _wpCustomizeSettings );\n";
257
+ ?>
258
+ </script>
259
+ <?php
260
+ }
261
+
262
  /**
263
  * This input will hold the values of this typography field
264
  */
295
  $font = $key;
296
  }
297
 
298
+ self::output_font_option( $font, $font_family, $font_type );
299
  }
300
  echo "</optgroup>";
301
  }
309
 
310
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
311
  foreach ( self::$std_fonts as $key => $font ) {
312
+ self::output_font_option( $font, $font_family, 'std' );
313
  }
314
  echo "</optgroup>";
315
  }
559
  /**
560
  * This method displays an <option> tag from the given params
561
  *
 
 
562
  * @param string|array $font
563
+ * @param string|false $active_font_family Optional. The active font family to add the selected attribute to the appropriate opt.
564
+ * False to not mark any opt as selected.
565
+ * @param string $type
566
+ */
567
+ public static function output_font_option( $font, $active_font_family = false, $type = 'google' ) {
568
+ echo self::get_font_option( $font, $active_font_family, $type );
569
+ }
570
+
571
+ /**
572
+ * This method returns an <option> tag from the given params
573
+ *
574
+ * @param string|array $font
575
+ * @param string|false $active_font_family Optional. The active font family to add the selected attribute to the appropriate opt.
576
+ * False to not mark any opt as selected.
577
+ * @param string $type
578
+ * @return string
579
  */
580
+ public static function get_font_option( $font, $active_font_family = false, $type = 'google' ) {
 
 
581
 
582
+ $html = '';
583
 
584
+ $data_attrs = ' data-type="' . esc_attr( $type ) . '"';
585
+
586
+ // We will handle Google Fonts separately
587
  if ( $type === 'google' ) {
588
  // Handle the font variants markup, if available
589
  if ( isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
590
+ $data_attrs .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
591
  }
592
 
593
  if ( isset( $font['subsets'] ) && ! empty( $font['subsets'] ) ) {
594
+ $data_attrs .= ' data-subsets="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['subsets'] ) ) . '"';
595
  }
596
 
597
  //determine if it's selected
598
+ $selected = ( false !== $active_font_family && $active_font_family === $font['family'] ) ? ' selected="selected" ' : '';
599
 
600
+ $html .= '<option value="' . $font['family'] . '"' . $selected . $data_attrs . '>' . $font['family'] . '</option>';
 
601
  } elseif ( $type === 'theme_font' ) {
602
+ $data_attrs = '';
603
 
604
  // Handle the font variants markup, if available
605
  if ( isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
606
+ $data_attrs .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
607
  }
608
 
609
+ $selected = ( false !== $active_font_family && $active_font_family === $font['family'] ) ? ' selected="selected" ' : '';
610
+ $data_attrs .= ' data-src="' . $font['src'] . '" data-type="theme_font"';
611
+
612
+ $html .= '<option value="' . $font['family'] . '"' . $selected . $data_attrs . '>' . $font['family'] . '</option>';
613
  } else {
614
  // Handle the font variants markup, if available
615
  if ( is_array( $font ) && isset( $font['variants'] ) && ! empty( $font['variants'] ) ) {
616
+ $data_attrs .= ' data-variants="' . PixCustomifyPlugin::encodeURIComponent( json_encode( (object) $font['variants'] ) ) . '"';
617
  }
618
 
619
  // by default, we assume we only get a font family string
624
  }
625
 
626
  //determine if it's selected
627
+ $selected = ( false !== $active_font_family && $active_font_family === $font_family ) ? ' selected="selected" ' : '';
628
 
629
  //now determine if we have a "pretty" display for this font family
630
  $font_family_display = $font_family;
638
  }
639
  $option_class = $type . '_font';
640
 
641
+ $html .= '<option class="' . esc_attr( $option_class ) . '" value="' . esc_attr( $font_family ) . '" ' . $selected . $data_attrs . '>' . $font_family_display . '</option>';
 
642
  }
643
+
644
+ return $html;
645
  }
646
 
647
  /** ==== Helpers ==== */
648
 
649
  /**
650
+ * Load the google fonts list from the local file, if not already loaded.
651
+ *
652
  * @return bool|mixed|null
653
  */
654
+ protected function maybe_load_google_fonts() {
655
 
656
+ if ( empty( self::$google_fonts ) ) {
657
+ $fonts_path = plugin_dir_path( __FILE__ ) . 'resources/google.fonts.php';
658
 
659
+ if ( file_exists( $fonts_path ) ) {
660
+ self::$google_fonts = apply_filters( 'customify_filter_google_fonts_list', require( $fonts_path ) );
661
+ }
662
  }
663
 
664
  if ( ! empty( self::$google_fonts ) ) {
665
+ return self::$google_fonts;
666
  }
667
 
668
  return false;
includes/class-customify-gutenberg.php CHANGED
@@ -61,6 +61,7 @@ class Customify_Gutenberg {
61
  '/^\s*\.site-/',
62
  '/\.search/',
63
  '/\.page/',
 
64
  '/\.attachment/',
65
  '/\.mobile/',
66
 
@@ -216,15 +217,17 @@ class Customify_Gutenberg {
216
 
217
  if ( PixCustomifyPlugin()->get_plugin_setting( 'enable_editor_style', true ) ) {
218
  add_filter( 'customify_typography_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10, 2 );
219
- PixCustomifyPlugin()->output_typography_dynamic_style();
 
220
  remove_filter( 'customify_typography_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10 );
221
 
222
  add_filter( 'customify_font_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10, 2 );
223
- Customify_Font_Selector::instance()->output_font_dynamic_style();
 
224
  remove_filter( 'customify_font_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10 );
225
 
226
  add_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10, 2 );
227
- PixCustomifyPlugin()->output_dynamic_style();
228
  remove_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10 );
229
 
230
  // Add color palettes classes.
61
  '/^\s*\.site-/',
62
  '/\.search/',
63
  '/\.page/',
64
+ '/\.mce-content-body/',
65
  '/\.attachment/',
66
  '/\.mobile/',
67
 
217
 
218
  if ( PixCustomifyPlugin()->get_plugin_setting( 'enable_editor_style', true ) ) {
219
  add_filter( 'customify_typography_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10, 2 );
220
+ wp_add_inline_script( $enqueue_parent_handle, PixCustomifyPlugin()->get_typography_dynamic_script() );
221
+ wp_add_inline_style( $enqueue_parent_handle, PixCustomifyPlugin()->get_typography_dynamic_style() );
222
  remove_filter( 'customify_typography_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10 );
223
 
224
  add_filter( 'customify_font_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10, 2 );
225
+ wp_add_inline_script( $enqueue_parent_handle, Customify_Font_Selector::instance()->get_fonts_dynamic_script() );
226
+ wp_add_inline_style( $enqueue_parent_handle, Customify_Font_Selector::instance()->get_fonts_dynamic_style() );
227
  remove_filter( 'customify_font_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10 );
228
 
229
  add_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10, 2 );
230
+ wp_add_inline_style( $enqueue_parent_handle, PixCustomifyPlugin()->get_dynamic_style() );
231
  remove_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10 );
232
 
233
  // Add color palettes classes.
js/customizer.js CHANGED
@@ -1,1264 +1,1263 @@
1
  (
2
- function( $, exports, wp ) {
3
- var api = wp.customize;
4
- var $window = $( window );
5
-
6
- // when the customizer is ready prepare our fields events
7
- wp.customize.bind( 'ready', function() {
8
- var timeout = null;
9
-
10
- // Create a stack of callbacks bound to parent settings to be able to unbind them
11
- // when altering the connected_fields attribute.
12
- if ( typeof window.connectedFieldsCallbacks === "undefined" ) {
13
- window.connectedFieldsCallbacks = {};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- // add ace editors
17
- $( '.customify_ace_editor' ).each( function( key, el ) {
18
- var id = $( this ).attr( 'id' ),
19
- css_editor = ace.edit( id );
20
-
21
- var editor_type = $( this ).data( 'editor_type' );
22
- // init the ace editor
23
- css_editor.setTheme( "ace/theme/github" );
24
- css_editor.getSession().setMode( "ace/mode/" + editor_type );
25
-
26
- // hide the textarea and enable the ace editor
27
- var textarea = $( '#' + id + '_textarea' ).hide();
28
- css_editor.getSession().setValue( textarea.val() );
29
-
30
- // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
31
- // if the user types faster than this delay then reset it
32
- css_editor.getSession().on( 'change', function( e ) {
33
- if ( timeout !== null ) {
34
- clearTimeout( timeout );
35
- timeout = null;
36
- } else {
37
- timeout = setTimeout( function() {
38
- //var state = css_editor.session.getState();
39
- textarea.val( css_editor.getSession().getValue() );
40
- textarea.trigger( 'change' );
41
- }, 1500 );
42
- }
43
- } );
44
- } );
45
-
46
- // simple select2 field
47
- $( '.customify_select2' ).select2();
48
-
49
- setTimeout( function() {
50
- CustomifyFontSelectFields.init();
51
- }, 333 );
52
-
53
- prepare_typography_field();
54
-
55
- /**
56
- * Make the customizer save on CMD/CTRL+S action
57
- * This is awesome!!!
58
- */
59
- $( window ).bind( 'keydown', function( event ) {
60
- if ( event.ctrlKey || event.metaKey ) {
61
- switch ( String.fromCharCode( event.which ).toLowerCase() ) {
62
- case 's':
63
- event.preventDefault();
64
- api.previewer.save();
65
- break;
66
- }
67
- }
68
- } );
69
-
70
- // for each range input add a value preview output
71
- $( '.accordion-section-content[id*="' + customify_settings.options_name + '"], #sub-accordion-section-style_manager_section' ).each( function() {
72
-
73
- // Initialize range fields logic
74
- customifyHandleRangeFields( this );
75
- } );
76
-
77
- if ( $( 'button[data-action="reset_customify"]' ).length > 0 ) {
78
- // reset_button
79
- $( document ).on( 'click', '#customize-control-reset_customify button', function( ev ) {
80
- ev.preventDefault();
81
-
82
- var iAgree = confirm( 'Do you really want to reset to defaults all the fields? Watch out, this will reset all your Customify options and will save them!' );
83
-
84
- if ( ! iAgree ) {
85
- return;
86
- }
87
-
88
- $.each( api.settings.controls, function( key, ctrl ) {
89
- var setting_id = key.replace( '_control', '' );
90
- var setting = customify_settings.settings[setting_id];
91
-
92
- if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
93
- api_set_setting_value( setting_id, setting.default );
94
- }
95
- } );
96
-
97
- api.previewer.save();
98
- } );
99
-
100
- // add a reset button for each panel
101
- $( '.panel-meta' ).each( function( el, key ) {
102
- var container = $( this ).parents( '.control-panel' ),
103
- id = container.attr( 'id' );
104
-
105
- if ( typeof id !== 'undefined' ) {
106
- var panel_id = id.replace( 'accordion-panel-', '' );
107
- $( this ).parent().append( '<button class="reset_panel button" data-panel="' + panel_id + '">Panel\'s defaults</button>' );
108
- }
109
- } );
110
-
111
- // reset panel
112
- $( document ).on( 'click', '.reset_panel', function( e ) {
113
- e.preventDefault();
114
-
115
- var panel_id = $( this ).data( 'panel' ),
116
- panel = api.panel( panel_id ),
117
- sections = panel.sections(),
118
- iAgree = confirm( "Do you really want to reset " + panel.params.title + "?" );
119
-
120
- if ( ! iAgree ) {
121
- return;
122
- }
123
- if ( sections.length > 0 ) {
124
- $.each( sections, function() {
125
- //var settings = this.settings();
126
- var controls = this.controls();
127
-
128
- if ( controls.length > 0 ) {
129
- $.each( controls, function( key, ctrl ) {
130
- var setting_id = ctrl.id.replace( '_control', '' ),
131
- setting = customify_settings.settings[setting_id];
132
-
133
- if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
134
- api_set_setting_value( setting_id, setting.default );
135
- }
136
- } );
137
- }
138
- } );
139
- }
140
- } );
141
-
142
- //add reset section
143
- $( '.accordion-section-content' ).each( function( el, key ) {
144
- var section_id = $( this ).attr( 'id' );
145
-
146
- if ( (
147
- (
148
- ! _.isUndefined( section_id )
149
- ) ? section_id.indexOf( customify_settings.options_name ) : - 1
150
- ) === - 1 ) {
151
- return;
152
- }
153
-
154
- if ( ! _.isUndefined( section_id ) && section_id.indexOf( 'sub-accordion-section-' ) > - 1 ) {
155
- var id = section_id.replace( 'sub-accordion-section-', '' );
156
- $( this ).append( '<button class="reset_section button" data-section="' + id + '">Reset All Options for This Section</button>' );
157
- }
158
- } );
159
-
160
- // reset section event
161
- $( document ).on( 'click', '.reset_section', function( e ) {
162
- e.preventDefault();
163
-
164
- var section_id = $( this ).data( 'section' ),
165
- section = api.section( section_id ),
166
- controls = section.controls();
167
-
168
- var iAgree = confirm( "Do you really want to reset " + section.params.title + "?" );
169
-
170
- if ( ! iAgree ) {
171
- return;
172
- }
173
-
174
- if ( controls.length > 0 ) {
175
- $.each( controls, function( key, ctrl ) {
176
- var setting_id = ctrl.id.replace( '_control', '' ),
177
- setting = customify_settings.settings[setting_id];
178
-
179
- if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
180
- api_set_setting_value( setting_id, setting.default );
181
- }
182
- } );
183
- }
184
- } );
185
- }
186
-
187
- $( document ).on( 'change keyup', '.customize-control-range input.range-value', function() {
188
- var range = $( this ).siblings( 'input[type="range"]' );
189
- range.val( $( this ).val() );
190
- range.trigger( 'change' );
191
- } );
192
-
193
- $( document ).on( 'change', '.customify_typography_font_subsets', function( ev ) {
194
-
195
- var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
196
- current_val = $input.val();
197
-
198
- current_val = JSON.parse( decodeURIComponent( current_val ) );
199
-
200
- //maybe the selected option holds a JSON in its value
201
- current_val.selected_subsets = maybeJsonParse( $( this ).val() );
202
-
203
- $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
204
-
205
- $input.trigger( 'change' );
206
- } );
207
-
208
- $( document ).on( 'change', '.customify_typography_font_weight', function( ev ) {
209
-
210
- var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
211
- current_val = $input.val();
212
-
213
- current_val = maybeJsonParse( current_val );
214
- // @todo currently the font weight selector works for one value only
215
- // maybe make this a multiselect
216
-
217
- //maybe the selected option holds a JSON in its value
218
- current_val.selected_variants = {0: maybeJsonParse( $( this ).val() )};
219
-
220
- $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
221
- $input.trigger( 'change' );
222
- } );
223
-
224
- $( 'body' ).on( 'customify:preset-change', function( e ) {
225
- const data = $( e.target ).data( 'options' );
226
-
227
- if ( ! _.isUndefined( data ) ) {
228
- $.each( data, function( setting_id, value ) {
229
- api_set_setting_value( setting_id, value );
230
- } );
231
- }
232
- } );
233
-
234
- $( document ).on( 'change', '.customify_preset.select', function() {
235
- const $source = $( this );
236
- const $target = $source.children( '[value="' + $source.val() + '"]' );
237
- $target.trigger( 'customify:preset-change' );
238
- } );
239
-
240
- $( document ).on( 'click', '.customify_preset.radio input, .customify_preset.radio_buttons input, .awesome_presets input', function() {
241
- $( this ).trigger( 'customify:preset-change' );
242
- } );
243
-
244
- // bind our event on click
245
- $( document ).on( 'click', '.customify_import_demo_data_button', function( event ) {
246
- let key = $( this ).data( 'key' );
247
- let import_queue = new Queue( api );
248
- let steps = [];
249
-
250
- if ( ! _.isUndefined( customify_settings.settings[key].imports ) ) {
251
-
252
- $.each( customify_settings.settings[key].imports, function( i, import_setts, k ) {
253
- if ( _.isUndefined( import_setts.steps ) ) {
254
- steps.push( {id: i, type: import_setts.type} );
255
- } else {
256
- var count = import_setts.steps;
257
-
258
- while ( count >= 1 ) {
259
- steps.push( {id: i, type: import_setts.type, count: count} );
260
- count = count - 1;
261
- }
262
- }
263
- } );
264
- }
265
-
266
- import_queue.add_steps( 'import_demo_data_action_id', steps );
267
- return false;
268
- } );
269
-
270
- customifyBackgroundJsControl.init();
271
-
272
- // sometimes a php save may be needed
273
- if ( getUrlVars( 'save_customizer_once' ) ) {
274
- api.previewer.save();
275
- }
276
-
277
- setTimeout( function() {
278
- customifyFoldingFields();
279
- }, 1000 );
280
-
281
-
282
- // Handle the section tabs (ex: Layout | Fonts | Colors)
283
- (
284
- function() {
285
- var $navs = $( '.js-section-navigation' );
286
-
287
- $navs.each( function() {
288
- var $nav = $( this );
289
- var $title = $nav.parents( '.accordion-section-content' ).find( '.customize-section-title' );
290
-
291
- $nav.closest( '.customize-control' ).addClass( 'screen-reader-text' );
292
- $title.append( $nav ).parent().addClass( 'has-nav' );
293
- } );
294
-
295
- $( '.js-section-navigation a' ).on( 'click', function( e ) {
296
- e.preventDefault();
297
-
298
- var $sidebar = $( this ).parents( '.customize-pane-child' );
299
- var $parent = $( this ).parents( '.accordion-section-content' );
300
- var href = $.attr( this, 'href' );
301
-
302
- if ( href != '#' ) {
303
- $sidebar.animate( {
304
- scrollTop: $( $.attr( this, 'href' ) ).position().top - $parent.find( '.customize-section-title' ).outerHeight()
305
- }, 500 );
306
- }
307
- } );
308
- }
309
- )();
310
-
311
- (
312
- function() {
313
- // Close a font field when clicking on another field
314
- $( '.customify_font_tooltip' ).on( 'click', function() {
315
- if ( $( this ).prop( 'checked' ) === true ) {
316
- $( '.customify_font_tooltip' ).prop( 'checked', false );
317
- $( this ).prop( 'checked', true );
318
- }
319
- } );
320
- }
321
- )();
322
-
323
- // Bind any connected fields, except those in the Style Manager.
324
- // Those are handled by the appropriate Style Manager component (Color Palettes, Font Palettes, etc ).
325
- bindConnectedFields();
326
-
327
- } );
328
-
329
- const getConnectedFieldsCallback = function( parent_setting_data, parent_setting_id ) {
330
- return function( new_value, old_value ) {
331
- _.each( parent_setting_data.connected_fields, function( connected_field_data ) {
332
- if ( _.isUndefined( connected_field_data ) || _.isUndefined( connected_field_data.setting_id ) || ! _.isString( connected_field_data.setting_id ) ) {
333
- return;
334
- }
335
- const setting = wp.customize( connected_field_data.setting_id );
336
- if ( _.isUndefined( setting ) ) {
337
- return;
338
- }
339
- setting.set( new_value );
340
- } );
341
  }
342
- };
343
-
344
- const bindConnectedFields = function() {
345
- _.each( wp.customize.settings.settings, function( parent_setting_data, parent_setting_id ) {
346
- // We don't want to handle the binding of the Style Manager settings
347
- if ( typeof ColorPalettes !== "undefined"
348
- && typeof ColorPalettes.masterSettingIds !== "undefined"
349
- && _.contains( ColorPalettes.masterSettingIds, parent_setting_id ) ) {
350
- return;
351
- }
352
- if ( typeof FontPalettes !== "undefined"
353
- && typeof FontPalettes.masterSettingIds !== "undefined"
354
- && _.contains( FontPalettes.masterSettingIds, parent_setting_id ) ) {
355
- return;
356
- }
357
 
358
- let parent_setting = wp.customize( parent_setting_id );
359
- if ( typeof parent_setting_data.connected_fields !== "undefined" ) {
360
- connectedFieldsCallbacks[parent_setting_id] = getConnectedFieldsCallback( parent_setting_data, parent_setting_id );
361
- parent_setting.bind( connectedFieldsCallbacks[parent_setting_id] );
362
- }
363
- } );
364
- };
365
-
366
- const unbindConnectedFields = function() {
367
- _.each( wp.customize.settings.settings, function( parent_setting_data, parent_setting_id ) {
368
- // We don't want to handle the binding of the Style Manager settings
369
- if ( typeof ColorPalettes !== "undefined"
370
- && typeof ColorPalettes.masterSettingIds !== "undefined"
371
- && _.contains( ColorPalettes.masterSettingIds, parent_setting_id ) ) {
372
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  }
374
- if ( typeof FontPalettes !== "undefined"
375
- && typeof FontPalettes.masterSettingIds !== "undefined"
376
- && _.contains( FontPalettes.masterSettingIds, parent_setting_id ) ) {
377
- return;
 
 
378
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
- let parent_setting = wp.customize( parent_setting_id );
381
- if ( typeof parent_setting_data.connected_fields !== "undefined" && typeof connectedFieldsCallbacks[parent_setting_id] !== "undefined" ) {
382
- parent_setting.unbind( connectedFieldsCallbacks[parent_setting_id] );
 
 
 
 
 
 
 
 
 
 
 
 
383
  }
384
- delete connectedFieldsCallbacks[parent_setting_id];
385
- } );
386
- };
387
-
388
- const customifyHandleRangeFields = function( el ) {
389
-
390
- // For each range input add a number field (for preview mainly - but it can also be used for input)
391
- $( el ).find( 'input[type="range"]' ).each( function() {
392
- if ( ! $( this ).siblings( '.range-value' ).length ) {
393
- var $clone = $( this ).clone();
394
-
395
- $clone
396
- .attr( 'type', 'number' )
397
- .attr( 'class', 'range-value' )
398
- .removeAttr( 'data-field' );
399
-
400
- $( this ).after( $clone );
401
- }
402
-
403
- // Update the number field when changing the range
404
- $( this ).on( 'change', function() {
405
- $( this ).siblings( '.range-value' ).val( $( this ).val() );
406
- } );
407
-
408
- // And the other way around, update the range field when changing the number
409
- $( $clone ).on( 'change', function() {
410
- $( this ).siblings( 'input[type="range"]' ).val( $( this ).val() );
411
- } );
412
- } );
413
- };
414
-
415
- /**
416
- * This function will search for all the interdependend fields and make a bound between them.
417
- * So whenever a target is changed, it will take actions to the dependent fields.
418
- * @TODO this is still written in a barbaric way, refactor when needed
419
- */
420
- var customifyFoldingFields = function() {
421
-
422
- if ( _.isUndefined( customify_settings ) || _.isUndefined( customify_settings.settings ) ) {
423
- return; // bail
424
- }
425
-
426
- /**
427
- * Let's iterate through all the customify settings and gather all the fields that have a "show_if"
428
- * property set.
429
- *
430
- * At the end `targets` will hold a list of [ target : [field, field,...], ... ]
431
- * so when a target is changed we will change all the fields.
432
- */
433
- var targets = {};
434
-
435
- $.fn.reactor.defaults.compliant = function() {
436
- $( this ).slideDown();
437
- // $(this).animate({opacity: 1});
438
- $( this ).find( ':disabled' ).attr( {disabled: false} );
439
- };
440
-
441
- $.fn.reactor.defaults.uncompliant = function() {
442
- $( this ).slideUp();
443
- // $(this).animate({opacity: 0.25});
444
- $( this ).find( ':enabled' ).attr( {disabled: true} );
445
- };
446
-
447
- var IS = $.extend( {}, $.fn.reactor.helpers );
448
-
449
- var bind_folding_events = function( parent_id, field, relation ) {
450
-
451
- var key = null;
452
-
453
- if ( _.isString( field ) ) {
454
- key = field;
455
- } else if ( ! _.isUndefined( field.id ) ) {
456
- key = field.id;
457
- } else if ( isString( field[0] ) ) {
458
- key = field[0];
459
- } else {
460
- return; // no key, no fun
461
- }
462
-
463
- var value = 1, // by default we use 1 the most used value for checkboxes or inputs
464
- compare = '==', // ... ye
465
- action = "show",
466
- between = [0, 1]; // can only be `show` or `hide`
467
-
468
- var target_key = customify_settings.options_name + '[' + key + ']';
469
-
470
- var target_type = customify_settings.settings[target_key].type;
471
-
472
- // we support the usual syntax like a config array like `array( 'id' => $id, 'value' => $value, 'compare' => $compare )`
473
- // but we also support a non-associative array like `array( $id, $value, $compare )`
474
- if ( ! _.isUndefined( field.value ) ) {
475
- value = field.value;
476
- } else if ( ! _.isUndefined( field[1] ) && ! _.isString( field[1] ) ) {
477
- value = field[1];
478
- }
479
-
480
- if ( ! _.isUndefined( field.compare ) ) {
481
- compare = field.compare;
482
- } else if ( ! _.isUndefined( field[2] ) ) {
483
- compare = field[2];
484
- }
485
-
486
- if ( ! _.isUndefined( field.action ) ) {
487
- action = field.action;
488
- } else if ( ! _.isUndefined( field[3] ) ) {
489
- action = field[3];
490
- }
491
-
492
- // a field can also overwrite the parent relation
493
- if ( ! _.isUndefined( field.relation ) ) {
494
- action = field.relation;
495
- } else if ( ! _.isUndefined( field[4] ) ) {
496
- action = field[4];
497
- }
498
-
499
- if ( ! _.isUndefined( field.between ) ) {
500
- between = field.between;
501
- }
502
-
503
- /**
504
- * Now for each target we have, we will bind a change event to hide or show the dependent fields
505
- */
506
- var target_selector = '[data-customize-setting-link="' + customify_settings.options_name + '[' + key + ']"]';
507
-
508
- switch ( target_type ) {
509
- case 'checkbox':
510
- $( parent_id ).reactIf( target_selector, function() {
511
- return $( this ).is( ':checked' ) == value;
512
- } );
513
- break;
514
-
515
- case 'radio':
516
- case 'sm_radio':
517
- case 'sm_switch':
518
- case 'radio_image':
519
-
520
- // in case of an array of values we use the ( val in array) condition
521
- if ( _.isObject( value ) ) {
522
- $( parent_id ).reactIf( target_selector, function() {
523
- return (
524
- value.indexOf( $( target_selector + ':checked' ).val() ) !== - 1
525
- );
526
- } );
527
- } else { // in any other case we use a simple == comparison
528
- $( parent_id ).reactIf( target_selector, function() {
529
- return $( target_selector + ':checked' ).val() == value;
530
- } );
531
- }
532
- break;
533
-
534
- case 'range':
535
- var x = IS.Between( between[0], between[1] );
536
-
537
- $( parent_id ).reactIf( target_selector, x );
538
- break;
539
-
540
- default:
541
- // in case of an array of values we use the ( val in array) condition
542
- if ( _.isObject( value ) ) {
543
- $( parent_id ).reactIf( target_selector, function() {
544
- return (
545
- value.indexOf( $( target_selector ).val() ) !== - 1
546
- );
547
- } );
548
- } else { // in any other case we use a simple == comparison
549
- $( parent_id ).reactIf( target_selector, function() {
550
- return $( target_selector ).val() == value;
551
- } );
552
- }
553
- break;
554
- }
555
-
556
- $( target_selector ).trigger( 'change' );
557
- $( '.reactor' ).trigger( 'change.reactor' ); // triggers all events on load
558
- };
559
-
560
- $.each( customify_settings.settings, function( id, field ) {
561
- /**
562
- * Here we have the id of the fields. but we know for sure that we just need his parent selector
563
- * So we just create it
564
- */
565
- var parent_id = id.replace( '[', '-' );
566
- parent_id = parent_id.replace( ']', '' );
567
- parent_id = '#customize-control-' + parent_id + '_control';
568
-
569
- // get only the fields that have a 'show_if' property
570
- if ( field.hasOwnProperty( 'show_if' ) ) {
571
- var relation = 'AND';
572
-
573
- if ( ! _.isUndefined( field.show_if.relation ) ) {
574
- relation = field.show_if.relation;
575
- // remove the relation property, we need the config to be array based only
576
- delete field.show_if.relation;
577
- }
578
-
579
- /**
580
- * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
581
- * Or it could be an array of multiple targets and we need to process both cases
582
- */
583
-
584
- if ( ! _.isUndefined( field.show_if.id ) ) {
585
- bind_folding_events( parent_id, field.show_if, relation );
586
- } else if ( _.isObject( field.show_if ) ) {
587
- $.each( field.show_if, function( i, j ) {
588
- bind_folding_events( parent_id, j, relation );
589
- } );
590
- }
591
- }
592
- } );
593
- };
594
-
595
- var get_typography_font_family = function( $el ) {
596
-
597
- var font_family_value = $el.val();
598
- // first time this will not be a json so catch that error
599
- try {
600
- font_family_value = JSON.parse( font_family_value );
601
- } catch ( e ) {
602
- return {font_family: font_family_value};
603
- }
604
-
605
- if ( ! _.isUndefined( font_family_value.font_family ) ) {
606
- return font_family_value.font_family;
607
- }
608
-
609
- return false;
610
- };
611
-
612
- // get each typography field and bind events
613
- // @todo Are we still using the typography field since we have the font field?
614
- var prepare_typography_field = function() {
615
-
616
- var $typos = $( '.customify_typography_font_family' );
617
-
618
- $typos.each( function() {
619
- var font_family_select = this,
620
- $input = $( font_family_select ).siblings( '.customify_typography_values' );
621
- // on change
622
- $( font_family_select ).on( 'change', function() {
623
- update_siblings_selects( font_family_select );
624
- $input.trigger( 'change' );
625
- } );
626
- update_siblings_selects( font_family_select );
627
- } );
628
- };
629
-
630
- var api_set_setting_value = function( setting_id, value ) {
631
- let setting = api( setting_id ),
632
- field = $( '[data-customize-setting-link="' + setting_id + '"]' ),
633
- field_class = $( field ).parent().attr( 'class' );
634
-
635
- // Legacy field type
636
- if ( ! _.isUndefined( field_class ) && field_class === 'customify_typography' ) {
637
-
638
- let family_select = field.siblings( 'select' );
639
-
640
- if ( _.isString( value ) ) {
641
- let this_option = family_select.find( 'option[value="' + value + '"]' );
642
- $( this_option[0] ).attr( 'selected', 'selected' );
643
- update_siblings_selects( family_select );
644
- } else if ( _.isObject( value ) ) {
645
- let this_family_option = family_select.find( 'option[value="' + value['font_family'] + '"]' );
646
-
647
- $( this_family_option[0] ).attr( 'selected', 'selected' );
648
-
649
- update_siblings_selects( this_family_option );
650
-
651
- setTimeout( function() {
652
- let weight_select = field.parent().siblings( '.options' ).find( '.customify_typography_font_weight' ),
653
- this_weight_option = weight_select.find( 'option[value="' + value['selected_variants'] + '"]' );
654
-
655
- $( this_weight_option[0] ).attr( 'selected', 'selected' );
656
-
657
- update_siblings_selects( this_family_option );
658
-
659
- weight_select.trigger( 'change' );
660
- }, 300 );
661
- }
662
-
663
- family_select.trigger( 'change' );
664
-
665
- } else if ( ! _.isUndefined( field_class ) && field_class === 'font-options__wrapper' ) {
666
-
667
- // if the value is a simple string it must be the font family
668
- if ( _.isString( value ) ) {
669
- let option = field.parent().find( 'option[value="' + value + '"]' );
670
-
671
- option.attr( 'selected', 'selected' );
672
- // option.parents('select').trigger('change');
673
- } else if ( _.isObject( value ) ) {
674
- // Find the options list wrapper
675
- let optionsList = field.parent().children( '.font-options__options-list' );
676
-
677
- if ( optionsList.length ) {
678
- // We will process each font property and update it
679
- _.each( value, function( val, key ) {
680
- // We need to map the keys to the data attributes we are using - I know :(
681
- let mappedKey = key;
682
- switch ( key ) {
683
- case 'font-family':
684
- mappedKey = 'font_family';
685
- break;
686
- case 'font-size':
687
- mappedKey = 'font_size';
688
- break;
689
- case 'font-weight':
690
- mappedKey = 'selected_variants';
691
- break;
692
- case 'letter-spacing':
693
- mappedKey = 'letter_spacing';
694
- break;
695
- case 'text-transform':
696
- mappedKey = 'text_transform';
697
- break;
698
- default:
699
- break;
700
- }
701
- let subField = optionsList.find( '[data-field="' + mappedKey + '"]' );
702
- if ( subField.length ) {
703
- subField.val( val );
704
- subField.trigger( 'change' );
705
- }
706
- } );
707
- }
708
- }
709
-
710
- } else {
711
- setting.set( value );
712
- }
713
- };
714
-
715
- var update_siblings_selects = function( font_select ) {
716
- var selected_font = $( font_select ).val(),
717
- $input = $( font_select ).siblings( '.customify_typography_values' ),
718
- current_val = $input.attr( 'value' );
719
-
720
- if ( current_val === '[object Object]' ) {
721
- current_val = $input.data( 'default' );
722
- } else if ( _.isString( current_val ) && ! isJsonString( current_val ) && current_val.substr( 0, 1 ) == '[' ) {
723
- // a rare case when the value isn't a json but is a representative string like [family,weight]
724
- current_val = current_val.split( ',' );
725
- var new_current_value = {};
726
- if ( ! _.isUndefined( current_val[0] ) ) {
727
- new_current_value['font_family'] = current_val[0];
728
- }
729
-
730
- if ( ! _.isUndefined( current_val[1] ) ) {
731
- new_current_value['selected_variants'] = current_val[1];
732
- }
733
-
734
- current_val = JSON.stringify( new_current_value );
735
- }
736
-
737
- var $font_weight = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_weight' ),
738
- $font_subsets = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_subsets' );
739
-
740
- try {
741
- current_val = JSON.parse( decodeURIComponent( current_val ) );
742
- } catch ( e ) {
743
-
744
- // in case of an error, force the rebuild of the json
745
- if ( _.isUndefined( $( font_select ).data( 'bound_once' ) ) ) {
746
-
747
- $( font_select ).data( 'bound_once', true );
748
-
749
- $( font_select ).change();
750
- $font_weight.change();
751
- $font_subsets.change();
752
- }
753
- }
754
-
755
- // first try to get the font from sure sources, not from the recommended list.
756
- var option_data = $( font_select ).find( ':not(optgroup[label=Recommended]) option[value="' + selected_font + '"]' );
757
- // however, if there isn't an option found, get what you can
758
- if ( option_data.length < 1 ) {
759
- option_data = $( font_select ).find( 'option[value="' + selected_font + '"]' );
760
- }
761
-
762
- if ( option_data.length > 0 ) {
763
-
764
- var font_type = option_data.data( 'type' ),
765
- value_to_add = {'type': font_type, 'font_family': selected_font},
766
- variants = null,
767
- subsets = null;
768
-
769
- if ( font_type == 'std' ) {
770
- variants = {
771
- 0: '100',
772
- 1: '200',
773
- 3: '300',
774
- 4: '400',
775
- 5: '500',
776
- 6: '600',
777
- 7: '700',
778
- 8: '800',
779
- 9: '900'
780
- };
781
- if ( ! _.isUndefined( $( option_data[0] ).data( 'variants' ) ) ) {
782
- //maybe the variants are a JSON
783
- variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
784
- }
785
- } else {
786
- //maybe the variants are a JSON
787
- variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
788
-
789
- //maybe the subsets are a JSON
790
- subsets = maybeJsonParse( $( option_data[0] ).data( 'subsets' ) );
791
- }
792
-
793
- // make the variants selector
794
- if ( ! _.isUndefined( variants ) && ! _.isNull( variants ) && ! _.isEmpty( variants ) ) {
795
-
796
- value_to_add['variants'] = variants;
797
- // when a font is selected force the first weight to load
798
- value_to_add['selected_variants'] = {0: variants[0]};
799
-
800
- var variants_options = '',
801
- count_weights = 0;
802
-
803
- if ( _.isArray( variants ) || _.isObject( variants ) ) {
804
- // Take each variant and produce the option markup
805
- $.each( variants, function( key, el ) {
806
- var is_selected = '';
807
- if ( _.isObject( current_val.selected_variants ) && inObject( el, current_val.selected_variants ) ) {
808
- is_selected = ' selected="selected"';
809
- } else if ( _.isString( current_val.selected_variants ) && el === current_val.selected_variants ) {
810
- is_selected = ' selected="selected"';
811
- }
812
-
813
- // initialize
814
- var variant_option_value = el,
815
- variant_option_display = el;
816
-
817
- // If we are dealing with a object variant then it means things get tricky (probably it's our fault but bear with us)
818
- // This probably comes from our Fonto plugin - a font with individually named variants - hence each has its own font-family
819
- if ( _.isObject( el ) ) {
820
- //put the entire object in the variation value - we will need it when outputting the custom CSS
821
- variant_option_value = encodeURIComponent( JSON.stringify( el ) );
822
- variant_option_display = '';
823
-
824
- //if we have weight and style then "compose" them into something standard
825
- if ( ! _.isUndefined( el['font-weight'] ) ) {
826
- variant_option_display += el['font-weight'];
827
- }
828
-
829
- if ( _.isString( el['font-style'] ) && $.inArray( el['font-style'].toLowerCase(), [
830
- "normal",
831
- "regular"
832
- ] ) < 0 ) { //this comparison means it hasn't been found
833
- variant_option_display += el['font-style'];
834
- }
835
- }
836
-
837
- variants_options += '<option value="' + variant_option_value + '"' + is_selected + '>' + variant_option_display + '</option>';
838
- count_weights ++;
839
- } );
840
- }
841
-
842
- if ( ! _.isUndefined( $font_weight ) ) {
843
- $font_weight.html( variants_options );
844
- // if there is no weight or just 1 we hide the weight select ... cuz is useless
845
- if ( $( font_select ).data( 'load_all_weights' ) === true || count_weights <= 1 ) {
846
- $font_weight.parent().css( 'display', 'none' );
847
- } else {
848
- $font_weight.parent().css( 'display', 'inline-block' );
849
- }
850
- }
851
- } else if ( ! _.isUndefined( $font_weight ) ) {
852
- $font_weight.parent().css( 'display', 'none' );
853
- }
854
-
855
- // make the subsets selector
856
- if ( ! _.isUndefined( subsets ) && ! _.isNull( subsets ) && ! _.isEmpty( subsets ) ) {
857
-
858
- value_to_add['subsets'] = subsets;
859
- // when a font is selected force the first subset to load
860
- value_to_add['selected_subsets'] = {0: subsets[0]};
861
- var subsets_options = '',
862
- count_subsets = 0;
863
- $.each( subsets, function( key, el ) {
864
- var is_selected = '';
865
- if ( _.isObject( current_val.selected_subsets ) && inObject( el, current_val.selected_subsets ) ) {
866
- is_selected = ' selected="selected"';
867
- }
868
-
869
- subsets_options += '<option value="' + el + '"' + is_selected + '>' + el + '</option>';
870
- count_subsets ++;
871
- } );
872
-
873
- if ( ! _.isUndefined( $font_subsets ) ) {
874
- $font_subsets.html( subsets_options );
875
-
876
- // if there is no subset or just 1 we hide the subsets select ... cuz is useless
877
- if ( count_subsets <= 1 ) {
878
- $font_subsets.parent().css( 'display', 'none' );
879
- } else {
880
- $font_subsets.parent().css( 'display', 'inline-block' );
881
- }
882
- }
883
- } else if ( ! _.isUndefined( $font_subsets ) ) {
884
- $font_subsets.parent().css( 'display', 'none' );
885
- }
886
-
887
- $input.val( encodeURIComponent( JSON.stringify( value_to_add ) ) );
888
- }
889
- };
890
-
891
- /** Modules **/
892
-
893
- var customifyBackgroundJsControl = (
894
- function() {
895
- "use strict";
896
-
897
- function init() {
898
- // Remove the image button
899
- $( '.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file' ).unbind( 'click' ).on( 'click', function( e ) {
900
- removeImage( $( this ).parents( '.customize-control-custom_background:first' ) );
901
- preview( $( this ) );
902
- return false;
903
- } );
904
-
905
- // Upload media button
906
- $( '.customize-control-custom_background .background_upload_button' ).unbind().on( 'click', function( event ) {
907
- addImage( event, $( this ).parents( '.customize-control-custom_background:first' ) );
908
- } );
909
-
910
- $( '.customify_background_select' ).on( 'change', function() {
911
- preview( $( this ) );
912
- } );
913
- }
914
-
915
- // Add a file via the wp.media function
916
- function addImage( event, selector ) {
917
-
918
- event.preventDefault();
919
-
920
- var frame;
921
- var jQueryel = jQuery( this );
922
-
923
- // If the media frame already exists, reopen it.
924
- if ( frame ) {
925
- frame.open();
926
- return;
927
- }
928
-
929
- // Create the media frame.
930
- frame = wp.media( {
931
- multiple: false,
932
- library: {
933
- //type: 'image' //Only allow images
934
- },
935
- // Set the title of the modal.
936
- title: jQueryel.data( 'choose' ),
937
-
938
- // Customize the submit button.
939
- button: {
940
- // Set the text of the button.
941
- text: jQueryel.data( 'update' )
942
- // Tell the button not to close the modal, since we're
943
- // going to refresh the page when the image is selected.
944
- }
945
- } );
946
-
947
- // When an image is selected, run a callback.
948
- frame.on( 'select', function() {
949
- // Grab the selected attachment.
950
- var attachment = frame.state().get( 'selection' ).first();
951
- frame.close();
952
-
953
- if ( attachment.attributes.type !== "image" ) {
954
- return;
955
- }
956
-
957
- selector.find( '.upload' ).attr( 'value', attachment.attributes.url );
958
- selector.find( '.upload-id' ).attr( 'value', attachment.attributes.id );
959
- selector.find( '.upload-height' ).attr( 'value', attachment.attributes.height );
960
- selector.find( '.upload-width' ).attr( 'value', attachment.attributes.width );
961
-
962
- var thumbSrc = attachment.attributes.url;
963
- if ( ! _.isUndefined( attachment.attributes.sizes ) && ! _.isUndefined( attachment.attributes.sizes.thumbnail ) ) {
964
- thumbSrc = attachment.attributes.sizes.thumbnail.url;
965
- } else if ( ! _.isUndefined( attachment.attributes.sizes ) ) {
966
- var height = attachment.attributes.height;
967
- for ( var key in attachment.attributes.sizes ) {
968
- var object = attachment.attributes.sizes[key];
969
- if ( object.height < height ) {
970
- height = object.height;
971
- thumbSrc = object.url;
972
- }
973
- }
974
- } else {
975
- thumbSrc = attachment.attributes.icon;
976
- }
977
-
978
- selector.find( '.customify_background_input.background-image' ).val( attachment.attributes.url );
979
-
980
- if ( ! selector.find( '.upload' ).hasClass( 'noPreview' ) ) {
981
- selector.find( '.preview_screenshot' ).empty().hide().append( '<img class="preview_image" src="' + thumbSrc + '">' ).slideDown( 'fast' );
982
- }
983
- //selector.find('.media_upload_button').unbind();
984
- selector.find( '.remove-image' ).removeClass( 'hide' );//show "Remove" button
985
- selector.find( '.customify_background_select' ).removeClass( 'hide' );//show "Remove" button
986
-
987
- preview( selector );
988
- } );
989
-
990
- // Finally, open the modal.
991
- frame.open();
992
- }
993
-
994
- // Update the background preview
995
- function preview( selector ) {
996
-
997
- var $parent = selector.parents( '.customize-control-custom_background:first' );
998
-
999
- if ( selector.hasClass( 'customize-control-custom_background' ) ) {
1000
- $parent = selector;
1001
- }
1002
-
1003
- if ( $parent.length > 0 ) {
1004
- $parent = $( $parent[0] );
1005
- } else {
1006
- return;
1007
- }
1008
-
1009
- var image_holder = $parent.find( '.background-preview' );
1010
-
1011
- if ( ! image_holder ) { // No preview present
1012
- return;
1013
- }
1014
-
1015
- var the_id = $parent.find( '.button.background_upload_button' ).data( 'setting_id' ),
1016
- this_setting = api.instance( the_id );
1017
-
1018
- var background_data = {};
1019
-
1020
- $parent.find( '.customify_background_select, .customify_background_input' ).each( function() {
1021
- var data = $( this ).serializeArray();
1022
-
1023
- data = data[0];
1024
- if ( data && data.name.indexOf( '[background-' ) != - 1 ) {
1025
-
1026
- background_data[$( this ).data( 'select_name' )] = data.value;
1027
-
1028
- //default_default[data.name] = data.value;
1029
- //if (data.name == "background-image") {
1030
- // css += data.name + ':url("' + data.value + '");';
1031
- //} else {
1032
- // css += data.name + ':' + data.value + ';';
1033
- //}
1034
- }
1035
- } );
1036
-
1037
- api.instance( the_id ).set( background_data );
1038
- //// Notify the customizer api about this change
1039
- api.trigger( 'change' );
1040
- api.previewer.refresh();
1041
-
1042
- //image_holder.attr('style', css).fadeIn();
1043
- }
1044
-
1045
- // Update the background preview
1046
- function removeImage( parent ) {
1047
- var selector = parent.find( '.upload_button_div' );
1048
- // This shouldn't have been run...
1049
- if ( ! selector.find( '.remove-image' ).addClass( 'hide' ) ) {
1050
- return;
1051
- }
1052
-
1053
- selector.find( '.remove-image' ).addClass( 'hide' );//hide "Remove" button
1054
- parent.find( '.customify_background_select' ).addClass( 'hide' );
1055
-
1056
- selector.find( '.upload' ).val( '' );
1057
- selector.find( '.upload-id' ).val( '' );
1058
- selector.find( '.upload-height' ).val( '' );
1059
- selector.find( '.upload-width' ).val( '' );
1060
- parent.find( '.customify_background_input.background-image' ).val( '' );
1061
-
1062
- var customizer_id = selector.find( '.background_upload_button' ).data( 'setting_id' ),
1063
- this_setting = api.control( customizer_id + '_control' ),
1064
- current_vals = this_setting.setting(),
1065
- screenshot = parent.find( '.preview_screenshot' ),
1066
- to_array = $.map( current_vals, function( value, index ) {
1067
- return [value];
1068
- } );
1069
-
1070
- // Hide the screenshot
1071
- screenshot.slideUp();
1072
- selector.find( '.remove-file' ).unbind();
1073
- to_array['background-image'] = '';
1074
- this_setting.setting( to_array );
1075
- }
1076
-
1077
- return {
1078
- init: init
1079
- }
1080
- }
1081
- )( jQuery );
1082
-
1083
- var Queue = function() {
1084
- var lastPromise = null;
1085
- var queueDeferred = null;
1086
- var methodDeferred = null;
1087
-
1088
- this.add_steps = function( key, steps, args ) {
1089
- var self = this;
1090
- this.methodDeferred = $.Deferred();
1091
- this.queueDeferred = this.setup();
1092
-
1093
- $.each( steps, function( i, step ) {
1094
- self.queue( key, step );
1095
- } );
1096
- };
1097
-
1098
- this.process_remote_step = function( key, data, step ) {
1099
- var self = this;
1100
-
1101
- if ( _.isUndefined( data ) || _.isNull( data ) ) {
1102
- return false;
1103
- }
1104
-
1105
- var new_step = step;
1106
- $.each( data, function( i, k ) {
1107
- debugger;
1108
- // prepare data for new requests
1109
- new_step.recall_data = k.data;
1110
- new_step.recall_type = k.type;
1111
- new_step.type = 'recall';
1112
-
1113
- self.queue( key, new_step, k.id );
1114
- } );
1115
- };
1116
-
1117
- this.log_action = function( action, key, msg ) {
1118
- if ( action === 'start' ) {
1119
- $( '.wpGrade-import-results' ).show();
1120
- $( '.wpGrade-import-results' ).append( '<span class="import_step_note imports_step_' + key + '" ><span class="step_info" data-balloon="Working on it" data-balloon-pos="up"></span>Importing ' + key + '</span>' );
1121
- } else if ( action === 'end' ) {
1122
- var $notice = $( '.imports_step_' + key + ' .step_info' );
1123
-
1124
- if ( $notice.length > 0 || msg !== "undefined" ) {
1125
- $notice.attr( 'data-balloon', msg );
1126
- $notice.addClass( 'success' );
1127
- } else {
1128
- $notice.attr( 'data-balloon', 'Done' );
1129
- $notice.addClass( 'failed' );
1130
- }
1131
- }
1132
- };
1133
-
1134
- this.queue = function( key, data, step_key ) {
1135
- var self = this;
1136
- if ( ! _.isUndefined( step_key ) ) {
1137
- this.log_action( 'start', step_key );
1138
- }
1139
-
1140
- // execute next queue method
1141
- this.queueDeferred.done( this.request( key, data, step_key ) );
1142
- lastPromise = self.methodDeferred.promise();
1143
- };
1144
-
1145
- this.request = function( key, step, step_key ) {
1146
- var self = this;
1147
- // call actual method and wrap output in deferred
1148
- //setTimeout( function() {
1149
- var data_args = {
1150
- action: 'customify_import_step',
1151
- step_id: step.id,
1152
- step_type: step.type,
1153
- option_key: key
1154
- };
1155
-
1156
- if ( ! _.isUndefined( step.recall_data ) ) {
1157
- data_args.recall_data = step.recall_data;
1158
- }
1159
-
1160
- if ( ! _.isUndefined( step.recall_type ) ) {
1161
- data_args.recall_type = step.recall_type;
1162
- }
1163
-
1164
- $.ajax( {
1165
- url: customify_settings.import_rest_url + 'customify/1.0/import',
1166
- method: 'POST',
1167
- beforeSend: function( xhr ) {
1168
- xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
1169
- },
1170
- dataType: 'json',
1171
- contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
1172
- data: data_args
1173
- } ).done( function( response ) {
1174
- if ( ! _.isUndefined( response.success ) && response.success ) {
1175
- var results = response.data;
1176
- if ( step.type === 'remote' ) {
1177
- self.process_remote_step( key, results, step );
1178
- }
1179
- }
1180
-
1181
- if ( ! _.isUndefined( step_key ) && ! _.isUndefined( response.message ) ) {
1182
- self.log_action( 'end', step_key, response.message );
1183
- }
1184
- } );
1185
-
1186
- self.methodDeferred.resolve();
1187
- //}, 3450 );
1188
- };
1189
-
1190
- this.setup = function() {
1191
- var self = this;
1192
-
1193
- self.queueDeferred = $.Deferred();
1194
-
1195
- // when the previous method returns, resolve this one
1196
- $.when( lastPromise ).always( function() {
1197
- self.queueDeferred.resolve();
1198
- } );
1199
-
1200
- return self.queueDeferred.promise();
1201
- }
1202
- };
1203
-
1204
- /** HELPERS **/
1205
-
1206
- /**
1207
- * Function to check if a value exists in an object
1208
- * @param value
1209
- * @param obj
1210
- * @returns {boolean}
1211
- */
1212
- var inObject = function( value, obj ) {
1213
- for ( var k in obj ) {
1214
- if ( ! obj.hasOwnProperty( k ) ) {
1215
- continue;
1216
- }
1217
- if ( _.isEqual( obj[k], value ) ) {
1218
- return true;
1219
- }
1220
- }
1221
- return false;
1222
- };
1223
-
1224
- var maybeJsonParse = function( value ) {
1225
- var parsed;
1226
-
1227
- //try and parse it, with decodeURIComponent
1228
- try {
1229
- parsed = JSON.parse( decodeURIComponent( value ) );
1230
- } catch ( e ) {
1231
-
1232
- // in case of an error, treat is as a string
1233
- parsed = value;
1234
- }
1235
-
1236
- return parsed;
1237
- };
1238
-
1239
- var getUrlVars = function( name ) {
1240
- var vars = [], hash;
1241
- var hashes = window.location.href.slice( window.location.href.indexOf( '?' ) + 1 ).split( '&' );
1242
- for ( var i = 0; i < hashes.length; i ++ ) {
1243
- hash = hashes[i].split( '=' );
1244
-
1245
- vars.push( hash[0] );
1246
- vars[hash[0]] = hash[1];
1247
- }
1248
-
1249
- if ( ! _.isUndefined( vars[name] ) ) {
1250
- return vars[name];
1251
- }
1252
- return false;
1253
- };
1254
-
1255
- var isJsonString = function( str ) {
1256
- try {
1257
- JSON.parse( str );
1258
- } catch ( e ) {
1259
- return false;
1260
- }
1261
- return true;
1262
- };
1263
- }
1264
- )( jQuery, window, wp );
1
  (
2
+ function ($, exports, wp) {
3
+ var api = wp.customize
4
+ var $window = $(window)
5
+
6
+ // when the customizer is ready prepare our fields events
7
+ wp.customize.bind('ready', function () {
8
+ var timeout = null
9
+
10
+ // Create a stack of callbacks bound to parent settings to be able to unbind them
11
+ // when altering the connected_fields attribute.
12
+ if (typeof window.connectedFieldsCallbacks === 'undefined') {
13
+ window.connectedFieldsCallbacks = {}
14
+ }
15
+
16
+ // add ace editors
17
+ $('.customify_ace_editor').each(function (key, el) {
18
+ var id = $(this).attr('id'),
19
+ css_editor = ace.edit(id)
20
+
21
+ var editor_type = $(this).data('editor_type')
22
+ // init the ace editor
23
+ css_editor.setTheme('ace/theme/github')
24
+ css_editor.getSession().setMode('ace/mode/' + editor_type)
25
+
26
+ // hide the textarea and enable the ace editor
27
+ var textarea = $('#' + id + '_textarea').hide()
28
+ css_editor.getSession().setValue(textarea.val())
29
+
30
+ // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
31
+ // if the user types faster than this delay then reset it
32
+ css_editor.getSession().on('change', function (e) {
33
+ if (timeout !== null) {
34
+ clearTimeout(timeout)
35
+ timeout = null
36
+ } else {
37
+ timeout = setTimeout(function () {
38
+ //var state = css_editor.session.getState();
39
+ textarea.val(css_editor.getSession().getValue())
40
+ textarea.trigger('change')
41
+ }, 1500)
42
+ }
43
+ })
44
+ })
45
+
46
+ // simple select2 field
47
+ $('.customify_select2').select2()
48
+
49
+ setTimeout(function () {
50
+ CustomifyFontSelectFields.init()
51
+ }, 333)
52
+
53
+ prepare_typography_field()
54
+
55
+ /**
56
+ * Make the customizer save on CMD/CTRL+S action
57
+ * This is awesome!!!
58
+ */
59
+ $(window).bind('keydown', function (event) {
60
+ if (event.ctrlKey || event.metaKey) {
61
+ switch (String.fromCharCode(event.which).toLowerCase()) {
62
+ case 's':
63
+ event.preventDefault()
64
+ api.previewer.save()
65
+ break
66
+ }
67
+ }
68
+ })
69
+
70
+ // for each range input add a value preview output
71
+ $('.accordion-section-content[id*="' + customify_settings.options_name + '"], #sub-accordion-section-style_manager_section').each(function () {
72
+
73
+ // Initialize range fields logic
74
+ customifyHandleRangeFields(this)
75
+ })
76
+
77
+ if ($('button[data-action="reset_customify"]').length > 0) {
78
+ // reset_button
79
+ $(document).on('click', '#customize-control-reset_customify button', function (ev) {
80
+ ev.preventDefault()
81
+
82
+ var iAgree = confirm('Do you really want to reset to defaults all the fields? Watch out, this will reset all your Customify options and will save them!')
83
+
84
+ if (!iAgree) {
85
+ return
86
+ }
87
+
88
+ $.each(api.settings.controls, function (key, ctrl) {
89
+ var setting_id = key.replace('_control', '')
90
+ var setting = customify_settings.settings[setting_id]
91
+
92
+ if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
93
+ api_set_setting_value(setting_id, setting.default)
94
  }
95
+ })
96
+
97
+ api.previewer.save()
98
+ })
99
+
100
+ // add a reset button for each panel
101
+ $('.panel-meta').each(function (el, key) {
102
+ var container = $(this).parents('.control-panel'),
103
+ id = container.attr('id')
104
+
105
+ if (typeof id !== 'undefined') {
106
+ var panel_id = id.replace('accordion-panel-', '')
107
+ $(this).parent().append('<button class="reset_panel button" data-panel="' + panel_id + '">Panel\'s defaults</button>')
108
+ }
109
+ })
110
+
111
+ // reset panel
112
+ $(document).on('click', '.reset_panel', function (e) {
113
+ e.preventDefault()
114
+
115
+ var panel_id = $(this).data('panel'),
116
+ panel = api.panel(panel_id),
117
+ sections = panel.sections(),
118
+ iAgree = confirm('Do you really want to reset ' + panel.params.title + '?')
119
+
120
+ if (!iAgree) {
121
+ return
122
+ }
123
+ if (sections.length > 0) {
124
+ $.each(sections, function () {
125
+ //var settings = this.settings();
126
+ var controls = this.controls()
127
+
128
+ if (controls.length > 0) {
129
+ $.each(controls, function (key, ctrl) {
130
+ var setting_id = ctrl.id.replace('_control', ''),
131
+ setting = customify_settings.settings[setting_id]
132
+
133
+ if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
134
+ api_set_setting_value(setting_id, setting.default)
135
+ }
136
+ })
137
+ }
138
+ })
139
+ }
140
+ })
141
+
142
+ //add reset section
143
+ $('.accordion-section-content').each(function (el, key) {
144
+ var section_id = $(this).attr('id')
145
+
146
+ if ((
147
+ (
148
+ !_.isUndefined(section_id)
149
+ ) ? section_id.indexOf(customify_settings.options_name) : -1
150
+ ) === -1) {
151
+ return
152
+ }
153
+
154
+ if (!_.isUndefined(section_id) && section_id.indexOf('sub-accordion-section-') > -1) {
155
+ var id = section_id.replace('sub-accordion-section-', '')
156
+ $(this).append('<button class="reset_section button" data-section="' + id + '">Reset All Options for This Section</button>')
157
+ }
158
+ })
159
+
160
+ // reset section event
161
+ $(document).on('click', '.reset_section', function (e) {
162
+ e.preventDefault()
163
+
164
+ var section_id = $(this).data('section'),
165
+ section = api.section(section_id),
166
+ controls = section.controls()
167
+
168
+ var iAgree = confirm('Do you really want to reset ' + section.params.title + '?')
169
+
170
+ if (!iAgree) {
171
+ return
172
+ }
173
+
174
+ if (controls.length > 0) {
175
+ $.each(controls, function (key, ctrl) {
176
+ var setting_id = ctrl.id.replace('_control', ''),
177
+ setting = customify_settings.settings[setting_id]
178
+
179
+ if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
180
+ api_set_setting_value(setting_id, setting.default)
181
+ }
182
+ })
183
+ }
184
+ })
185
+ }
186
+
187
+ $(document).on('change keyup', '.customize-control-range input.range-value', function () {
188
+ var range = $(this).siblings('input[type="range"]')
189
+ range.val($(this).val())
190
+ range.trigger('change')
191
+ })
192
+
193
+ $(document).on('change', '.customify_typography_font_subsets', function (ev) {
194
+
195
+ var $input = $(this).parents('.options').siblings('.customify_typography').children('.customify_typography_values'),
196
+ current_val = $input.val()
197
+
198
+ current_val = JSON.parse(decodeURIComponent(current_val))
199
+
200
+ //maybe the selected option holds a JSON in its value
201
+ current_val.selected_subsets = maybeJsonParse($(this).val())
202
+
203
+ $input.val(encodeURIComponent(JSON.stringify(current_val)))
204
+
205
+ $input.trigger('change')
206
+ })
207
+
208
+ $(document).on('change', '.customify_typography_font_weight', function (ev) {
209
+
210
+ var $input = $(this).parents('.options').siblings('.customify_typography').children('.customify_typography_values'),
211
+ current_val = $input.val()
212
+
213
+ current_val = maybeJsonParse(current_val)
214
+ // @todo currently the font weight selector works for one value only
215
+ // maybe make this a multiselect
216
+
217
+ //maybe the selected option holds a JSON in its value
218
+ current_val.selected_variants = {0: maybeJsonParse($(this).val())}
219
+
220
+ $input.val(encodeURIComponent(JSON.stringify(current_val)))
221
+ $input.trigger('change')
222
+ })
223
+
224
+ $('body').on('customify:preset-change', function (e) {
225
+ const data = $(e.target).data('options')
226
+
227
+ if (!_.isUndefined(data)) {
228
+ $.each(data, function (setting_id, value) {
229
+ api_set_setting_value(setting_id, value)
230
+ })
231
+ }
232
+ })
233
+
234
+ $(document).on('change', '.customify_preset.select', function () {
235
+ const $source = $(this)
236
+ const $target = $source.children('[value="' + $source.val() + '"]')
237
+ $target.trigger('customify:preset-change')
238
+ })
239
+
240
+ $(document).on('click', '.customify_preset.radio input, .customify_preset.radio_buttons input, .awesome_presets input', function () {
241
+ $(this).trigger('customify:preset-change')
242
+ })
243
+
244
+ // bind our event on click
245
+ $(document).on('click', '.customify_import_demo_data_button', function (event) {
246
+ let key = $(this).data('key')
247
+ let import_queue = new Queue(api)
248
+ let steps = []
249
+
250
+ if (!_.isUndefined(customify_settings.settings[key].imports)) {
251
 
252
+ $.each(customify_settings.settings[key].imports, function (i, import_setts, k) {
253
+ if (_.isUndefined(import_setts.steps)) {
254
+ steps.push({id: i, type: import_setts.type})
255
+ } else {
256
+ var count = import_setts.steps
257
+
258
+ while (count >= 1) {
259
+ steps.push({id: i, type: import_setts.type, count: count})
260
+ count = count - 1
261
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
+ })
264
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
+ import_queue.add_steps('import_demo_data_action_id', steps)
267
+ return false
268
+ })
269
+
270
+ customifyBackgroundJsControl.init()
271
+
272
+ // sometimes a php save may be needed
273
+ if (getUrlVars('save_customizer_once')) {
274
+ api.previewer.save()
275
+ }
276
+
277
+ setTimeout(function () {
278
+ customifyFoldingFields()
279
+ }, 1000);
280
+
281
+ // Handle the section tabs (ex: Layout | Fonts | Colors)
282
+ (
283
+ function () {
284
+ var $navs = $('.js-section-navigation')
285
+
286
+ $navs.each(function () {
287
+ var $nav = $(this)
288
+ var $title = $nav.parents('.accordion-section-content').find('.customize-section-title')
289
+
290
+ $nav.closest('.customize-control').addClass('screen-reader-text')
291
+ $title.append($nav).parent().addClass('has-nav')
292
+ })
293
+
294
+ $('.js-section-navigation a').on('click', function (e) {
295
+ e.preventDefault()
296
+
297
+ var $sidebar = $(this).parents('.customize-pane-child')
298
+ var $parent = $(this).parents('.accordion-section-content')
299
+ var href = $.attr(this, 'href')
300
+
301
+ if (href != '#') {
302
+ $sidebar.animate({
303
+ scrollTop: $($.attr(this, 'href')).position().top - $parent.find('.customize-section-title').outerHeight()
304
+ }, 500)
305
+ }
306
+ })
307
+ }
308
+ )();
309
+
310
+ (
311
+ function () {
312
+ // Close a font field when clicking on another field
313
+ $('.customify_font_tooltip').on('click', function () {
314
+ if ($(this).prop('checked') === true) {
315
+ $('.customify_font_tooltip').prop('checked', false)
316
+ $(this).prop('checked', true)
317
+ }
318
+ })
319
+ }
320
+ )()
321
+
322
+ // Bind any connected fields, except those in the Style Manager.
323
+ // Those are handled by the appropriate Style Manager component (Color Palettes, Font Palettes, etc ).
324
+ bindConnectedFields()
325
+
326
+ })
327
+
328
+ const getConnectedFieldsCallback = function (parent_setting_data, parent_setting_id) {
329
+ return function (new_value, old_value) {
330
+ _.each(parent_setting_data.connected_fields, function (connected_field_data) {
331
+ if (_.isUndefined(connected_field_data) || _.isUndefined(connected_field_data.setting_id) || !_.isString(connected_field_data.setting_id)) {
332
+ return
333
+ }
334
+ const setting = wp.customize(connected_field_data.setting_id)
335
+ if (_.isUndefined(setting)) {
336
+ return
337
+ }
338
+ setting.set(new_value)
339
+ })
340
+ }
341
+ }
342
+
343
+ const bindConnectedFields = function () {
344
+ _.each(wp.customize.settings.settings, function (parent_setting_data, parent_setting_id) {
345
+ // We don't want to handle the binding of the Style Manager settings
346
+ if (typeof ColorPalettes !== 'undefined'
347
+ && typeof ColorPalettes.masterSettingIds !== 'undefined'
348
+ && _.contains(ColorPalettes.masterSettingIds, parent_setting_id)) {
349
+ return
350
+ }
351
+ if (typeof FontPalettes !== 'undefined'
352
+ && typeof FontPalettes.masterSettingIds !== 'undefined'
353
+ && _.contains(FontPalettes.masterSettingIds, parent_setting_id)) {
354
+ return
355
+ }
356
+
357
+ let parent_setting = wp.customize(parent_setting_id)
358
+ if (typeof parent_setting_data.connected_fields !== 'undefined') {
359
+ connectedFieldsCallbacks[parent_setting_id] = getConnectedFieldsCallback(parent_setting_data, parent_setting_id)
360
+ parent_setting.bind(connectedFieldsCallbacks[parent_setting_id])
361
+ }
362
+ })
363
+ }
364
+
365
+ const unbindConnectedFields = function () {
366
+ _.each(wp.customize.settings.settings, function (parent_setting_data, parent_setting_id) {
367
+ // We don't want to handle the binding of the Style Manager settings
368
+ if (typeof ColorPalettes !== 'undefined'
369
+ && typeof ColorPalettes.masterSettingIds !== 'undefined'
370
+ && _.contains(ColorPalettes.masterSettingIds, parent_setting_id)) {
371
+ return
372
+ }
373
+ if (typeof FontPalettes !== 'undefined'
374
+ && typeof FontPalettes.masterSettingIds !== 'undefined'
375
+ && _.contains(FontPalettes.masterSettingIds, parent_setting_id)) {
376
+ return
377
+ }
378
+
379
+ let parent_setting = wp.customize(parent_setting_id)
380
+ if (typeof parent_setting_data.connected_fields !== 'undefined' && typeof connectedFieldsCallbacks[parent_setting_id] !== 'undefined') {
381
+ parent_setting.unbind(connectedFieldsCallbacks[parent_setting_id])
382
+ }
383
+ delete connectedFieldsCallbacks[parent_setting_id]
384
+ })
385
+ }
386
+
387
+ const customifyHandleRangeFields = function (el) {
388
+
389
+ // For each range input add a number field (for preview mainly - but it can also be used for input)
390
+ $(el).find('input[type="range"]').each(function () {
391
+ if (!$(this).siblings('.range-value').length) {
392
+ var $clone = $(this).clone()
393
+
394
+ $clone
395
+ .attr('type', 'number')
396
+ .attr('class', 'range-value')
397
+ .removeAttr('data-field')
398
+
399
+ $(this).after($clone)
400
+ }
401
+
402
+ // Update the number field when changing the range
403
+ $(this).on('change', function () {
404
+ $(this).siblings('.range-value').val($(this).val())
405
+ })
406
+
407
+ // And the other way around, update the range field when changing the number
408
+ $($clone).on('change', function () {
409
+ $(this).siblings('input[type="range"]').val($(this).val())
410
+ })
411
+ })
412
+ }
413
+
414
+ /**
415
+ * This function will search for all the interdependend fields and make a bound between them.
416
+ * So whenever a target is changed, it will take actions to the dependent fields.
417
+ * @TODO this is still written in a barbaric way, refactor when needed
418
+ */
419
+ var customifyFoldingFields = function () {
420
+
421
+ if (_.isUndefined(customify_settings) || _.isUndefined(customify_settings.settings)) {
422
+ return // bail
423
+ }
424
+
425
+ /**
426
+ * Let's iterate through all the customify settings and gather all the fields that have a "show_if"
427
+ * property set.
428
+ *
429
+ * At the end `targets` will hold a list of [ target : [field, field,...], ... ]
430
+ * so when a target is changed we will change all the fields.
431
+ */
432
+ var targets = {}
433
+
434
+ $.fn.reactor.defaults.compliant = function () {
435
+ $(this).slideDown()
436
+ // $(this).animate({opacity: 1});
437
+ $(this).find(':disabled').attr({disabled: false})
438
+ }
439
+
440
+ $.fn.reactor.defaults.uncompliant = function () {
441
+ $(this).slideUp()
442
+ // $(this).animate({opacity: 0.25});
443
+ $(this).find(':enabled').attr({disabled: true})
444
+ }
445
+
446
+ var IS = $.extend({}, $.fn.reactor.helpers)
447
+
448
+ var bind_folding_events = function (parent_id, field, relation) {
449
+
450
+ var key = null
451
+
452
+ if (_.isString(field)) {
453
+ key = field
454
+ } else if (!_.isUndefined(field.id)) {
455
+ key = field.id
456
+ } else if (isString(field[0])) {
457
+ key = field[0]
458
+ } else {
459
+ return // no key, no fun
460
+ }
461
+
462
+ var value = 1, // by default we use 1 the most used value for checkboxes or inputs
463
+ compare = '==', // ... ye
464
+ action = 'show',
465
+ between = [0, 1] // can only be `show` or `hide`
466
+
467
+ var target_key = customify_settings.options_name + '[' + key + ']'
468
+
469
+ var target_type = customify_settings.settings[target_key].type
470
+
471
+ // we support the usual syntax like a config array like `array( 'id' => $id, 'value' => $value, 'compare' => $compare )`
472
+ // but we also support a non-associative array like `array( $id, $value, $compare )`
473
+ if (!_.isUndefined(field.value)) {
474
+ value = field.value
475
+ } else if (!_.isUndefined(field[1]) && !_.isString(field[1])) {
476
+ value = field[1]
477
+ }
478
+
479
+ if (!_.isUndefined(field.compare)) {
480
+ compare = field.compare
481
+ } else if (!_.isUndefined(field[2])) {
482
+ compare = field[2]
483
+ }
484
+
485
+ if (!_.isUndefined(field.action)) {
486
+ action = field.action
487
+ } else if (!_.isUndefined(field[3])) {
488
+ action = field[3]
489
+ }
490
+
491
+ // a field can also overwrite the parent relation
492
+ if (!_.isUndefined(field.relation)) {
493
+ action = field.relation
494
+ } else if (!_.isUndefined(field[4])) {
495
+ action = field[4]
496
+ }
497
+
498
+ if (!_.isUndefined(field.between)) {
499
+ between = field.between
500
+ }
501
+
502
+ /**
503
+ * Now for each target we have, we will bind a change event to hide or show the dependent fields
504
+ */
505
+ var target_selector = '[data-customize-setting-link="' + customify_settings.options_name + '[' + key + ']"]'
506
+
507
+ switch (target_type) {
508
+ case 'checkbox':
509
+ $(parent_id).reactIf(target_selector, function () {
510
+ return $(this).is(':checked') == value
511
+ })
512
+ break
513
+
514
+ case 'radio':
515
+ case 'sm_radio':
516
+ case 'sm_switch':
517
+ case 'radio_image':
518
+
519
+ // in case of an array of values we use the ( val in array) condition
520
+ if (_.isObject(value)) {
521
+ $(parent_id).reactIf(target_selector, function () {
522
+ return (
523
+ value.indexOf($(target_selector + ':checked').val()) !== -1
524
+ )
525
+ })
526
+ } else { // in any other case we use a simple == comparison
527
+ $(parent_id).reactIf(target_selector, function () {
528
+ return $(target_selector + ':checked').val() == value
529
+ })
530
+ }
531
+ break
532
+
533
+ case 'range':
534
+ var x = IS.Between(between[0], between[1])
535
+
536
+ $(parent_id).reactIf(target_selector, x)
537
+ break
538
+
539
+ default:
540
+ // in case of an array of values we use the ( val in array) condition
541
+ if (_.isObject(value)) {
542
+ $(parent_id).reactIf(target_selector, function () {
543
+ return (
544
+ value.indexOf($(target_selector).val()) !== -1
545
+ )
546
+ })
547
+ } else { // in any other case we use a simple == comparison
548
+ $(parent_id).reactIf(target_selector, function () {
549
+ return $(target_selector).val() == value
550
+ })
551
+ }
552
+ break
553
+ }
554
+
555
+ $(target_selector).trigger('change')
556
+ $('.reactor').trigger('change.reactor') // triggers all events on load
557
+ }
558
+
559
+ $.each(customify_settings.settings, function (id, field) {
560
+ /**
561
+ * Here we have the id of the fields. but we know for sure that we just need his parent selector
562
+ * So we just create it
563
+ */
564
+ var parent_id = id.replace('[', '-')
565
+ parent_id = parent_id.replace(']', '')
566
+ parent_id = '#customize-control-' + parent_id + '_control'
567
+
568
+ // get only the fields that have a 'show_if' property
569
+ if (field.hasOwnProperty('show_if')) {
570
+ var relation = 'AND'
571
+
572
+ if (!_.isUndefined(field.show_if.relation)) {
573
+ relation = field.show_if.relation
574
+ // remove the relation property, we need the config to be array based only
575
+ delete field.show_if.relation
576
+ }
577
+
578
+ /**
579
+ * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
580
+ * Or it could be an array of multiple targets and we need to process both cases
581
+ */
582
+
583
+ if (!_.isUndefined(field.show_if.id)) {
584
+ bind_folding_events(parent_id, field.show_if, relation)
585
+ } else if (_.isObject(field.show_if)) {
586
+ $.each(field.show_if, function (i, j) {
587
+ bind_folding_events(parent_id, j, relation)
588
+ })
589
+ }
590
+ }
591
+ })
592
+ }
593
+
594
+ var get_typography_font_family = function ($el) {
595
+
596
+ var font_family_value = $el.val()
597
+ // first time this will not be a json so catch that error
598
+ try {
599
+ font_family_value = JSON.parse(font_family_value)
600
+ } catch (e) {
601
+ return {font_family: font_family_value}
602
+ }
603
+
604
+ if (!_.isUndefined(font_family_value.font_family)) {
605
+ return font_family_value.font_family
606
+ }
607
+
608
+ return false
609
+ }
610
+
611
+ // get each typography field and bind events
612
+ // @todo Are we still using the typography field since we have the font field?
613
+ var prepare_typography_field = function () {
614
+
615
+ var $typos = $('.customify_typography_font_family')
616
+
617
+ $typos.each(function () {
618
+ var font_family_select = this,
619
+ $input = $(font_family_select).siblings('.customify_typography_values')
620
+ // on change
621
+ $(font_family_select).on('change', function () {
622
+ update_siblings_selects(font_family_select)
623
+ $input.trigger('change')
624
+ })
625
+ update_siblings_selects(font_family_select)
626
+ })
627
+ }
628
+
629
+ var api_set_setting_value = function (setting_id, value) {
630
+ let setting = api(setting_id),
631
+ field = $('[data-customize-setting-link="' + setting_id + '"]'),
632
+ field_class = $(field).parent().attr('class')
633
+
634
+ // Legacy field type
635
+ if (!_.isUndefined(field_class) && field_class === 'customify_typography') {
636
+
637
+ let family_select = field.siblings('select')
638
+
639
+ if (_.isString(value)) {
640
+ let this_option = family_select.find('option[value="' + value + '"]')
641
+ $(this_option[0]).attr('selected', 'selected')
642
+ update_siblings_selects(family_select)
643
+ } else if (_.isObject(value)) {
644
+ let this_family_option = family_select.find('option[value="' + value['font_family'] + '"]')
645
+
646
+ $(this_family_option[0]).attr('selected', 'selected')
647
+
648
+ update_siblings_selects(this_family_option)
649
+
650
+ setTimeout(function () {
651
+ let weight_select = field.parent().siblings('.options').find('.customify_typography_font_weight'),
652
+ this_weight_option = weight_select.find('option[value="' + value['selected_variants'] + '"]')
653
+
654
+ $(this_weight_option[0]).attr('selected', 'selected')
655
+
656
+ update_siblings_selects(this_family_option)
657
+
658
+ weight_select.trigger('change')
659
+ }, 300)
660
+ }
661
+
662
+ family_select.trigger('change')
663
+
664
+ } else if (!_.isUndefined(field_class) && field_class === 'font-options__wrapper') {
665
+
666
+ // if the value is a simple string it must be the font family
667
+ if (_.isString(value)) {
668
+ let option = field.parent().find('option[value="' + value + '"]')
669
+
670
+ option.attr('selected', 'selected')
671
+ // option.parents('select').trigger('change');
672
+ } else if (_.isObject(value)) {
673
+ // Find the options list wrapper
674
+ let optionsList = field.parent().children('.font-options__options-list')
675
+
676
+ if (optionsList.length) {
677
+ // We will process each font property and update it
678
+ _.each(value, function (val, key) {
679
+ // We need to map the keys to the data attributes we are using - I know :(
680
+ let mappedKey = key
681
+ switch (key) {
682
+ case 'font-family':
683
+ mappedKey = 'font_family'
684
+ break
685
+ case 'font-size':
686
+ mappedKey = 'font_size'
687
+ break
688
+ case 'font-weight':
689
+ mappedKey = 'selected_variants'
690
+ break
691
+ case 'letter-spacing':
692
+ mappedKey = 'letter_spacing'
693
+ break
694
+ case 'text-transform':
695
+ mappedKey = 'text_transform'
696
+ break
697
+ default:
698
+ break
699
+ }
700
+ let subField = optionsList.find('[data-field="' + mappedKey + '"]')
701
+ if (subField.length) {
702
+ subField.val(val)
703
+ subField.trigger('change')
704
+ }
705
+ })
706
+ }
707
+ }
708
+
709
+ } else {
710
+ setting.set(value)
711
+ }
712
+ }
713
+
714
+ var update_siblings_selects = function (font_select) {
715
+ var selected_font = $(font_select).val(),
716
+ $input = $(font_select).siblings('.customify_typography_values'),
717
+ current_val = $input.attr('value')
718
+
719
+ if (current_val === '[object Object]') {
720
+ current_val = $input.data('default')
721
+ } else if (_.isString(current_val) && !isJsonString(current_val) && current_val.substr(0, 1) == '[') {
722
+ // a rare case when the value isn't a json but is a representative string like [family,weight]
723
+ current_val = current_val.split(',')
724
+ var new_current_value = {}
725
+ if (!_.isUndefined(current_val[0])) {
726
+ new_current_value['font_family'] = current_val[0]
727
+ }
728
+
729
+ if (!_.isUndefined(current_val[1])) {
730
+ new_current_value['selected_variants'] = current_val[1]
731
+ }
732
+
733
+ current_val = JSON.stringify(new_current_value)
734
+ }
735
+
736
+ var $font_weight = $(font_select).parent().siblings('ul.options').find('.customify_typography_font_weight'),
737
+ $font_subsets = $(font_select).parent().siblings('ul.options').find('.customify_typography_font_subsets')
738
+
739
+ try {
740
+ current_val = JSON.parse(decodeURIComponent(current_val))
741
+ } catch (e) {
742
+
743
+ // in case of an error, force the rebuild of the json
744
+ if (_.isUndefined($(font_select).data('bound_once'))) {
745
+
746
+ $(font_select).data('bound_once', true)
747
+
748
+ $(font_select).change()
749
+ $font_weight.change()
750
+ $font_subsets.change()
751
+ }
752
+ }
753
+
754
+ // first try to get the font from sure sources, not from the recommended list.
755
+ var option_data = $(font_select).find(':not(optgroup[label=Recommended]) option[value="' + selected_font + '"]')
756
+ // however, if there isn't an option found, get what you can
757
+ if (option_data.length < 1) {
758
+ option_data = $(font_select).find('option[value="' + selected_font + '"]')
759
+ }
760
+
761
+ if (option_data.length > 0) {
762
+
763
+ var font_type = option_data.data('type'),
764
+ value_to_add = {'type': font_type, 'font_family': selected_font},
765
+ variants = null,
766
+ subsets = null
767
+
768
+ if (font_type == 'std') {
769
+ variants = {
770
+ 0: '100',
771
+ 1: '200',
772
+ 3: '300',
773
+ 4: '400',
774
+ 5: '500',
775
+ 6: '600',
776
+ 7: '700',
777
+ 8: '800',
778
+ 9: '900'
779
+ }
780
+ if (!_.isUndefined($(option_data[0]).data('variants'))) {
781
+ //maybe the variants are a JSON
782
+ variants = maybeJsonParse($(option_data[0]).data('variants'))
783
+ }
784
+ } else {
785
+ //maybe the variants are a JSON
786
+ variants = maybeJsonParse($(option_data[0]).data('variants'))
787
+
788
+ //maybe the subsets are a JSON
789
+ subsets = maybeJsonParse($(option_data[0]).data('subsets'))
790
+ }
791
+
792
+ // make the variants selector
793
+ if (!_.isUndefined(variants) && !_.isNull(variants) && !_.isEmpty(variants)) {
794
+
795
+ value_to_add['variants'] = variants
796
+ // when a font is selected force the first weight to load
797
+ value_to_add['selected_variants'] = {0: variants[0]}
798
+
799
+ var variants_options = '',
800
+ count_weights = 0
801
+
802
+ if (_.isArray(variants) || _.isObject(variants)) {
803
+ // Take each variant and produce the option markup
804
+ $.each(variants, function (key, el) {
805
+ var is_selected = ''
806
+ if (_.isObject(current_val.selected_variants) && inObject(el, current_val.selected_variants)) {
807
+ is_selected = ' selected="selected"'
808
+ } else if (_.isString(current_val.selected_variants) && el === current_val.selected_variants) {
809
+ is_selected = ' selected="selected"'
810
+ }
811
+
812
+ // initialize
813
+ var variant_option_value = el,
814
+ variant_option_display = el
815
+
816
+ // If we are dealing with a object variant then it means things get tricky (probably it's our fault but bear with us)
817
+ // This probably comes from our Fonto plugin - a font with individually named variants - hence each has its own font-family
818
+ if (_.isObject(el)) {
819
+ //put the entire object in the variation value - we will need it when outputting the custom CSS
820
+ variant_option_value = encodeURIComponent(JSON.stringify(el))
821
+ variant_option_display = ''
822
+
823
+ //if we have weight and style then "compose" them into something standard
824
+ if (!_.isUndefined(el['font-weight'])) {
825
+ variant_option_display += el['font-weight']
826
  }
827
+
828
+ if (_.isString(el['font-style']) && $.inArray(el['font-style'].toLowerCase(), [
829
+ 'normal',
830
+ 'regular'
831
+ ]) < 0) { //this comparison means it hasn't been found
832
+ variant_option_display += el['font-style']
833
  }
834
+ }
835
+
836
+ variants_options += '<option value="' + variant_option_value + '"' + is_selected + '>' + variant_option_display + '</option>'
837
+ count_weights++
838
+ })
839
+ }
840
+
841
+ if (!_.isUndefined($font_weight)) {
842
+ $font_weight.html(variants_options)
843
+ // if there is no weight or just 1 we hide the weight select ... cuz is useless
844
+ if ($(font_select).data('load_all_weights') === true || count_weights <= 1) {
845
+ $font_weight.parent().css('display', 'none')
846
+ } else {
847
+ $font_weight.parent().css('display', 'inline-block')
848
+ }
849
+ }
850
+ } else if (!_.isUndefined($font_weight)) {
851
+ $font_weight.parent().css('display', 'none')
852
+ }
853
+
854
+ // make the subsets selector
855
+ if (!_.isUndefined(subsets) && !_.isNull(subsets) && !_.isEmpty(subsets)) {
856
+
857
+ value_to_add['subsets'] = subsets
858
+ // when a font is selected force the first subset to load
859
+ value_to_add['selected_subsets'] = {0: subsets[0]}
860
+ var subsets_options = '',
861
+ count_subsets = 0
862
+ $.each(subsets, function (key, el) {
863
+ var is_selected = ''
864
+ if (_.isObject(current_val.selected_subsets) && inObject(el, current_val.selected_subsets)) {
865
+ is_selected = ' selected="selected"'
866
+ }
867
+
868
+ subsets_options += '<option value="' + el + '"' + is_selected + '>' + el + '</option>'
869
+ count_subsets++
870
+ })
871
+
872
+ if (!_.isUndefined($font_subsets)) {
873
+ $font_subsets.html(subsets_options)
874
+
875
+ // if there is no subset or just 1 we hide the subsets select ... cuz is useless
876
+ if (count_subsets <= 1) {
877
+ $font_subsets.parent().css('display', 'none')
878
+ } else {
879
+ $font_subsets.parent().css('display', 'inline-block')
880
+ }
881
+ }
882
+ } else if (!_.isUndefined($font_subsets)) {
883
+ $font_subsets.parent().css('display', 'none')
884
+ }
885
+
886
+ $input.val(encodeURIComponent(JSON.stringify(value_to_add)))
887
+ }
888
+ }
889
+
890
+ /** Modules **/
891
+
892
+ var customifyBackgroundJsControl = (
893
+ function () {
894
+ 'use strict'
895
+
896
+ function init () {
897
+ // Remove the image button
898
+ $('.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file').unbind('click').on('click', function (e) {
899
+ removeImage($(this).parents('.customize-control-custom_background:first'))
900
+ preview($(this))
901
+ return false
902
+ })
903
+
904
+ // Upload media button
905
+ $('.customize-control-custom_background .background_upload_button').unbind().on('click', function (event) {
906
+ addImage(event, $(this).parents('.customize-control-custom_background:first'))
907
+ })
908
+
909
+ $('.customify_background_select').on('change', function () {
910
+ preview($(this))
911
+ })
912
+ }
913
+
914
+ // Add a file via the wp.media function
915
+ function addImage (event, selector) {
916
+
917
+ event.preventDefault()
918
+
919
+ var frame
920
+ var jQueryel = jQuery(this)
921
+
922
+ // If the media frame already exists, reopen it.
923
+ if (frame) {
924
+ frame.open()
925
+ return
926
+ }
927
+
928
+ // Create the media frame.
929
+ frame = wp.media({
930
+ multiple: false,
931
+ library: {
932
+ //type: 'image' //Only allow images
933
+ },
934
+ // Set the title of the modal.
935
+ title: jQueryel.data('choose'),
936
+
937
+ // Customize the submit button.
938
+ button: {
939
+ // Set the text of the button.
940
+ text: jQueryel.data('update')
941
+ // Tell the button not to close the modal, since we're
942
+ // going to refresh the page when the image is selected.
943
+ }
944
+ })
945
+
946
+ // When an image is selected, run a callback.
947
+ frame.on('select', function () {
948
+ // Grab the selected attachment.
949
+ var attachment = frame.state().get('selection').first()
950
+ frame.close()
951
+
952
+ if (attachment.attributes.type !== 'image') {
953
+ return
954
+ }
955
 
956
+ selector.find('.upload').attr('value', attachment.attributes.url)
957
+ selector.find('.upload-id').attr('value', attachment.attributes.id)
958
+ selector.find('.upload-height').attr('value', attachment.attributes.height)
959
+ selector.find('.upload-width').attr('value', attachment.attributes.width)
960
+
961
+ var thumbSrc = attachment.attributes.url
962
+ if (!_.isUndefined(attachment.attributes.sizes) && !_.isUndefined(attachment.attributes.sizes.thumbnail)) {
963
+ thumbSrc = attachment.attributes.sizes.thumbnail.url
964
+ } else if (!_.isUndefined(attachment.attributes.sizes)) {
965
+ var height = attachment.attributes.height
966
+ for (var key in attachment.attributes.sizes) {
967
+ var object = attachment.attributes.sizes[key]
968
+ if (object.height < height) {
969
+ height = object.height
970
+ thumbSrc = object.url
971
  }
972
+ }
973
+ } else {
974
+ thumbSrc = attachment.attributes.icon
975
+ }
976
+
977
+ selector.find('.customify_background_input.background-image').val(attachment.attributes.url)
978
+
979
+ if (!selector.find('.upload').hasClass('noPreview')) {
980
+ selector.find('.preview_screenshot').empty().hide().append('<img class="preview_image" src="' + thumbSrc + '">').slideDown('fast')
981
+ }
982
+ //selector.find('.media_upload_button').unbind();
983
+ selector.find('.remove-image').removeClass('hide')//show "Remove" button
984
+ selector.find('.customify_background_select').removeClass('hide')//show "Remove" button
985
+
986
+ preview(selector)
987
+ })
988
+
989
+ // Finally, open the modal.
990
+ frame.open()
991
+ }
992
+
993
+ // Update the background preview
994
+ function preview (selector) {
995
+
996
+ var $parent = selector.parents('.customize-control-custom_background:first')
997
+
998
+ if (selector.hasClass('customize-control-custom_background')) {
999
+ $parent = selector
1000
+ }
1001
+
1002
+ if ($parent.length > 0) {
1003
+ $parent = $($parent[0])
1004
+ } else {
1005
+ return
1006
+ }
1007
+
1008
+ var image_holder = $parent.find('.background-preview')
1009
+
1010
+ if (!image_holder) { // No preview present
1011
+ return
1012
+ }
1013
+
1014
+ var the_id = $parent.find('.button.background_upload_button').data('setting_id'),
1015
+ this_setting = api.instance(the_id)
1016
+
1017
+ var background_data = {}
1018
+
1019
+ $parent.find('.customify_background_select, .customify_background_input').each(function () {
1020
+ var data = $(this).serializeArray()
1021
+
1022
+ data = data[0]
1023
+ if (data && data.name.indexOf('[background-') != -1) {
1024
+
1025
+ background_data[$(this).data('select_name')] = data.value
1026
+
1027
+ //default_default[data.name] = data.value;
1028
+ //if (data.name == "background-image") {
1029
+ // css += data.name + ':url("' + data.value + '");';
1030
+ //} else {
1031
+ // css += data.name + ':' + data.value + ';';
1032
+ //}
1033
+ }
1034
+ })
1035
+
1036
+ api.instance(the_id).set(background_data)
1037
+ //// Notify the customizer api about this change
1038
+ api.trigger('change')
1039
+ api.previewer.refresh()
1040
+
1041
+ //image_holder.attr('style', css).fadeIn();
1042
+ }
1043
+
1044
+ // Update the background preview
1045
+ function removeImage (parent) {
1046
+ var selector = parent.find('.upload_button_div')
1047
+ // This shouldn't have been run...
1048
+ if (!selector.find('.remove-image').addClass('hide')) {
1049
+ return
1050
+ }
1051
+
1052
+ selector.find('.remove-image').addClass('hide')//hide "Remove" button
1053
+ parent.find('.customify_background_select').addClass('hide')
1054
+
1055
+ selector.find('.upload').val('')
1056
+ selector.find('.upload-id').val('')
1057
+ selector.find('.upload-height').val('')
1058
+ selector.find('.upload-width').val('')
1059
+ parent.find('.customify_background_input.background-image').val('')
1060
+
1061
+ var customizer_id = selector.find('.background_upload_button').data('setting_id'),
1062
+ this_setting = api.control(customizer_id + '_control'),
1063
+ current_vals = this_setting.setting(),
1064
+ screenshot = parent.find('.preview_screenshot'),
1065
+ to_array = $.map(current_vals, function (value, index) {
1066
+ return [value]
1067
+ })
1068
+
1069
+ // Hide the screenshot
1070
+ screenshot.slideUp()
1071
+ selector.find('.remove-file').unbind()
1072
+ to_array['background-image'] = ''
1073
+ this_setting.setting(to_array)
1074
+ }
1075
+
1076
+ return {
1077
+ init: init
1078
+ }
1079
+ }
1080
+ )(jQuery)
1081
+
1082
+ var Queue = function () {
1083
+ var lastPromise = null
1084
+ var queueDeferred = null
1085
+ var methodDeferred = null
1086
+
1087
+ this.add_steps = function (key, steps, args) {
1088
+ var self = this
1089
+ this.methodDeferred = $.Deferred()
1090
+ this.queueDeferred = this.setup()
1091
+
1092
+ $.each(steps, function (i, step) {
1093
+ self.queue(key, step)
1094
+ })
1095
+ }
1096
+
1097
+ this.process_remote_step = function (key, data, step) {
1098
+ var self = this
1099
+
1100
+ if (_.isUndefined(data) || _.isNull(data)) {
1101
+ return false
1102
+ }
1103
+
1104
+ var new_step = step
1105
+ $.each(data, function (i, k) {
1106
+ debugger
1107
+ // prepare data for new requests
1108
+ new_step.recall_data = k.data
1109
+ new_step.recall_type = k.type
1110
+ new_step.type = 'recall'
1111
+
1112
+ self.queue(key, new_step, k.id)
1113
+ })
1114
+ }
1115
+
1116
+ this.log_action = function (action, key, msg) {
1117
+ if (action === 'start') {
1118
+ $('.wpGrade-import-results').show()
1119
+ $('.wpGrade-import-results').append('<span class="import_step_note imports_step_' + key + '" ><span class="step_info" data-balloon="Working on it" data-balloon-pos="up"></span>Importing ' + key + '</span>')
1120
+ } else if (action === 'end') {
1121
+ var $notice = $('.imports_step_' + key + ' .step_info')
1122
+
1123
+ if ($notice.length > 0 || msg !== 'undefined') {
1124
+ $notice.attr('data-balloon', msg)
1125
+ $notice.addClass('success')
1126
+ } else {
1127
+ $notice.attr('data-balloon', 'Done')
1128
+ $notice.addClass('failed')
1129
+ }
1130
+ }
1131
+ }
1132
+
1133
+ this.queue = function (key, data, step_key) {
1134
+ var self = this
1135
+ if (!_.isUndefined(step_key)) {
1136
+ this.log_action('start', step_key)
1137
+ }
1138
+
1139
+ // execute next queue method
1140
+ this.queueDeferred.done(this.request(key, data, step_key))
1141
+ lastPromise = self.methodDeferred.promise()
1142
+ }
1143
+
1144
+ this.request = function (key, step, step_key) {
1145
+ var self = this
1146
+ // call actual method and wrap output in deferred
1147
+ //setTimeout( function() {
1148
+ var data_args = {
1149
+ action: 'customify_import_step',
1150
+ step_id: step.id,
1151
+ step_type: step.type,
1152
+ option_key: key
1153
+ }
1154
+
1155
+ if (!_.isUndefined(step.recall_data)) {
1156
+ data_args.recall_data = step.recall_data
1157
+ }
1158
+
1159
+ if (!_.isUndefined(step.recall_type)) {
1160
+ data_args.recall_type = step.recall_type
1161
+ }
1162
+
1163
+ $.ajax({
1164
+ url: customify_settings.import_rest_url + 'customify/1.0/import',
1165
+ method: 'POST',
1166
+ beforeSend: function (xhr) {
1167
+ xhr.setRequestHeader('X-WP-Nonce', WP_API_Settings.nonce)
1168
+ },
1169
+ dataType: 'json',
1170
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
1171
+ data: data_args
1172
+ }).done(function (response) {
1173
+ if (!_.isUndefined(response.success) && response.success) {
1174
+ var results = response.data
1175
+ if (step.type === 'remote') {
1176
+ self.process_remote_step(key, results, step)
1177
+ }
1178
+ }
1179
+
1180
+ if (!_.isUndefined(step_key) && !_.isUndefined(response.message)) {
1181
+ self.log_action('end', step_key, response.message)
1182
+ }
1183
+ })
1184
+
1185
+ self.methodDeferred.resolve()
1186
+ //}, 3450 );
1187
+ }
1188
+
1189
+ this.setup = function () {
1190
+ var self = this
1191
+
1192
+ self.queueDeferred = $.Deferred()
1193
+
1194
+ // when the previous method returns, resolve this one
1195
+ $.when(lastPromise).always(function () {
1196
+ self.queueDeferred.resolve()
1197
+ })
1198
+
1199
+ return self.queueDeferred.promise()
1200
+ }
1201
+ }
1202
+
1203
+ /** HELPERS **/
1204
+
1205
+ /**
1206
+ * Function to check if a value exists in an object
1207
+ * @param value
1208
+ * @param obj
1209
+ * @returns {boolean}
1210
+ */
1211
+ var inObject = function (value, obj) {
1212
+ for (var k in obj) {
1213
+ if (!obj.hasOwnProperty(k)) {
1214
+ continue
1215
+ }
1216
+ if (_.isEqual(obj[k], value)) {
1217
+ return true
1218
+ }
1219
+ }
1220
+ return false
1221
+ }
1222
+
1223
+ var maybeJsonParse = function (value) {
1224
+ var parsed
1225
+
1226
+ //try and parse it, with decodeURIComponent
1227
+ try {
1228
+ parsed = JSON.parse(decodeURIComponent(value))
1229
+ } catch (e) {
1230
+
1231
+ // in case of an error, treat is as a string
1232
+ parsed = value
1233
+ }
1234
+
1235
+ return parsed
1236
+ }
1237
+
1238
+ var getUrlVars = function (name) {
1239
+ var vars = [], hash
1240
+ var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
1241
+ for (var i = 0; i < hashes.length; i++) {
1242
+ hash = hashes[i].split('=')
1243
+
1244
+ vars.push(hash[0])
1245
+ vars[hash[0]] = hash[1]
1246
+ }
1247
+
1248
+ if (!_.isUndefined(vars[name])) {
1249
+ return vars[name]
1250
+ }
1251
+ return false
1252
+ }
1253
+
1254
+ var isJsonString = function (str) {
1255
+ try {
1256
+ JSON.parse(str)
1257
+ } catch (e) {
1258
+ return false
1259
+ }
1260
+ return true
1261
+ }
1262
+ }
1263
+ )(jQuery, window, wp)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/customizer/font-select-fields.js CHANGED
@@ -1,449 +1,465 @@
1
  // This is for the Customizer Font control
2
- let CustomifyFontSelectFields = ( function( $, exports, wp ) {
3
- const
4
- wrapperSelector = '.font-options__wrapper',
5
- valueHolderSelector = '.customify_font_values',
6
- fontFamilySelector = '.customify_font_family',
7
- fontWeightSelector = '.customify_font_weight',
8
- fontSubsetsSelector = '.customify_font_subsets',
9
- selectPlaceholder = "Select a font family",
10
- weightPlaceholder = "Select a font weight",
11
- subsetPlaceholder = "Extra Subsets";
12
-
13
- // We will use this to remember that we are self-updating the field from the subfields.
14
- // We will save this info for each setting ID.
15
- var updatingValue = {},
16
- loadingValue = {};
17
-
18
- function init() {
19
- let $fontFamilyFields = $( fontFamilySelector );
20
-
21
- // Initialize the select2 field for the font family
22
- $fontFamilyFields.select2( {
23
- placeholder: selectPlaceholder
24
- } ).on( 'change', function( e ) {
25
- let new_option = $( e.target ).find( 'option:selected' ),
26
- wrapper = $( e.target ).closest( wrapperSelector );
27
-
28
- // Update the weight subfield with the new options given by the selected font family.
29
- updateWeightField( new_option, wrapper );
30
-
31
- // Update the subset subfield with the new options given by the selected font family.
32
- updateSubsetField( new_option, wrapper );
33
-
34
- // Serialize subfield values and refresh the fonts in the preview window.
35
- selfUpdateValue( wrapper );
36
- } );
37
-
38
- // Initialize the select2 field for the font weight
39
- $( fontWeightSelector ).each( function( i, el ) {
40
-
41
- let select2_args = {
42
- placeholder: weightPlaceholder
43
- };
44
-
45
- // all this fuss is for the case when the font doesn't come with variants from PHP, like a theme_font
46
- if ( this.options.length === 0 ) {
47
- let wrapper = $( el ).closest( wrapperSelector ),
48
- font = wrapper.find( fontFamilySelector ),
49
- option = font[0].options[font[0].selectedIndex],
50
- variants = maybeJsonParse( $( option ).data( 'variants' ) ),
51
- data = [],
52
- selecter_variants = $( el ).data( 'default' ) || null;
53
-
54
- if ( typeof variants === "undefined" ) {
55
- $( this ).hide();
56
- return;
57
- }
58
-
59
- $.each( variants, function( index, weight ) {
60
- let this_value = {
61
- id: weight,
62
- text: weight
63
- };
64
-
65
- if ( selecter_variants !== null && weight == selecter_variants ) {
66
- this_value.selected = true;
67
- }
68
-
69
- data.push( this_value );
70
- } );
71
-
72
- if ( data !== [] ) {
73
- select2_args.data = data;
74
- }
75
- }
76
-
77
- $( this ).select2(
78
- select2_args
79
- ).on( 'change', function( e ) {
80
- let wrapper = $( e.target ).closest( wrapperSelector );
81
-
82
- // Serialize subfield values and refresh the fonts in the preview window.
83
- selfUpdateValue( wrapper );
84
- } );
85
- } );
86
-
87
- // Initialize the select2 field for the font subsets
88
- $( fontSubsetsSelector )
89
- .select2( {
90
- placeholder: subsetPlaceholder
91
- } )
92
- .on( 'change', function( e ) {
93
- let wrapper = $( e.target ).closest( wrapperSelector );
94
-
95
- // Serialize subfield values and refresh the fonts in the preview window.
96
- selfUpdateValue( wrapper );
97
- } );
98
-
99
- let rangers = $fontFamilyFields.parents( wrapperSelector ).find( 'input[type=range]' ),
100
- selects = $fontFamilyFields.parents( wrapperSelector ).find( 'select' ).not( "select[class*=' select2'],select[class^='select2']" );
101
-
102
- // Initialize the all the regular selects in the font controls
103
- if ( selects.length > 0 ) {
104
- selects.on( 'change', function( e ) {
105
- let wrapper = $( e.target ).closest( wrapperSelector );
106
-
107
- // Serialize subfield values and refresh the fonts in the preview window.
108
- selfUpdateValue( wrapper );
109
- } );
110
  }
 
 
 
111
 
112
- // Initialize the all the range fields in the font controls
113
- if ( rangers.length > 0 ) {
114
- rangers.on( 'change', function( e ) {
115
- let wrapper = $( e.target ).closest( wrapperSelector );
 
 
116
 
117
- // Serialize subfield values and refresh the fonts in the preview window.
118
- selfUpdateValue( wrapper );
119
 
120
- wp.customize.previewer.send( 'font-changed' );
121
- } );
122
- }
123
 
124
- // When the previewer window is ready, render the fonts
125
- var self = this;
126
- wp.customize.previewer.bind( 'ready', function() {
127
- self.renderFonts();
128
- } );
129
-
130
- // Handle the reverse value direction, when the customize setting is updated and the subfields need to update their values.
131
- $fontFamilyFields.each( function( i, el ) {
132
- let wrapper = $( el ).closest( wrapperSelector ),
133
- value_holder = wrapper.children( valueHolderSelector ),
134
- setting_id = $( value_holder ).data( 'customize-setting-link' ),
135
- setting = wp.customize( setting_id );
136
-
137
- setting.bind( function( newValue, oldValue ) {
138
- if ( ! updatingValue[this.id] ) {
139
- value_holder.val( newValue );
140
-
141
- loadFontValue( wrapper );
142
- }
143
- } )
144
- } )
145
- }
146
 
147
- /**
148
- * This function updates the data in font weight selector from the given <option> element
149
- *
150
- * @param option
151
- * @param wraper
152
- */
153
- function updateWeightField( option, wraper ) {
154
- let variants = $( option ).data( 'variants' ),
155
- font_weights = wraper.find( fontWeightSelector ),
156
- selected_variant = font_weights.val() ? font_weights.val() : font_weights.data( 'default' ),
157
- new_variants = [],
158
- id = wraper.find( valueHolderSelector ).data( 'customizeSettingLink' );
159
-
160
- variants = maybeJsonParse( variants );
161
-
162
- if ( customify_settings.settings[id].load_all_weights || typeof variants === "undefined" || Object.keys( variants ).length < 2 || font_weights.data('disabled') !== undefined ) {
163
- font_weights.parent().hide();
164
- } else {
165
- font_weights.parent().show();
166
- }
167
 
168
- // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
169
- $.each( variants, function( index, variant ) {
170
- new_variants[index] = {
171
- 'id': variant,
172
- 'text': variant
173
- };
174
-
175
- if ( selected_variant == variant ) {
176
- new_variants[index].selected = true;
177
- }
178
- } );
179
-
180
- // We need to clear the old select2 field and reinitialize it.
181
- $( font_weights ).select2().empty();
182
- $( font_weights ).select2( {
183
- data: new_variants
184
- } ).on( 'change', function( e ) {
185
- let wrapper = $( e.target ).closest( wrapperSelector );
186
-
187
- // Serialize subfield values and refresh the fonts in the preview window.
188
- selfUpdateValue( wrapper );
189
- } );
190
  }
191
 
192
- /**
193
- * This function updates the data in font subset selector from the given <option> element
194
- * @param option
195
- * @param wraper
196
- */
197
- function updateSubsetField( option, wraper ) {
198
- let subsets = $( option ).data( 'subsets' ),
199
- font_subsets = wraper.find( fontSubsetsSelector ),
200
- new_subsets = [],
201
- type = $( option ).data( 'type' );
202
-
203
- if ( type !== 'google' ) {
204
- font_subsets.parent().hide();
205
- return;
 
 
 
 
206
  }
207
 
208
- let current_value = wraper.children( valueHolderSelector ).val();
209
-
210
- current_value = maybeJsonParse( current_value );
211
- if ( _.isUndefined( current_value.selected_subsets ) ) {
212
- return;
213
  }
214
- current_value = current_value.selected_subsets;
215
 
216
- subsets = maybeJsonParse( subsets );
 
217
 
218
- if ( typeof subsets !== "undefined" && Object.keys( subsets ).length < 2 || font_subsets.data('disabled') !== undefined ) {
219
- font_subsets.parent().hide();
220
- } else {
221
- font_subsets.parent().show();
222
- }
223
-
224
- // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
225
- $.each( subsets, function( index, subset ) {
226
- new_subsets[index] = {
227
- 'id': subset,
228
- 'text': subset
229
- };
230
-
231
- // current_subsets
232
- if ( typeof current_value !== "undefined" && current_value !== null && current_value.indexOf( subset ) !== - 1 ) {
233
- new_subsets[index].selected = true;
234
- }
235
- } );
236
-
237
- // We need to clear the old select2 field and reinitialize it.
238
- $( font_subsets ).select2().empty();
239
- $( font_subsets ).select2( {
240
- data: new_subsets
241
- } ).on( 'change', function( e ) {
242
- let wrapper = $( e.target ).closest( wrapperSelector );
243
-
244
- // Serialize subfield values and refresh the fonts in the preview window.
245
- selfUpdateValue( wrapper );
246
- } );
247
  }
248
 
249
- function getValue( wrapper ) {
250
- let value_holder = wrapper.children( valueHolderSelector );
251
-
252
- if ( value_holder.length ) {
253
- return maybeJsonParse( value_holder.val() );
254
- }
255
-
256
- return [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  }
258
 
259
- function updateValue( wrapper, value ) {
260
- let value_holder = wrapper.children( valueHolderSelector ),
261
- setting_id = $( value_holder ).data( 'customize-setting-link' ),
262
- setting = wp.customize( setting_id );
263
-
264
- if ( ! value_holder.length ) {
265
- return;
266
- }
267
-
268
- if ( _.isArrayLikeObject( value ) ) {
269
- value = encodeValues( value );
270
- }
271
-
272
- // Set the serialized value in the hidden field.
273
- value_holder.val( value );
274
- // Update also the Customizer setting value.
275
- setting.set( value );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  }
277
 
278
- /**
279
- * This function is a custom value serializer for our entire font field
280
- * It collects values and saves them (encoded) into the `.customify_font_values` input's value
281
- */
282
- function selfUpdateValue( wrapper ) {
283
- let options_list = $( wrapper ).find( '.font-options__options-list' ),
284
- inputs = options_list.find( '[data-field]' ),
285
- value_holder = wrapper.children( valueHolderSelector ),
286
- setting_id = $( value_holder ).data( 'customize-setting-link' ),
287
- setting = wp.customize( setting_id ),
288
- newFontData = {};
289
-
290
- // If we are already self-updating this and we haven't finished, we need to stop here to prevent infinite loops
291
- // This call might have come from a subfield detecting the change the triggering a further update_font_value()
292
- if ( true === updatingValue[setting_id] ) {
293
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  }
 
295
 
296
- // If we are loading this setting value and haven't finished, there is no point in updating it as this would cause infinite loops.
297
- if ( true === loadingValue[setting_id] ) {
298
- return;
299
- }
300
 
301
- // Mark the fact that we are self-updating the field value
302
- updatingValue[setting_id] = true;
303
-
304
- inputs.each( function( key, el ) {
305
- let field = $( el ).data( 'field' ),
306
- value = $( el ).val();
307
-
308
- if ( 'font_family' === field ) {
309
- // the font family also holds the type
310
- let selected_opt = $( el.options[el.selectedIndex] ),
311
- type = selected_opt.data( 'type' ),
312
- subsets = selected_opt.data( 'subsets' ),
313
- variants = selected_opt.data( 'variants' );
314
-
315
- if ( ! _.isUndefined( type ) ) {
316
- newFontData['type'] = type;
317
- if ( type === 'theme_font' ) {
318
- newFontData['src'] = selected_opt.data( 'src' );
319
- }
320
- }
321
-
322
- if ( ! _.isUndefined( variants ) ) {
323
- newFontData['variants'] = maybeJsonParse( variants );
324
- }
325
-
326
- if ( ! _.isUndefined( subsets ) ) {
327
- newFontData['subsets'] = maybeJsonParse( subsets );
328
- }
329
- }
330
-
331
- if ( ! _.isUndefined( field ) && ! _.isUndefined( value ) && ! _.isNull( value ) && value !== '' ) {
332
- newFontData[field] = value;
333
- }
334
- } );
335
-
336
- // Serialize the newly gathered font data
337
- let serializedNewFontData = encodeValues( newFontData );
338
- // Set the serialized value in the hidden field.
339
- value_holder.val( serializedNewFontData );
340
- // Update also the Customizer setting value.
341
- setting.set( serializedNewFontData );
342
-
343
-
344
- // Finished with the field value self-updating.
345
- updatingValue[setting_id] = false;
346
-
347
- return newFontData;
348
  }
349
 
350
- /**
351
- * This function is a reverse of update_font_value(), initializing the entire font field controls based on the value stored in the hidden input.
352
- */
353
- function loadFontValue( wrapper ) {
354
- let options_list = $( wrapper ).find( '.font-options__options-list' ),
355
- inputs = options_list.find( '[data-field]' ),
356
- value_holder = wrapper.children( valueHolderSelector ),
357
- value = maybeJsonParse( value_holder.val() ),
358
- setting_id = $( value_holder ).data( 'customize-setting-link' );
359
-
360
- // If we are already loading this setting value and haven't finished, there is no point in starting again.
361
- if ( true === loadingValue[setting_id] ) {
362
- return;
363
- }
364
-
365
- // Mark the fact that we are loading the field value
366
- loadingValue[setting_id] = true;
367
-
368
- inputs.each( function( key, el ) {
369
- let field = $( el ).data( 'field' );
370
-
371
- // In the case of select2, only the original selects have the data field, thus excluding select2 created select DOM elements
372
- if ( typeof field !== "undefined" && field !== "" && typeof value[field] !== "undefined" ) {
373
- // If the value contains also the unit (it is not a number) we need to split it and change the subfield accordingly.
374
- let cleanValue = value[field],
375
- unit = '';
376
- // We will do this only for numerical fields.
377
- if ( _.contains( ['letter_spacing','line_height', 'font_size'], field ) && isNaN( cleanValue ) ) {
378
- // If we have a standardized value field (as array), use that.
379
- if ( typeof cleanValue.value !== "undefined" ) {
380
- if ( typeof cleanValue.unit !== "undefined" ) {
381
- unit = cleanValue.unit;
382
- }
383
-
384
- cleanValue = cleanValue.value;
385
- } else {
386
- // Treat the case when the value is a string.
387
- let matches = cleanValue.match(/^([\d.\-+]+)(.+)/i);
388
- if (matches !== null && typeof matches[1] !== "undefined") {
389
- cleanValue = matches[1];
390
- unit = matches[2];
391
- }
392
- }
393
- }
394
-
395
- if ( unit !== '' ) {
396
- $( el ).attr( 'unit', unit );
397
- }
398
-
399
- // If this field has a min/max attribute we need to make sure that those attributes allow for the value we are trying to impose.
400
- // But only for numerical values.
401
- if ( ! isNaN( cleanValue ) ) {
402
- if ( $(el).attr('min') && $(el).attr('min') > cleanValue ) {
403
- $(el).attr('min', cleanValue );
404
- }
405
- if ( $(el).attr('max') && $(el).attr('max') < cleanValue ) {
406
- $(el).attr('max', cleanValue );
407
- }
408
- }
409
-
410
- $( el ).val( cleanValue ).trigger( 'change' );
411
- }
412
- } );
413
-
414
- // Finished with the field value loading.
415
- loadingValue[setting_id] = false;
416
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
- const maybeJsonParse = function( value ) {
419
- let parsed;
420
-
421
- //try and parse it, with decodeURIComponent
422
- try {
423
- parsed = JSON.parse( decodeURIComponent( value ) );
424
- } catch ( e ) {
425
 
426
- // in case of an error, treat is as a string
427
- parsed = value;
 
 
 
 
 
 
428
  }
 
429
 
430
- return parsed;
431
- };
432
-
433
- const encodeValues = function( obj ) {
434
- return encodeURIComponent( JSON.stringify( obj ) );
435
- };
436
-
437
- const renderFonts = function() {
438
- $( '.customify_font_family' ).select2().trigger( 'change' )
439
- };
440
-
441
- return {
442
- renderFonts: renderFonts,
443
- init: init,
444
- getValue: getValue,
445
- updateValue: updateValue,
446
- selfUpdateValue: selfUpdateValue,
447
- encodeValues: encodeValues,
448
- };
449
- } )( jQuery, window, wp );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  // This is for the Customizer Font control
2
+ let CustomifyFontSelectFields = (function ($, exports, wp) {
3
+ const
4
+ wrapperSelector = '.font-options__wrapper',
5
+ valueHolderSelector = '.customify_font_values',
6
+ fontFamilySelector = '.customify_font_family',
7
+ fontWeightSelector = '.customify_font_weight',
8
+ fontSubsetsSelector = '.customify_font_subsets',
9
+ selectPlaceholder = 'Select a font family',
10
+ weightPlaceholder = 'Select a font weight',
11
+ subsetPlaceholder = 'Extra Subsets'
12
+
13
+ // We will use this to remember that we are self-updating the field from the subfields.
14
+ // We will save this info for each setting ID.
15
+ var updatingValue = {},
16
+ loadingValue = {}
17
+
18
+ function init () {
19
+ let $fontFamilyFields = $(fontFamilySelector)
20
+
21
+ // Add the Google Fonts opts to each control.
22
+ if ( typeof wp.customize.settings['google_fonts_opts'] !== "undefined" ) {
23
+ $fontFamilyFields.each(function (i, el) {
24
+ let google_opts_placeholder = $(el).find('.google-fonts-opts-placeholder').first();
25
+ if ( google_opts_placeholder ) {
26
+ // Replace the placeholder with the HTML for the Google fonts select options.
27
+ google_opts_placeholder.replaceWith( wp.customize.settings['google_fonts_opts'] );
28
+
29
+ // The active font family might be a Google font so we need to set the current value after we've added the options.
30
+ let active_font_family = $(el).data('active_font_family');
31
+ if ( typeof active_font_family !== "undefined" ) {
32
+ $(el).val(active_font_family);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
+ }
35
+ })
36
+ }
37
 
38
+ // Initialize the select2 field for the font family
39
+ $fontFamilyFields.select2({
40
+ placeholder: selectPlaceholder
41
+ }).on('change', function (e) {
42
+ let new_option = $(e.target).find('option:selected'),
43
+ wrapper = $(e.target).closest(wrapperSelector)
44
 
45
+ // Update the weight subfield with the new options given by the selected font family.
46
+ updateWeightField(new_option, wrapper)
47
 
48
+ // Update the subset subfield with the new options given by the selected font family.
49
+ updateSubsetField(new_option, wrapper)
 
50
 
51
+ // Serialize subfield values and refresh the fonts in the preview window.
52
+ selfUpdateValue(wrapper)
53
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ // Initialize the select2 field for the font weight
56
+ $(fontWeightSelector).each(function (i, el) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ let select2_args = {
59
+ placeholder: weightPlaceholder
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
+ // all this fuss is for the case when the font doesn't come with variants from PHP, like a theme_font
63
+ if (this.options.length === 0) {
64
+ let wrapper = $(el).closest(wrapperSelector),
65
+ font = wrapper.find(fontFamilySelector),
66
+ option = font[0].options[font[0].selectedIndex],
67
+ variants = maybeJsonParse($(option).data('variants')),
68
+ data = [],
69
+ selecter_variants = $(el).data('default') || null
70
+
71
+ if (typeof variants === 'undefined') {
72
+ $(this).hide()
73
+ return
74
+ }
75
+
76
+ $.each(variants, function (index, weight) {
77
+ let this_value = {
78
+ id: weight,
79
+ text: weight
80
  }
81
 
82
+ if (selecter_variants !== null && weight == selecter_variants) {
83
+ this_value.selected = true
 
 
 
84
  }
 
85
 
86
+ data.push(this_value)
87
+ })
88
 
89
+ if (data !== []) {
90
+ select2_args.data = data
91
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
93
 
94
+ $(this).select2(
95
+ select2_args
96
+ ).on('change', function (e) {
97
+ let wrapper = $(e.target).closest(wrapperSelector)
98
+
99
+ // Serialize subfield values and refresh the fonts in the preview window.
100
+ selfUpdateValue(wrapper)
101
+ })
102
+ })
103
+
104
+ // Initialize the select2 field for the font subsets
105
+ $(fontSubsetsSelector)
106
+ .select2({
107
+ placeholder: subsetPlaceholder
108
+ })
109
+ .on('change', function (e) {
110
+ let wrapper = $(e.target).closest(wrapperSelector)
111
+
112
+ // Serialize subfield values and refresh the fonts in the preview window.
113
+ selfUpdateValue(wrapper)
114
+ })
115
+
116
+ let rangers = $fontFamilyFields.parents(wrapperSelector).find('input[type=range]'),
117
+ selects = $fontFamilyFields.parents(wrapperSelector).find('select').not('select[class*=\' select2\'],select[class^=\'select2\']')
118
+
119
+ // Initialize the all the regular selects in the font controls
120
+ if (selects.length > 0) {
121
+ selects.on('change', function (e) {
122
+ let wrapper = $(e.target).closest(wrapperSelector)
123
+
124
+ // Serialize subfield values and refresh the fonts in the preview window.
125
+ selfUpdateValue(wrapper)
126
+ })
127
+ }
128
+
129
+ // Initialize the all the range fields in the font controls
130
+ if (rangers.length > 0) {
131
+ rangers.on('change', function (e) {
132
+ let wrapper = $(e.target).closest(wrapperSelector)
133
+
134
+ // Serialize subfield values and refresh the fonts in the preview window.
135
+ selfUpdateValue(wrapper)
136
+
137
+ wp.customize.previewer.send('font-changed')
138
+ })
139
+ }
140
+
141
+ // When the previewer window is ready, render the fonts
142
+ var self = this
143
+ wp.customize.previewer.bind('ready', function () {
144
+ self.renderFonts()
145
+ })
146
+
147
+ // Handle the reverse value direction, when the customize setting is updated and the subfields need to update their values.
148
+ $fontFamilyFields.each(function (i, el) {
149
+ let wrapper = $(el).closest(wrapperSelector),
150
+ value_holder = wrapper.children(valueHolderSelector),
151
+ setting_id = $(value_holder).data('customize-setting-link'),
152
+ setting = wp.customize(setting_id)
153
+
154
+ setting.bind(function (newValue, oldValue) {
155
+ if (!updatingValue[this.id]) {
156
+ value_holder.val(newValue)
157
+
158
+ loadFontValue(wrapper)
159
+ }
160
+ })
161
+ })
162
+ }
163
+
164
+ /**
165
+ * This function updates the data in font weight selector from the given <option> element
166
+ *
167
+ * @param option
168
+ * @param wraper
169
+ */
170
+ function updateWeightField (option, wraper) {
171
+ let variants = $(option).data('variants'),
172
+ font_weights = wraper.find(fontWeightSelector),
173
+ selected_variant = font_weights.val() ? font_weights.val() : font_weights.data('default'),
174
+ new_variants = [],
175
+ id = wraper.find(valueHolderSelector).data('customizeSettingLink')
176
+
177
+ variants = maybeJsonParse(variants)
178
+
179
+ if (customify_settings.settings[id].load_all_weights || typeof variants === 'undefined' || Object.keys(variants).length < 2 || font_weights.data('disabled') !== undefined) {
180
+ font_weights.parent().hide()
181
+ } else {
182
+ font_weights.parent().show()
183
+ }
184
+
185
+ // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
186
+ $.each(variants, function (index, variant) {
187
+ new_variants[index] = {
188
+ 'id': variant,
189
+ 'text': variant
190
  }
191
 
192
+ if (selected_variant == variant) {
193
+ new_variants[index].selected = true
194
+ }
195
+ })
196
+
197
+ // We need to clear the old select2 field and reinitialize it.
198
+ $(font_weights).select2().empty()
199
+ $(font_weights).select2({
200
+ data: new_variants
201
+ }).on('change', function (e) {
202
+ let wrapper = $(e.target).closest(wrapperSelector)
203
+
204
+ // Serialize subfield values and refresh the fonts in the preview window.
205
+ selfUpdateValue(wrapper)
206
+ })
207
+ }
208
+
209
+ /**
210
+ * This function updates the data in font subset selector from the given <option> element
211
+ * @param option
212
+ * @param wraper
213
+ */
214
+ function updateSubsetField (option, wraper) {
215
+ let subsets = $(option).data('subsets'),
216
+ font_subsets = wraper.find(fontSubsetsSelector),
217
+ new_subsets = [],
218
+ type = $(option).data('type')
219
+
220
+ if (type !== 'google') {
221
+ font_subsets.parent().hide()
222
+ return
223
+ }
224
+
225
+ let current_value = wraper.children(valueHolderSelector).val()
226
+
227
+ current_value = maybeJsonParse(current_value)
228
+ if (_.isUndefined(current_value.selected_subsets)) {
229
+ return
230
+ }
231
+ current_value = current_value.selected_subsets
232
+
233
+ subsets = maybeJsonParse(subsets)
234
+
235
+ if (typeof subsets !== 'undefined' && Object.keys(subsets).length < 2 || font_subsets.data('disabled') !== undefined) {
236
+ font_subsets.parent().hide()
237
+ } else {
238
+ font_subsets.parent().show()
239
+ }
240
+
241
+ // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
242
+ $.each(subsets, function (index, subset) {
243
+ new_subsets[index] = {
244
+ 'id': subset,
245
+ 'text': subset
246
  }
247
 
248
+ // current_subsets
249
+ if (typeof current_value !== 'undefined' && current_value !== null && current_value.indexOf(subset) !== -1) {
250
+ new_subsets[index].selected = true
251
+ }
252
+ })
253
+
254
+ // We need to clear the old select2 field and reinitialize it.
255
+ $(font_subsets).select2().empty()
256
+ $(font_subsets).select2({
257
+ data: new_subsets
258
+ }).on('change', function (e) {
259
+ let wrapper = $(e.target).closest(wrapperSelector)
260
+
261
+ // Serialize subfield values and refresh the fonts in the preview window.
262
+ selfUpdateValue(wrapper)
263
+ })
264
+ }
265
+
266
+ function getValue (wrapper) {
267
+ let value_holder = wrapper.children(valueHolderSelector)
268
+
269
+ if (value_holder.length) {
270
+ return maybeJsonParse(value_holder.val())
271
+ }
272
+
273
+ return []
274
+ }
275
+
276
+ function updateValue (wrapper, value) {
277
+ let value_holder = wrapper.children(valueHolderSelector),
278
+ setting_id = $(value_holder).data('customize-setting-link'),
279
+ setting = wp.customize(setting_id)
280
+
281
+ if (!value_holder.length) {
282
+ return
283
+ }
284
+
285
+ if (_.isArrayLikeObject(value)) {
286
+ value = encodeValues(value)
287
+ }
288
+
289
+ // Set the serialized value in the hidden field.
290
+ value_holder.val(value)
291
+ // Update also the Customizer setting value.
292
+ setting.set(value)
293
+ }
294
+
295
+ /**
296
+ * This function is a custom value serializer for our entire font field
297
+ * It collects values and saves them (encoded) into the `.customify_font_values` input's value
298
+ */
299
+ function selfUpdateValue (wrapper) {
300
+ let options_list = $(wrapper).find('.font-options__options-list'),
301
+ inputs = options_list.find('[data-field]'),
302
+ value_holder = wrapper.children(valueHolderSelector),
303
+ setting_id = $(value_holder).data('customize-setting-link'),
304
+ setting = wp.customize(setting_id),
305
+ newFontData = {}
306
+
307
+ // If we are already self-updating this and we haven't finished, we need to stop here to prevent infinite loops
308
+ // This call might have come from a subfield detecting the change the triggering a further update_font_value()
309
+ if (true === updatingValue[setting_id]) {
310
+ return
311
+ }
312
+
313
+ // If we are loading this setting value and haven't finished, there is no point in updating it as this would cause infinite loops.
314
+ if (true === loadingValue[setting_id]) {
315
+ return
316
+ }
317
+
318
+ // Mark the fact that we are self-updating the field value
319
+ updatingValue[setting_id] = true
320
+
321
+ inputs.each(function (key, el) {
322
+ let field = $(el).data('field'),
323
+ value = $(el).val()
324
+
325
+ if ('font_family' === field) {
326
+ // the font family also holds the type
327
+ let selected_opt = $(el.options[el.selectedIndex]),
328
+ type = selected_opt.data('type'),
329
+ subsets = selected_opt.data('subsets'),
330
+ variants = selected_opt.data('variants')
331
+
332
+ if (!_.isUndefined(type)) {
333
+ newFontData['type'] = type
334
+ if (type === 'theme_font') {
335
+ newFontData['src'] = selected_opt.data('src')
336
  }
337
+ }
338
 
339
+ if (!_.isUndefined(variants)) {
340
+ newFontData['variants'] = maybeJsonParse(variants)
341
+ }
 
342
 
343
+ if (!_.isUndefined(subsets)) {
344
+ newFontData['subsets'] = maybeJsonParse(subsets)
345
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
 
348
+ if (!_.isUndefined(field) && !_.isUndefined(value) && !_.isNull(value) && value !== '') {
349
+ newFontData[field] = value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  }
351
+ })
352
+
353
+ // Serialize the newly gathered font data
354
+ let serializedNewFontData = encodeValues(newFontData)
355
+ // Set the serialized value in the hidden field.
356
+ value_holder.val(serializedNewFontData)
357
+ // Update also the Customizer setting value.
358
+ setting.set(serializedNewFontData)
359
+
360
+ // Finished with the field value self-updating.
361
+ updatingValue[setting_id] = false
362
+
363
+ return newFontData
364
+ }
365
+
366
+ /**
367
+ * This function is a reverse of update_font_value(), initializing the entire font field controls based on the value stored in the hidden input.
368
+ */
369
+ function loadFontValue (wrapper) {
370
+ let options_list = $(wrapper).find('.font-options__options-list'),
371
+ inputs = options_list.find('[data-field]'),
372
+ value_holder = wrapper.children(valueHolderSelector),
373
+ value = maybeJsonParse(value_holder.val()),
374
+ setting_id = $(value_holder).data('customize-setting-link')
375
+
376
+ // If we are already loading this setting value and haven't finished, there is no point in starting again.
377
+ if (true === loadingValue[setting_id]) {
378
+ return
379
+ }
380
+
381
+ // Mark the fact that we are loading the field value
382
+ loadingValue[setting_id] = true
383
+
384
+ inputs.each(function (key, el) {
385
+ let field = $(el).data('field')
386
+
387
+ // In the case of select2, only the original selects have the data field, thus excluding select2 created select DOM elements
388
+ if (typeof field !== 'undefined' && field !== '' && typeof value[field] !== 'undefined') {
389
+ // If the value contains also the unit (it is not a number) we need to split it and change the subfield accordingly.
390
+ let cleanValue = value[field],
391
+ unit = ''
392
+ // We will do this only for numerical fields.
393
+ if (_.contains(['letter_spacing', 'line_height', 'font_size'], field) && isNaN(cleanValue)) {
394
+ // If we have a standardized value field (as array), use that.
395
+ if (typeof cleanValue.value !== 'undefined') {
396
+ if (typeof cleanValue.unit !== 'undefined') {
397
+ unit = cleanValue.unit
398
+ }
399
+
400
+ cleanValue = cleanValue.value
401
+ } else {
402
+ // Treat the case when the value is a string.
403
+ let matches = cleanValue.match(/^([\d.\-+]+)(.+)/i)
404
+ if (matches !== null && typeof matches[1] !== 'undefined') {
405
+ cleanValue = matches[1]
406
+ unit = matches[2]
407
+ }
408
+ }
409
+ }
410
 
411
+ if (unit !== '') {
412
+ $(el).attr('unit', unit)
413
+ }
 
 
 
 
414
 
415
+ // If this field has a min/max attribute we need to make sure that those attributes allow for the value we are trying to impose.
416
+ // But only for numerical values.
417
+ if (!isNaN(cleanValue)) {
418
+ if ($(el).attr('min') && $(el).attr('min') > cleanValue) {
419
+ $(el).attr('min', cleanValue)
420
+ }
421
+ if ($(el).attr('max') && $(el).attr('max') < cleanValue) {
422
+ $(el).attr('max', cleanValue)
423
  }
424
+ }
425
 
426
+ $(el).val(cleanValue).trigger('change')
427
+ }
428
+ })
429
+
430
+ // Finished with the field value loading.
431
+ loadingValue[setting_id] = false
432
+ }
433
+
434
+ const maybeJsonParse = function (value) {
435
+ let parsed
436
+
437
+ //try and parse it, with decodeURIComponent
438
+ try {
439
+ parsed = JSON.parse(decodeURIComponent(value))
440
+ } catch (e) {
441
+
442
+ // in case of an error, treat is as a string
443
+ parsed = value
444
+ }
445
+
446
+ return parsed
447
+ }
448
+
449
+ const encodeValues = function (obj) {
450
+ return encodeURIComponent(JSON.stringify(obj))
451
+ }
452
+
453
+ const renderFonts = function () {
454
+ $('.customify_font_family').select2().trigger('change')
455
+ }
456
+
457
+ return {
458
+ renderFonts: renderFonts,
459
+ init: init,
460
+ getValue: getValue,
461
+ updateValue: updateValue,
462
+ selfUpdateValue: selfUpdateValue,
463
+ encodeValues: encodeValues,
464
+ }
465
+ })(jQuery, window, wp)
js/customizer_preview.js CHANGED
@@ -1,357 +1,338 @@
1
  ;(function ($, window, document, undefined) {
2
 
3
- const fonts_cache = [];
4
-
5
- $(document).ready(function () {
6
- var api = parent.wp.customize,
7
- wp_settings = api.settings.settings;
8
-
9
- load_webfont_once();
10
-
11
- $.each(customify_settings.settings, function (key, el) {
12
-
13
- if (el.type === "font") {
14
- api(key, function (setting) {
15
- setting.bind(function (to) {
16
- let $values = maybeJsonParse(to);
17
-
18
- if (typeof $values !== "undefined" ) {
19
- if (typeof $values.font_family !== "undefined") {
20
- maybeLoadFontFamily($values);
21
- }
22
-
23
- let vls = get_CSS_values(this.id, $values),
24
- CSS = get_CSS_code(this.id, vls),
25
- field_style = $('#customify_font_output_for_' + el.html_safe_option_id);
26
-
27
- field_style.html(CSS);
28
- }
29
- });
30
- });
31
-
32
- } else if (typeof wp_settings !== "undefined" && typeof wp_settings[key] !== "undefined" && typeof el.css !== "undefined" && typeof el.live !== 'undefined' && el.live === true) {
33
- api(key, function (setting) {
34
-
35
- setting.bind(function (to) {
36
-
37
- $.each(el.css, function (counter, property_config) {
38
- let properties = [];
39
-
40
- properties[property_config.property] = property_config.selector;
41
- if (typeof property_config.callback_filter !== "undefined") {
42
- properties['callback'] = property_config.callback_filter;
43
- }
44
-
45
- let css_update_args = {
46
- properties: properties,
47
- propertyValue: to,
48
- negative_value: property_config.hasOwnProperty( 'negative_value' ) ? property_config['negative_value'] : false
49
- };
50
-
51
- if (typeof this.unit !== 'undefined') {
52
- css_update_args.unit = this.unit;
53
- }
54
-
55
- // Replace all dashes with underscores thus making the CSS property safe to us in a HTML ID.
56
- let regex_for_multiple_replace = new RegExp('-', 'g'),
57
- cssStyleSelector = '.dynamic_setting_' + el.html_safe_option_id + '_property_' + property_config.property.replace(regex_for_multiple_replace, '_') + '_' + counter;
58
-
59
- $(cssStyleSelector).cssUpdate(css_update_args);
60
- });
61
-
62
- });
63
- });
64
- } else if (typeof el.live === "object" && el.live.length > 0) {
65
- // if the live parameter is an object it means that is a list of css classes
66
- // these classes should be affected by the change of the text fields
67
- var field_class = el.live.join();
68
-
69
- // if this field is allowed to modify text then we'll edit this live
70
- if ($.inArray(el.type, ['text', 'textarea', 'ace_editor']) > -1) {
71
- wp.customize(key, function (value) {
72
- value.bind(function (text) {
73
- let sanitizer = document.createElement('div');
74
-
75
- sanitizer.innerHTML = text;
76
- $(field_class).html(text);
77
- });
78
- });
79
- }
80
- }
81
- });
82
-
83
- /** Bind Custom Events **/
84
-
85
- // api.previewer.bind('highlight',function(e){
86
- // $('.customizerHighlight').removeClass('customizerHighlight');
87
- //
88
- // if ( $(e).length > 0 ) {
89
- // $(e).each(function(){
90
- // $(this).addClass('customizerHighlight');
91
- // });
92
- // }
93
- // });
94
-
95
- api('live_css_edit', function (setting) {
96
- setting.bind(function (new_text) {
97
- $('#customify_css_editor_output').text(new_text);
98
- });
99
- });
100
-
101
-
102
- /*** HELPERS **/
103
-
104
- function load_webfont_once() {
105
- if (typeof WebFont === "undefined") {
106
- let tk = document.createElement('script');
107
- tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
108
- tk.type = 'text/javascript';
109
- let s = document.getElementsByTagName('script')[0];
110
- s.parentNode.insertBefore(tk, s);
111
- }
112
- }
113
-
114
- const get_CSS_values = function (ID, values) {
115
-
116
- let store = {};
117
-
118
- if (typeof values.font_family !== "undefined") {
119
- store['font-family'] = values.font_family;
120
- }
121
-
122
- if (typeof values.selected_variants !== "undefined") {
123
-
124
- let variants = null;
125
-
126
- if (typeof values.selected_variants !== "undefined" && values.selected_variants !== null) {
127
- variants = values.selected_variants;
128
- } else if (typeof values.variants !== "undefined" && typeof values.variants[0] !== "undefined") {
129
- variants = values.variants[0];
130
- }
131
-
132
- // google fonts also have the italic string inside, split that
133
- if (variants !== null && _.isString( variants ) && variants.indexOf('italic') !== -1) {
134
- store['font-style'] = 'italic';
135
- variants = variants.replace('italic', '');
136
- }
137
-
138
- if (variants !== "") {
139
- if (variants === 'regular') {
140
- variants = 'normal';
141
- }
142
-
143
- store['font-weight'] = variants;
144
- }
145
- }
146
-
147
- if (typeof values.font_size !== "undefined") {
148
- store['font-size'] = values.font_size;
149
- // If the value already contains a unit (is not numeric), go with that.
150
- if ( isNaN(values.font_size) ) {
151
- // If we have a standardized value field (as array), use that.
152
- if ( typeof values.font_size.value !== "undefined" ) {
153
- store['font-size'] = values.font_size.value;
154
- if ( typeof values.font_size.unit !== "undefined" ) {
155
- store['font-size'] += values.font_size.unit;
156
- }
157
- } else {
158
- store['font-size'] += get_field_unit(ID, 'font-size');
159
- }
160
- } else {
161
- store['font-size'] += get_field_unit(ID, 'font-size');
162
- }
163
- }
164
-
165
- if (typeof values.letter_spacing !== "undefined") {
166
- store['letter-spacing'] = values.letter_spacing;
167
- // If the value already contains a unit (is not numeric), go with that.
168
- if ( isNaN(values.letter_spacing) ) {
169
- // If we have a standardized value field (as array), use that.
170
- if ( typeof values.letter_spacing.value !== "undefined" ) {
171
- store['letter-spacing'] = values.letter_spacing.value;
172
- if ( typeof values.letter_spacing.unit !== "undefined" ) {
173
- store['letter-spacing'] += values.letter_spacing.unit;
174
- }
175
- } else {
176
- store['letter-spacing'] += get_field_unit(ID, 'letter-spacing');
177
- }
178
- } else {
179
- store['letter-spacing'] += get_field_unit(ID, 'letter-spacing');
180
- }
181
- }
182
-
183
- if (typeof values.line_height !== "undefined") {
184
- store['line-height'] = values.line_height;
185
- // If the value already contains a unit (is not numeric), go with that.
186
- if ( isNaN(values.line_height) ) {
187
- // If we have a standardized value field (as array), use that.
188
- if ( typeof values.line_height.value !== "undefined" ) {
189
- store['line-height'] = values.line_height.value;
190
- if ( typeof values.line_height.unit !== "undefined" ) {
191
- store['line-height'] += values.line_height.unit;
192
- }
193
- } else {
194
- store['line-height'] += get_field_unit(ID, 'line-height');
195
- }
196
- } else {
197
- store['line-height'] += get_field_unit(ID, 'line-height');
198
- }
199
- }
200
-
201
- if (typeof values.text_align !== "undefined") {
202
- store['text-align'] = values.text_align;
203
- }
204
-
205
- if (typeof values.text_transform !== "undefined") {
206
- store['text-transform'] = values.text_transform;
207
- }
208
- if (typeof values.text_decoration !== "undefined") {
209
- store['text-decoration'] = values.text_decoration;
210
- }
211
-
212
- return store;
213
- };
214
-
215
- const get_CSS_code = function (ID, values) {
216
-
217
- let field = customify_settings.settings[ID];
218
- let output = '';
219
-
220
- if (typeof window !== "undefined" && typeof field.callback !== "undefined" && typeof window[field.callback] === "function") {
221
- output = window[field.callback](values, field);
222
- } else {
223
- output = field.selector + "{\n";
224
- $.each(values, function (k, v) {
225
- output += k + ': ' + v + ";\n";
226
- });
227
- output += "}\n";
228
- }
229
-
230
- return output;
231
- };
232
-
233
- const get_field_unit = function (ID, field) {
234
- let unit = '';
235
- if (typeof customify_settings.settings[ID] === "undefined" || typeof customify_settings.settings[ID].fields[field] === "undefined") {
236
- return unit;
237
- }
238
-
239
- if (typeof customify_settings.settings[ID].fields[field].unit !== "undefined") {
240
- return customify_settings.settings[ID].fields[field].unit;
241
- } else if (typeof customify_settings.settings[ID].fields[field][3] !== "undefined") {
242
- // in case of an associative array
243
- return customify_settings.settings[ID].fields[field][3];
244
- }
245
- };
246
-
247
- const maybeLoadFontFamily = function (font) {
248
-
249
- if (typeof WebFont === "undefined") {
250
- let tk = document.createElement('script');
251
- tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
252
- tk.type = 'text/javascript';
253
- let s = document.getElementsByTagName('script')[0];
254
- s.parentNode.insertBefore(tk, s);
255
- }
256
-
257
- // If the font type is not defined, we assume is a Google font.
258
- if ( typeof font.type === "undefined" ) {
259
- font.type = 'google';
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
- if (font.type === 'theme_font') {
263
- WebFont.load({
264
- custom: {
265
- families: [font.font_family],
266
- urls: [font.src]
267
- }
268
- });
269
- } else if (font.type === 'google') {
270
- let family = font.font_family,
271
- variants = null,
272
- subsets = null;
273
-
274
- if (typeof font.variants !== "undefined") {
275
- variants = maybeJsonParse(font.variants);
276
-
277
- if ( typeof variants === "string" || typeof variants === "number") {
278
- variants = [ variants ];
279
- }
280
-
281
- $.each(variants, function (k, v) {
282
-
283
- if (k == 0) {
284
- family = family + ':';
285
- }
286
-
287
- family = family + v;
288
-
289
- if ( Object.keys(variants).length > ( parseInt(k) + 1 ) ) {
290
- family = family + ',';
291
- } else if ( typeof font.selected_subsets !== "undefined" ) {
292
- // in case there is a subset selected, we need to separate it from the font weight
293
- family = family + ':';
294
- }
295
- });
296
- }
297
-
298
- if (typeof font.selected_subsets !== "undefined") {
299
- subsets = maybeJsonParse(font.selected_subsets);
300
-
301
- $.each(subsets, function (k, v) {
302
-
303
- if ( k === "0" ) {
304
- family = family + ':';
305
- }
306
-
307
- family = family + v;
308
-
309
- if ( Object.keys(subsets).length > ( parseInt(k) + 1 ) ) {
310
- family = family + ',';
311
- }
312
- });
313
- }
314
-
315
- if (fonts_cache.indexOf(family) === -1) {
316
- setTimeout(function(){
317
- WebFont.load({
318
- google: {families: [family]},
319
- classes: false,
320
- events: false,
321
- error: function (e) {
322
- console.log(e);
323
- },
324
- active: function () {
325
- sessionStorage.fonts = true;
326
- }
327
- });
328
- },10);
329
-
330
- fonts_cache.push(family);
331
- }
332
-
333
- } else {
334
- // else what?
335
- }
336
- };
337
-
338
- const maybeJsonParse = function (value) {
339
- let parsed;
340
-
341
- if ( typeof value !== "string" ) {
342
- return value;
343
  }
 
 
344
 
345
- //try and parse it, with decodeURIComponent
346
- try {
347
- parsed = JSON.parse(decodeURIComponent(value));
348
- } catch (e) {
349
 
350
- // in case of an error, treat is as a string
351
- parsed = value;
352
- }
353
 
354
- return parsed;
355
- };
356
- });
357
- })(jQuery, window, document);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ;(function ($, window, document, undefined) {
2
 
3
+ const fonts_cache = []
4
+
5
+ $(document).ready(function () {
6
+ var api = parent.wp.customize,
7
+ wp_settings = api.settings.settings
8
+
9
+ load_webfont_once()
10
+
11
+ $.each(customify_settings.settings, function (key, el) {
12
+
13
+ if (el.type === 'font') {
14
+ api(key, function (setting) {
15
+ setting.bind(function (to) {
16
+ let $values = maybeJsonParse(to)
17
+
18
+ if (typeof $values !== 'undefined') {
19
+ if (typeof $values.font_family !== 'undefined') {
20
+ maybeLoadFontFamily($values)
21
+ }
22
+
23
+ let vls = get_CSS_values(this.id, $values),
24
+ CSS = get_CSS_code(this.id, vls),
25
+ field_style = $('#customify_font_output_for_' + el.html_safe_option_id)
26
+
27
+ field_style.html(CSS)
28
+ }
29
+ })
30
+ })
31
+
32
+ } else if (typeof wp_settings !== 'undefined' && typeof wp_settings[key] !== 'undefined' && typeof el.css !== 'undefined' && typeof el.live !== 'undefined' && el.live === true) {
33
+ api(key, function (setting) {
34
+
35
+ setting.bind(function (to) {
36
+
37
+ $.each(el.css, function (counter, property_config) {
38
+ let properties = []
39
+
40
+ properties[property_config.property] = property_config.selector
41
+ if (typeof property_config.callback_filter !== 'undefined') {
42
+ properties['callback'] = property_config.callback_filter
43
+ }
44
+
45
+ let css_update_args = {
46
+ properties: properties,
47
+ propertyValue: to,
48
+ negative_value: property_config.hasOwnProperty('negative_value') ? property_config['negative_value'] : false
49
+ }
50
+
51
+ if (typeof this.unit !== 'undefined') {
52
+ css_update_args.unit = this.unit
53
+ }
54
+
55
+ // Replace all dashes with underscores thus making the CSS property safe to us in a HTML ID.
56
+ let regex_for_multiple_replace = new RegExp('-', 'g'),
57
+ cssStyleSelector = '.dynamic_setting_' + el.html_safe_option_id + '_property_' + property_config.property.replace(regex_for_multiple_replace, '_') + '_' + counter
58
+
59
+ $(cssStyleSelector).cssUpdate(css_update_args)
60
+ })
61
+
62
+ })
63
+ })
64
+ } else if (typeof el.live === 'object' && el.live.length > 0) {
65
+ // if the live parameter is an object it means that is a list of css classes
66
+ // these classes should be affected by the change of the text fields
67
+ var field_class = el.live.join()
68
+
69
+ // if this field is allowed to modify text then we'll edit this live
70
+ if ($.inArray(el.type, ['text', 'textarea', 'ace_editor']) > -1) {
71
+ wp.customize(key, function (value) {
72
+ value.bind(function (text) {
73
+ let sanitizer = document.createElement('div')
74
+
75
+ sanitizer.innerHTML = text
76
+ $(field_class).html(text)
77
+ })
78
+ })
79
+ }
80
+ }
81
+ })
82
+
83
+ /*** HELPERS **/
84
+
85
+ function load_webfont_once () {
86
+ if (typeof WebFont === 'undefined') {
87
+ let tk = document.createElement('script')
88
+ tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'
89
+ tk.type = 'text/javascript'
90
+ let s = document.getElementsByTagName('script')[0]
91
+ s.parentNode.insertBefore(tk, s)
92
+ }
93
+ }
94
+
95
+ const get_CSS_values = function (ID, values) {
96
+
97
+ let store = {}
98
+
99
+ if (typeof values.font_family !== 'undefined') {
100
+ store['font-family'] = values.font_family
101
+ }
102
+
103
+ if (typeof values.selected_variants !== 'undefined') {
104
+
105
+ let variants = null
106
+
107
+ if (typeof values.selected_variants !== 'undefined' && values.selected_variants !== null) {
108
+ variants = values.selected_variants
109
+ } else if (typeof values.variants !== 'undefined' && typeof values.variants[0] !== 'undefined') {
110
+ variants = values.variants[0]
111
+ }
112
+
113
+ // google fonts also have the italic string inside, split that
114
+ if (variants !== null && _.isString(variants) && variants.indexOf('italic') !== -1) {
115
+ store['font-style'] = 'italic'
116
+ variants = variants.replace('italic', '')
117
+ }
118
+
119
+ if (variants !== '') {
120
+ if (variants === 'regular') {
121
+ variants = 'normal'
122
+ }
123
+
124
+ store['font-weight'] = variants
125
+ }
126
+ }
127
+
128
+ if (typeof values.font_size !== 'undefined') {
129
+ store['font-size'] = values.font_size
130
+ // If the value already contains a unit (is not numeric), go with that.
131
+ if (isNaN(values.font_size)) {
132
+ // If we have a standardized value field (as array), use that.
133
+ if (typeof values.font_size.value !== 'undefined') {
134
+ store['font-size'] = values.font_size.value
135
+ if (typeof values.font_size.unit !== 'undefined') {
136
+ store['font-size'] += values.font_size.unit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }
138
+ } else {
139
+ store['font-size'] += get_field_unit(ID, 'font-size')
140
+ }
141
+ } else {
142
+ store['font-size'] += get_field_unit(ID, 'font-size')
143
+ }
144
+ }
145
+
146
+ if (typeof values.letter_spacing !== 'undefined') {
147
+ store['letter-spacing'] = values.letter_spacing
148
+ // If the value already contains a unit (is not numeric), go with that.
149
+ if (isNaN(values.letter_spacing)) {
150
+ // If we have a standardized value field (as array), use that.
151
+ if (typeof values.letter_spacing.value !== 'undefined') {
152
+ store['letter-spacing'] = values.letter_spacing.value
153
+ if (typeof values.letter_spacing.unit !== 'undefined') {
154
+ store['letter-spacing'] += values.letter_spacing.unit
155
+ }
156
+ } else {
157
+ store['letter-spacing'] += get_field_unit(ID, 'letter-spacing')
158
+ }
159
+ } else {
160
+ store['letter-spacing'] += get_field_unit(ID, 'letter-spacing')
161
+ }
162
+ }
163
+
164
+ if (typeof values.line_height !== 'undefined') {
165
+ store['line-height'] = values.line_height
166
+ // If the value already contains a unit (is not numeric), go with that.
167
+ if (isNaN(values.line_height)) {
168
+ // If we have a standardized value field (as array), use that.
169
+ if (typeof values.line_height.value !== 'undefined') {
170
+ store['line-height'] = values.line_height.value
171
+ if (typeof values.line_height.unit !== 'undefined') {
172
+ store['line-height'] += values.line_height.unit
173
+ }
174
+ } else {
175
+ store['line-height'] += get_field_unit(ID, 'line-height')
176
+ }
177
+ } else {
178
+ store['line-height'] += get_field_unit(ID, 'line-height')
179
+ }
180
+ }
181
+
182
+ if (typeof values.text_align !== 'undefined') {
183
+ store['text-align'] = values.text_align
184
+ }
185
+
186
+ if (typeof values.text_transform !== 'undefined') {
187
+ store['text-transform'] = values.text_transform
188
+ }
189
+ if (typeof values.text_decoration !== 'undefined') {
190
+ store['text-decoration'] = values.text_decoration
191
+ }
192
+
193
+ return store
194
+ }
195
+
196
+ const get_CSS_code = function (ID, values) {
197
+
198
+ let field = customify_settings.settings[ID]
199
+ let output = ''
200
+
201
+ if (typeof window !== 'undefined' && typeof field.callback !== 'undefined' && typeof window[field.callback] === 'function') {
202
+ output = window[field.callback](values, field)
203
+ } else {
204
+ output = field.selector + '{\n'
205
+ $.each(values, function (k, v) {
206
+ output += k + ': ' + v + ';\n'
207
+ })
208
+ output += '}\n'
209
+ }
210
+
211
+ return output
212
+ }
213
+
214
+ const get_field_unit = function (ID, field) {
215
+ let unit = ''
216
+ if (typeof customify_settings.settings[ID] === 'undefined' || typeof customify_settings.settings[ID].fields[field] === 'undefined') {
217
+ return unit
218
+ }
219
+
220
+ if (typeof customify_settings.settings[ID].fields[field].unit !== 'undefined') {
221
+ return customify_settings.settings[ID].fields[field].unit
222
+ } else if (typeof customify_settings.settings[ID].fields[field][3] !== 'undefined') {
223
+ // in case of an associative array
224
+ return customify_settings.settings[ID].fields[field][3]
225
+ }
226
+ }
227
+
228
+ const maybeLoadFontFamily = function (font) {
229
+
230
+ if (typeof WebFont === 'undefined') {
231
+ let tk = document.createElement('script')
232
+ tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'
233
+ tk.type = 'text/javascript'
234
+ let s = document.getElementsByTagName('script')[0]
235
+ s.parentNode.insertBefore(tk, s)
236
+ }
237
+
238
+ // If the font type is not defined, we assume is a Google font.
239
+ if (typeof font.type === 'undefined') {
240
+ font.type = 'google'
241
+ }
242
+
243
+ if (font.type === 'theme_font') {
244
+ WebFont.load({
245
+ custom: {
246
+ families: [font.font_family],
247
+ urls: [font.src]
248
+ }
249
+ })
250
+ } else if (font.type === 'google') {
251
+ let family = font.font_family,
252
+ variants = null,
253
+ subsets = null
254
+
255
+ if (typeof font.variants !== 'undefined') {
256
+ variants = maybeJsonParse(font.variants)
257
+
258
+ if (typeof variants === 'string' || typeof variants === 'number') {
259
+ variants = [variants]
260
+ }
261
+
262
+ $.each(variants, function (k, v) {
263
+
264
+ if (k == 0) {
265
+ family = family + ':'
266
+ }
267
+
268
+ family = family + v
269
 
270
+ if (Object.keys(variants).length > (parseInt(k) + 1)) {
271
+ family = family + ','
272
+ } else if (typeof font.selected_subsets !== 'undefined') {
273
+ // in case there is a subset selected, we need to separate it from the font weight
274
+ family = family + ':'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
+ })
277
+ }
278
 
279
+ if (typeof font.selected_subsets !== 'undefined') {
280
+ subsets = maybeJsonParse(font.selected_subsets)
 
 
281
 
282
+ $.each(subsets, function (k, v) {
 
 
283
 
284
+ if (k === '0') {
285
+ family = family + ':'
286
+ }
287
+
288
+ family = family + v
289
+
290
+ if (Object.keys(subsets).length > (parseInt(k) + 1)) {
291
+ family = family + ','
292
+ }
293
+ })
294
+ }
295
+
296
+ if (fonts_cache.indexOf(family) === -1) {
297
+ setTimeout(function () {
298
+ WebFont.load({
299
+ google: {families: [family]},
300
+ classes: false,
301
+ events: false,
302
+ error: function (e) {
303
+ console.log(e)
304
+ },
305
+ active: function () {
306
+ sessionStorage.fonts = true
307
+ }
308
+ })
309
+ }, 10)
310
+
311
+ fonts_cache.push(family)
312
+ }
313
+
314
+ } else {
315
+ // else what?
316
+ }
317
+ }
318
+
319
+ const maybeJsonParse = function (value) {
320
+ let parsed
321
+
322
+ if (typeof value !== 'string') {
323
+ return value
324
+ }
325
+
326
+ //try and parse it, with decodeURIComponent
327
+ try {
328
+ parsed = JSON.parse(decodeURIComponent(value))
329
+ } catch (e) {
330
+
331
+ // in case of an error, treat is as a string
332
+ parsed = value
333
+ }
334
+
335
+ return parsed
336
+ }
337
+ })
338
+ })(jQuery, window, document)
js/live_css_editor.js DELETED
@@ -1,39 +0,0 @@
1
- (function($, exports){
2
-
3
- var timeout = null;
4
-
5
- $(document).ready(function(){
6
- var api = wp.customize;
7
- if ( $('#css_editor').length < 1 ) {
8
- return ;
9
- }
10
- var css_editor = ace.edit("css_editor");
11
- css_editor.$blockScrolling = Infinity;
12
-
13
- // init the ace editor
14
- css_editor.setTheme("ace/theme/github");
15
- css_editor.getSession().setMode("ace/mode/css");
16
-
17
- // hide the textarea and enable the ace editor
18
- var textarea = $('#css_editor_textarea').hide();
19
- css_editor.getSession().setValue(textarea.val());
20
-
21
- var customizer_overlay = $('.wp-full-overlay');
22
-
23
- // open the ace editor section when we click on the panel
24
- $('#accordion-section-live_css_edit_section').on('click', function(){
25
- customizer_overlay.addClass('editor_opened');
26
- });
27
-
28
- $(document).on('click', '.customize-section-back', function(){
29
- customizer_overlay.removeClass('editor_opened');
30
- });
31
-
32
- // each time a change is triggered also make those edits in the preview
33
- css_editor.getSession().on('change', function(e) {
34
- textarea.val(css_editor.getSession().getValue());
35
- textarea.trigger('change');
36
- });
37
-
38
- });
39
- })(jQuery, window);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
plugin-config.php CHANGED
@@ -24,7 +24,6 @@ return array(
24
  'general' => include 'settings/general' . EXT,
25
  'output' => include 'settings/output' . EXT,
26
  'typography' => include 'settings/typography' . EXT,
27
- 'css_editor' => include 'settings/css_editor' . EXT,
28
  'tools' => include 'settings/tools' . EXT,
29
  ),
30
  'processor' => array(
@@ -59,4 +58,4 @@ return array(
59
 
60
  )
61
 
62
- ); # config
24
  'general' => include 'settings/general' . EXT,
25
  'output' => include 'settings/output' . EXT,
26
  'typography' => include 'settings/typography' . EXT,
 
27
  'tools' => include 'settings/tools' . EXT,
28
  ),
29
  'processor' => array(
58
 
59
  )
60
 
61
+ ); # config
plugin-defaults.php CHANGED
@@ -1,4 +1,6 @@
1
- <?php return array(
 
 
2
 
3
  # Hidden fields
4
  'settings_saved_once' => '0',
1
+ <?php defined( 'ABSPATH' ) or die;
2
+
3
+ return array(
4
 
5
  # Hidden fields
6
  'settings_saved_once' => '0',
readme.txt CHANGED
@@ -1,5 +1,5 @@
1
  === Customify - A Theme Customizer Booster ===
2
- Contributors: pixelgrade, euthelup, babbardel, vlad.olaru, cristianfrumusanu, raduconstantin, razvanonofrei
3
  Tags: customizer, css, editor, live, preview, customizer
4
  Requires at least: 4.7.0
5
  Tested up to: 5.0.0
@@ -45,6 +45,11 @@ With [Customify](https://github.com/pixelgrade/customify), developers can easily
45
 
46
  == Changelog ==
47
 
 
 
 
 
 
48
  = 2.2.0 =
49
  * Added support for the new **Gutenberg block editor.**
50
 
1
  === Customify - A Theme Customizer Booster ===
2
+ Contributors: pixelgrade, euthelup, babbardel, vlad.olaru, raduconstantin, razvanonofrei
3
  Tags: customizer, css, editor, live, preview, customizer
4
  Requires at least: 4.7.0
5
  Tested up to: 5.0.0
45
 
46
  == Changelog ==
47
 
48
+ = 2.3.0 =
49
+ * Improved support for the new **Gutenberg block editor.** Compatible with the latest WordPress 5.0 beta version.
50
+ * Big **performance improvements** both in the frontend and also in the Customizer.
51
+ * Cleanup regarding old and deprecated features.
52
+
53
  = 2.2.0 =
54
  * Added support for the new **Gutenberg block editor.**
55
 
settings/css_editor.php DELETED
@@ -1,28 +0,0 @@
1
- <?php
2
- //not used yet - moved them to a per gallery option
3
-
4
- return array(
5
- 'type' => 'postbox',
6
- 'label' => 'CSS Editor',
7
- 'options' => array(
8
- 'css_editor' => array(
9
- 'label' => __( 'Enable CSS Editor', 'customify' ),
10
- 'default' => true,
11
- 'type' => 'switch',
12
- 'show_group' => 'css_editor_group',
13
- 'display_option' => true
14
- ),
15
-
16
- 'css_editor_group' => array(
17
- 'type' => 'group',
18
- 'options' => array(
19
- 'css_editor_use_ace' => array(
20
- 'name' => 'typography_standard_fonts',
21
- 'label' => __( 'Use Ace Editor?', 'customify' ),
22
- 'default' => true,
23
- 'type' => 'switch',
24
- )
25
- )
26
- )
27
- )
28
- ); # config