Customify – A Theme Customizer Booster - Version 2.3.4

Version Description

  • Fixed warnings that were appearing when PHP has version 7.2.0+.
Download this release

Release Info

Developer babbardel
Plugin Icon Customify – A Theme Customizer Booster
Version 2.3.4
Comparing to
See all releases

Code changes from version 2.3.3 to 2.3.4

Files changed (60) hide show
  1. customify.php +8 -44
  2. features/class-Customify_Importer.php +4 -8
  3. features/class-Font_Selector.php +18 -16
  4. features/customizer/controls/class-Pix_Customize_Font_Control.php +5 -5
  5. features/customizer/controls/class-Pix_Customize_Preset_Control.php +6 -4
  6. features/customizer/controls/class-Pix_Customize_Typography_Control.php +3 -3
  7. features/index.php +3 -0
  8. includes/admin-notifications-manager/class-admin-notifications-manager.php +2 -2
  9. includes/admin-notifications-manager/index.php +3 -0
  10. includes/admin-settings/core/README.md +33 -0
  11. includes/admin-settings/core/bootstrap.php +22 -0
  12. includes/admin-settings/core/callbacks/cleanup/switch_not_available.php +5 -0
  13. includes/admin-settings/core/callbacks/validation/is_numeric.php +5 -0
  14. includes/admin-settings/core/callbacks/validation/not_empty.php +5 -0
  15. includes/admin-settings/core/classes/HTMLElement.php +106 -0
  16. includes/admin-settings/core/classes/HTMLTag.php +75 -0
  17. includes/admin-settings/core/classes/Meta.php +115 -0
  18. includes/admin-settings/core/classes/Processor.php +331 -0
  19. includes/admin-settings/core/classes/Validator.php +118 -0
  20. includes/admin-settings/core/classes/forms/Form.php +190 -0
  21. includes/admin-settings/core/classes/forms/FormField.php +110 -0
  22. includes/admin-settings/core/core.php +293 -0
  23. includes/admin-settings/core/defaults.php +44 -0
  24. includes/admin-settings/core/index.php +3 -0
  25. includes/admin-settings/core/interfaces/HTMLElement.php +50 -0
  26. includes/admin-settings/core/interfaces/HTMLTag.php +30 -0
  27. includes/admin-settings/core/interfaces/Meta.php +57 -0
  28. includes/admin-settings/core/interfaces/Processor.php +45 -0
  29. includes/admin-settings/core/interfaces/Validator.php +22 -0
  30. includes/admin-settings/core/interfaces/extended/Form.php +69 -0
  31. includes/admin-settings/core/interfaces/extended/FormField.php +29 -0
  32. includes/admin-settings/core/tests/bootstrap.php +11 -0
  33. includes/admin-settings/core/tests/sample-config.php +13 -0
  34. includes/admin-settings/core/views/form-partials/fields/color.php +13 -0
  35. includes/admin-settings/core/views/form-partials/fields/counter.php +43 -0
  36. includes/admin-settings/core/views/form-partials/fields/group.php +32 -0
  37. includes/admin-settings/core/views/form-partials/fields/hidden.php +22 -0
  38. includes/admin-settings/core/views/form-partials/fields/postbox.php +38 -0
  39. includes/admin-settings/core/views/form-partials/fields/select.php +41 -0
  40. includes/admin-settings/core/views/form-partials/fields/switch.php +64 -0
  41. includes/admin-settings/core/views/form-partials/fields/tabular-group.php +36 -0
  42. includes/admin-settings/core/views/form-partials/fields/text.php +38 -0
  43. includes/admin-settings/core/views/form-partials/linear.php +15 -0
  44. includes/admin-settings/views/admin.php +65 -0
  45. includes/admin-settings/views/form-partials/fields/multicheckbox.php +36 -0
  46. includes/admin-settings/views/form-partials/fields/reset_theme_mod.php +64 -0
  47. includes/admin-settings/views/index.php +1 -0
  48. includes/class-customify-color-palettes.php +17 -19
  49. includes/class-customify-customizer.php +1953 -0
  50. includes/class-customify-gutenberg.php +13 -12
  51. includes/class-customify-settings.php +430 -0
  52. includes/class-pixcustomify.php +830 -0
  53. includes/extras.php +354 -0
  54. includes/index.php +3 -0
  55. includes/lib/index.php +3 -0
  56. js/admin.js +38 -38
  57. js/customizer.js +1261 -1261
  58. js/customizer/style-manager.js +126 -127
  59. js/customizer_preview.js +332 -332
  60. readme.txt +11 -5
customify.php CHANGED
@@ -2,8 +2,8 @@
2
  /*
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
- Description: A Theme Customizer Booster
6
- Version: 2.3.3
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
@@ -11,8 +11,8 @@ Text Domain: customify
11
  License: GPL-2.0+
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.txt
13
  Domain Path: /languages/
14
- Requires at least: 4.9
15
- Tested up to: 5.0.2
16
  */
17
 
18
  // If this file is called directly, abort.
@@ -20,35 +20,8 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  die;
21
  }
22
 
23
- // ensure EXT is defined
24
- if ( ! defined('EXT')) {
25
- define('EXT', '.php');
26
- }
27
-
28
- require 'core/bootstrap.php';
29
-
30
- // Include our helper array class.
31
- require 'includes/lib/class-customify-array.php';
32
-
33
- $config = include 'plugin-config.php';
34
-
35
- // set textdomain
36
- pixcustomify::settextdomain( 'customify' );
37
-
38
- // Ensure Test Data
39
- // ----------------
40
-
41
- $defaults = include 'plugin-defaults.php';
42
-
43
- $current_data = get_option( $config['settings-key'] );
44
-
45
- if ( $current_data === false ) {
46
- add_option( $config['settings-key'], $defaults );
47
- } elseif ( count( array_diff_key( $defaults, $current_data ) ) != 0) {
48
- $plugindata = array_merge( $defaults, $current_data );
49
- update_option( $config['settings-key'], $plugindata );
50
- }
51
- # else: data is available; do nothing
52
 
53
  /**
54
  * Returns the main instance of PixCustomifyPlugin to prevent the need to use globals.
@@ -57,20 +30,11 @@ if ( $current_data === false ) {
57
  * @return PixCustomifyPlugin
58
  */
59
  function PixCustomifyPlugin() {
60
- /**
61
- * The core plugin class that is used to define internationalization,
62
- * admin-specific hooks, and public-facing site hooks.
63
- */
64
- require_once plugin_dir_path( __FILE__ ) . 'class-pixcustomify.php';
65
-
66
- $instance = PixCustomifyPlugin::instance( __FILE__, '2.3.3' );
67
 
68
- return $instance;
69
  }
70
 
71
  // Now get the party started
72
  // We will keep this global variable for legacy
73
  $pixcustomify_plugin = PixCustomifyPlugin();
74
-
75
- // Load custom modules
76
- require_once( 'features/class-Font_Selector.php' );
2
  /*
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
+ Description: A Theme Customizer Booster to easily customize Fonts, Colors, and other options for your site.
6
+ Version: 2.3.4
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
11
  License: GPL-2.0+
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.txt
13
  Domain Path: /languages/
14
+ Requires at least: 4.9.9
15
+ Tested up to: 5.2.0
16
  */
17
 
18
  // If this file is called directly, abort.
20
  die;
21
  }
22
 
23
+ require_once 'includes/lib/class-customify-array.php';
24
+ require_once 'includes/extras.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  /**
27
  * Returns the main instance of PixCustomifyPlugin to prevent the need to use globals.
30
  * @return PixCustomifyPlugin
31
  */
32
  function PixCustomifyPlugin() {
33
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-pixcustomify.php';
 
 
 
 
 
 
34
 
35
+ return PixCustomifyPlugin::instance( __FILE__, '2.3.4' );
36
  }
37
 
38
  // Now get the party started
39
  // We will keep this global variable for legacy
40
  $pixcustomify_plugin = PixCustomifyPlugin();
 
 
 
features/class-Customify_Importer.php CHANGED
@@ -144,7 +144,7 @@ final class Customify_Importer_Controller {
144
  wp_send_json_error( 'i don\'t evan kno\'' );
145
  }
146
 
147
- $data = $this->process_remote_data( $data );
148
  break;
149
  }
150
 
@@ -193,7 +193,7 @@ final class Customify_Importer_Controller {
193
  continue;
194
  }
195
 
196
- $updated = update_option( $step_id, $value );
197
  }
198
  }
199
 
@@ -382,8 +382,6 @@ final class Customify_Importer_Controller {
382
  $this->add_step( 'widgets', 'widgets', $data['widgets'] );
383
  }
384
 
385
- if ( isset( $data['widgets'] ) && ! empty( $data['widgets'] ) ) {}
386
-
387
  // select what you can get from the export
388
  if ( isset( $data['taxonomies'] ) && ! empty( $data['taxonomies'] ) ) {
389
 
@@ -471,11 +469,9 @@ final class Customify_Importer_Controller {
471
  $options = PixCustomifyPlugin()->get_options_configs();
472
 
473
  if ( ! isset( $options[ $option_key ] ) ) {
474
- wp_send_json_error( 'inexistent key' );
475
  }
476
 
477
- $option_config = $options[ $option_key ];
478
-
479
  if ( ! isset( $options[ $option_key ]['imports'] ) ) {
480
  wp_send_json_error( 'where is imports????' );
481
  }
@@ -618,4 +614,4 @@ final class Customify_Importer_Controller {
618
 
619
  return $new_widget_name;
620
  }
621
- }
144
  wp_send_json_error( 'i don\'t evan kno\'' );
145
  }
146
 
147
+ $this->process_remote_data( $data );
148
  break;
149
  }
150
 
193
  continue;
194
  }
195
 
196
+ update_option( $step_id, $value );
197
  }
198
  }
199
 
382
  $this->add_step( 'widgets', 'widgets', $data['widgets'] );
383
  }
384
 
 
 
385
  // select what you can get from the export
386
  if ( isset( $data['taxonomies'] ) && ! empty( $data['taxonomies'] ) ) {
387
 
469
  $options = PixCustomifyPlugin()->get_options_configs();
470
 
471
  if ( ! isset( $options[ $option_key ] ) ) {
472
+ wp_send_json_error( 'nonexistent key' );
473
  }
474
 
 
 
475
  if ( ! isset( $options[ $option_key ]['imports'] ) ) {
476
  wp_send_json_error( 'where is imports????' );
477
  }
614
 
615
  return $new_widget_name;
616
  }
617
+ }
features/class-Font_Selector.php CHANGED
@@ -18,12 +18,12 @@ class Customify_Font_Selector {
18
  function __construct() {
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
 
@@ -99,7 +99,7 @@ class Customify_Font_Selector {
99
  return $this->theme_fonts;
100
  }
101
 
102
- function add_customify_theme_fonts( $active_font_family, $val ) {
103
  //first get all the published custom fonts
104
  if ( empty( $this->theme_fonts ) ) {
105
  return;
@@ -136,9 +136,9 @@ class Customify_Font_Selector {
136
  /** @var PixCustomifyPlugin $local_plugin */
137
  $local_plugin = PixCustomifyPlugin();
138
 
139
- self::$options_list = $local_plugin->get_options();
140
 
141
- $local_plugin->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
142
 
143
  if ( empty( self::$typo_settings ) ) {
144
  return $args;
@@ -158,7 +158,7 @@ class Customify_Font_Selector {
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
  }
163
 
164
  //bail if by this time we don't have a value of some sort
@@ -168,7 +168,7 @@ class Customify_Font_Selector {
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.
@@ -243,8 +243,8 @@ class Customify_Font_Selector {
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;
@@ -284,8 +284,8 @@ class Customify_Font_Selector {
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;
@@ -322,7 +322,7 @@ class Customify_Font_Selector {
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
@@ -332,7 +332,7 @@ class Customify_Font_Selector {
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 );
@@ -412,7 +412,7 @@ class Customify_Font_Selector {
412
  }
413
 
414
  if ( ! empty( $value['font_weight'] ) ) {
415
- $italic_font = $this->display_weight_property( $value['font_weight'] );
416
  }
417
 
418
  if ( ! empty( $value['font_size'] ) ) {
@@ -510,7 +510,9 @@ class Customify_Font_Selector {
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
 
18
  function __construct() {
19
  $this->theme_fonts = apply_filters( 'customify_theme_fonts', array() );
20
 
21
+ $load_location = PixCustomifyPlugin()->settings->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, 1 );
25
 
26
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'enable_editor_style', true ) ) {
27
  add_action( 'admin_head', array( $this, 'script_to_add_customizer_settings_into_wp_editor' ) );
28
  }
29
 
99
  return $this->theme_fonts;
100
  }
101
 
102
+ function add_customify_theme_fonts( $active_font_family ) {
103
  //first get all the published custom fonts
104
  if ( empty( $this->theme_fonts ) ) {
105
  return;
136
  /** @var PixCustomifyPlugin $local_plugin */
137
  $local_plugin = PixCustomifyPlugin();
138
 
139
+ self::$options_list = $local_plugin->get_options_details();
140
 
141
+ $local_plugin->customizer->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
142
 
143
  if ( empty( self::$typo_settings ) ) {
144
  return $args;
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->customizer->get_font_defaults_value( str_replace( '"', '', $font['value'] ) );
162
  }
163
 
164
  //bail if by this time we don't have a value of some sort
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->customizer->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.
243
  /** @var PixCustomifyPlugin $local_plugin */
244
  $local_plugin = PixCustomifyPlugin();
245
 
246
+ self::$options_list = $local_plugin->get_options_details();
247
+ $local_plugin->customizer->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
248
 
249
  if ( empty( self::$typo_settings ) ) {
250
  return;
284
  /** @var PixCustomifyPlugin $local_plugin */
285
  $local_plugin = PixCustomifyPlugin();
286
 
287
+ self::$options_list = $local_plugin->get_options_details();
288
+ $local_plugin->customizer->get_typography_fields( self::$options_list, 'type', 'font', self::$typo_settings );
289
 
290
  if ( empty( self::$typo_settings ) ) {
291
  return $output;
322
  $value = $this->maybe_decode_value( $font['value'] );
323
 
324
  if ( $value === null ) {
325
+ $value = $local_plugin->customizer->get_font_defaults_value( $font['value'] );
326
  }
327
 
328
  // shim the old case when the default was only the font name
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->customizer->standardize_non_associative_font_default( $value );
336
  }
337
 
338
  $value = $this->validate_font_values( $value );
412
  }
413
 
414
  if ( ! empty( $value['font_weight'] ) ) {
415
+ $this->display_weight_property( $value['font_weight'] );
416
  }
417
 
418
  if ( ! empty( $value['font_size'] ) ) {
510
  function get_fonts_dynamic_script() {
511
  $args = $this->get_fonts_args();
512
 
513
+ if ( ( empty ( $args['local_families'] ) && empty ( $args['google_families'] ) )
514
+ || ! PixCustomifyPlugin()->settings->get_plugin_setting( 'typography', '1' )
515
+ || ! PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
516
  return '';
517
  }
518
 
features/customizer/controls/class-Pix_Customize_Font_Control.php CHANGED
@@ -159,7 +159,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
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
  } ?>
@@ -200,12 +200,12 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
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 ) {
@@ -236,7 +236,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
236
  }
237
 
238
  public function customize_pane_settings_google_fonts_options() {
239
- if ( ! PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
240
  return;
241
  }
242
 
@@ -305,7 +305,7 @@ class Pix_Customize_Font_Control extends Pix_Customize_Control {
305
  // Allow others to add options here
306
  do_action( 'customify_font_family_before_standard_fonts_options', $font_family, $current_value );
307
 
308
- if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_standard_fonts' ) ) {
309
 
310
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
311
  foreach ( self::$std_fonts as $key => $font ) {
159
 
160
  do_action( 'customify_font_family_before_google_fonts_options' );
161
 
162
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts' ) ) {
163
 
164
  echo '<optgroup class="google-fonts-opts-placeholder" label="' . __( 'Google fonts', 'customify' ) . '"></optgroup>';
165
  } ?>
200
 
201
  public function get_google_fonts_opts_html() {
202
  $html = '';
203
+ if ( ! PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts' ) ) {
204
  return $html;
205
  }
206
 
207
  ob_start();
208
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_group_google_fonts' ) ) {
209
 
210
  $grouped_google_fonts = array();
211
  foreach ( self::$google_fonts as $key => $font ) {
236
  }
237
 
238
  public function customize_pane_settings_google_fonts_options() {
239
+ if ( ! PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts' ) ) {
240
  return;
241
  }
242
 
305
  // Allow others to add options here
306
  do_action( 'customify_font_family_before_standard_fonts_options', $font_family, $current_value );
307
 
308
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_standard_fonts' ) ) {
309
 
310
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
311
  foreach ( self::$std_fonts as $key => $font ) {
features/customizer/controls/class-Pix_Customize_Preset_Control.php CHANGED
@@ -204,7 +204,7 @@ class Pix_Customize_Preset_Control extends Pix_Customize_Control {
204
  </label>
205
  <div class="palette">
206
  <?php foreach ( $choice_config['options'] as $color_setting_id => $color_value ) {
207
- $field_config = PixCustomifyPlugin()->get_option_customizer_config( $color_setting_id );
208
  echo '<div class="palette__item ' . esc_attr( $color_setting_id ) . '" style="background: ' . esc_attr( $color_value ) . '"></div>' . PHP_EOL;
209
  } ?>
210
  </div>
@@ -386,8 +386,10 @@ class Pix_Customize_Preset_Control extends Pix_Customize_Control {
386
  * @return boolean
387
  */
388
  public function isLight( $color = FALSE ){
389
- // Get our color
390
- $color = ($color) ? $color : $this->_hex;
 
 
391
  // Calculate straight from rbg
392
  $r = hexdec($color[0].$color[1]);
393
  $g = hexdec($color[2].$color[3]);
@@ -450,7 +452,7 @@ class Pix_Customize_Preset_Control extends Pix_Customize_Control {
450
  }
451
 
452
  foreach ( $options as $option_id => $option_value ) {
453
- $option_config = $localPlugin->get_option_customizer_config( $option_id );
454
  if ( empty( $option_config ) ) {
455
  continue;
456
  }
204
  </label>
205
  <div class="palette">
206
  <?php foreach ( $choice_config['options'] as $color_setting_id => $color_value ) {
207
+ $field_config = PixCustomifyPlugin()->get_option_details( $color_setting_id );
208
  echo '<div class="palette__item ' . esc_attr( $color_setting_id ) . '" style="background: ' . esc_attr( $color_value ) . '"></div>' . PHP_EOL;
209
  } ?>
210
  </div>
386
  * @return boolean
387
  */
388
  public function isLight( $color = FALSE ){
389
+ if ( false === $color ) {
390
+ return false;
391
+ }
392
+
393
  // Calculate straight from rbg
394
  $r = hexdec($color[0].$color[1]);
395
  $g = hexdec($color[2].$color[3]);
452
  }
453
 
454
  foreach ( $options as $option_id => $option_value ) {
455
+ $option_config = $localPlugin->get_option_details( $option_id );
456
  if ( empty( $option_config ) ) {
457
  continue;
458
  }
features/customizer/controls/class-Pix_Customize_Typography_Control.php CHANGED
@@ -188,7 +188,7 @@ class Pix_Customize_Typography_Control extends Pix_Customize_Control {
188
  // Allow others to add options here
189
  do_action( 'customify_typography_font_family_before_standard_fonts_options', $font_family, $current_value );
190
 
191
- if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_standard_fonts' ) ) {
192
 
193
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
194
  foreach ( self::$std_fonts as $key => $font ) {
@@ -200,9 +200,9 @@ class Pix_Customize_Typography_Control extends Pix_Customize_Control {
200
  // Allow others to add options here
201
  do_action( 'customify_typography_font_family_before_google_fonts_options' );
202
 
203
- if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_google_fonts' ) ) {
204
 
205
- if ( PixCustomifyPlugin()->get_plugin_setting( 'typography_group_google_fonts' ) ) {
206
 
207
  $grouped_google_fonts = array();
208
  foreach ( self::$google_fonts as $key => $font ) {
188
  // Allow others to add options here
189
  do_action( 'customify_typography_font_family_before_standard_fonts_options', $font_family, $current_value );
190
 
191
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_standard_fonts' ) ) {
192
 
193
  echo '<optgroup label="' . __( 'Standard fonts', 'customify' ) . '">';
194
  foreach ( self::$std_fonts as $key => $font ) {
200
  // Allow others to add options here
201
  do_action( 'customify_typography_font_family_before_google_fonts_options' );
202
 
203
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts' ) ) {
204
 
205
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_group_google_fonts' ) ) {
206
 
207
  $grouped_google_fonts = array();
208
  foreach ( self::$google_fonts as $key => $font ) {
features/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden
3
+ // Golden is deprecated
includes/admin-notifications-manager/class-admin-notifications-manager.php CHANGED
@@ -1167,7 +1167,7 @@ class Pixcloud_Admin_Notifications_Manager {
1167
  * @param int $user_id User ID from whom to remove dismissals. Set to 0 for all users.
1168
  * @param array $notice_ids Array of notice ids to remove
1169
  */
1170
- public function remove_user_notifications_data( $user_id = 0, array $notice_ids ) {
1171
 
1172
  if ( ! empty( $notice_ids ) ) {
1173
 
@@ -1297,4 +1297,4 @@ class Pixcloud_Admin_Notifications_Manager {
1297
  _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1298
  } // End __wakeup ()
1299
 
1300
- }
1167
  * @param int $user_id User ID from whom to remove dismissals. Set to 0 for all users.
1168
  * @param array $notice_ids Array of notice ids to remove
1169
  */
1170
+ public function remove_user_notifications_data( $user_id = 0, $notice_ids = array() ) {
1171
 
1172
  if ( ! empty( $notice_ids ) ) {
1173
 
1297
  _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1298
  } // End __wakeup ()
1299
 
1300
+ }
includes/admin-notifications-manager/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden
3
+ // Golden is deprecated
includes/admin-settings/core/README.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Field Configuration Spec
3
+ ========================
4
+
5
+ The following are system reserved field metadata keys:
6
+
7
+ - type
8
+ - name (optional)
9
+ - idname (auto-generated based on name)
10
+ - default (optional)
11
+ - label (optional)
12
+ - desc (optional)
13
+ - form (internal)
14
+ - cleanup (optional)
15
+ - checks (optional)
16
+ - rendering (optional)
17
+
18
+ Field detection
19
+ ---------------
20
+
21
+ If an array inside the dedicated fields configuration block has the "type"
22
+ key it is considered to be a field configuration regardless of where it is
23
+ inside the configuration (since fields may have other fields as children
24
+ that themselves have other fields, etc).
25
+
26
+ Field name
27
+ ----------
28
+
29
+ The key pointing to a field configuration is considered the name of the
30
+ field unless a the optional key "name" is provided. If neither is available
31
+ or the name key is empty the field is considered esthetic and while it will
32
+ be processed by the rendering routines it won't be processed by the data
33
+ handling routines (since it's not a named entity).
includes/admin-settings/core/bootstrap.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined( 'ABSPATH' ) or die;
2
+
3
+ // ensure EXT is defined
4
+ if ( ! defined( 'EXT' ) ) {
5
+ define( 'EXT', '.php' );
6
+ }
7
+
8
+ $basepath = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
9
+ require $basepath . 'core' . EXT;
10
+
11
+ // load classes
12
+
13
+ $interfacepath = $basepath . 'interfaces' . DIRECTORY_SEPARATOR;
14
+ pixcustomify::require_all( $interfacepath );
15
+
16
+ $classpath = $basepath . 'classes' . DIRECTORY_SEPARATOR;
17
+ pixcustomify::require_all( $classpath );
18
+
19
+ // load callbacks
20
+
21
+ $callbackpath = $basepath . 'callbacks' . DIRECTORY_SEPARATOR;
22
+ pixcustomify::require_all( $callbackpath );
includes/admin-settings/core/callbacks/cleanup/switch_not_available.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ function pixcustomify_cleanup_switch_not_available($fieldvalue, $meta, $processor) {
4
+ return $fieldvalue !== null ? $fieldvalue : false;
5
+ }
includes/admin-settings/core/callbacks/validation/is_numeric.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ function pixcustomify_validate_is_numeric($fieldvalue, $validator) {
4
+ return is_int($fieldvalue) || preg_match('/^[0-9\.]+$/', $fieldvalue);
5
+ }
includes/admin-settings/core/callbacks/validation/not_empty.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ function pixcustomify_validate_not_empty($fieldvalue, $processor) {
4
+ return ! empty($fieldvalue);
5
+ }
includes/admin-settings/core/classes/HTMLElement.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * A HTMLElement is a HTMLTag with meta support integrated into it. A normal
5
+ * HTMLTag only cares for it's attributes meta and nothing else, but more
6
+ * specialized tags such as forms or form fields require misc metadata to be
7
+ * attached on the object itself.
8
+ *
9
+ * @package pixcustomify
10
+ * @category core
11
+ * @author Pixelgrade Team
12
+ * @copyright (c) 2013, Pixelgrade
13
+ */
14
+ class PixCustomifyHTMLElementImpl extends PixCustomifyHTMLTagImpl implements PixCustomifyHTMLElement {
15
+
16
+ /** @var array configuration values */
17
+ protected $meta = null;
18
+
19
+ /**
20
+ * @param array config
21
+ */
22
+ static function instance($config = null) {
23
+ $i = new self;
24
+ $i->configure($config);
25
+ return $i;
26
+ }
27
+
28
+ /**
29
+ * Apply configuration.
30
+ */
31
+ protected function configure($config = null) {
32
+ // gurantee configuration
33
+ $config !== null or $config = array();
34
+
35
+ // invoke htmltag instance configuration
36
+ if (isset($config['attrs'])) {
37
+ parent::configure($config['attrs']);
38
+ unset($config['attrs']);
39
+ }
40
+ else { // no html attributes set
41
+ parent::configure(array());
42
+ }
43
+
44
+ // setup meta fields
45
+ $this->meta = pixcustomify::instance('PixCustomifyMeta', $config);
46
+ }
47
+
48
+
49
+ // Meta
50
+ // ------------------------------------------------------------------------
51
+
52
+ /**
53
+ * @param string meta key
54
+ * @return boolean true if key exists, false otherwise
55
+ */
56
+ function hasmeta($key) {
57
+ return $this->meta->has($key);
58
+ }
59
+
60
+ /**
61
+ * @return mixed value or default
62
+ */
63
+ function getmeta($key, $default = null) {
64
+ return $this->meta->get($key, $default);
65
+ }
66
+
67
+ /**
68
+ * @return static $this
69
+ */
70
+ function setmeta($key, $value) {
71
+ $this->meta->set($key, $value);
72
+ return $this;
73
+ }
74
+
75
+ /**
76
+ * Set the key if it's not already set.
77
+ *
78
+ * @param string key
79
+ * @param string value
80
+ */
81
+ function ensuremeta($key, $value) {
82
+ $this->meta->ensure($key, $value);
83
+ return $this;
84
+ }
85
+
86
+ /**
87
+ * If the key is currently a non-array value it will be converted to an
88
+ * array maintaning the previous value (along with the new one).
89
+ *
90
+ * @param string name
91
+ * @param mixed value
92
+ * @return static $this
93
+ */
94
+ function addmeta($name, $value) {
95
+ $this->meta->add($name, $value);
96
+ return $this;
97
+ }
98
+
99
+ /**
100
+ * @return PixCustomifyMeta
101
+ */
102
+ function meta() {
103
+ return $this->meta;
104
+ }
105
+
106
+ } # class
includes/admin-settings/core/classes/HTMLTag.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyHTMLTagImpl implements PixCustomifyHTMLTag {
10
+
11
+ /** @var array html attributes */
12
+ protected $attrs = null;
13
+
14
+ /**
15
+ * @param array config
16
+ */
17
+ static function instance($config = null) {
18
+ $i = new self;
19
+ $i->configure($config);
20
+ return $i;
21
+ }
22
+
23
+ /**
24
+ * Apply configuration.
25
+ */
26
+ protected function configure($config = null) {
27
+ $this->attrs = pixcustomify::instance('PixCustomifyMeta', $config);
28
+ }
29
+
30
+ /**
31
+ * @param string key
32
+ * @param mixed default
33
+ * @return mixed
34
+ */
35
+ function get($key, $default = null) {
36
+ return $this->attrs->get($key, $default);
37
+ }
38
+
39
+ /**
40
+ * @param string key
41
+ * @param mixed value
42
+ * @return static $this
43
+ */
44
+ function set($key, $value) {
45
+ $this->attrs->set($key, $value);
46
+ return $this;
47
+ }
48
+
49
+ /**
50
+ * @return string attributes
51
+ */
52
+ function htmlattributes(array $extra = array()) {
53
+ $attr_segments = array();
54
+ $attributes = pixcustomify::merge($this->attrs->metadata_array(), $extra);
55
+ foreach ($attributes as $key => $value) {
56
+ if ($value !== false && $value !== null) {
57
+ if ( ! empty($value)) {
58
+ if (is_array($value)) {
59
+ $htmlvalue = implode(' ', $value);
60
+ $attr_segments[] = "$key=\"$htmlvalue\"";
61
+ }
62
+ else { // value is not an array
63
+ $attr_segments[] = "$key=\"$value\"";
64
+ }
65
+ }
66
+ else { // empty html tag; ie. no value html tag
67
+ $attr_segments[] = $key;
68
+ }
69
+ }
70
+ }
71
+
72
+ return implode(' ', $attr_segments);
73
+ }
74
+
75
+ } # class
includes/admin-settings/core/classes/Meta.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyMetaImpl implements PixCustomifyMeta {
10
+
11
+ /** @var array metadat */
12
+ protected $metadata = array();
13
+
14
+ /**
15
+ * @param array metadata
16
+ * @return PixCustomifyMeta
17
+ */
18
+ static function instance($metadata) {
19
+ $i = new self;
20
+ $i->metadata = $metadata;
21
+ return $i;
22
+ }
23
+
24
+ /**
25
+ * @param string meta key
26
+ * @return boolean true if key exists, false otherwise
27
+ */
28
+ function has($key) {
29
+ return isset($this->metadata[$key]);
30
+ }
31
+
32
+ /**
33
+ * @param string key
34
+ * @param mixed default
35
+ * @return mixed
36
+ */
37
+ function get($key, $default = null) {
38
+ return $this->has($key) ? $this->metadata[$key] : $default;
39
+ }
40
+
41
+ /**
42
+ * @param string key
43
+ * @param mixed value
44
+ * @return static $this
45
+ */
46
+ function set($key, $value) {
47
+ $this->metadata[$key] = $value;
48
+ return $this;
49
+ }
50
+
51
+ /**
52
+ * Set the key if it's not already set.
53
+ *
54
+ * @param string key
55
+ * @param string value
56
+ */
57
+ function ensure($key, $value) {
58
+ if ( ! $this->has($key)) {
59
+ $this->set($key, $value);
60
+ }
61
+
62
+ return $this;
63
+ }
64
+
65
+ /**
66
+ * If the key is currently a non-array value it will be converted to an
67
+ * array maintaning the previous value (along with the new one).
68
+ *
69
+ * @param string name
70
+ * @param mixed value
71
+ * @return static $this
72
+ */
73
+ function add($name, $value) {
74
+
75
+ // Cleanup
76
+ // -------
77
+
78
+ if ( ! isset($this->metadata[$name])) {
79
+ $this->metadata[$name] = array();
80
+ }
81
+ else if ( ! is_array($this->metadata[$name]))
82
+ {
83
+ $this->metadata[$name] = array($this->metadata[$name]);
84
+ }
85
+ # else: array, no cleanup required
86
+
87
+ // Register new value
88
+ // ------------------
89
+
90
+ $this->metadata[$name][] = $value;
91
+
92
+ return $this;
93
+ }
94
+
95
+ /**
96
+ * @return array all metadata as array
97
+ */
98
+ function metadata_array() {
99
+ return $this->metadata;
100
+ }
101
+
102
+ /**
103
+ * Shorthand for a calling set on multiple keys.
104
+ *
105
+ * @return static $this
106
+ */
107
+ function overwritemeta($overwrites) {
108
+ foreach ($overwrites as $key => $value) {
109
+ $this->set($key, $value);
110
+ }
111
+
112
+ return $this;
113
+ }
114
+
115
+ } # class
includes/admin-settings/core/classes/Processor.php ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyProcessorImpl implements PixCustomifyProcessor {
10
+
11
+ /** @var PixCustomifyMeta plugin configuration */
12
+ protected $meta = null;
13
+
14
+ /** @var PixCustomifyMeta field information */
15
+ protected $fields = null;
16
+
17
+ /**
18
+ * @param array config
19
+ */
20
+ static function instance($config = null) {
21
+ $i = new self;
22
+ $i->configure($config);
23
+ return $i;
24
+ }
25
+
26
+ /**
27
+ * Apply configuration.
28
+ */
29
+ protected function configure($config = null) {
30
+ $this->meta = pixcustomify::instance('PixCustomifyMeta', $config);
31
+
32
+ // extract fields from configuration
33
+ $fields = $this->extract($this->meta->get('fields', array()));
34
+ $this->fields = pixcustomify::instance('PixCustomifyMeta', $fields);
35
+ }
36
+
37
+ /**
38
+ * Extracts fields from raw fields configuration returning an array with
39
+ * the fields in a flat plain.
40
+ *
41
+ * Fields are extracted as follows: if an array has the key "type" it's a
42
+ * field configuration and the key above it is considered the field name
43
+ * unless a "name" key is also available.
44
+ *
45
+ * @param array raw fields
46
+ * @return array flat field list
47
+ */
48
+ protected function extract($rawfields) {
49
+ $fields = array();
50
+
51
+ foreach ($rawfields as $key => $value) {
52
+ if (is_array($value)) {
53
+ if (isset($value['type'])) {
54
+ if (is_string($key)) {
55
+ $fields[$key] = $value;
56
+ }
57
+ else if (isset($value['name'])) {
58
+ $fields[$value['name']] = $value;
59
+ }
60
+ # else: assume rendering sugar or other
61
+ }
62
+
63
+ // search deeper for embeded fields
64
+ $embeded_fields = $this->extract($value);
65
+ $fields = array_merge($fields, $embeded_fields);
66
+ }
67
+ }
68
+
69
+ return $fields;
70
+ }
71
+
72
+ /** @var array status */
73
+ protected $status = null;
74
+
75
+ /** @var PixCustomifyMeta current data; including submitted data */
76
+ protected $data = null;
77
+
78
+ /**
79
+ * @return static $this
80
+ */
81
+ function run() {
82
+ // if the status has been generated we skip execution
83
+ if ($this->status !== null) {
84
+ return $this;
85
+ }
86
+
87
+ $this->status = array
88
+ (
89
+ 'state' => 'nominal',
90
+ 'errors' => array(),
91
+ 'dataupdate' => false,
92
+ );
93
+
94
+ try {
95
+ $option_key = $this->meta->get('settings-key', null);
96
+
97
+ if ($option_key === null) {
98
+ throw new Exception('Missing option_key in plugin configuration.');
99
+ }
100
+
101
+ if ($this->form_was_submitted()) {
102
+ $input = $this->cleanup_input($_POST);
103
+ $errors = $this->validate_input($input);
104
+
105
+ if (empty($errors)) {
106
+ $this->preupdate($input);
107
+ $this->status['dataupdate'] = true;
108
+ $current_values = get_option($option_key);
109
+ $new_option = array_merge($current_values, $input);
110
+ update_option($option_key, $new_option);
111
+ $this->data = pixcustomify::instance('PixCustomifyMeta', $input);
112
+ $this->postupdate($input);
113
+ }
114
+ else { // got errors
115
+ $this->status['errors'] = $errors;
116
+ $this->load_data_from_database($option_key);
117
+ $this->data->overwritemeta($input);
118
+ }
119
+ }
120
+ else { // GET request
121
+ $this->load_data_from_database($option_key);
122
+ }
123
+ }
124
+ catch (Exception $e) {
125
+ if ($this->meta->get('debug', false)) {
126
+ throw $e;
127
+ }
128
+
129
+ $this->status['state'] = 'error';
130
+ $this->status['message'] = $e->getMessage();
131
+ }
132
+
133
+ return $this;
134
+ }
135
+
136
+ /**
137
+ * @return static $this
138
+ */
139
+ protected function load_data_from_database($option_key) {
140
+ $dbconfig = get_option($option_key);
141
+
142
+ if ($dbconfig === false) {
143
+ throw new Exception('Unable to retrieve options.');
144
+ }
145
+
146
+ $this->data = pixcustomify::instance('PixCustomifyMeta', $dbconfig);
147
+ }
148
+
149
+ /**
150
+ * @param array input
151
+ * @return array cleaned up input
152
+ */
153
+ protected function cleanup_input($input) {
154
+ $defaults = pixcustomify::defaults();
155
+ $plugin_cleanup = $this->meta->get('cleanup', array());
156
+
157
+ foreach ($this->fields->metadata_array() as $key => $field) {
158
+
159
+ // ensure a value is present
160
+ if ( ! isset($input[$key])) {
161
+ $input[$key] = null;
162
+ }
163
+
164
+ // Calculate cleanup rules
165
+ // -----------------------
166
+
167
+ $cleanup = array();
168
+ // check pixcustomify defaults
169
+ if (isset($defaults['cleanup'][$field['type']])) {
170
+ $cleanup = $defaults['cleanup'][$field['type']];
171
+ }
172
+ // check plugin defaults
173
+ if (isset($plugin_cleanup[$field['type']])) {
174
+ $cleanup = array_merge($cleanup, $plugin_cleanup[$field['type']]);
175
+ }
176
+ // check field presets
177
+ if (isset($field['cleanup'])) {
178
+ $cleanup = array_merge($cleanup, $field['cleanup']);
179
+ }
180
+
181
+ // Perform Cleanup
182
+ // ---------------
183
+
184
+ foreach ($cleanup as $rule) {
185
+ $callback = pixcustomify::callback($rule, $this->meta);
186
+ $input[$key] = call_user_func($callback, $input[$key], $field, $this);
187
+ }
188
+ }
189
+
190
+ return $input;
191
+ }
192
+
193
+ /**
194
+ * @param array input
195
+ * @return array
196
+ */
197
+ protected function validate_input($input) {
198
+ $validator = pixcustomify::instance('PixCustomifyValidator', $this->meta, $this->fields);
199
+ return $validator->validate($input);
200
+ }
201
+
202
+ /**
203
+ * @return boolean
204
+ */
205
+ protected function form_was_submitted() {
206
+ return $_SERVER['REQUEST_METHOD'] === 'POST';
207
+ }
208
+
209
+ /**
210
+ * @return array
211
+ */
212
+ function status() {
213
+ if ($this->status === null) {
214
+ $this->run();
215
+ }
216
+
217
+ return $this->status;
218
+ }
219
+
220
+ /**
221
+ * @return PixCustomifyMeta current data (influenced by user submitted data)
222
+ */
223
+ function data() {
224
+ if ($this->status === null) {
225
+ $this->run();
226
+ }
227
+
228
+ return $this->data;
229
+ }
230
+
231
+ /**
232
+ * Shorthand.
233
+ *
234
+ * @return array
235
+ */
236
+ function errors() {
237
+ if ($this->status === null) {
238
+ $this->run();
239
+ }
240
+
241
+ return $this->status['errors'];
242
+ }
243
+
244
+ /**
245
+ * Shorthand.
246
+ *
247
+ * @return boolean
248
+ */
249
+ function performed_update() {
250
+ if ($this->status === null) {
251
+ $this->run();
252
+ }
253
+
254
+ return $this->status['dataupdate'];
255
+ }
256
+
257
+ /**
258
+ * @return boolean true if state is nominal
259
+ */
260
+ function ok() {
261
+ if ($this->status === null) {
262
+ $this->run();
263
+ }
264
+
265
+ return $this->status['state'] == 'nominal';
266
+ }
267
+
268
+ // ------------------------------------------------------------------------
269
+ // Hooks
270
+
271
+ /**
272
+ * Execute preupdate hooks on input.
273
+ */
274
+ protected function preupdate($input)
275
+ {
276
+ $defaults = pixcustomify::defaults();
277
+ $plugin_hooks = $this->meta->get('processor', array('preupdate' => array(), 'postupdate' => array()));
278
+
279
+ // Calculate hooks
280
+ // ---------------
281
+
282
+ $hooks = array();
283
+ // check pixcustomify defaults
284
+ if (isset($defaults['processor']['preupdate'])) {
285
+ $hooks = $defaults['processor']['preupdate'];
286
+ }
287
+ // check plugin defaults
288
+ if (isset($plugin_hooks['preupdate'])) {
289
+ $hooks = array_merge($hooks, $plugin_hooks['preupdate']);
290
+ }
291
+
292
+ // Execute hooks
293
+ // -------------
294
+
295
+ foreach ($hooks as $rule) {
296
+ $callback = pixcustomify::callback($rule, $this->meta);
297
+ call_user_func($callback, $input, $this);
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Execute postupdate hooks on input.
303
+ */
304
+ protected function postupdate($input)
305
+ {
306
+ $defaults = pixcustomify::defaults();
307
+ $plugin_hooks = $this->meta->get('processor', array('preupdate' => array(), 'postupdate' => array()));
308
+
309
+ // Calculate hooks
310
+ // ---------------
311
+
312
+ $hooks = array();
313
+ // check pixcustomify defaults
314
+ if (isset($defaults['processor']['postupdate'])) {
315
+ $hooks = $defaults['processor']['postupdate'];
316
+ }
317
+ // check plugin defaults
318
+ if (isset($plugin_hooks['postupdate'])) {
319
+ $hooks = array_merge($hooks, $plugin_hooks['postupdate']);
320
+ }
321
+
322
+ // Execute hooks
323
+ // -------------
324
+
325
+ foreach ($hooks as $rule) {
326
+ $callback = pixcustomify::callback($rule, $this->meta);
327
+ call_user_func($callback, $input, $this);
328
+ }
329
+ }
330
+
331
+ } # class
includes/admin-settings/core/classes/Validator.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyValidatorImpl implements PixCustomifyValidator {
10
+
11
+ /** @var PixCustomifyMeta plugin configuration */
12
+ protected $meta = null;
13
+
14
+ /** @var PixCustomifyMeta field information */
15
+ protected $fields = null;
16
+
17
+ /**
18
+ * @param array config
19
+ */
20
+ static function instance($config = null, $fields = null) {
21
+ $i = new self;
22
+ $i->configure($config, $fields);
23
+ return $i;
24
+ }
25
+
26
+ /**
27
+ * Apply configuration.
28
+ *
29
+ * Fields array is assumed to be flat. The class will not perform any field
30
+ * extraction itself.
31
+ */
32
+ protected function configure($config = null, $fields = null) {
33
+ $config !== null or $config = array();
34
+ $fields !== null or $fields = array();
35
+
36
+ if (is_array($config)) {
37
+ $this->meta = pixcustomify::instance('PixCustomifyMeta', $config);
38
+ }
39
+ else { // non-array; assume meta object
40
+ $this->meta = $config;
41
+ }
42
+
43
+ if (is_array($fields)) {
44
+ $this->fields = pixcustomify::instance('PixCustomifyMeta', $fields);
45
+ }
46
+ else { // non-array; assume meta object
47
+ $this->fields = $fields;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Validation will only be performed on input keys not on all field keys to
53
+ * allow for partial input validation.
54
+ *
55
+ * @param array input
56
+ * @return array errors (empty if no errors)
57
+ */
58
+ function validate($input) {
59
+ $errors = array();
60
+ $defaults = pixcustomify::defaults();
61
+ $plugin_checks = $this->meta->get('checks', array());
62
+
63
+ foreach ($input as $key => $value) {
64
+
65
+ $field = $this->fields->get($key);
66
+
67
+ // Calculate validation rules
68
+ // --------------------------
69
+
70
+ $rules = array();
71
+ // check pixcustomify defaults
72
+ if (isset($defaults['checks'][$field['type']])) {
73
+ $rules = $defaults['checks'][$field['type']];
74
+ }
75
+ // check theme defaults
76
+ if (isset($plugin_checks[$field['type']])) {
77
+ $rules = array_merge($rules, $plugin_checks[$field['type']]);
78
+ }
79
+ // check field presets
80
+ if (isset($field['checks'])) {
81
+ $rules = array_merge($rules, $field['checks']);
82
+ }
83
+
84
+ // Perform validation
85
+ // ------------------
86
+
87
+ foreach ($rules as $rule) {
88
+ $callback = pixcustomify::callback($rule, $this->meta);
89
+ $valid = call_user_func($callback, $input[$key], $field, $this);
90
+ if ( ! $valid) {
91
+ isset($errors[$key]) or $errors[$key] = array();
92
+ $errors[$key][$rule] = $this->error_message($rule);
93
+ }
94
+ }
95
+ }
96
+
97
+ return $errors;
98
+ }
99
+
100
+ /** @var array error messages */
101
+ protected static $error_message_cache = null;
102
+
103
+ /**
104
+ * @param string rule
105
+ * @return string error message
106
+ */
107
+ function error_message($rule) {
108
+ if (self::$error_message_cache === null) {
109
+ $defaults = pixcustomify::defaults();
110
+ $default_errors = $defaults['errors'];
111
+ $plugin_errors = $this->meta->get('errors', array());
112
+ self::$error_message_cache = array_merge($default_errors, $plugin_errors);
113
+ }
114
+
115
+ return self::$error_message_cache[$rule];
116
+ }
117
+
118
+ } # class
includes/admin-settings/core/classes/forms/Form.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyFormImpl extends PixCustomifyHTMLElementImpl implements PixCustomifyForm {
10
+
11
+ /** @var array templates */
12
+ protected $fields = null;
13
+
14
+ /**
15
+ * @param array config
16
+ */
17
+ static function instance($config = null) {
18
+ $i = new self;
19
+ $i->configure($config);
20
+ return $i;
21
+ }
22
+
23
+ /**
24
+ * Apply configuration.
25
+ */
26
+ protected function configure($config = null) {
27
+ if ($config === null) {
28
+ $config = array('template-paths' => array(), 'fields' => array());
29
+ }
30
+
31
+ // setup errors
32
+ $this->errors = array();
33
+
34
+ // setup default autocomplete
35
+ $this->autocomplete = pixcustomify::instance('PixCustomifyMeta', array());
36
+
37
+ // setup fields
38
+ $this->fields = pixcustomify::instance('PixCustomifyMeta', $config['fields']);
39
+ unset($config['fields']);
40
+
41
+ // invoke htmltag instance configuration
42
+ parent::configure($config);
43
+
44
+ // setup paths
45
+ $this->setmeta('template-paths', $config['template-paths']);
46
+
47
+ // @todo CLEANUP the empty action should redirect to the same page but
48
+ // it's probably wiser to explicitly provide the right page url
49
+ $this->set('action', '');
50
+ $this->set('method', 'POST');
51
+ }
52
+
53
+ /**
54
+ * Shorthand.
55
+ *
56
+ * @return static $this
57
+ */
58
+ function addtemplatepath($path) {
59
+ return $this->addmeta('template-paths', $path);
60
+ }
61
+
62
+ /**
63
+ * Note: the field configuration parameter is indented for use when
64
+ * invoking fields as part of creating other fields (ie. embeded field
65
+ * configuration inside custom fields). It is not meant for overwriting
66
+ * configuration and will not accept partial configuration; albeit the
67
+ * minimal field configuration is fairly minimal.
68
+ *
69
+ * @param string field name
70
+ * @param array complete field configuration
71
+ * @return string
72
+ */
73
+ function field($fieldname, $fieldconfig = null) {
74
+ if ($fieldconfig === null) {
75
+ $fieldconfig = $this->fields->get($fieldname);
76
+ }
77
+
78
+ return pixcustomify::instance('PixCustomifyFormField', $fieldconfig)
79
+ ->setmeta('form', $this)
80
+ ->setmeta('name', $fieldname);
81
+ }
82
+
83
+ // Errors
84
+ // ------------------------------------------------------------------------
85
+
86
+ /** @var array field errors */
87
+ protected $errors = null;
88
+
89
+ /**
90
+ * @return static $this
91
+ */
92
+ function errors($errors) {
93
+ $this->errors = $errors;
94
+ return $this;
95
+ }
96
+
97
+ /**
98
+ * @param string field name
99
+ * @return array error keys with messages
100
+ */
101
+ function errors_for($fieldname) {
102
+ if (isset($this->errors[$fieldname])) {
103
+ return $this->errors[$fieldname];
104
+ }
105
+ else { // no errors set
106
+ return array();
107
+ }
108
+ }
109
+
110
+
111
+ // Autocomplete
112
+ // ------------------------------------------------------------------------
113
+
114
+ /** @var PixCustomifyMeta autocomplete */
115
+ protected $autocomplete = null;
116
+
117
+ /**
118
+ * Autocomplete meta object passed on by the processor.
119
+ *
120
+ * @param PixCustomifyMeta autocomplete values
121
+ * @return static $this
122
+ */
123
+ function autocomplete(PixCustomifyMeta $autocomplete) {
124
+ $this->autocomplete = $autocomplete;
125
+ return $this;
126
+ }
127
+
128
+ /**
129
+ * Retrieves the value registered for auto-complete. This will not fallback
130
+ * to the default value set in the configuration since fields are
131
+ * responsible for managing their internal complexity.
132
+ *
133
+ * Typically the autocomplete values are what the processor passes on to
134
+ * the form.
135
+ *
136
+ * @return mixed
137
+ */
138
+ function autovalue($key, $default = null) {
139
+ return $this->autocomplete->get($key, $default);
140
+ }
141
+
142
+ // Rendering
143
+ // ------------------------------------------------------------------------
144
+
145
+ /**
146
+ * @return string
147
+ */
148
+ function __toString() {
149
+ return $this->startform();;
150
+ }
151
+
152
+ /**
153
+ * @return string
154
+ */
155
+ function startform() {
156
+ return "<form {$this->htmlattributes()}>";
157
+ }
158
+
159
+ /**
160
+ * @return string
161
+ */
162
+ function endform() {
163
+ return '</form>';
164
+ }
165
+
166
+ /**
167
+ * @param string template path
168
+ * @param array configuration
169
+ * @return string
170
+ */
171
+ function fieldtemplate($templatepath, $conf = array()) {
172
+ $config = pixcustomify::instance('PixCustomifyMeta', $conf);
173
+ return $this->fieldtemplate_render($templatepath, $config);
174
+ }
175
+
176
+ /**
177
+ * @param string template path
178
+ * @param PixCustomifyMeta configuration
179
+ * @return string
180
+ */
181
+ protected function fieldtemplate_render($_template_path, PixCustomifyMeta $conf) {
182
+ // variables which we wish to expose to template
183
+ $form = $this; # $this will also work
184
+
185
+ ob_start();
186
+ include $_template_path;
187
+ return ob_get_clean();
188
+ }
189
+
190
+ } # class
includes/admin-settings/core/classes/forms/FormField.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ class PixCustomifyFormFieldImpl extends PixCustomifyHTMLElementImpl implements PixCustomifyFormField {
10
+
11
+ /**
12
+ * @param array config
13
+ */
14
+ static function instance($config = null) {
15
+ $i = new self;
16
+ $i->configure($config);
17
+ return $i;
18
+ }
19
+
20
+ // Error Handling Helpers
21
+ // ------------------------------------------------------------------------
22
+
23
+ /**
24
+ * @return boolean true if field has errors
25
+ */
26
+ function has_errors() {
27
+ $form = $this->getmeta('form', null);
28
+ $errors = $form->errors_for($this->getmeta('name'));
29
+ return ! empty($errors);
30
+ }
31
+
32
+ /**
33
+ * @return string first error message
34
+ */
35
+ function one_error() {
36
+ $form = $this->getmeta('form', null);
37
+ $errors = $form->errors_for($this->getmeta('name'));
38
+ return array_shift($errors);
39
+ }
40
+
41
+ // Rendering
42
+ // ------------------------------------------------------------------------
43
+
44
+ /**
45
+ * Render field emulates wordpress template behaviour. First searches for
46
+ * name, then searches field type and so on.
47
+ *
48
+ * @return string
49
+ */
50
+ function render() {
51
+ $form = $this->getmeta('form');
52
+
53
+ // we reverse the order so that last added is first checked
54
+ $template_paths = array_reverse($form->getmeta('template-paths', array()));
55
+
56
+ if (empty($template_paths)) {
57
+ throw new Exception('Missing template paths.');
58
+ }
59
+
60
+ // the following are the file patterns we look for
61
+ $patterns = array
62
+ (
63
+ 'fields/'.$this->getmeta('name'),
64
+ 'fields/'.$this->getmeta('type'),
65
+ $this->getmeta('name'),
66
+ $this->getmeta('type')
67
+ );
68
+
69
+ foreach ($patterns as $pattern) {
70
+ foreach ($template_paths as $path) {
71
+ $dirpath = rtrim($path, '\\/').DIRECTORY_SEPARATOR;
72
+ if (file_exists($dirpath.$pattern.EXT)) {
73
+ return $this->render_template_file($dirpath.$pattern.EXT);
74
+ }
75
+ }
76
+ }
77
+
78
+ throw new Exception('Failed to match any pattern for field ['.$this->getmeta('name').'] of type '.$this->getmeta('type', '[unknown]'));
79
+ }
80
+
81
+ /**
82
+ * @param string template path
83
+ * @return string rendered field
84
+ */
85
+ protected function render_template_file($_template_filepath) {
86
+ // variables which we wish to expose to template
87
+ $field = $this; # $this will also work
88
+ $form = $this->getmeta('form');
89
+ $name = $this->getmeta('name', null);
90
+ $label = $this->getmeta('label', null);
91
+ $default = $this->getmeta('default', null);
92
+ $desc = $this->getmeta('desc', '');
93
+ $rendering = $this->getmeta('rendering', 'standard');
94
+
95
+ // cleaned name (names may be "something[]")
96
+ $idname = preg_replace('/[^a-zA-Z0-9_-]/', '', $name);
97
+
98
+ ob_start();
99
+ include $_template_filepath;
100
+ return ob_get_clean();
101
+ }
102
+
103
+ /**
104
+ * @return string
105
+ */
106
+ function __toString() {
107
+ return $this->render();
108
+ }
109
+
110
+ } # class
includes/admin-settings/core/core.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixel Grade Team
7
+ * @copyright (c) 2013, Pixel Grade Media
8
+ */
9
+ class pixcustomify {
10
+
11
+ /** @var array core defaults */
12
+ protected static $defaults = null;
13
+
14
+ /**
15
+ * @return array
16
+ */
17
+ static function defaults() {
18
+ if (self::$defaults === null) {
19
+ self::$defaults = include self::corepath().'defaults'.EXT;
20
+ }
21
+
22
+ return self::$defaults;
23
+ }
24
+
25
+ // Simple Dependency Injection Container
26
+ // ------------------------------------------------------------------------
27
+
28
+ /** @var array interface -> implementation mapping */
29
+ protected static $mapping = array();
30
+
31
+ /**
32
+ * @return mixed instance of class registered for the given interface
33
+ */
34
+ static function instance() {
35
+ $args = func_get_args();
36
+ $interface = array_shift($args);
37
+
38
+ if (isset(self::$mapping[$interface])) {
39
+ $class = self::$mapping[$interface];
40
+ }
41
+ else { // the interface isn't mapped to a class
42
+ // we fallback to interface name + "Impl" suffix
43
+ $class = $interface.'Impl';
44
+ }
45
+
46
+ return call_user_func_array(array($class, 'instance'), $args);
47
+ }
48
+
49
+ /**
50
+ * Registers a class for the given interface. If no class is registered for
51
+ * an interface the interface name with a Impl suffix is used.
52
+ */
53
+ static function use_impl($interface, $class) {
54
+ self::$mapping[$interface] = $class;
55
+ }
56
+
57
+
58
+ // Syntactic Sugar
59
+ // ------------------------------------------------------------------------
60
+
61
+ /**
62
+ * @param array configuration
63
+ * @return PixCustomifyForm
64
+ */
65
+ static function form($config, $processor) {
66
+ $form = self::instance('PixCustomifyForm', $config);
67
+ $form->autocomplete($processor->data());
68
+ $form->errors($processor->errors());
69
+ return $form;
70
+ }
71
+
72
+ /**
73
+ * @param array configuration
74
+ * @return PixCustomifyProcessor
75
+ */
76
+ static function processor($config) {
77
+ return self::instance('PixCustomifyProcessor', $config);
78
+ }
79
+
80
+
81
+ // Paths
82
+ // ------------------------------------------------------------------------
83
+
84
+ /**
85
+ * @return string root path for core
86
+ */
87
+ static function corepath() {
88
+ return dirname(__FILE__).DIRECTORY_SEPARATOR;
89
+ }
90
+
91
+ /** @var string plugin path */
92
+ protected static $pluginpath = null;
93
+
94
+ /**
95
+ * @return string path
96
+ */
97
+ static function pluginpath() {
98
+ if (self::$pluginpath === null) {
99
+ self::$pluginpath = realpath(self::corepath().'..').DIRECTORY_SEPARATOR;
100
+ }
101
+
102
+ return self::$pluginpath;
103
+ }
104
+
105
+ /**
106
+ * Sets a custom plugin path; required in non-standard plugin structures.
107
+ */
108
+ static function setpluginpath($path) {
109
+ self::$pluginpath = $path;
110
+ }
111
+
112
+
113
+ // Helpers
114
+ // ------------------------------------------------------------------------
115
+
116
+ /**
117
+ * Hirarchical array merge. Will always return an array.
118
+ *
119
+ * @param ... arrays
120
+ * @return array
121
+ */
122
+ static function merge() {
123
+ $base = array();
124
+ $args = func_get_args();
125
+
126
+ foreach ($args as $arg) {
127
+ self::array_merge($base, $arg);
128
+ }
129
+
130
+ return $base;
131
+ }
132
+
133
+ /**
134
+ * Overwrites base array with overwrite array.
135
+ *
136
+ * @param array base
137
+ * @param array overwrite
138
+ */
139
+ protected static function array_merge(array &$base, array $overwrite) {
140
+ foreach ($overwrite as $key => &$value)
141
+ {
142
+ if (is_int($key))
143
+ {
144
+ // add only if it doesn't exist
145
+ if ( ! in_array($overwrite[$key], $base))
146
+ {
147
+ $base[] = $overwrite[$key];
148
+ }
149
+ }
150
+ // non-int key
151
+ else if (is_array($value))
152
+ {
153
+ if (isset($base[$key]) && is_array($base[$key]))
154
+ {
155
+ self::array_merge($base[$key], $value);
156
+ }
157
+ else # does not exist or it's a non-array
158
+ {
159
+ $base[$key] = $value;
160
+ }
161
+ }
162
+ else # not an array and not numeric key
163
+ {
164
+ $base[$key] = $value;
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * @param string callback key
171
+ * @return string callback function name
172
+ * @throws Exception
173
+ */
174
+ static function callback($key, PixCustomifyMeta $meta) {
175
+ $defaults = pixcustomify::defaults();
176
+ $default_callbacks = $defaults['callbacks'];
177
+ $plugin_callbacks = $meta->get('callbacks', array());
178
+
179
+ $callbacks = array_merge($default_callbacks, $plugin_callbacks);
180
+
181
+ if (isset($callbacks[$key])) {
182
+ return $callbacks[$key];
183
+ }
184
+ else { // missing callback
185
+ throw new Exception('Missing callback for ['.$key.'].');
186
+ }
187
+ }
188
+
189
+ /** @var string the translation text domain */
190
+ protected static $textdomain = 'customify';
191
+
192
+ /**
193
+ * @return string text domain
194
+ */
195
+ static function textdomain() {
196
+ return self::$textdomain;
197
+ }
198
+
199
+ /**
200
+ * Sets a custom text domain; if null is passed the text domain will revert
201
+ * to the default text domain.
202
+ */
203
+ static function settextdomain($textdomain) {
204
+ if ( ! empty($textdomain)) {
205
+ self::$textdomain = $textdomain;
206
+ }
207
+ else { // null or otherwise empty value
208
+ // revert to default
209
+ self::$textdomain = 'customify';
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Recursively finds all files in a directory.
215
+ *
216
+ * @param string directory to search
217
+ * @return array found files
218
+ */
219
+ static function find_files($dir)
220
+ {
221
+ $found_files = array();
222
+ $files = scandir($dir);
223
+
224
+ foreach ($files as $value) {
225
+ // skip special dot files and directories
226
+ if (strpos($value,'.') === 0) {
227
+ continue;
228
+ }
229
+
230
+ // is it a file?
231
+ if (is_file("$dir/$value")) {
232
+ $found_files []= "$dir/$value";
233
+ continue;
234
+ }
235
+ else { // it's a directory
236
+ foreach (self::find_files("$dir/$value") as $value) {
237
+ $found_files []= $value;
238
+ }
239
+ }
240
+ }
241
+
242
+ return $found_files;
243
+ }
244
+
245
+ /**
246
+ * Requires all PHP files in a directory.
247
+ * Use case: callback directory, removes the need to manage callbacks.
248
+ *
249
+ * Should be used on a small directory chunks with no sub directories to
250
+ * keep code clear.
251
+ *
252
+ * @param string path
253
+ */
254
+ static function require_all($path)
255
+ {
256
+ $files = self::find_files(rtrim($path, '\\/'));
257
+
258
+ $priority_list = array();
259
+ foreach ($files as $file) {
260
+ $priority_list[$file] = self::file_priority($file);
261
+ }
262
+
263
+ asort($priority_list, SORT_ASC);
264
+
265
+ foreach ($priority_list as $file => $priority) {
266
+ if (strpos($file, EXT)) {
267
+ require_once $file;
268
+ }
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Priority based on path length and number of directories. Files in the
274
+ * same directory have higher priority if their path is shorter; files in
275
+ * directories have +100 priority bonus for every directory.
276
+ *
277
+ * @param string file path
278
+ * @return int
279
+ */
280
+ protected static function file_priority($path) {
281
+ $path = str_replace('\\', '/', $path);
282
+ return strlen($path) + substr_count($path, '/') * 100;
283
+ }
284
+
285
+ static function option( $option, $default = null ) {
286
+ /** @var PixCustomifyPlugin $local_plugin */
287
+ $local_plugin = PixCustomifyPlugin();
288
+
289
+ return $local_plugin->get_option($option, $default = null);
290
+
291
+ }
292
+
293
+ } # class
includes/admin-settings/core/defaults.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return array
2
+ (
3
+ 'cleanup' => array
4
+ (
5
+ 'switch' => array('switch_not_available'),
6
+ ),
7
+
8
+ 'checks' => array
9
+ (
10
+ 'counter' => array('is_numeric', 'not_empty'),
11
+ ),
12
+
13
+ 'processor' => array
14
+ (
15
+ // callback signature: (array $input, PixCustomifyProcessor $processor)
16
+
17
+ 'preupdate' => array
18
+ (
19
+ // callbacks to run before update process
20
+ // cleanup and validation has been performed on data
21
+ ),
22
+ 'postupdate' => array
23
+ (
24
+ // callbacks to run post update
25
+ ),
26
+ ),
27
+
28
+ 'errors' => array
29
+ (
30
+ 'is_numeric' => __('Numberic value required.', 'customify' ),
31
+ 'not_empty' => __('Field is required.', 'customify' ),
32
+ ),
33
+
34
+ 'callbacks' => array
35
+ (
36
+ // cleanup callbacks
37
+ 'switch_not_available' => 'pixcustomify_cleanup_switch_not_available',
38
+
39
+ // validation callbacks
40
+ 'is_numeric' => 'pixcustomify_validate_is_numeric',
41
+ 'not_empty' => 'pixcustomify_validate_not_empty'
42
+ )
43
+
44
+ ); # config
includes/admin-settings/core/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden
3
+ // Golden is deprecated
includes/admin-settings/core/interfaces/HTMLElement.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyHTMLElement extends PixCustomifyHTMLTag {
10
+
11
+ /**
12
+ * @param string meta key
13
+ * @return boolean true if key exists, false otherwise
14
+ */
15
+ function hasmeta($key);
16
+
17
+ /**
18
+ * @return mixed value or default
19
+ */
20
+ function getmeta($key, $default = null);
21
+
22
+ /**
23
+ * @return static $this
24
+ */
25
+ function setmeta($key, $value);
26
+
27
+ /**
28
+ * Set the key if it's not already set.
29
+ *
30
+ * @param string key
31
+ * @param string value
32
+ */
33
+ function ensuremeta($key, $value);
34
+
35
+ /**
36
+ * If the key is currently a non-array value it will be converted to an
37
+ * array maintaning the previous value (along with the new one).
38
+ *
39
+ * @param string name
40
+ * @param mixed value
41
+ * @return static $this
42
+ */
43
+ function addmeta($name, $value);
44
+
45
+ /**
46
+ * @return PixCustomifyMeta form meta
47
+ */
48
+ function meta();
49
+
50
+ } # interface
includes/admin-settings/core/interfaces/HTMLTag.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyHTMLTag {
10
+
11
+ /**
12
+ * @param string key
13
+ * @param mixed default
14
+ * @return mixed
15
+ */
16
+ function get($key, $default = null);
17
+
18
+ /**
19
+ * @param string key
20
+ * @param mixed value
21
+ * @return static $this
22
+ */
23
+ function set($key, $value);
24
+
25
+ /**
26
+ * @return string
27
+ */
28
+ function htmlattributes(array $extra = array());
29
+
30
+ } # interface
includes/admin-settings/core/interfaces/Meta.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyMeta {
10
+
11
+ /**
12
+ * @param string meta key
13
+ * @return boolean true if key exists, false otherwise
14
+ */
15
+ function has($key);
16
+
17
+ /**
18
+ * @return mixed value or default
19
+ */
20
+ function get($key, $default = null);
21
+
22
+ /**
23
+ * @return static $this
24
+ */
25
+ function set($key, $value);
26
+
27
+ /**
28
+ * Set the key if it's not already set.
29
+ *
30
+ * @param string key
31
+ * @param string value
32
+ */
33
+ function ensure($key, $value);
34
+
35
+ /**
36
+ * If the key is currently a non-array value it will be converted to an
37
+ * array maintaning the previous value (along with the new one).
38
+ *
39
+ * @param string name
40
+ * @param mixed value
41
+ * @return static $this
42
+ */
43
+ function add($name, $value);
44
+
45
+ /**
46
+ * @return array all metadata as array
47
+ */
48
+ function metadata_array();
49
+
50
+ /**
51
+ * Shorthand for a calling set on multiple keys.
52
+ *
53
+ * @return static $this
54
+ */
55
+ function overwritemeta($overwrites);
56
+
57
+ } # interface
includes/admin-settings/core/interfaces/Processor.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyProcessor {
10
+
11
+ /**
12
+ * @return static $this
13
+ */
14
+ function run();
15
+
16
+ /**
17
+ * @return array
18
+ */
19
+ function status();
20
+
21
+ /**
22
+ * @return PixCustomifyMeta current data (influenced by user submitted data)
23
+ */
24
+ function data();
25
+
26
+ /**
27
+ * Shorthand.
28
+ *
29
+ * @return array
30
+ */
31
+ function errors();
32
+
33
+ /**
34
+ * Shorthand.
35
+ *
36
+ * @return boolean
37
+ */
38
+ function performed_update();
39
+
40
+ /**
41
+ * @return boolean true if state is nominal
42
+ */
43
+ function ok();
44
+
45
+ } # interface
includes/admin-settings/core/interfaces/Validator.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyValidator {
10
+
11
+ /**
12
+ * @return array errors
13
+ */
14
+ function validate($input);
15
+
16
+ /**
17
+ * @param string rule
18
+ * @return string error message
19
+ */
20
+ function error_message($rule);
21
+
22
+ } # interface
includes/admin-settings/core/interfaces/extended/Form.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyForm extends PixCustomifyHTMLElement {
10
+
11
+ /**
12
+ * @return static $this
13
+ */
14
+ function addtemplatepath($path);
15
+
16
+ /**
17
+ * @return PixCustomifyFormField
18
+ */
19
+ function field($fieldname);
20
+
21
+ /**
22
+ * @return static $this
23
+ */
24
+ function errors($errors);
25
+
26
+ /**
27
+ * @param string field name
28
+ * @return array error keys with messages
29
+ */
30
+ function errors_for($fieldname);
31
+
32
+ /**
33
+ * Autocomplete meta object passed on by the processor.
34
+ *
35
+ * @param PixCustomifyMeta autocomplete values
36
+ * @return static $this
37
+ */
38
+ function autocomplete(PixCustomifyMeta $autocomplete);
39
+
40
+ /**
41
+ * Retrieves the value registered for auto-complete. This will not fallback
42
+ * to the default value set in the configuration since fields are
43
+ * responsible for managing their internal complexity.
44
+ *
45
+ * Typically the autocomplete values are what the processor passes on to
46
+ * the form.
47
+ *
48
+ * @return mixed
49
+ */
50
+ function autovalue($key, $default = null);
51
+
52
+ /**
53
+ * @return string
54
+ */
55
+ function startform();
56
+
57
+ /**
58
+ * @return string
59
+ */
60
+ function endform();
61
+
62
+ /**
63
+ * @param string template path
64
+ * @param array configuration
65
+ * @return string
66
+ */
67
+ function fieldtemplate($templatepath, $conf = array());
68
+
69
+ } # interface
includes/admin-settings/core/interfaces/extended/FormField.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ /**
4
+ * @package pixcustomify
5
+ * @category core
6
+ * @author Pixelgrade Team
7
+ * @copyright (c) 2013, Pixelgrade
8
+ */
9
+ interface PixCustomifyFormField extends PixCustomifyHTMLElement {
10
+
11
+ /**
12
+ * @return boolean true if field has errors
13
+ */
14
+ function has_errors();
15
+
16
+ /**
17
+ * @return string first error message
18
+ */
19
+ function one_error();
20
+
21
+ /**
22
+ * Render field emulates wordpress template behaviour. First searches for
23
+ * name, then searches field type and so on.
24
+ *
25
+ * @return string
26
+ */
27
+ function render();
28
+
29
+ } # interface
includes/admin-settings/core/tests/bootstrap.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+
3
+ // ensure EXT is defined
4
+ if ( ! defined('EXT')) {
5
+ define('EXT', '.php');
6
+ }
7
+
8
+ error_reporting(-1);
9
+
10
+ $basepath = realpath('..').DIRECTORY_SEPARATOR;
11
+ require $basepath.'bootstrap'.EXT;
includes/admin-settings/core/tests/sample-config.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return array
2
+ (
3
+ 'template-paths' => array
4
+ (
5
+ dirname(__FILE__).DIRECTORY_SEPARATOR.'sample-templates'
6
+ ),
7
+
8
+ 'fields' => array
9
+ (
10
+ // empty
11
+ ),
12
+
13
+ ); # config
includes/admin-settings/core/views/form-partials/fields/color.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ $type = 'color';
12
+ include 'text'.EXT;
13
+
includes/admin-settings/core/views/form-partials/fields/counter.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ // [!!] the counter field needs to be able to work inside other fields; if
12
+ // the field is in another field it will have a null label
13
+
14
+ $value = $form->autovalue($name, $default);
15
+
16
+ $attrs = array
17
+ (
18
+ 'name' => $name,
19
+ 'id' => $idname,
20
+ 'type' => 'number',
21
+ 'value' => $value,
22
+ 'step' => 1,
23
+ 'class' => array(),
24
+ );
25
+
26
+ $is_inline_field = empty($label) || $rendering == 'inline';
27
+
28
+ if ($field->has_errors()) {
29
+ $error_message = $field->one_error();
30
+ $attrs['class'][] = 'field-error';
31
+ $attrs['title'] = "Error: $error_message";
32
+ }
33
+ ?>
34
+
35
+ <?php if ($is_inline_field): ?>
36
+ <?php $attrs['class'][] = 'small-text' ?>
37
+ <input <?php echo $field->htmlattributes($attrs) ?> class="small-text" />
38
+ <?php else: # standard field ?>
39
+ <label for="<?php echo $idname ?>">
40
+ <input <?php echo $field->htmlattributes($attrs) ?> />
41
+ <?php echo $label ?>
42
+ </label>
43
+ <?php endif; ?>
includes/admin-settings/core/views/form-partials/fields/group.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+ /* @var string $show_on */
11
+
12
+ /* $show_on = $field->getmeta('show_on'); ?>
13
+ <div class="group" <?php if ( !empty($show_on) ) echo 'show_on="'. $show_on .'"'; */ ?>
14
+ <div class="group">
15
+ <?php foreach ($field->getmeta('options', array()) as $fieldname => $fieldconfig):
16
+
17
+ $field = $form->field($fieldname, $fieldconfig);
18
+ // we set the fields to default to inline
19
+ $field->ensuremeta('rendering', 'blocks');
20
+ // export field meta for processing
21
+ $fielddesc = $field->getmeta('desc', null);
22
+ $fieldexample = $field->getmeta('group-example', null);
23
+ $fieldnote = $field->getmeta('group-note', null); ?>
24
+ <div class="field" <?php if ( $fieldconfig['type'] == 'group' ) echo 'id="' . $fieldname . '"'; ?> >
25
+ <?php echo $field->render();
26
+ if ( ! empty($fieldnote)): ?>
27
+ <span class="field-note"><?php echo $fieldnote ?></span>
28
+ <?php endif; ?>
29
+ </div>
30
+ <?php endforeach; ?>
31
+ </div>
32
+ <!--</div>-->
includes/admin-settings/core/views/form-partials/fields/hidden.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ isset($type) or $type = 'hidden';
12
+
13
+ $attrs = array
14
+ (
15
+ 'name' => $name,
16
+ 'id' => $idname,
17
+ 'type' => 'hidden',
18
+ 'value' => $form->autovalue($name)
19
+ );
20
+ ?>
21
+
22
+ <input <?php echo $field->htmlattributes($attrs) ?>/>
includes/admin-settings/core/views/form-partials/fields/postbox.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ ?>
12
+ <div class="postbox">
13
+ <div class="handlediv" title="Click to toggle"><br></div>
14
+ <h3 class="hndle"><span><?php echo $label ?></span></h3>
15
+
16
+ <div class="inside">
17
+ <?php foreach ($field->getmeta('options', array()) as $fieldname => $fieldconfig):
18
+
19
+ $field = $form->field($fieldname, $fieldconfig);
20
+ // we set the fields to default to inline
21
+ $field->ensuremeta('rendering', 'blocks');
22
+ // export field meta for processing
23
+ $fielddesc = $field->getmeta('desc', null);
24
+ $show_group = $field->getmeta('show_group', null); ?>
25
+
26
+ <div class="row" <?php if ( $fieldconfig['type'] == 'group' ) echo 'id="' . $fieldname . '"'; ?>>
27
+ <?php if ( ! empty($fielddesc)): ?>
28
+ <div class="field-desc"><?php echo $fielddesc ?></div>
29
+ <?php endif;
30
+ echo $field->render();
31
+ if ( ! empty($fieldnote)): ?>
32
+ <span class="note"><?php echo $fieldnote ?></span>
33
+ <?php endif; ?>
34
+ </div>
35
+
36
+ <?php endforeach; ?>
37
+ </div>
38
+ </div>
includes/admin-settings/core/views/form-partials/fields/select.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined( 'ABSPATH' ) or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ // [!!] the counter field needs to be able to work inside other fields; if
12
+ // the field is in another field it will have a null label
13
+
14
+ $selected = $form->autovalue( $name, $default );
15
+
16
+ $attrs = array
17
+ (
18
+ 'name' => $name,
19
+ 'id' => $idname,
20
+ );
21
+
22
+ // group show
23
+
24
+ if ($field->hasmeta('show_group')) {
25
+ $attrs['data-show_group'] = $field->getmeta('show_group');
26
+ }
27
+
28
+ if ($field->hasmeta('display_option')) {
29
+ $attrs['data-display_option'] = $field->getmeta('display_option');
30
+ }?>
31
+ <div class="select">
32
+ <label id="<?php echo $name ?>"><?php echo $label ?></label>
33
+ <select <?php echo $field->htmlattributes( $attrs ) ?>>
34
+ <?php foreach ( $this->getmeta( 'options', array() ) as $key => $label ): ?>
35
+ <option <?php if ($key == $selected): ?>selected<?php endif; ?>
36
+ value="<?php echo $key ?>">
37
+ <?php echo $label ?>
38
+ </option>
39
+ <?php endforeach; ?>
40
+ </select>
41
+ </div>
includes/admin-settings/core/views/form-partials/fields/switch.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ // [!!] a switch is a checkbox that is only ever either on or off; not to
12
+ // be confused with a fully functional checkbox which may be many values
13
+
14
+ $checked = $form->autovalue($name, $default);
15
+
16
+ $attrs = array
17
+ (
18
+ 'name' => $name,
19
+ 'type' => 'checkbox',
20
+ 'id' => $idname,
21
+ 'value' => 1,
22
+ );
23
+
24
+ // is the checkbox checked?
25
+ if ($checked) {
26
+ $attrs['checked'] = 'checked';
27
+ }
28
+
29
+ // Label Fillins
30
+ // -------------
31
+
32
+ if ($field->hasmeta('label-fillins')) {
33
+ $fillers = array();
34
+ foreach ($field->getmeta('label-fillins', array()) as $fieldname => $conf) {
35
+ $fillers[":$fieldname"] = $form->field($fieldname, $conf)->render();
36
+ }
37
+
38
+ $processed_label = strtr($label, $fillers);
39
+ }
40
+ else { // no fillins available
41
+ $processed_label = $label;
42
+ }
43
+
44
+ // group show
45
+
46
+ if ($field->hasmeta('show_group')) {
47
+ $attrs['data-show_group'] = $field->getmeta('show_group');
48
+ }
49
+ ?>
50
+
51
+ <?php if ($rendering == 'inline'): ?>
52
+ <input <?php echo $field->htmlattributes($attrs) ?> />
53
+
54
+ <?php elseif ($rendering == 'blocks'): ?>
55
+ <div class="switch">
56
+ <input <?php echo $field->htmlattributes($attrs) ?> />
57
+ <label for="<?php echo $idname ?>"><?php echo $processed_label ?></label>
58
+ </div>
59
+ <?php else: # rendering != 'inline' ?>
60
+ <label for="<?php echo $idname ?>">
61
+ <input <?php echo $field->htmlattributes($attrs) ?> />
62
+ <?php echo $processed_label ?>
63
+ </label>
64
+ <?php endif; ?>
includes/admin-settings/core/views/form-partials/fields/tabular-group.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+ ?>
11
+
12
+ <tr valign="top">
13
+ <th scope="row">
14
+ <?php echo $label ?>
15
+ </th>
16
+ <td>
17
+ <fieldset>
18
+
19
+ <legend class="screen-reader-text">
20
+ <span><?php echo $label ?></span>
21
+ </legend>
22
+
23
+ <?php foreach ($field->getmeta('options', array()) as $fieldname => $conf): ?>
24
+ <?php echo $form->field($fieldname, $conf)->render() ?>
25
+ <br/>
26
+ <?php endforeach; ?>
27
+
28
+ <?php if ($field->hasmeta('note')): ?>
29
+ <small>
30
+ <em>(<?php echo $field->getmeta('note') ?>)</em>
31
+ </small>
32
+ <?php endif; ?>
33
+
34
+ </fieldset>
35
+ </td>
36
+ </tr>
includes/admin-settings/core/views/form-partials/fields/text.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ isset($type) or $type = 'text';
12
+
13
+ $attrs = array
14
+ (
15
+ 'name' => $name,
16
+ 'id' => $idname,
17
+ 'type' => 'text',
18
+ 'value' => $form->autovalue($name)
19
+ );
20
+ ?>
21
+
22
+ <?php if ($rendering == 'inline'): ?>
23
+ <input <?php echo $field->htmlattributes($attrs) ?>/>
24
+ <?php elseif ($rendering == 'blocks'): ?>
25
+ <div class="text">
26
+ <label id="<?php echo $name ?>"><?php echo $label ?></label>
27
+ <input <?php echo $field->htmlattributes($attrs) ?> />
28
+ <span><?php echo $desc ?></span>
29
+ </div>
30
+ <?php else: # ?>
31
+ <div>
32
+ <p><?php echo $desc ?></p>
33
+ <label id="<?php echo $name ?>">
34
+ <?php echo $label ?>
35
+ <input <?php echo $field->htmlattributes($attrs) ?>/>
36
+ </label>
37
+ </div>
38
+ <?php endif; ?>
includes/admin-settings/core/views/form-partials/linear.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined('ABSPATH') or die;
2
+ /* @var $form PixCustomifyForm */
3
+ /* @var $conf PixCustomifyMeta */
4
+
5
+ /* @var $f PixCustomifyForm */
6
+ $f = &$form;
7
+ ?>
8
+
9
+ <?php foreach ($conf->get('fields', array()) as $fieldname): ?>
10
+
11
+ <?php echo $f->field($fieldname)
12
+ ->addmeta('special_sekrit_property', '!!')
13
+ ->render() ?>
14
+
15
+ <?php endforeach; ?>
includes/admin-settings/views/admin.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Represents the view for the administration dashboard.
4
+ *
5
+ * This includes the header, options, and other information that should
6
+ * provide the user interface to the end user.
7
+ *
8
+ * @package customify
9
+ * @author Pixelgrade <contact@pixelgrade.com>
10
+ * @license GPL-2.0+
11
+ * @link http://pixelgrade.com
12
+ * @copyright 2013 Pixel Grade Media
13
+ */
14
+
15
+ $config = Customify_Settings::get_plugin_config();
16
+
17
+ // invoke processor
18
+ $processor = pixcustomify::processor( $config );
19
+ $status = $processor->status();
20
+ $errors = $processor->errors(); ?>
21
+
22
+ <div class="wrap" id="pixcustomify_form">
23
+
24
+ <div id="icon-options-general" class="icon32"><br></div>
25
+
26
+ <h2><?php esc_html_e( 'PixCustomify', 'customify' ); ?></h2>
27
+
28
+ <?php if ( $processor->ok() ): ?>
29
+
30
+ <?php if ( ! empty( $errors ) ): ?>
31
+ <br/>
32
+ <p class="update-nag">
33
+ <strong><?php esc_html_e( 'Unable to save settings.', 'customify' ); ?></strong>
34
+ <?php esc_html_e( 'Please check the fields for errors and typos.', 'customify' ); ?>
35
+ </p>
36
+ <?php endif;
37
+
38
+ if ( $processor->performed_update() ): ?>
39
+ <br/>
40
+ <p class="update-nag">
41
+ <?php esc_html_e( 'Settings have been updated.', 'customify' ); ?>
42
+ </p>
43
+ <?php endif;
44
+ $f = pixcustomify::form( $config, $processor );
45
+ echo $f->startform();
46
+
47
+ echo $f->field( 'hiddens' )->render();
48
+ echo $f->field( 'general' )->render();
49
+ echo $f->field( 'output' )->render();
50
+ echo $f->field( 'typography' )->render();
51
+ echo $f->field( 'tools' )->render(); ?>
52
+ <button type="submit" class="button button-primary">
53
+ <?php esc_html_e( 'Save Changes', 'customify' ); ?>
54
+ </button>
55
+
56
+ <?php echo $f->endform();
57
+
58
+ elseif ( $status['state'] == 'error' ): ?>
59
+
60
+ <h3><?php esc_html_e( 'Critical Error', 'customify' ); ?></h3>
61
+
62
+ <p><?php echo $status['message'] ?></p>
63
+
64
+ <?php endif; ?>
65
+ </div>
includes/admin-settings/views/form-partials/fields/multicheckbox.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined( 'ABSPATH' ) or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ // [!!] the counter field needs to be able to work inside other fields; if
12
+ // the field is in another field it will have a null label
13
+
14
+ $selected = $form->autovalue( $name, $default );
15
+
16
+ $option = get_option( 'pixcustomify_settings' );
17
+ $attrs = array(
18
+ 'type' => 'checkbox',
19
+ );
20
+ ?>
21
+ <div class="multicheckbox">
22
+ <?php
23
+ foreach ( $this->getmeta( 'options', array() ) as $value => $label ) {
24
+ $attrs['name'] = $name . '[' . $value . ']';
25
+
26
+ if ( is_array($selected) && array_key_exists( $value, $selected) ) {
27
+ $attrs['checked'] = 'checked';
28
+ } else {
29
+ unset($attrs['checked']);
30
+ } ?>
31
+ <fieldset class="multicheckbox_option">
32
+ <input <?php echo $field->htmlattributes( $attrs ) ?>>
33
+ <label id="<?php echo $value ?>"><?php echo $label ?></label>
34
+ </fieldset>
35
+ <?php } ?>
36
+ </div>
includes/admin-settings/views/form-partials/fields/reset_theme_mod.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php defined( 'ABSPATH' ) or die;
2
+ /* @var PixCustomifyFormField $field */
3
+ /* @var PixCustomifyForm $form */
4
+ /* @var mixed $default */
5
+ /* @var string $name */
6
+ /* @var string $idname */
7
+ /* @var string $label */
8
+ /* @var string $desc */
9
+ /* @var string $rendering */
10
+
11
+ // [!!] the counter field needs to be able to work inside other fields; if
12
+ // the field is in another field it will have a null label
13
+
14
+ $selected = $form->autovalue( $name, $default );
15
+
16
+ $config = apply_filters('customify_filter_fields', array() );
17
+
18
+ $key = $config[ 'opt-name' ];
19
+
20
+ $mods = get_theme_mods();
21
+
22
+ $option = get_option( 'pixcustomify_settings' );
23
+
24
+ $attrs = array(
25
+ 'type' => 'checkbox',
26
+ ); ?>
27
+ <div class="reset_customify_theme_mod">
28
+ <div class="button" id="reset_theme_mods"><?php esc_html_e( 'Reset Theme Mods', 'customify' ); ?></div>
29
+ <script>
30
+ (function ($) {
31
+ $(document).ready(function () {
32
+ $('#reset_theme_mods').on('click', function () {
33
+ var confirm = window.confirm('Are you sure?');
34
+
35
+ if ( ! confirm ) {
36
+ return false;
37
+ }
38
+
39
+ $.ajax({
40
+ url: customify_settings.wp_rest.root + 'customify/v1/delete_theme_mod',
41
+ method: 'POST',
42
+ beforeSend: function (xhr) {
43
+ xhr.setRequestHeader('X-WP-Nonce', customify_settings.wp_rest.nonce);
44
+ },
45
+ data: {
46
+ 'customify_settings_nonce': customify_settings.wp_rest.customify_settings_nonce
47
+ }
48
+ }).done(function (response) {
49
+ if ( response.success ) {
50
+ alert( 'Success: ' + response.data );
51
+ } else {
52
+ alert( 'No luck: ' + response.data );
53
+ }
54
+ }).error(function (e) {
55
+ console.log(e);
56
+ });
57
+ });
58
+ });
59
+
60
+ })(jQuery)
61
+ </script>
62
+ </div>
63
+ <br>
64
+ <div class="field-desc"><?php esc_html_e('Resets all the Customizer settings introduced by the plugin. It will NOT reset core Customizer settings or plugin settings.'); ?></div>
includes/admin-settings/views/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
includes/class-customify-color-palettes.php CHANGED
@@ -1049,25 +1049,25 @@ class Customify_Color_Palettes {
1049
  /**
1050
  * Get all the defined Style Manager master color field ids.
1051
  *
1052
- * @since 1.7.4
1053
- *
1054
- * @param array $options
1055
  *
1056
  * @return array
 
 
1057
  */
1058
- public function get_all_master_color_controls_ids( $options = null ) {
1059
  $control_ids = array();
1060
 
1061
- if ( empty( $options ) ) {
1062
- $options = PixCustomifyPlugin()->get_options_configs();
1063
  }
1064
 
1065
- if ( empty( $options ) ) {
1066
  return $control_ids;
1067
  }
1068
 
1069
- foreach ( $options as $option_id => $option_settings ) {
1070
- if ( ! empty( $option_settings['type'] ) && 'color' === $option_settings['type'] && 0 === strpos( $option_id, 'sm_' ) ) {
1071
  $control_ids[] = $option_id;
1072
  }
1073
  }
@@ -1078,26 +1078,24 @@ class Customify_Color_Palettes {
1078
  /**
1079
  * Get all the defined Style Manager final (master) color field ids.
1080
  *
1081
- * @since 2.2.0
1082
- *
1083
- * @param array $options
1084
  *
1085
  * @return array
1086
  */
1087
- public function get_all_final_master_color_controls_ids( $options = null ) {
1088
  $control_ids = array();
1089
 
1090
- if ( empty( $options ) ) {
1091
- $options = PixCustomifyPlugin()->get_options_configs();
1092
  }
1093
 
1094
- if ( empty( $options ) ) {
1095
  return $control_ids;
1096
  }
1097
 
1098
- foreach ( $options as $option_id => $option_settings ) {
1099
- if ( ! empty( $option_settings['type'] )
1100
- && 'hidden' === $option_settings['type']
1101
  && 0 === strpos( $option_id, 'sm_' )
1102
  && '__final' === substr( $option_id, - strlen( '__final' ) ) ) {
1103
  $control_ids[] = $option_id;
1049
  /**
1050
  * Get all the defined Style Manager master color field ids.
1051
  *
1052
+ * @param array $options_details
 
 
1053
  *
1054
  * @return array
1055
+ *@since 1.7.4
1056
+ *
1057
  */
1058
+ public function get_all_master_color_controls_ids( $options_details = null ) {
1059
  $control_ids = array();
1060
 
1061
+ if ( empty( $options_details ) ) {
1062
+ $options_details = PixCustomifyPlugin()->get_options_configs(true);
1063
  }
1064
 
1065
+ if ( empty( $options_details ) ) {
1066
  return $control_ids;
1067
  }
1068
 
1069
+ foreach ( $options_details as $option_id => $option_details ) {
1070
+ if ( ! empty( $option_details['type'] ) && 'color' === $option_details['type'] && 0 === strpos( $option_id, 'sm_' ) ) {
1071
  $control_ids[] = $option_id;
1072
  }
1073
  }
1078
  /**
1079
  * Get all the defined Style Manager final (master) color field ids.
1080
  *
1081
+ * @param array $options_details
 
 
1082
  *
1083
  * @return array
1084
  */
1085
+ public function get_all_final_master_color_controls_ids( $options_details = null ) {
1086
  $control_ids = array();
1087
 
1088
+ if ( empty( $options_details ) ) {
1089
+ $options_details = PixCustomifyPlugin()->get_options_configs(true);
1090
  }
1091
 
1092
+ if ( empty( $options_details ) ) {
1093
  return $control_ids;
1094
  }
1095
 
1096
+ foreach ( $options_details as $option_id => $option_details ) {
1097
+ if ( ! empty( $option_details['type'] )
1098
+ && 'hidden' === $option_details['type']
1099
  && 0 === strpos( $option_id, 'sm_' )
1100
  && '__final' === substr( $option_id, - strlen( '__final' ) ) ) {
1101
  $control_ids[] = $option_id;
includes/class-customify-customizer.php ADDED
@@ -0,0 +1,1953 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This is the class that handles the overall logic for the Customizer.
4
+ *
5
+ * @see https://pixelgrade.com
6
+ * @author Pixelgrade
7
+ * @since 2.4.0
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit; // Exit if accessed directly
12
+ }
13
+
14
+ if ( ! class_exists( 'Customify_Customizer' ) ) :
15
+
16
+ class Customify_Customizer {
17
+
18
+ /**
19
+ * Holds the only instance of this class.
20
+ * @var null|Customify_Customizer
21
+ * @access protected
22
+ * @since 2.4.0
23
+ */
24
+ protected static $_instance = null;
25
+
26
+ protected $localized = array();
27
+
28
+ protected $typo_settings;
29
+
30
+ protected $google_fonts = null;
31
+
32
+ protected $theme_fonts = null;
33
+
34
+ protected $media_queries = array();
35
+
36
+ // these properties will get 'px' as a default unit
37
+ public static $pixel_dependent_css_properties = array(
38
+ 'width',
39
+ 'max-width',
40
+ 'min-width',
41
+
42
+ 'height',
43
+ 'max-height',
44
+ 'min-height',
45
+
46
+ 'padding',
47
+ 'padding-left',
48
+ 'padding-right',
49
+ 'padding-top',
50
+ 'padding-bottom',
51
+
52
+ 'margin',
53
+ 'margin-right',
54
+ 'margin-left',
55
+ 'margin-top',
56
+ 'margin-bottom',
57
+
58
+ 'right',
59
+ 'left',
60
+ 'top',
61
+ 'bottom',
62
+
63
+ 'font-size',
64
+ 'letter-spacing',
65
+
66
+ 'border-size',
67
+ 'border-width',
68
+ 'border-bottom-width',
69
+ 'border-left-width',
70
+ 'border-right-width',
71
+ 'border-top-width'
72
+ );
73
+
74
+ /**
75
+ * Constructor.
76
+ *
77
+ * @since 2.4.0
78
+ */
79
+ protected function __construct() {
80
+ // We will initialize the Customizer logic after the plugin has finished with it's configuration (at priority 15).
81
+ add_action( 'init', array( $this, 'init' ), 15 );
82
+ }
83
+
84
+ /**
85
+ * Initialize this module.
86
+ *
87
+ * @since 2.4.0
88
+ */
89
+ public function init() {
90
+
91
+ $this->localized['options_name'] = PixCustomifyPlugin()->get_options_key();
92
+
93
+ if ( $this->import_button_exists() ) {
94
+ $this->localized['import_rest_url'] = get_rest_url( '/customify/1.0/' );
95
+ $this->localized['import_rest_nonce'] = wp_create_nonce( 'wp_rest' );
96
+
97
+ $this->register_import_api();
98
+ }
99
+
100
+ require_once( PixCustomifyPlugin()->get_base_path() . 'features/class-Font_Selector.php' );
101
+ $this->localized['theme_fonts'] = $this->theme_fonts = Customify_Font_Selector::instance()->get_theme_fonts();
102
+
103
+ $this->localized['ajax_url'] = admin_url( 'admin-ajax.php' );
104
+ $this->localized['style_manager_user_feedback_nonce'] = wp_create_nonce( 'customify_style_manager_user_feedback' );
105
+ $this->localized['style_manager_user_feedback_provided'] = get_option( 'style_manager_user_feedback_provided', false );
106
+
107
+ // Hook up.
108
+ $this->add_hooks();
109
+ }
110
+
111
+ /**
112
+ * Use this function when you need to know if an import button is used
113
+ * @return bool
114
+ */
115
+ protected function import_button_exists() {
116
+ $options_details = PixCustomifyPlugin()->get_options_details(true);
117
+
118
+ foreach ( $options_details as $option ) {
119
+ if ( isset( $option['type'] ) && 'import_demo_data' === $option['type'] ) {
120
+ return true;
121
+ break;
122
+ }
123
+ }
124
+
125
+ return false;
126
+ }
127
+
128
+ protected function register_import_api() {
129
+
130
+ include_once( PixCustomifyPlugin()->get_base_path() . 'features/class-Customify_Importer.php' );
131
+ $controller = new Customify_Importer_Controller();
132
+ $controller->init();
133
+ }
134
+
135
+ /**
136
+ * Initiate our hooks
137
+ *
138
+ * @since 2.4.0
139
+ */
140
+ public function add_hooks() {
141
+
142
+ // Styles for the Customizer
143
+ add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_styles' ), 10 );
144
+ add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_styles' ), 10 );
145
+ // Scripts enqueued in the Customizer
146
+ add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_scripts' ), 15 );
147
+ add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_scripts' ), 15 );
148
+
149
+ // Scripts enqueued only in the theme preview
150
+ add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_register_scripts' ), 10 );
151
+ add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_enqueue_scripts' ), 99999 );
152
+
153
+ // Add extra settings data to _wpCustomizeSettings.settings of the parent window.
154
+ add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings_additional_data' ), 10000 );
155
+
156
+ // The frontend effects of the Customizer controls
157
+ $load_location = PixCustomifyPlugin()->settings->get_plugin_setting( 'style_resources_location', 'wp_head' );
158
+
159
+ add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
160
+ add_action( 'wp_head', array( $this, 'output_typography_dynamic_script' ), 10 );
161
+ add_action( 'wp_head', array( $this, 'output_typography_dynamic_style' ), 10 );
162
+
163
+ add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
164
+ add_action( 'customize_register', array( $this, 'register_customizer' ), 12 );
165
+ // Maybe the theme has instructed us to do things like removing sections or controls.
166
+ add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
167
+
168
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting( 'enable_editor_style', true ) ) {
169
+ add_action( 'admin_enqueue_scripts', array( $this, 'script_to_add_customizer_settings_into_wp_editor' ), 10, 1 );
170
+ }
171
+
172
+ /*
173
+ * Development related
174
+ */
175
+ if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' ) && true === CUSTOMIFY_DEV_FORCE_DEFAULTS ) {
176
+ // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not save anything in the database
177
+ // Always go with the default
178
+ add_filter( 'customize_changeset_save_data', array( $this, 'prevent_changeset_save_in_devmode' ), 50, 1 );
179
+ // Add a JS to display a notification
180
+ add_action( 'customize_controls_print_footer_scripts', array( $this, 'prevent_changeset_save_in_devmode_notification' ), 100 );
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Register Customizer admin styles
186
+ */
187
+ function register_admin_customizer_styles() {
188
+ wp_register_style( 'customify_select2', plugins_url( 'js/select2/css/select2.css', PixCustomifyPlugin()->get_file() ), array(), PixCustomifyPlugin()->get_version() );
189
+ wp_register_style( 'customify_style', plugins_url( 'css/customizer.css', PixCustomifyPlugin()->get_file() ), array( 'customify_select2' ), PixCustomifyPlugin()->get_version() );
190
+ }
191
+
192
+ /**
193
+ * Enqueue Customizer admin styles
194
+ */
195
+ function enqueue_admin_customizer_styles() {
196
+ wp_enqueue_style( 'customify_style' );
197
+ }
198
+
199
+ /**
200
+ * Register Customizer admin scripts
201
+ */
202
+ function register_admin_customizer_scripts() {
203
+
204
+ wp_register_script( 'customify_select2', plugins_url( 'js/select2/js/select2.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version() );
205
+ wp_register_script( 'jquery-react', plugins_url( 'js/jquery-react.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version() );
206
+
207
+ wp_register_script( 'customify-scale', plugins_url( 'js/customizer/scale-iframe.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version() );
208
+ wp_register_script( 'customify-fontselectfields', plugins_url( 'js/customizer/font-select-fields.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version() );
209
+
210
+ wp_register_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts', plugins_url( 'js/customizer.js', PixCustomifyPlugin()->get_file() ), array(
211
+ 'jquery',
212
+ 'customify_select2',
213
+ 'underscore',
214
+ 'customize-controls',
215
+ 'customify-fontselectfields',
216
+
217
+ 'customify-scale',
218
+ ), PixCustomifyPlugin()->get_version() );
219
+ }
220
+
221
+ /**
222
+ * Enqueue Customizer admin scripts
223
+ */
224
+ function enqueue_admin_customizer_scripts() {
225
+ wp_enqueue_script( 'jquery-react' );
226
+ wp_enqueue_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts' );
227
+
228
+ wp_localize_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts', 'customify_settings', apply_filters( 'customify_localized_js_settings', $this->localized ) );
229
+ }
230
+
231
+ /** Register Customizer scripts loaded only on previewer page */
232
+ function customizer_live_preview_register_scripts() {
233
+ wp_register_script( PixCustomifyPlugin()->get_slug() . 'CSSOM', plugins_url( 'js/CSSOM.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version(), true );
234
+ wp_register_script( PixCustomifyPlugin()->get_slug() . 'cssUpdate', plugins_url( 'js/jquery.cssUpdate.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version(), true );
235
+ wp_register_script( PixCustomifyPlugin()->get_slug() . '-previewer-scripts', plugins_url( 'js/customizer_preview.js', PixCustomifyPlugin()->get_file() ), array(
236
+ 'jquery',
237
+ 'customize-preview',
238
+ PixCustomifyPlugin()->get_slug() . 'CSSOM',
239
+ PixCustomifyPlugin()->get_slug() . 'cssUpdate'
240
+ ), PixCustomifyPlugin()->get_version(), true );
241
+ }
242
+
243
+ /** Enqueue Customizer scripts loaded only on previewer page */
244
+ function customizer_live_preview_enqueue_scripts() {
245
+ wp_enqueue_script( PixCustomifyPlugin()->get_slug() . '-previewer-scripts' );
246
+
247
+ // when a live preview field is in action we need to know which props need 'px' as defaults
248
+ $this->localized['px_dependent_css_props'] = self::$pixel_dependent_css_properties;
249
+
250
+ wp_localize_script( PixCustomifyPlugin()->get_slug() . '-previewer-scripts', 'customify_settings', $this->localized );
251
+ }
252
+
253
+ /**
254
+ * Prevent saving of plugin options in the Customizer
255
+ *
256
+ * @param array $data The data to save
257
+ *
258
+ * @return array
259
+ */
260
+ public function prevent_changeset_save_in_devmode( $data ) {
261
+ // Get the options key
262
+ $options_key = PixCustomifyPlugin()->get_options_key();
263
+ if ( ! empty( $options_key ) ) {
264
+ // Remove any Customify data thus preventing it from saving
265
+ foreach ( $data as $option_id => $value ) {
266
+ if ( false !== strpos( $option_id, $options_key ) && ! PixCustomifyPlugin()->skip_dev_mode_force_defaults( $option_id ) ) {
267
+ unset( $data[ $option_id ] );
268
+ }
269
+ }
270
+ }
271
+
272
+ return $data;
273
+ }
274
+
275
+ public function prevent_changeset_save_in_devmode_notification() { ?>
276
+ <script type="application/javascript">
277
+ (function ( $, exports, wp ) {
278
+ 'use strict';
279
+ // when the customizer is ready add our notification
280
+ wp.customize.bind('ready', function () {
281
+ wp.customize.notifications.add( 'customify_force_defaults', new wp.customize.Notification(
282
+ 'customify_force_defaults',
283
+ {
284
+ type: 'warning',
285
+ message: '<strong style="margin-bottom: ">Customify: Development Mode</strong><p>All the options are switched to default. While they are changing in the live preview, they will not be kept when you hit publish.</p>'
286
+ }
287
+ ) );
288
+ });
289
+ })(jQuery, window, wp);
290
+ </script>
291
+ <?php }
292
+
293
+
294
+
295
+ /**
296
+ * Output CSS style generated by customizer
297
+ */
298
+ function output_dynamic_style() { ?>
299
+ <style id="customify_output_style">
300
+ <?php echo $this->get_dynamic_style(); ?>
301
+ </style>
302
+ <?php
303
+
304
+ /**
305
+ * from now on we output only style tags only for the preview purpose
306
+ * so don't cry if you see 30+ style tags for each section
307
+ */
308
+ if ( ! isset( $GLOBALS['wp_customize'] ) ) {
309
+ return;
310
+ }
311
+
312
+ foreach ( PixCustomifyPlugin()->get_options_details( false, true) as $option_id => $option_details ) {
313
+
314
+ if ( isset( $option_details['type'] ) && $option_details['type'] === 'custom_background' ) {
315
+ $custom_background_output = $this->process_custom_background_field_output( $option_details ); ?>
316
+
317
+ <style id="custom_background_output_for_<?php echo sanitize_html_class( $option_id ); ?>">
318
+ <?php
319
+ if ( ! empty( $custom_background_output )) {
320
+ echo $custom_background_output;
321
+ } ?>
322
+ </style>
323
+ <?php }
324
+
325
+ if ( ! isset( $option_details['live'] ) || $option_details['live'] !== true ) {
326
+ continue;
327
+ }
328
+
329
+ if ( ! empty( $option_details['css'] ) ) {
330
+ foreach ( $option_details['css'] as $key => $properties_set ) {
331
+ // We need to use a class because we may have multiple <style>s with the same "ID" for example
332
+ // when targeting the same property but with different selectors.
333
+ $unique_class = 'dynamic_setting_' . $option_id . '_property_' . str_replace( '-', '_', $properties_set['property'] ) . '_' . $key;
334
+
335
+ $inline_style = '<style class="' . sanitize_html_class( $unique_class ) . '" type="text/css">';
336
+
337
+ if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
338
+ $inline_style .= '@media '. $properties_set['media'] . ' {';
339
+ }
340
+
341
+ if ( isset( $properties_set['selector'] ) && isset( $properties_set['property'] ) ) {
342
+ $css_output = $this->process_css_property($properties_set, $option_details['value']);
343
+ if ( ! empty( $css_output ) ) {
344
+ $inline_style .= $css_output;
345
+ }
346
+ }
347
+
348
+ if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
349
+ $inline_style .= '}';
350
+ }
351
+ $inline_style .= '</style>';
352
+
353
+ echo $inline_style;
354
+ }
355
+ }
356
+ }
357
+ }
358
+
359
+ function get_dynamic_style() {
360
+ $custom_css = '';
361
+
362
+ foreach ( PixCustomifyPlugin()->get_options_details(true) as $option_id => $option_details ) {
363
+
364
+ if ( isset( $option_details['css'] ) && ! empty( $option_details['css'] ) ) {
365
+ // now process each
366
+ $custom_css .= $this->convert_setting_to_css( $option_id, $option_details );
367
+ }
368
+
369
+ if ( isset( $option_details['type'] ) && $option_details['type'] === 'custom_background' ) {
370
+ $custom_css .= $this->process_custom_background_field_output( $option_details ) . PHP_EOL;
371
+ }
372
+ }
373
+
374
+ if ( ! empty( $this->media_queries ) ) {
375
+
376
+ foreach ( $this->media_queries as $media_query => $properties ) {
377
+
378
+ if ( empty( $properties ) ) {
379
+ continue;
380
+ }
381
+
382
+ $media_query_custom_css = '';
383
+
384
+ foreach ( $properties as $key => $property ) {
385
+ $property_settings = $property['property'];
386
+ $property_value = $property['value'];
387
+ $css_output = $this->process_css_property( $property_settings, $property_value );
388
+ if ( ! empty( $css_output ) ) {
389
+ $media_query_custom_css .= "\t" . $css_output . PHP_EOL;
390
+ }
391
+ }
392
+
393
+ if ( ! empty( $media_query_custom_css ) ) {
394
+ $media_query_custom_css = PHP_EOL . '@media ' . $media_query . " { " . PHP_EOL . PHP_EOL . $media_query_custom_css . "}" . PHP_EOL;
395
+ }
396
+
397
+ if ( ! empty( $media_query_custom_css ) ) {
398
+ $custom_css .= $media_query_custom_css;
399
+ }
400
+
401
+ }
402
+ }
403
+
404
+ return apply_filters( 'customify_dynamic_style', $custom_css );
405
+ }
406
+
407
+ protected function load_google_fonts() {
408
+ $fonts_path = PixCustomifyPlugin()->get_base_path() . 'features/customizer/controls/resources/google.fonts.php';
409
+
410
+ if ( file_exists( $fonts_path ) ) {
411
+ $this->google_fonts = require( $fonts_path );
412
+ }
413
+
414
+ if ( ! empty( $this->google_fonts ) ) {
415
+ return $this->google_fonts;
416
+ }
417
+
418
+ return false;
419
+ }
420
+
421
+ function output_typography_dynamic_style() {
422
+ $style = $this->get_typography_dynamic_style();
423
+
424
+ if ( ! empty( $style ) ) { ?>
425
+ <style id="customify_typography_output_style">
426
+ <?php echo $style; ?>
427
+ </style>
428
+ <?php }
429
+ }
430
+
431
+ function get_typography_dynamic_style() {
432
+ $output = '';
433
+
434
+ $this->get_typography_fields( PixCustomifyPlugin()->get_options_details(true), 'type', 'typography', $this->typo_settings );
435
+
436
+ if ( empty( $this->typo_settings ) ) {
437
+ return $output;
438
+ }
439
+
440
+ ob_start();
441
+ foreach ( $this->typo_settings as $font ) {
442
+ $selector = apply_filters( 'customify_typography_css_selector', $font['selector'], $font );
443
+
444
+ $load_all_weights = false;
445
+ if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
446
+ $load_all_weights = true;
447
+ }
448
+
449
+ if ( isset( $selector ) && isset( $font['value'] ) && ! empty( $font['value'] ) ) {
450
+ // Make sure that the value is in the proper format
451
+ $value = PixCustomifyPlugin::decodeURIComponent( $font['value'] );
452
+ if ( is_string( $value ) ) {
453
+ $value = json_decode( $value, true );
454
+ }
455
+
456
+ // In case the value is null (most probably because the json_decode failed),
457
+ // try the default value (mostly for google fonts)
458
+ if ( $value === null ) {
459
+ $value = $this->get_font_defaults_value( $font['value'] );
460
+ }
461
+
462
+ // Shim the old case when the default was only the font name
463
+ if ( ! empty( $value ) && is_string( $value ) ) {
464
+ $value = array( 'font_family' => $value );
465
+ }
466
+
467
+ // Handle special logic for when the $value array is not an associative array
468
+ if ( ! PixCustomifyPlugin()->is_assoc( $value ) ) {
469
+ $value = $this->standardize_non_associative_font_default( $value );
470
+ }
471
+
472
+ // Bail if empty or we don't have an array
473
+ if ( empty( $value ) || ! is_array( $value ) ) {
474
+ continue;
475
+ }
476
+
477
+ $selected_variant = '';
478
+ if ( ! empty( $value['selected_variants'] ) ) {
479
+ if ( is_array( $value['selected_variants'] ) ) {
480
+ $selected_variant = $value['selected_variants'][0];
481
+ } else {
482
+ $selected_variant = $value['selected_variants'];
483
+ }
484
+ }
485
+
486
+ // First handle the case where we have the font-family in the selected variant (usually this means a custom font from our Fonto plugin)
487
+ if ( ! empty( $selected_variant ) && is_array( $selected_variant ) && ! empty( $selected_variant['font-family'] ) ) {
488
+ // The variant's font-family
489
+ echo $selector . " {\nfont-family: " . $selected_variant['font-family'] . ";\n";
490
+
491
+ if ( ! $load_all_weights ) {
492
+ // If this is a custom font (like from our plugin Fonto) with individual styles & weights - i.e. the font-family says it all
493
+ // we need to "force" the font-weight and font-style
494
+ if ( ! empty( $value['type'] ) && 'custom_individual' == $value['type'] ) {
495
+ $selected_variant['font-weight'] = '400 !important';
496
+ $selected_variant['font-style'] = 'normal !important';
497
+ }
498
+
499
+ // Output the font weight, if available
500
+ if ( ! empty( $selected_variant['font-weight'] ) ) {
501
+ echo "font-weight: " . $selected_variant['font-weight'] . ";\n";
502
+ }
503
+
504
+ // Output the font style, if available
505
+ if ( ! empty( $selected_variant['font-style'] ) ) {
506
+ echo "font-style: " . $selected_variant['font-style'] . ";\n";
507
+ }
508
+ }
509
+
510
+ echo "}\n";
511
+ } elseif ( isset( $value['font_family'] ) ) {
512
+ // The selected font family
513
+ echo $selector . " {\n font-family: " . $value['font_family'] . ";\n";
514
+
515
+ if ( ! empty( $selected_variant ) && ! $load_all_weights ) {
516
+ $weight_and_style = strtolower( $selected_variant );
517
+
518
+ $italic_font = false;
519
+
520
+ //determine if this is an italic font (the $weight_and_style is usually like '400' or '400italic' )
521
+ if ( strpos( $weight_and_style, 'italic' ) !== false ) {
522
+ $weight_and_style = str_replace( 'italic', '', $weight_and_style);
523
+ $italic_font = true;
524
+ }
525
+
526
+ if ( ! empty( $weight_and_style ) ) {
527
+ //a little bit of sanity check - in case it's not a number
528
+ if( $weight_and_style === 'regular' ) {
529
+ $weight_and_style = 'normal';
530
+ }
531
+ echo "font-weight: " . $weight_and_style . ";\n";
532
+ }
533
+
534
+ if ( $italic_font ) {
535
+ echo "font-style: italic;\n";
536
+ }
537
+ }
538
+
539
+ echo "}\n";
540
+ }
541
+ }
542
+ }
543
+
544
+ $output = ob_get_clean();
545
+
546
+ return $output;
547
+ }
548
+
549
+ function output_typography_dynamic_script() {
550
+
551
+ $script = $this->get_typography_dynamic_script();
552
+ if ( ! empty ( $script ) ) { ?>
553
+ <script type="text/javascript">
554
+ <?php echo $script; ?>
555
+ </script>
556
+ <?php }
557
+ }
558
+
559
+ function get_typography_dynamic_script() {
560
+ $output = '';
561
+
562
+ $this->get_typography_fields( PixCustomifyPlugin()->get_options_details(true), 'type', 'typography', $this->typo_settings );
563
+
564
+ if ( empty( $this->typo_settings ) ) {
565
+ return $output;
566
+ }
567
+
568
+ $families = '';
569
+
570
+ foreach ( $this->typo_settings as $id => $font ) {
571
+ if ( isset ( $font['value'] ) ) {
572
+
573
+ $load_all_weights = false;
574
+ if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
575
+ $load_all_weights = true;
576
+ }
577
+
578
+ // shim the time when this was an array
579
+ // @todo Is this really needed? Or does it make sense?
580
+ if ( is_array( $font['value'] ) ) {
581
+ $font['value'] = stripslashes_deep( $font['value'] );
582
+ $font['value'] = json_encode( $font['value'] );
583
+ }
584
+
585
+ $value = wp_unslash( PixCustomifyPlugin::decodeURIComponent( $font['value'] ) );
586
+ if ( is_string( $value ) ) {
587
+ $value = json_decode( $value, true );
588
+ }
589
+
590
+ // In case the value is still null, try default value (mostly for google fonts)
591
+ if ( $value === null || ! is_array( $value ) ) {
592
+ $value = $this->get_font_defaults_value( str_replace( '"', '', $font['value'] ) );
593
+ }
594
+
595
+ // Bail if by this time we don't have a value of some sort
596
+ if ( empty( $value ) ) {
597
+ continue;
598
+ }
599
+
600
+ // Handle special logic for when the $value array is not an associative array
601
+ if ( ! PixCustomifyPlugin()->is_assoc( $value ) ) {
602
+ $value = $this->standardize_non_associative_font_default( $value );
603
+ }
604
+
605
+ // Bail if empty or we don't have an array
606
+ if ( empty( $value ) || ! is_array( $value ) ) {
607
+ continue;
608
+ }
609
+
610
+ if ( isset( $value['font_family'] ) && isset( $value['type'] ) && $value['type'] == 'google' ) {
611
+ $families .= "'" . $value['font_family'];
612
+
613
+ if ( $load_all_weights && is_array( $value['variants'] ) ) {
614
+ $families .= ":" . implode( ',', $value['variants'] );
615
+ } elseif ( isset( $value['selected_variants'] ) && ! empty( $value['selected_variants'] ) ) {
616
+ if ( is_array( $value['selected_variants'] ) ) {
617
+ $families .= ":" . implode( ',', $value['selected_variants'] );
618
+ } elseif ( is_string( $value['selected_variants'] ) || is_numeric( $value['selected_variants'] ) ) {
619
+ $families .= ":" . $value['selected_variants'];
620
+ }
621
+ } elseif ( isset( $value['variants'] ) && ! empty( $value['variants'] ) ) {
622
+ if ( is_array( $value['variants'] ) ) {
623
+ $families .= ":" . implode( ',', $value['variants'] );
624
+ } else {
625
+ $families .= ":" . $value['variants'];
626
+ }
627
+ }
628
+
629
+ if ( isset( $value['selected_subsets'] ) && ! empty( $value['selected_subsets'] ) ) {
630
+ if ( is_array( $value['selected_subsets'] ) ) {
631
+ $families .= ":" . implode( ',', $value['selected_subsets'] );
632
+ } else {
633
+ $families .= ":" . $value['selected_subsets'];
634
+ }
635
+ } elseif ( isset( $value['subsets'] ) && ! empty( $value['subsets'] ) ) {
636
+ if ( is_array( $value['subsets'] ) ) {
637
+ $families .= ":" . implode( ',', $value['subsets'] );
638
+ } else {
639
+ $families .= ":" . $value['subsets'];
640
+ }
641
+ }
642
+
643
+ $families .= '\',';
644
+ }
645
+ }
646
+ }
647
+
648
+ if ( ! empty ( $families ) && PixCustomifyPlugin()->settings->get_plugin_setting( 'typography', '1' )
649
+ && PixCustomifyPlugin()->settings->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
650
+ ob_start();
651
+ ?>
652
+ if (typeof WebFont !== 'undefined') {<?php // if there is a WebFont object, use it ?>
653
+ WebFont.load({
654
+ google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
655
+ classes: false,
656
+ events: false
657
+ });
658
+ } else {<?php // basically when we don't have the WebFont object we create the google script dynamically ?>
659
+
660
+ var tk = document.createElement('script');
661
+ tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
662
+ tk.type = 'text/javascript';
663
+
664
+ tk.onload = tk.onreadystatechange = function () {
665
+ WebFont.load({
666
+ google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
667
+ classes: false,
668
+ events: false
669
+ });
670
+ };
671
+
672
+ var s = document.getElementsByTagName('script')[0];
673
+ s.parentNode.insertBefore(tk, s);
674
+ }<?php
675
+ $output = ob_get_clean();
676
+ }
677
+
678
+ return $output;
679
+ }
680
+
681
+ /**
682
+ * Handle special logic for when the $value array is not an associative array
683
+ * Return a new associative array with proper keys
684
+ */
685
+ public function standardize_non_associative_font_default( $value ) {
686
+ // If the value provided is not array, simply return it
687
+ if ( ! is_array( $value ) ) {
688
+ return $value;
689
+ }
690
+
691
+ $new_value = array();
692
+
693
+ // Let's determine some type of font
694
+ if ( ! isset( $value[2] ) || 'google' == $value[2] ) {
695
+ $new_value = $this->get_font_defaults_value( $value[0] );
696
+ } else {
697
+ $new_value['type'] = $value[2];
698
+ }
699
+
700
+ if ( null == $new_value ) {
701
+ $new_value = array();
702
+ }
703
+
704
+ // The first entry is the font-family
705
+ if ( isset( $value[0] ) ) {
706
+ $new_value['font_family'] = $value[0];
707
+ }
708
+
709
+ // In case we don't have an associative array
710
+ // The second entry is the variants
711
+ if ( isset( $value[1] ) ) {
712
+ $new_value['selected_variants'] = $value[1];
713
+ }
714
+
715
+ return $new_value;
716
+ }
717
+
718
+ /**
719
+ *
720
+ * @param $font_name
721
+ *
722
+ * @return null
723
+ */
724
+ public function get_font_defaults_value( $font_name ) {
725
+
726
+ if ( empty( $this->google_fonts ) ) {
727
+ $this->load_google_fonts();
728
+ }
729
+
730
+ if ( isset( $this->google_fonts[ $font_name ] ) ) {
731
+ $value = $this->google_fonts[ $font_name ];
732
+ $value['font_family'] = $font_name;
733
+ $value['type'] = 'google';
734
+
735
+ return $value;
736
+ } elseif ( isset( $this->theme_fonts[ $font_name ] ) ) {
737
+ $value['type'] = 'theme_font';
738
+ $value['src'] = $this->theme_fonts[ $font_name ]['src'];
739
+ $value['variants'] = $this->theme_fonts[ $font_name ]['variants'];
740
+ $value['font_family'] = $this->theme_fonts[ $font_name ]['family'];
741
+
742
+ return $value;
743
+ }
744
+
745
+ return null;
746
+ }
747
+
748
+ /**
749
+ * Turn css options into a valid CSS output
750
+ *
751
+ * @param $option_id
752
+ * @param array $option_details
753
+ *
754
+ * @return string
755
+ */
756
+ protected function convert_setting_to_css( $option_id, $option_details ) {
757
+ $output = '';
758
+
759
+ if ( empty( $option_details['css'] ) ) {
760
+ return $output;
761
+ }
762
+
763
+ foreach ( $option_details['css'] as $css_property ) {
764
+
765
+ if ( isset( $css_property['media'] ) && ! empty( $css_property['media'] ) ) {
766
+ $this->media_queries[ $css_property['media'] ][ $option_id ] = array(
767
+ 'property' => $css_property,
768
+ 'value' => $option_details['value']
769
+ );
770
+ continue;
771
+ }
772
+
773
+ if ( isset( $css_property['selector'] ) && isset( $css_property['property'] ) ) {
774
+ $output .= $this->process_css_property( $css_property, $option_details['value'] );
775
+ }
776
+ }
777
+
778
+ return $output;
779
+ }
780
+
781
+ protected function process_css_property( $css_property, $value ) {
782
+ $unit = '';
783
+
784
+ if ( isset( $css_property['unit'] ) ) {
785
+ $unit = $css_property['unit'];
786
+ }
787
+
788
+ // if the unit isn't specified but the property should have a unit force 'px' as it
789
+ if ( empty( $unit ) && in_array( $css_property['property'], self::$pixel_dependent_css_properties ) ) {
790
+ $unit = 'px';
791
+ }
792
+
793
+ // lose the tons of tabs
794
+ $css_property['selector'] = trim( preg_replace( '/\t+/', '', $css_property['selector'] ) );
795
+
796
+ $css_property['selector'] = apply_filters( 'customify_css_selector', $css_property['selector'], $css_property );
797
+
798
+ if ( empty( $css_property['selector'] ) ) {
799
+ return '';
800
+ }
801
+ $property_output = $css_property['selector'] . ' { ' . $css_property['property'] . ': ' . $value . $unit . "; }" . PHP_EOL;
802
+
803
+ // Handle the value filter callback.
804
+ if ( isset( $css_property['filter_value_cb'] ) ) {
805
+ $value = $this->maybe_apply_filter( $css_property['filter_value_cb'], $value );
806
+ }
807
+
808
+ // Handle output callback.
809
+ if ( isset( $css_property['callback_filter'] ) && is_callable( $css_property['callback_filter'] ) ) {
810
+ $property_output = call_user_func( $css_property['callback_filter'], $value, $css_property['selector'], $css_property['property'], $unit );
811
+ }
812
+
813
+ return $property_output;
814
+ }
815
+
816
+ /**
817
+ * Apply a filter (config) to a value.
818
+ *
819
+ * We currently handle filters like these:
820
+ * // Elaborate filter config
821
+ * array(
822
+ * 'callback' => 'is_post_type_archive',
823
+ * // The arguments we should pass to the check function.
824
+ * // Think post types, taxonomies, or nothing if that is the case.
825
+ * // It can be an array of values or a single value.
826
+ * 'args' => array(
827
+ * 'jetpack-portfolio',
828
+ * ),
829
+ * ),
830
+ * // Simple filter - just the function name
831
+ * 'is_404',
832
+ *
833
+ * @param array|string $filter
834
+ * @param mixed $value The value to apply the filter to.
835
+ *
836
+ * @return mixed The filtered value.
837
+ */
838
+ public function maybe_apply_filter( $filter, $value ) {
839
+ // Let's get some obvious things off the table.
840
+ // On invalid data, we just return what we've received.
841
+ if ( empty( $filter ) ) {
842
+ return $value;
843
+ }
844
+
845
+ // First, we handle the shorthand version: just a function name
846
+ if ( is_string( $filter ) && is_callable( $filter ) ) {
847
+ $value = call_user_func( $filter );
848
+ } elseif ( is_array( $filter ) && ! empty( $filter['callback'] ) && is_callable( $filter['callback'] ) ) {
849
+ if ( empty( $filter['args'] ) ) {
850
+ $filter['args'] = array();
851
+ }
852
+ // The value is always the first argument.
853
+ $filter['args'] = array( $value ) + $filter['args'];
854
+
855
+ $value = call_user_func_array( $filter['callback'], $filter['args'] );
856
+ }
857
+
858
+ return $value;
859
+ }
860
+
861
+ protected function process_custom_background_field_output( $option_details ) {
862
+ $selector = $output = '';
863
+
864
+ if ( ! isset( $option_details['value'] ) ) {
865
+ return false;
866
+ }
867
+ $value = $option_details['value'];
868
+
869
+ if ( ! isset( $option_details['output'] ) ) {
870
+ return $selector;
871
+ } elseif ( is_string( $option_details['output'] ) ) {
872
+ $selector = $option_details['output'];
873
+ } elseif ( is_array( $option_details['output'] ) ) {
874
+ $selector = implode( ' ', $option_details['output'] );
875
+ }
876
+
877
+ // Loose the ton of tabs.
878
+ $selector = trim( preg_replace( '/\t+/', '', $selector ) );
879
+
880
+ $output .= $selector . " {";
881
+ if ( isset( $value['background-image'] ) && ! empty( $value['background-image'] ) ) {
882
+ $output .= "background-image: url( " . $value['background-image'] . ");";
883
+ } else {
884
+ $output .= "background-image: none;";
885
+ }
886
+
887
+ if ( isset( $value['background-repeat'] ) && ! empty( $value['background-repeat'] ) ) {
888
+ $output .= "background-repeat:" . $value['background-repeat'] . ";";
889
+ }
890
+
891
+ if ( isset( $value['background-position'] ) && ! empty( $value['background-position'] ) ) {
892
+ $output .= "background-position:" . $value['background-position'] . ";";
893
+ }
894
+
895
+ if ( isset( $value['background-size'] ) && ! empty( $value['background-size'] ) ) {
896
+ $output .= "background-size:" . $value['background-size'] . ";";
897
+ }
898
+
899
+ if ( isset( $value['background-attachment'] ) && ! empty( $value['background-attachment'] ) ) {
900
+ $output .= "background-attachment:" . $value['background-attachment'] . ";";
901
+ }
902
+ $output .= "}\n";
903
+
904
+ return $output;
905
+ }
906
+
907
+ /**
908
+ * add our customizer styling edits into the wp_editor
909
+ */
910
+ function script_to_add_customizer_settings_into_wp_editor() {
911
+
912
+ ob_start();
913
+ $this->output_typography_dynamic_script();
914
+ $this->output_typography_dynamic_style();
915
+ $this->output_dynamic_style();
916
+
917
+ $custom_css = ob_get_clean();
918
+
919
+ ob_start(); ?>
920
+ (function ($) {
921
+ $(window).load(function () {
922
+ /**
923
+ * @param iframe_id the id of the frame you want to append the style
924
+ * @param style_element the style element you want to append - boooom
925
+ */
926
+ var append_script_to_iframe = function (ifrm_id, scriptEl) {
927
+ var myIframe = document.getElementById(ifrm_id);
928
+
929
+ var script = myIframe.contentWindow.document.createElement("script");
930
+ script.type = "text/javascript";
931
+ script.innerHTML = scriptEl.innerHTML;
932
+
933
+ myIframe.contentWindow.document.head.appendChild(script);
934
+ };
935
+
936
+ var append_style_to_iframe = function (ifrm_id, styleElement) {
937
+ var ifrm = window.frames[ifrm_id];
938
+ if ( typeof ifrm === "undefined" ) {
939
+ return;
940
+ }
941
+ ifrm = ( ifrm.contentDocument || ifrm.contentDocument || ifrm.document );
942
+ var head = ifrm.getElementsByTagName('head')[0];
943
+
944
+ if (typeof styleElement !== "undefined") {
945
+ head.appendChild(styleElement);
946
+ }
947
+ };
948
+
949
+ var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
950
+ parser = new DOMParser(),
951
+ doc = parser.parseFromString(xmlString, "text/html");
952
+
953
+ if (typeof window.frames['content_ifr'] !== 'undefined') {
954
+
955
+ $.each(doc.head.childNodes, function (key, el) {
956
+ if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
957
+
958
+ switch (el.tagName) {
959
+ case 'STYLE' :
960
+ append_style_to_iframe('content_ifr', el);
961
+ break;
962
+ case 'SCRIPT' :
963
+ append_script_to_iframe('content_ifr', el);
964
+ break;
965
+ default:
966
+ break;
967
+ }
968
+ }
969
+ });
970
+ }
971
+ });
972
+ })(jQuery);
973
+ <?php
974
+ $script = ob_get_clean();
975
+ wp_add_inline_script( 'editor', $script );
976
+
977
+ }
978
+
979
+ protected function register_customizer_controls() {
980
+
981
+ // first get the base customizer extend class
982
+ require_once( PixCustomifyPlugin()->get_base_path() . 'features/customizer/class-Pix_Customize_Control.php' );
983
+
984
+ // now get all the controls
985
+ $path = PixCustomifyPlugin()->get_base_path() . 'features/customizer/controls/';
986
+ pixcustomify::require_all( $path );
987
+ }
988
+
989
+ /**
990
+ * Maybe process certain "commands" from the config.
991
+ *
992
+ * Mainly things like removing sections, controls, etc.
993
+ *
994
+ * @since 1.9.0
995
+ *
996
+ * @param WP_Customize_Manager $wp_customize
997
+ */
998
+ public function maybe_process_config_extras( $wp_customize ) {
999
+ $customizer_config = PixCustomifyPlugin()->get_customizer_config();
1000
+
1001
+ // Bail if we have no external theme config data.
1002
+ if ( empty( $customizer_config ) || ! is_array( $customizer_config ) ) {
1003
+ return;
1004
+ }
1005
+
1006
+ // Maybe remove panels
1007
+ if ( ! empty( $customizer_config['remove_panels'] ) ) {
1008
+ // Standardize it.
1009
+ if ( is_string( $customizer_config['remove_panels'] ) ) {
1010
+ $customizer_config['remove_panels'] = array( $customizer_config['remove_panels'] );
1011
+ }
1012
+
1013
+ foreach ( $customizer_config['remove_panels'] as $panel_id ) {
1014
+ $wp_customize->remove_panel( $panel_id );
1015
+ }
1016
+ }
1017
+
1018
+ // Maybe change panel props.
1019
+ if ( ! empty( $customizer_config['change_panel_props'] ) ) {
1020
+ foreach ( $customizer_config['change_panel_props'] as $panel_id => $panel_props ) {
1021
+ if ( ! is_array( $panel_props ) ) {
1022
+ continue;
1023
+ }
1024
+
1025
+ $panel = $wp_customize->get_panel( $panel_id );
1026
+ if ( empty( $panel ) || ! $panel instanceof WP_Customize_Panel ) {
1027
+ continue;
1028
+ }
1029
+
1030
+ $public_props = get_class_vars( get_class( $panel ) );
1031
+ foreach ( $panel_props as $prop_name => $prop_value ) {
1032
+
1033
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1034
+ continue;
1035
+ }
1036
+
1037
+ $panel->$prop_name = $prop_value;
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ // Maybe remove sections
1043
+ if ( ! empty( $customizer_config['remove_sections'] ) ) {
1044
+ // Standardize it.
1045
+ if ( is_string( $customizer_config['remove_sections'] ) ) {
1046
+ $customizer_config['remove_sections'] = array( $customizer_config['remove_sections'] );
1047
+ }
1048
+
1049
+ foreach ( $customizer_config['remove_sections'] as $section_id ) {
1050
+
1051
+ if ( 'widgets' === $section_id ) {
1052
+ global $wp_registered_sidebars;
1053
+
1054
+ foreach ( $wp_registered_sidebars as $widget => $settings ) {
1055
+ $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1056
+ }
1057
+ continue;
1058
+ }
1059
+
1060
+ $wp_customize->remove_section( $section_id );
1061
+ }
1062
+ }
1063
+
1064
+ // Maybe change section props.
1065
+ if ( ! empty( $customizer_config['change_section_props'] ) ) {
1066
+ foreach ( $customizer_config['change_section_props'] as $section_id => $section_props ) {
1067
+ if ( ! is_array( $section_props ) ) {
1068
+ continue;
1069
+ }
1070
+
1071
+ $section = $wp_customize->get_section( $section_id );
1072
+ if ( empty( $section ) || ! $section instanceof WP_Customize_Section ) {
1073
+ continue;
1074
+ }
1075
+
1076
+ $public_props = get_class_vars( get_class( $section ) );
1077
+ foreach ( $section_props as $prop_name => $prop_value ) {
1078
+
1079
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1080
+ continue;
1081
+ }
1082
+
1083
+ $section->$prop_name = $prop_value;
1084
+ }
1085
+ }
1086
+ }
1087
+
1088
+ // Maybe remove settings
1089
+ if ( ! empty( $customizer_config['remove_settings'] ) ) {
1090
+ // Standardize it.
1091
+ if ( is_string( $customizer_config['remove_settings'] ) ) {
1092
+ $customizer_config['remove_settings'] = array( $customizer_config['remove_settings'] );
1093
+ }
1094
+
1095
+ foreach ( $customizer_config['remove_settings'] as $setting_id ) {
1096
+ $wp_customize->remove_setting( $setting_id );
1097
+ }
1098
+ }
1099
+
1100
+ // Maybe change setting props.
1101
+ if ( ! empty( $customizer_config['change_setting_props'] ) ) {
1102
+ foreach ( $customizer_config['change_setting_props'] as $setting_id => $setting_props ) {
1103
+ if ( ! is_array( $setting_props ) ) {
1104
+ continue;
1105
+ }
1106
+
1107
+ $setting = $wp_customize->get_setting( $setting_id );
1108
+ if ( empty( $setting ) || ! $setting instanceof WP_Customize_Setting ) {
1109
+ continue;
1110
+ }
1111
+
1112
+ $public_props = get_class_vars( get_class( $setting ) );
1113
+ foreach ( $setting_props as $prop_name => $prop_value ) {
1114
+
1115
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1116
+ continue;
1117
+ }
1118
+
1119
+ $setting->$prop_name = $prop_value;
1120
+ }
1121
+ }
1122
+ }
1123
+
1124
+ // Maybe remove controls
1125
+ if ( ! empty( $customizer_config['remove_controls'] ) ) {
1126
+ // Standardize it.
1127
+ if ( is_string( $customizer_config['remove_controls'] ) ) {
1128
+ $customizer_config['remove_controls'] = array( $customizer_config['remove_controls'] );
1129
+ }
1130
+
1131
+ foreach ( $customizer_config['remove_controls'] as $control_id ) {
1132
+ $wp_customize->remove_control( $control_id );
1133
+ }
1134
+ }
1135
+
1136
+ // Maybe change control props.
1137
+ if ( ! empty( $customizer_config['change_control_props'] ) ) {
1138
+ foreach ( $customizer_config['change_control_props'] as $control_id => $control_props ) {
1139
+ if ( ! is_array( $control_props ) ) {
1140
+ continue;
1141
+ }
1142
+
1143
+ $control = $wp_customize->get_control( $control_id );
1144
+ if ( empty( $control ) || ! $control instanceof WP_Customize_Control ) {
1145
+ continue;
1146
+ }
1147
+
1148
+ $public_props = get_class_vars( get_class( $control ) );
1149
+ foreach ( $control_props as $prop_name => $prop_value ) {
1150
+
1151
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1152
+ continue;
1153
+ }
1154
+
1155
+ $control->$prop_name = $prop_value;
1156
+ }
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * @param WP_Customize_Manager $wp_customize
1163
+ */
1164
+ function register_customizer( $wp_customize ) {
1165
+
1166
+ $this->register_customizer_controls();
1167
+
1168
+ $customizer_settings = PixCustomifyPlugin()->get_customizer_config();
1169
+
1170
+ if ( ! empty ( $customizer_settings ) ) {
1171
+
1172
+ // first check the very needed options name
1173
+ if ( empty( $customizer_settings['opt-name'] ) ) {
1174
+ return;
1175
+ }
1176
+ $options_name = $customizer_settings['opt-name'];
1177
+ $wp_customize->options_key = $options_name;
1178
+
1179
+ // let's check if we have sections or panels
1180
+ if ( isset( $customizer_settings['panels'] ) && ! empty( $customizer_settings['panels'] ) ) {
1181
+
1182
+ foreach ( $customizer_settings['panels'] as $panel_id => $panel_config ) {
1183
+
1184
+ if ( ! empty( $panel_id ) && isset( $panel_config['sections'] ) && ! empty( $panel_config['sections'] ) ) {
1185
+
1186
+ // If we have been explicitly given a panel ID we will use that
1187
+ if ( ! empty( $panel_config['panel_id'] ) ) {
1188
+ $panel_id = $panel_config['panel_id'];
1189
+ } else {
1190
+ $panel_id = $options_name . '[' . $panel_id . ']';
1191
+ }
1192
+
1193
+ $panel_args = array(
1194
+ 'priority' => 10,
1195
+ 'capability' => 'edit_theme_options',
1196
+ 'title' => __( 'Panel title is required', 'customify' ),
1197
+ 'description' => __( 'Description of what this panel does.', 'customify' ),
1198
+ 'auto_expand_sole_section' => false,
1199
+ );
1200
+
1201
+ if ( isset( $panel_config['priority'] ) && ! empty( $panel_config['priority'] ) ) {
1202
+ $panel_args['priority'] = $panel_config['priority'];
1203
+ }
1204
+
1205
+ if ( isset( $panel_config['title'] ) && ! empty( $panel_config['title'] ) ) {
1206
+ $panel_args['title'] = $panel_config['title'];
1207
+ }
1208
+
1209
+ if ( isset( $panel_config['description'] ) && ! empty( $panel_config['description'] ) ) {
1210
+ $panel_args['description'] = $panel_config['description'];
1211
+ }
1212
+
1213
+ if ( isset( $panel_config['auto_expand_sole_section'] ) ) {
1214
+ $panel_args['auto_expand_sole_section'] = $panel_config['auto_expand_sole_section'];
1215
+ }
1216
+
1217
+
1218
+ $wp_customize->add_panel( $panel_id, $panel_args );
1219
+
1220
+ foreach ( $panel_config['sections'] as $section_id => $section_config ) {
1221
+ if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
1222
+ $this->register_section( $panel_id, $section_id, $options_name, $section_config, $wp_customize );
1223
+ }
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+
1229
+ if ( isset( $customizer_settings['sections'] ) && ! empty( $customizer_settings['sections'] ) ) {
1230
+
1231
+ foreach ( $customizer_settings['sections'] as $section_id => $section_config ) {
1232
+ if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
1233
+ $this->register_section( $panel_id = false, $section_id, $options_name, $section_config, $wp_customize );
1234
+ }
1235
+ }
1236
+ }
1237
+
1238
+ if ( PixCustomifyPlugin()->settings->get_plugin_setting('enable_reset_buttons') ) {
1239
+ // create a toolbar section which will be present all the time
1240
+ $reset_section_settings = array(
1241
+ 'title' => 'Customify Toolbox',
1242
+ 'capability' => 'manage_options',
1243
+ 'priority' => 999999999,
1244
+ 'options' => array(
1245
+ 'reset_all_button' => array(
1246
+ 'type' => 'button',
1247
+ 'label' => 'Reset Customify',
1248
+ 'action' => 'reset_customify',
1249
+ 'value' => 'Reset'
1250
+ ),
1251
+ )
1252
+ );
1253
+
1254
+ $wp_customize->add_section(
1255
+ 'customify_toolbar',
1256
+ $reset_section_settings
1257
+ );
1258
+
1259
+ $wp_customize->add_setting(
1260
+ 'reset_customify',
1261
+ array()
1262
+ );
1263
+ $wp_customize->add_control( new Pix_Customize_Button_Control(
1264
+ $wp_customize,
1265
+ 'reset_customify',
1266
+ array(
1267
+ 'label' => __( 'Reset All Customify Options to Default', 'customify' ),
1268
+ 'section' => 'customify_toolbar',
1269
+ 'settings' => 'reset_customify',
1270
+ 'action' => 'reset_customify',
1271
+ )
1272
+ ) );
1273
+ }
1274
+ }
1275
+
1276
+ do_action( 'customify_create_custom_control', $wp_customize );
1277
+ }
1278
+
1279
+ /**
1280
+ * @param string $panel_id
1281
+ * @param string $section_key
1282
+ * @param string $options_name
1283
+ * @param array $section_config
1284
+ * @param WP_Customize_Manager $wp_customize
1285
+ */
1286
+ protected function register_section( $panel_id, $section_key, $options_name, $section_config, $wp_customize ) {
1287
+
1288
+ // If we have been explicitly given a section ID we will use that
1289
+ if ( ! empty( $section_config['section_id'] ) ) {
1290
+ $section_id = $section_config['section_id'];
1291
+ } else {
1292
+ $section_id = $options_name . '[' . $section_key . ']';
1293
+ }
1294
+
1295
+ // Add the new section to the Customizer, but only if it is not already added.
1296
+ if ( ! $wp_customize->get_section( $section_id ) ) {
1297
+ // Merge the section settings with the defaults
1298
+ $section_args = wp_parse_args( $section_config, array(
1299
+ 'priority' => 10,
1300
+ 'panel' => $panel_id,
1301
+ 'capability' => 'edit_theme_options',
1302
+ 'theme_supports' => '',
1303
+ 'title' => esc_html__( 'Title Section is required', 'customify' ),
1304
+ 'description' => '',
1305
+ 'type' => 'default',
1306
+ 'description_hidden' => false,
1307
+ ) );
1308
+
1309
+ $wp_customize->add_section( $section_id, $section_args );
1310
+ }
1311
+
1312
+ // Now go through each section option and add the fields
1313
+ foreach ( $section_config['options'] as $option_id => $option_config ) {
1314
+
1315
+ if ( empty( $option_id ) || ! isset( $option_config['type'] ) ) {
1316
+ continue;
1317
+ }
1318
+
1319
+ // If we have been explicitly given a setting ID we will use that
1320
+ if ( ! empty( $option_config['setting_id'] ) ) {
1321
+ $setting_id = $option_config['setting_id'];
1322
+ } else {
1323
+ $setting_id = $options_name . '[' . $option_id . ']';
1324
+ }
1325
+
1326
+ // Add the option config to the localized array so we can pass the info to JS.
1327
+ // @todo Maybe we should ensure that the connected_fields configs passed here follow the same format and logic as the ones in ::customize_pane_settings_additional_data() thus maybe having the data in the same place.
1328
+ $this->localized['settings'][ $setting_id ] = $option_config;
1329
+
1330
+ // Generate a safe option ID (not the final setting ID) to us in HTML attributes like ID or class
1331
+ $this->localized['settings'][ $setting_id ]['html_safe_option_id'] = sanitize_html_class( $option_id );
1332
+
1333
+ $this->register_field( $section_id, $setting_id, $option_config, $wp_customize );
1334
+ }
1335
+
1336
+ }
1337
+
1338
+ /**
1339
+ * Register a Customizer field (setting and control).
1340
+ *
1341
+ * @see WP_Customize_Setting
1342
+ * @see WP_Customize_Control
1343
+ *
1344
+ * @param string $section_id
1345
+ * @param string $setting_id
1346
+ * @param array $field_config
1347
+ * @param WP_Customize_Manager $wp_customize
1348
+ */
1349
+ protected function register_field( $section_id, $setting_id, $field_config, $wp_customize ) {
1350
+
1351
+ $add_control = true;
1352
+ // defaults
1353
+ $setting_args = array(
1354
+ 'default' => '',
1355
+ 'capability' => 'edit_theme_options',
1356
+ 'transport' => 'refresh',
1357
+ );
1358
+ $control_args = array(
1359
+ 'priority' => 10,
1360
+ 'label' => '',
1361
+ 'section' => $section_id,
1362
+ 'settings' => $setting_id,
1363
+ );
1364
+
1365
+ // sanitize settings
1366
+ if ( ! empty( $field_config['live'] ) || $field_config['type'] === 'font' ) {
1367
+ $setting_args['transport'] = 'postMessage';
1368
+ }
1369
+
1370
+ if ( isset( $field_config['default'] ) ) {
1371
+ $setting_args['default'] = $field_config['default'];
1372
+ }
1373
+
1374
+ if ( ! empty( $field_config['capability'] ) ) {
1375
+ $setting_args['capability'] = $field_config['capability'];
1376
+ }
1377
+
1378
+ // If the setting defines it's own type we will respect that, otherwise we will follow the global plugin setting.
1379
+ if ( ! empty( $field_config['setting_type'] ) ) {
1380
+ if ( 'option' === $field_config['setting_type'] ) {
1381
+ $setting_args['type'] = 'option';
1382
+ } else {
1383
+ $setting_args['type'] = 'theme_mod';
1384
+ }
1385
+ } elseif ( PixCustomifyPlugin()->settings->get_plugin_setting('values_store_mod') === 'option' ) {
1386
+ $setting_args['type'] = 'option';
1387
+ }
1388
+
1389
+ // if we arrive here this means we have a custom field control
1390
+ switch ( $field_config['type'] ) {
1391
+
1392
+ case 'checkbox':
1393
+
1394
+ $setting_args['sanitize_callback'] = array( $this, 'setting_sanitize_checkbox' );
1395
+ break;
1396
+
1397
+ default:
1398
+ break;
1399
+ }
1400
+
1401
+ if ( ! empty( $field_config['sanitize_callback'] ) && is_callable( $field_config['sanitize_callback'] ) ) {
1402
+ $setting_args['sanitize_callback'] = $field_config['sanitize_callback'];
1403
+ }
1404
+
1405
+ // Add the setting
1406
+ $wp_customize->add_setting( $setting_id, $setting_args );
1407
+
1408
+ // Stop the control registration, if we are presented with the right type.
1409
+ if ( 'hidden_control' === $field_config['type'] ) {
1410
+ return;
1411
+ }
1412
+
1413
+ $control_args['type'] = $field_config['type'];
1414
+
1415
+ // now sanitize the control
1416
+ if ( ! empty( $field_config['label'] ) ) {
1417
+ $control_args['label'] = $field_config['label'];
1418
+ }
1419
+
1420
+ if ( ! empty( $field_config['priority'] ) ) {
1421
+ $control_args['priority'] = $field_config['priority'];
1422
+ }
1423
+
1424
+ if ( ! empty( $field_config['desc'] ) ) {
1425
+ $control_args['description'] = $field_config['desc'];
1426
+ }
1427
+
1428
+ if ( ! empty( $field_config['active_callback'] ) ) {
1429
+ $control_args['active_callback'] = $field_config['active_callback'];
1430
+ }
1431
+
1432
+ // select the control type
1433
+ // but first init a default
1434
+ $control_class_name = 'Pix_Customize_Text_Control';
1435
+
1436
+ // If is a standard wp field type call it here and skip the rest.
1437
+ if ( in_array( $field_config['type'], array(
1438
+ 'checkbox',
1439
+ 'dropdown-pages',
1440
+ 'url',
1441
+ 'date',
1442
+ 'time',
1443
+ 'datetime',
1444
+ 'week',
1445
+ 'search'
1446
+ ) ) ) {
1447
+ $wp_customize->add_control( $setting_id . '_control', $control_args );
1448
+
1449
+ return;
1450
+ } elseif ( in_array( $field_config['type'], array(
1451
+ 'radio',
1452
+ 'select'
1453
+ ) ) && ! empty( $field_config['choices'] )
1454
+ ) {
1455
+ $control_args['choices'] = $field_config['choices'];
1456
+ $wp_customize->add_control( $setting_id . '_control', $control_args );
1457
+
1458
+ return;
1459
+ } elseif ( in_array( $field_config['type'], array( 'range' ) ) && ! empty( $field_config['input_attrs'] ) ) {
1460
+
1461
+ $control_args['input_attrs'] = $field_config['input_attrs'];
1462
+
1463
+ $wp_customize->add_control( $setting_id . '_control', $control_args );
1464
+ }
1465
+
1466
+ // If we arrive here this means we have a custom field control.
1467
+ switch ( $field_config['type'] ) {
1468
+
1469
+ case 'text':
1470
+ if ( isset( $field_config['live'] ) ) {
1471
+ $control_args['live'] = $field_config['live'];
1472
+ }
1473
+
1474
+ $control_class_name = 'Pix_Customize_Text_Control';
1475
+ break;
1476
+
1477
+ case 'textarea':
1478
+ if ( isset( $field_config['live'] ) ) {
1479
+ $control_args['live'] = $field_config['live'];
1480
+ }
1481
+
1482
+ $control_class_name = 'Pix_Customize_Textarea_Control';
1483
+ break;
1484
+
1485
+ case 'color':
1486
+ $control_class_name = 'WP_Customize_Color_Control';
1487
+ break;
1488
+
1489
+ case 'color_drop':
1490
+ $control_class_name = 'Pix_Customize_Color_Drop_Control';
1491
+ break;
1492
+
1493
+ case 'ace_editor':
1494
+ if ( isset( $field_config['live'] ) ) {
1495
+ $control_args['live'] = $field_config['live'];
1496
+ }
1497
+
1498
+ if ( isset( $field_config['editor_type'] ) ) {
1499
+ $control_args['editor_type'] = $field_config['editor_type'];
1500
+ }
1501
+
1502
+ $control_class_name = 'Pix_Customize_Ace_Editor_Control';
1503
+ break;
1504
+
1505
+ case 'upload':
1506
+ $control_class_name = 'WP_Customize_Upload_Control';
1507
+ break;
1508
+
1509
+ case 'image':
1510
+ $control_class_name = 'WP_Customize_Image_Control';
1511
+ break;
1512
+
1513
+ case 'media':
1514
+ $control_class_name = 'WP_Customize_Media_Control';
1515
+ break;
1516
+
1517
+ case 'custom_background':
1518
+ if ( isset( $field_config['field'] ) ) {
1519
+ $control_args['field'] = $field_config['field'];
1520
+ }
1521
+
1522
+ $control_class_name = 'Pix_Customize_Background_Control';
1523
+ break;
1524
+
1525
+ case 'cropped_image':
1526
+ case 'cropped_media': // 'cropped_media' no longer works
1527
+ if ( isset( $field_config['width'] ) ) {
1528
+ $control_args['width'] = $field_config['width'];
1529
+ }
1530
+
1531
+ if ( isset( $field_config['height'] ) ) {
1532
+ $control_args['height'] = $field_config['height'];
1533
+ }
1534
+
1535
+ if ( isset( $field_config['flex_width'] ) ) {
1536
+ $control_args['flex_width'] = $field_config['flex_width'];
1537
+ }
1538
+
1539
+ if ( isset( $field_config['flex_height'] ) ) {
1540
+ $control_args['flex_height'] = $field_config['flex_height'];
1541
+ }
1542
+
1543
+ if ( isset( $field_config['button_labels'] ) ) {
1544
+ $control_args['button_labels'] = $field_config['button_labels'];
1545
+ }
1546
+
1547
+ $control_class_name = 'WP_Customize_Cropped_Image_Control';
1548
+ break;
1549
+
1550
+ // Custom types
1551
+ case 'typography' :
1552
+ $use_typography = PixCustomifyPlugin()->settings->get_plugin_setting( 'typography', '1' );
1553
+
1554
+ if ( $use_typography === false ) {
1555
+ $add_control = false;
1556
+ break;
1557
+ }
1558
+
1559
+ $control_class_name = 'Pix_Customize_Typography_Control';
1560
+
1561
+ if ( isset( $field_config['backup'] ) ) {
1562
+ $control_args['backup'] = $field_config['backup'];
1563
+ }
1564
+
1565
+ if ( isset( $field_config['font_weight'] ) ) {
1566
+ $control_args['font_weight'] = $field_config['font_weight'];
1567
+ }
1568
+
1569
+ if ( isset( $field_config['subsets'] ) ) {
1570
+ $control_args['subsets'] = $field_config['subsets'];
1571
+ }
1572
+
1573
+ if ( isset( $field_config['recommended'] ) ) {
1574
+ $control_args['recommended'] = array_flip( $field_config['recommended'] );
1575
+ }
1576
+
1577
+ if ( isset( $field_config['load_all_weights'] ) ) {
1578
+ $control_args['load_all_weights'] = $field_config['load_all_weights'];
1579
+ }
1580
+
1581
+ if ( isset( $field_config['default'] ) ) {
1582
+ $control_args['default'] = $field_config['default'];
1583
+ }
1584
+
1585
+ break;
1586
+
1587
+ case 'font' :
1588
+ $use_typography = PixCustomifyPlugin()->settings->get_plugin_setting( 'typography', '1' );
1589
+
1590
+ if ( $use_typography === false ) {
1591
+ $add_control = false;
1592
+ break;
1593
+ }
1594
+
1595
+ $control_class_name = 'Pix_Customize_Font_Control';
1596
+
1597
+ if ( isset( $field_config['backup'] ) ) {
1598
+ $control_args['backup'] = $field_config['backup'];
1599
+ }
1600
+
1601
+ if ( isset( $field_config['font_weight'] ) ) {
1602
+ $control_args['font_weight'] = $field_config['font_weight'];
1603
+ }
1604
+
1605
+ if ( isset( $field_config['subsets'] ) ) {
1606
+ $control_args['subsets'] = $field_config['subsets'];
1607
+ }
1608
+
1609
+ if ( isset( $field_config['recommended'] ) ) {
1610
+ $control_args['recommended'] = array_flip( $field_config['recommended'] );
1611
+ }
1612
+
1613
+ if ( isset( $field_config['load_all_weights'] ) ) {
1614
+ $control_args['load_all_weights'] = $field_config['load_all_weights'];
1615
+ }
1616
+
1617
+ if ( isset( $field_config['default'] ) ) {
1618
+ $control_args['default'] = $field_config['default'];
1619
+ }
1620
+
1621
+ if ( isset( $field_config['fields'] ) ) {
1622
+ $control_args['fields'] = $field_config['fields'];
1623
+ }
1624
+ $control_args['live'] = true;
1625
+
1626
+ break;
1627
+
1628
+ case 'select2' :
1629
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1630
+ return;
1631
+ }
1632
+
1633
+ $control_args['choices'] = $field_config['choices'];
1634
+
1635
+ $control_class_name = 'Pix_Customize_Select2_Control';
1636
+ break;
1637
+
1638
+ case 'sm_radio' :
1639
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1640
+ return;
1641
+ }
1642
+
1643
+ $control_args['choices'] = $field_config['choices'];
1644
+
1645
+ $control_class_name = 'Pix_Customize_SM_radio_Control';
1646
+ break;
1647
+
1648
+ case 'sm_palette_filter' :
1649
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1650
+ return;
1651
+ }
1652
+
1653
+ $control_args['choices'] = $field_config['choices'];
1654
+
1655
+ $control_class_name = 'Pix_Customize_SM_palette_filter_Control';
1656
+ break;
1657
+
1658
+ case 'sm_switch' :
1659
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1660
+ return;
1661
+ }
1662
+
1663
+ $control_args['choices'] = $field_config['choices'];
1664
+
1665
+ $control_class_name = 'Pix_Customize_SM_switch_Control';
1666
+ break;
1667
+
1668
+ case 'preset' :
1669
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1670
+ return;
1671
+ }
1672
+
1673
+ $control_args['choices'] = $field_config['choices'];
1674
+
1675
+ if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
1676
+ $control_args['choices_type'] = $field_config['choices_type'];
1677
+ }
1678
+
1679
+ if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
1680
+ $control_args['description'] = $field_config['desc'];
1681
+ }
1682
+
1683
+
1684
+ $control_class_name = 'Pix_Customize_Preset_Control';
1685
+ break;
1686
+
1687
+ case 'radio_image' :
1688
+ if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1689
+ return;
1690
+ }
1691
+
1692
+ $control_args['choices'] = $field_config['choices'];
1693
+
1694
+ if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
1695
+ $control_args['choices_type'] = $field_config['choices_type'];
1696
+ }
1697
+
1698
+ if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
1699
+ $control_args['description'] = $field_config['desc'];
1700
+ }
1701
+
1702
+
1703
+ $control_class_name = 'Pix_Customize_Radio_Image_Control';
1704
+ break;
1705
+
1706
+ case 'button' :
1707
+ if ( ! isset( $field_config['action'] ) || empty( $field_config['action'] ) ) {
1708
+ return;
1709
+ }
1710
+
1711
+ $control_args['action'] = $field_config['action'];
1712
+
1713
+ $control_class_name = 'Pix_Customize_Button_Control';
1714
+
1715
+ break;
1716
+
1717
+ case 'html' :
1718
+ if ( isset( $field_config['html'] ) || ! empty( $field_config['html'] ) ) {
1719
+ $control_args['html'] = $field_config['html'];
1720
+ }
1721
+
1722
+ $control_class_name = 'Pix_Customize_HTML_Control';
1723
+ break;
1724
+
1725
+ case 'import_demo_data' :
1726
+ if ( isset( $field_config['html'] ) || ! empty( $field_config['html'] ) ) {
1727
+ $control_args['html'] = $field_config['html'];
1728
+ }
1729
+
1730
+ if ( ! isset( $field_config['label'] ) || empty( $field_config['label'] ) ) {
1731
+ $control_args['label'] = esc_html__( 'Import', 'customify' );
1732
+ } else {
1733
+ $control_args['label'] = $field_config['label'];
1734
+ }
1735
+
1736
+ if ( isset( $field_config['notices'] ) && ! empty( $field_config['notices'] ) ) {
1737
+ $control_args['notices'] = $field_config['notices'];
1738
+ }
1739
+
1740
+ $control_class_name = 'Pix_Customize_Import_Demo_Data_Control';
1741
+ break;
1742
+
1743
+ default:
1744
+ // if we don't have a real control just quit, it doesn't even matter
1745
+ return;
1746
+ break;
1747
+ }
1748
+
1749
+ $this_control = new $control_class_name(
1750
+ $wp_customize,
1751
+ $setting_id . '_control',
1752
+ $control_args
1753
+ );
1754
+
1755
+ if ( $add_control ) {
1756
+ $wp_customize->add_control( $this_control );
1757
+ }
1758
+ }
1759
+
1760
+ /**
1761
+ * Remove the sections selected by user
1762
+ *
1763
+ * @param WP_Customize_Manager $wp_customize
1764
+ */
1765
+ function remove_default_sections( $wp_customize ) {
1766
+ global $wp_registered_sidebars;
1767
+
1768
+ $to_remove = PixCustomifyPlugin()->settings->get_plugin_setting( 'disable_default_sections' );
1769
+
1770
+ if ( ! empty( $to_remove ) ) {
1771
+ foreach ( $to_remove as $section => $nothing ) {
1772
+
1773
+ if ( $section === 'widgets' ) {
1774
+ foreach ( $wp_registered_sidebars as $widget => $settings ) {
1775
+ $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1776
+ }
1777
+ continue;
1778
+ }
1779
+
1780
+ $wp_customize->remove_section( $section );
1781
+ }
1782
+ }
1783
+ }
1784
+
1785
+ /**
1786
+ * Print JavaScript for adding additional data to _wpCustomizeSettings.settings object of the main window (not the preview window).
1787
+ */
1788
+ public function customize_pane_settings_additional_data() {
1789
+ /**
1790
+ * @global WP_Customize_Manager $wp_customize
1791
+ */
1792
+ global $wp_customize;
1793
+
1794
+ $options_name = PixCustomifyPlugin()->get_options_key();
1795
+ // Without an options name we can't do much.
1796
+ if ( empty( $options_name ) ) {
1797
+ return;
1798
+ }
1799
+
1800
+ $customizer_settings = $wp_customize->settings();
1801
+ ?>
1802
+ <script type="text/javascript">
1803
+ if ( 'undefined' === typeof _wpCustomizeSettings.settings ) {
1804
+ _wpCustomizeSettings.settings = {};
1805
+ }
1806
+
1807
+ <?php
1808
+ echo "(function ( sAdditional ){\n";
1809
+
1810
+ $options = PixCustomifyPlugin()->get_options_details();
1811
+ foreach ( $options as $option_id => $option_config ) {
1812
+ // If we have been explicitly given a setting ID we will use that
1813
+ if ( ! empty( $option_config['setting_id'] ) ) {
1814
+ $setting_id = $option_config['setting_id'];
1815
+ } else {
1816
+ $setting_id = $options_name . '[' . $option_id . ']';
1817
+ }
1818
+ // @todo Right now we only handle the connected_fields key - make this more dynamic by adding the keys that are not returned by WP_Customize_Setting->json()
1819
+ if ( ! empty( $customizer_settings[ $setting_id ] ) && ! empty( $option_config['connected_fields'] ) ) {
1820
+ // Pass through all the connected fields and make sure the id is in the final format
1821
+ $connected_fields = array();
1822
+ foreach ( $option_config['connected_fields'] as $key => $connected_field_config ) {
1823
+ $connected_field_data = array();
1824
+
1825
+ if ( is_string( $connected_field_config ) ) {
1826
+ $connected_field_id = $connected_field_config;
1827
+ } elseif ( is_array( $connected_field_config ) ) {
1828
+ // We have a full blown connected field config
1829
+ if ( is_string( $key ) ) {
1830
+ $connected_field_id = $key;
1831
+ } else {
1832
+ continue;
1833
+ }
1834
+
1835
+ // We will pass to JS all the configured connected field details.
1836
+ $connected_field_data = $connected_field_config;
1837
+ }
1838
+
1839
+ // Continue if we don't have a connected field ID to work with.
1840
+ if ( empty( $connected_field_id ) ) {
1841
+ continue;
1842
+ }
1843
+
1844
+ // If the connected setting is not one of our's, we will use it's ID as it is.
1845
+ if ( ! array_key_exists( $connected_field_id, $options ) ) {
1846
+ $connected_field_data['setting_id'] = $connected_field_id;
1847
+ }
1848
+ // If the connected setting specifies a setting ID, we will not prefix it and use it as it is.
1849
+ elseif ( ! empty( $options[ $connected_field_id ] ) && ! empty( $options[ $connected_field_id ]['setting_id'] ) ) {
1850
+ $connected_field_data['setting_id'] = $options[ $connected_field_id ]['setting_id'];
1851
+ } else {
1852
+ $connected_field_data['setting_id'] = $options_name . '[' . $connected_field_id . ']';
1853
+ }
1854
+
1855
+ $connected_fields[] = $connected_field_data;
1856
+ }
1857
+
1858
+ printf(
1859
+ "sAdditional[%s].%s = %s;\n",
1860
+ wp_json_encode( $setting_id ),
1861
+ 'connected_fields',
1862
+ wp_json_encode( $connected_fields, JSON_FORCE_OBJECT )
1863
+ );
1864
+ }
1865
+ }
1866
+ echo "})( _wpCustomizeSettings.settings );\n";
1867
+ ?>
1868
+ </script>
1869
+ <?php
1870
+ }
1871
+
1872
+ public function get_typography_fields( $fields_config, $key, $value, &$results, $input_key = 0 ) {
1873
+ if ( ! is_array( $fields_config ) ) {
1874
+ return;
1875
+ }
1876
+
1877
+ if ( isset( $fields_config[ $key ] ) && $fields_config[ $key ] == $value ) {
1878
+ $results[ $input_key ] = $fields_config;
1879
+
1880
+ $default = null;
1881
+
1882
+ if ( isset( $fields_config['default'] ) && is_array( $fields_config['default'] ) ) {
1883
+ $default = json_encode( $fields_config['default'] );
1884
+ }
1885
+
1886
+ $results[ $input_key ]['value'] = PixCustomifyPlugin()->get_option( $input_key, $default );
1887
+ }
1888
+
1889
+ foreach ( $fields_config as $i => $subarray ) {
1890
+ $this->get_typography_fields( $subarray, $key, $value, $results, $i );
1891
+ }
1892
+ }
1893
+
1894
+ /**
1895
+ * Sanitize functions
1896
+ */
1897
+
1898
+ /**
1899
+ * Sanitize the checkbox.
1900
+ *
1901
+ * @param boolean $input .
1902
+ *
1903
+ * @return boolean true if is 1 or '1', false if anything else
1904
+ */
1905
+ function setting_sanitize_checkbox( $input ) {
1906
+ if ( 1 == $input ) {
1907
+ return true;
1908
+ } else {
1909
+ return false;
1910
+ }
1911
+ }
1912
+
1913
+ /**
1914
+ * Main Customify_Customizer Instance
1915
+ *
1916
+ * Ensures only one instance of Customify_Customizer is loaded or can be loaded.
1917
+ *
1918
+ * @since 2.4.0
1919
+ * @static
1920
+ *
1921
+ * @return Customify_Customizer Main Customify_Customizer instance
1922
+ */
1923
+ public static function instance() {
1924
+
1925
+ if ( is_null( self::$_instance ) ) {
1926
+ self::$_instance = new self();
1927
+ }
1928
+
1929
+ return self::$_instance;
1930
+ } // End instance ()
1931
+
1932
+ /**
1933
+ * Cloning is forbidden.
1934
+ *
1935
+ * @since 2.4.0
1936
+ */
1937
+ public function __clone() {
1938
+
1939
+ _doing_it_wrong( __FUNCTION__,esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1940
+ } // End __clone ()
1941
+
1942
+ /**
1943
+ * Unserializing instances of this class is forbidden.
1944
+ *
1945
+ * @since 2.4.0
1946
+ */
1947
+ public function __wakeup() {
1948
+
1949
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1950
+ } // End __wakeup ()
1951
+ }
1952
+
1953
+ endif;
includes/class-customify-gutenberg.php CHANGED
@@ -124,7 +124,7 @@ class Customify_Gutenberg {
124
  // Styles on the front end.
125
  add_action( 'wp_enqueue_scripts', array( $this, 'frontend_styles' ), 999 );
126
 
127
- add_action( 'init', array( $this, 'editor_color_palettes' ), 20 );
128
  }
129
 
130
  /**
@@ -215,19 +215,20 @@ class Customify_Gutenberg {
215
  public function dynamic_styles() {
216
  $enqueue_parent_handle = $this->get_editor_style_handle();
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( 'wp-editor', 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( 'wp-editor', 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.
@@ -298,7 +299,7 @@ class Customify_Gutenberg {
298
  return implode( ', ', $new_selectors );
299
  }
300
 
301
- public function gutenbergify_font_css_selectors( $selectors, $font ) {
302
 
303
  // Treat the selector(s) as an array.
304
  $selectors = $this->maybeExplodeSelectors( $selectors );
@@ -395,7 +396,7 @@ class Customify_Gutenberg {
395
  return;
396
  }
397
 
398
- $options = PixCustomifyPlugin()->get_options_configs();
399
 
400
  $master_color_control_ids = Customify_Color_Palettes::instance()->get_all_master_color_controls_ids();
401
  if ( empty( $master_color_control_ids ) ) {
@@ -404,7 +405,7 @@ class Customify_Gutenberg {
404
 
405
  $editor_color_palettes = array();
406
  foreach ( $master_color_control_ids as $control_id ) {
407
- if ( empty( $options[ $control_id ] ) ) {
408
  continue;
409
  }
410
 
@@ -414,7 +415,7 @@ class Customify_Gutenberg {
414
  }
415
 
416
  $editor_color_palettes[] = array(
417
- 'name' => $options[ $control_id ]['label'],
418
  'slug' => $control_id,
419
  'color' => esc_html( $value ),
420
  );
@@ -442,7 +443,7 @@ class Customify_Gutenberg {
442
  return '';
443
  }
444
 
445
- $options = PixCustomifyPlugin()->get_options_configs();
446
 
447
  $master_color_control_ids = Customify_Color_Palettes::instance()->get_all_master_color_controls_ids();
448
  if ( empty( $master_color_control_ids ) ) {
@@ -452,7 +453,7 @@ class Customify_Gutenberg {
452
  // Build styles.
453
  $css = '';
454
  foreach ( $master_color_control_ids as $control_id ) {
455
- if ( empty( $options[ $control_id ] ) ) {
456
  continue;
457
  }
458
 
@@ -462,7 +463,7 @@ class Customify_Gutenberg {
462
  }
463
 
464
  $editor_color_palettes[] = array(
465
- 'name' => $options[ $control_id ]['label'],
466
  'slug' => $control_id,
467
  'color' => esc_html( $value ),
468
  );
124
  // Styles on the front end.
125
  add_action( 'wp_enqueue_scripts', array( $this, 'frontend_styles' ), 999 );
126
 
127
+ add_action( 'admin_init', array( $this, 'editor_color_palettes' ), 20 );
128
  }
129
 
130
  /**
215
  public function dynamic_styles() {
216
  $enqueue_parent_handle = $this->get_editor_style_handle();
217
 
218
+ if ( PixCustomifyPlugin()->settings->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( 'wp-editor', PixCustomifyPlugin()->customizer->get_typography_dynamic_script() );
221
+ wp_add_inline_style( $enqueue_parent_handle, PixCustomifyPlugin()->customizer->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
+ require_once( PixCustomifyPlugin()->get_base_path() . 'features/class-Font_Selector.php' );
226
  wp_add_inline_script( 'wp-editor', Customify_Font_Selector::instance()->get_fonts_dynamic_script() );
227
  wp_add_inline_style( $enqueue_parent_handle, Customify_Font_Selector::instance()->get_fonts_dynamic_style() );
228
  remove_filter( 'customify_font_css_selector', array( $this, 'gutenbergify_font_css_selectors' ), 10 );
229
 
230
  add_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10, 2 );
231
+ wp_add_inline_style( $enqueue_parent_handle, PixCustomifyPlugin()->customizer->get_dynamic_style() );
232
  remove_filter( 'customify_css_selector', array( $this, 'gutenbergify_css_selectors' ), 10 );
233
 
234
  // Add color palettes classes.
299
  return implode( ', ', $new_selectors );
300
  }
301
 
302
+ public function gutenbergify_font_css_selectors( $selectors ) {
303
 
304
  // Treat the selector(s) as an array.
305
  $selectors = $this->maybeExplodeSelectors( $selectors );
396
  return;
397
  }
398
 
399
+ $options_details = PixCustomifyPlugin()->get_options_configs();
400
 
401
  $master_color_control_ids = Customify_Color_Palettes::instance()->get_all_master_color_controls_ids();
402
  if ( empty( $master_color_control_ids ) ) {
405
 
406
  $editor_color_palettes = array();
407
  foreach ( $master_color_control_ids as $control_id ) {
408
+ if ( empty( $options_details[ $control_id ] ) ) {
409
  continue;
410
  }
411
 
415
  }
416
 
417
  $editor_color_palettes[] = array(
418
+ 'name' => $options_details[ $control_id ]['label'],
419
  'slug' => $control_id,
420
  'color' => esc_html( $value ),
421
  );
443
  return '';
444
  }
445
 
446
+ $options_details = PixCustomifyPlugin()->get_options_configs();
447
 
448
  $master_color_control_ids = Customify_Color_Palettes::instance()->get_all_master_color_controls_ids();
449
  if ( empty( $master_color_control_ids ) ) {
453
  // Build styles.
454
  $css = '';
455
  foreach ( $master_color_control_ids as $control_id ) {
456
+ if ( empty( $options_details[ $control_id ] ) ) {
457
  continue;
458
  }
459
 
463
  }
464
 
465
  $editor_color_palettes[] = array(
466
+ 'name' => $options_details[ $control_id ]['label'],
467
  'slug' => $control_id,
468
  'color' => esc_html( $value ),
469
  );
includes/class-customify-settings.php ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This is the class that handles the plugin settings page.
4
+ *
5
+ * @see https://pixelgrade.com
6
+ * @author Pixelgrade
7
+ * @since 2.4.0
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit; // Exit if accessed directly
12
+ }
13
+
14
+ class Customify_Settings {
15
+
16
+ /**
17
+ * Instance of this class.
18
+ * @var object
19
+ */
20
+ protected static $_instance = null;
21
+
22
+ /**
23
+ * Slug of the plugin screen.
24
+ * @var string
25
+ */
26
+ protected $plugin_screen_hook_suffix = null;
27
+
28
+ public $display_admin_menu = false;
29
+
30
+ public $plugin_settings;
31
+
32
+ /**
33
+ * The main plugin file.
34
+ * @var string
35
+ * @access public
36
+ */
37
+ public $file;
38
+
39
+ public $slug;
40
+ public $version;
41
+
42
+ private $plugin_config = array();
43
+
44
+ protected function __construct( $file, $slug, $version = '1.0.0' ) {
45
+ $this->file = $file;
46
+ $this->slug = $slug;
47
+ $this->version = $version;
48
+
49
+ require plugin_dir_path( $this->file ) . 'includes/admin-settings/core/bootstrap.php';
50
+
51
+ // Load the config file
52
+ $this->plugin_config = self::get_plugin_config();
53
+ // Load the plugin's settings from the DB
54
+ $this->plugin_settings = get_option( $this->plugin_config['settings-key'] );
55
+
56
+ // Register all the needed hooks
57
+ $this->register_hooks();
58
+ }
59
+
60
+ /**
61
+ * Register our actions and filters
62
+ */
63
+ function register_hooks() {
64
+
65
+ // Starting with the menu item for this plugin
66
+ add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );
67
+
68
+ // Add an action link pointing to the options page.
69
+ add_filter( 'plugin_action_links_' . plugin_basename( $this->file ), array( $this, 'add_action_links' ) );
70
+
71
+ // Load admin stylesheet and JavaScript.
72
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
73
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
74
+
75
+
76
+ add_action( 'rest_api_init', array( $this, 'add_rest_routes_api' ) );
77
+ }
78
+
79
+ /**
80
+ * Register the administration menu for this plugin into the WordPress Dashboard menu.
81
+ */
82
+ function add_plugin_admin_menu() {
83
+ $this->plugin_screen_hook_suffix = add_options_page( esc_html__( 'Customify', 'customify' ), esc_html__( 'Customify', 'customify' ), 'edit_plugins', $this->slug, array(
84
+ $this,
85
+ 'display_plugin_admin_page'
86
+ ) );
87
+ }
88
+
89
+ /**
90
+ * Render the settings page for this plugin.
91
+ */
92
+ function display_plugin_admin_page() {
93
+ include_once plugin_dir_path( $this->file ) . 'includes/admin-settings/views/admin.php';
94
+ }
95
+
96
+ /**
97
+ * Settings page styles
98
+ */
99
+ function enqueue_admin_styles() {
100
+
101
+ if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
102
+ return;
103
+ }
104
+
105
+ $screen = get_current_screen();
106
+ if ( $screen->id === $this->plugin_screen_hook_suffix ) {
107
+ wp_enqueue_style( $this->slug . '-admin-styles', plugins_url( 'css/admin.css', $this->file ), array(), $this->version );
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Settings page scripts
113
+ */
114
+ function enqueue_admin_scripts() {
115
+
116
+ if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
117
+ return;
118
+ }
119
+
120
+ $screen = get_current_screen();
121
+ if ( $screen->id == $this->plugin_screen_hook_suffix ) {
122
+ wp_enqueue_script( $this->slug . '-admin-script', plugins_url( 'js/admin.js', $this->file ), array( 'jquery' ), $this->version );
123
+ wp_localize_script( $this->slug . '-admin-script', 'customify_settings', array(
124
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
125
+ 'wp_rest' => array(
126
+ 'root' => esc_url_raw( rest_url() ),
127
+ 'nonce' => wp_create_nonce( 'wp_rest' ),
128
+ 'customify_settings_nonce' => wp_create_nonce( 'customify_settings_nonce' )
129
+ ),
130
+ ) );
131
+ }
132
+
133
+ wp_localize_script( $this->slug . '-customizer-scripts', 'WP_API_Settings', array(
134
+ 'root' => esc_url_raw( rest_url() ),
135
+ 'nonce' => wp_create_nonce( 'wp_rest' )
136
+ ) );
137
+ }
138
+
139
+ /**
140
+ * Add settings action link to the plugins page.
141
+ */
142
+ public function add_action_links( $links ) {
143
+ return array_merge( array( 'settings' => '<a href="' . esc_url( menu_page_url( $this->slug, false ) ) . '">' . esc_html__( 'Settings', 'customify' ) . '</a>' ), $links );
144
+ }
145
+
146
+ public function add_rest_routes_api() {
147
+ register_rest_route( 'customify/v1', '/delete_theme_mod', array(
148
+ 'methods' => 'POST',
149
+ 'callback' => array( $this, 'delete_theme_mod' ),
150
+ 'permission_callback' => array( $this, 'permission_nonce_callback' ),
151
+ ) );
152
+ }
153
+
154
+ public function delete_theme_mod() {
155
+ if ( ! current_user_can( 'manage_options' ) ) {
156
+ wp_send_json_error( esc_html__('You don\'t have admin privileges.', 'customify' ) );
157
+ }
158
+
159
+ $key = PixCustomifyPlugin()->get_options_key();
160
+
161
+ if ( empty( $key ) ) {
162
+ wp_send_json_error('no option key');
163
+ }
164
+
165
+ remove_theme_mod( $key );
166
+
167
+ wp_send_json_success('Deleted ' . $key . ' theme mod!');
168
+ }
169
+
170
+ public function permission_nonce_callback() {
171
+ return wp_verify_nonce( $this->get_nonce(), 'customify_settings_nonce' );
172
+ }
173
+
174
+ private function get_nonce() {
175
+ $nonce = null;
176
+
177
+ if ( isset( $_REQUEST['customify_settings_nonce'] ) ) {
178
+ $nonce = wp_unslash( $_REQUEST['customify_settings_nonce'] );
179
+ } elseif ( isset( $_POST['customify_settings_nonce'] ) ) {
180
+ $nonce = wp_unslash( $_POST['customify_settings_nonce'] );
181
+ }
182
+
183
+ return $nonce;
184
+ }
185
+
186
+ static public function get_plugin_config() {
187
+
188
+ $debug = false;
189
+ if ( isset( $_GET['debug'] ) && $_GET['debug'] === 'true' ) {
190
+ $debug = true;
191
+ }
192
+
193
+ return array(
194
+ 'plugin-name' => 'pixcustomify',
195
+ 'settings-key' => 'pixcustomify_settings',
196
+ 'textdomain' => 'customify',
197
+ 'template-paths' => array(
198
+ plugin_dir_path( __FILE__ ) . 'admin-settings/core/views/form-partials/',
199
+ plugin_dir_path( __FILE__ ) . 'admin-settings/views/form-partials/',
200
+ ),
201
+ 'fields' => array(
202
+ 'hiddens' => array(
203
+ 'type' => 'group',
204
+ 'options' => array(
205
+ 'settings_saved_once' => array(
206
+ 'default' => '0',
207
+ 'value' => '1',
208
+ 'type' => 'hidden',
209
+ ),
210
+ ),
211
+ ),
212
+ 'general' => array(
213
+ 'type' => 'postbox',
214
+ 'label' => esc_html__( 'General Settings', 'customify' ),
215
+ 'options' => array(
216
+ 'values_store_mod' => array(
217
+ 'name' => 'values_store_mod',
218
+ 'label' => esc_html__( 'Store values as:', 'customify' ),
219
+ 'desc' => esc_html__( 'You can store the values globally so you can use them with other themes or store them as a "theme_mod" which will make an individual set of options only for the current theme', 'customify' ),
220
+ 'default' => 'option',
221
+ 'type' => 'select',
222
+ 'options' => array(
223
+ 'option' => esc_html__( 'Option (global options)', 'customify' ),
224
+ 'theme_mod' => esc_html__( 'Theme Mod (per theme options)', 'customify' ),
225
+ ),
226
+ ),
227
+
228
+ 'disable_default_sections' => array(
229
+ 'name' => 'disable_default_sections',
230
+ 'label' => esc_html__( 'Disable default sections', 'customify' ),
231
+ 'desc' => esc_html__( 'You can disable default sections', 'customify' ),
232
+ 'type' => 'multicheckbox',
233
+ 'options' => array(
234
+ 'nav' => esc_html__( 'Navigation', 'customify' ),
235
+ 'static_front_page' => esc_html__( 'Front Page', 'customify' ),
236
+ 'title_tagline' => esc_html__( 'Title', 'customify' ),
237
+ 'colors' => esc_html__( 'Colors', 'customify' ),
238
+ 'background_image' => esc_html__( 'Background', 'customify' ),
239
+ 'header_image' => esc_html__( 'Header', 'customify' ),
240
+ 'widgets' => esc_html__( 'Widgets', 'customify' ),
241
+ ),
242
+ ),
243
+
244
+ 'enable_reset_buttons' => array(
245
+ 'name' => 'enable_reset_buttons',
246
+ 'label' => esc_html__( 'Enable Reset Buttons', 'customify' ),
247
+ 'desc' => esc_html__( 'You can enable "Reset to defaults" buttons for panels / sections or all settings. We have disabled this feature by default to avoid accidental resets. If you are sure that you need it please enable this.', 'customify' ),
248
+ 'default' => false,
249
+ 'type' => 'switch',
250
+ ),
251
+
252
+ 'enable_editor_style' => array(
253
+ 'name' => 'enable_editor_style',
254
+ 'label' => esc_html__( 'Enable Editor Style', 'customify' ),
255
+ 'desc' => esc_html__( 'The styling added by Customify in front-end can be added in the WordPress editor too by enabling this option', 'customify' ),
256
+ 'default' => true,
257
+ 'type' => 'switch',
258
+ ),
259
+ ),
260
+ ),
261
+ 'output' => array(
262
+ 'type' => 'postbox',
263
+ 'label' => esc_html__( 'Output Settings', 'customify' ),
264
+ 'options' => array(
265
+ 'style_resources_location' => array(
266
+ 'name' => 'style_resources_location',
267
+ 'label' => esc_html__( 'Styles location:', 'customify' ),
268
+ 'desc' => esc_html__( 'Here you can decide where to put your style output, in header or footer', 'customify' ),
269
+ 'default' => 'wp_footer',
270
+ 'type' => 'select',
271
+ 'options' => array(
272
+ 'wp_head' => esc_html__( 'In header (just before the head tag)', 'customify' ),
273
+ 'wp_footer' => esc_html__( 'Footer (just before the end of the body tag)', 'customify' ),
274
+ ),
275
+ ),
276
+ ),
277
+ ),
278
+ 'typography' => array(
279
+ 'type' => 'postbox',
280
+ 'label' => esc_html__( 'Typography Settings', 'customify' ),
281
+ 'options' => array(
282
+ 'typography' => array(
283
+ 'label' => esc_html__( 'Enable Typography Options', 'customify' ),
284
+ 'default' => true,
285
+ 'type' => 'switch',
286
+ 'show_group' => 'typography_group',
287
+ 'display_option' => true,
288
+ ),
289
+
290
+ 'typography_group' => array(
291
+ 'type' => 'group',
292
+ 'options' => array(
293
+ 'typography_standard_fonts' => array(
294
+ 'name' => 'typography_standard_fonts',
295
+ 'label' => esc_html__( 'Use Standard fonts:', 'customify' ),
296
+ 'desc' => esc_html__( 'Would you like them?', 'customify' ),
297
+ 'default' => true,
298
+ 'type' => 'switch',
299
+ ),
300
+ 'typography_google_fonts' => array(
301
+ 'name' => 'typography_google_fonts',
302
+ 'label' => esc_html__( 'Use Google fonts:', 'customify' ),
303
+ 'desc' => esc_html__( 'Would you like them?', 'customify' ),
304
+ 'default' => true,
305
+ 'type' => 'switch',
306
+ 'show_group' => 'typography_google_fonts_group',
307
+ 'display_option' => true,
308
+ ),
309
+ 'typography_google_fonts_group' => array(
310
+ 'type' => 'group',
311
+ 'options' => array(
312
+ 'typography_group_google_fonts' => array(
313
+ 'name' => 'typography_standard_fonts',
314
+ 'label' => esc_html__( 'Group Google fonts:', 'customify' ),
315
+ 'desc' => esc_html__( 'You can chose to see the google fonts in groups', 'customify' ),
316
+ 'default' => true,
317
+ 'type' => 'switch',
318
+ ),
319
+ ),
320
+ ),
321
+ ),
322
+ ),
323
+ ),
324
+ ),
325
+ 'tools' => array(
326
+ 'type' => 'postbox',
327
+ 'label' => esc_html__( 'Tools', 'customify' ),
328
+ 'options' => array(
329
+ 'reset_theme_mod' => array(
330
+ 'name' => 'reset_theme_mod',
331
+ 'label' => esc_html__( 'Reset', 'customify' ),
332
+ 'type' => 'reset_theme_mod',
333
+ ),
334
+ ),
335
+ ),
336
+ ),
337
+ 'processor' => array(
338
+ // callback signature: (array $input, customifyProcessor $processor)
339
+ 'preupdate' => array(
340
+ // callbacks to run before update process
341
+ // cleanup and validation has been performed on data
342
+ ),
343
+ ),
344
+ 'cleanup' => array(
345
+ 'switch' => array( 'switch_not_available' ),
346
+ ),
347
+ 'checks' => array(
348
+ 'counter' => array( 'is_numeric', 'not_empty' ),
349
+ ),
350
+ 'errors' => array(
351
+ 'not_empty' => __( 'Invalid Value.', 'customify' ),
352
+ ),
353
+ // shows exception traces on error
354
+ 'debug' => $debug,
355
+
356
+ ); # config
357
+ }
358
+
359
+ /**
360
+ * Get an option's value from the config file
361
+ *
362
+ * @param $option
363
+ * @param null $default
364
+ *
365
+ * @return bool|null
366
+ */
367
+ public function get_config_option( $option, $default = null ) {
368
+
369
+ if ( isset( $this->plugin_config[ $option ] ) ) {
370
+ return $this->plugin_config[ $option ];
371
+ } elseif ( $default !== null ) {
372
+ return $default;
373
+ }
374
+
375
+ return false;
376
+ }
377
+
378
+ public function get_plugin_setting( $option, $default = null ) {
379
+
380
+ if ( isset( $this->plugin_settings[ $option ] ) ) {
381
+ return $this->plugin_settings[ $option ];
382
+ } elseif ( $default !== null ) {
383
+ return $default;
384
+ }
385
+
386
+ return false;
387
+ }
388
+
389
+ /**
390
+ * Main Customify_Settings Instance
391
+ *
392
+ * Ensures only one instance of Customify_Settings is loaded or can be loaded.
393
+ *
394
+ * @static
395
+ *
396
+ * @param string $file File.
397
+ * @param string $slug Plugin slug.
398
+ * @param string $version Optional.
399
+ *
400
+ * @return Customify_Settings Main Customify_Settings instance
401
+ */
402
+ public static function instance( $file, $slug, $version = '1.0.0' ) {
403
+ // If the single instance hasn't been set, set it now.
404
+ if ( is_null( self::$_instance ) ) {
405
+ self::$_instance = new self( $file, $slug, $version );
406
+ }
407
+
408
+ return self::$_instance;
409
+ }
410
+
411
+ /**
412
+ * Cloning is forbidden.
413
+ *
414
+ * @since 1.5.0
415
+ */
416
+ public function __clone() {
417
+
418
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
419
+ } // End __clone ()
420
+
421
+ /**
422
+ * Unserializing instances of this class is forbidden.
423
+ *
424
+ * @since 1.5.0
425
+ */
426
+ public function __wakeup() {
427
+
428
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
429
+ } // End __wakeup ()
430
+ }
includes/class-pixcustomify.php ADDED
@@ -0,0 +1,830 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PixCustomify.
4
+ * @package PixCustomify
5
+ * @author Pixelgrade <contact@pixelgrade.com>
6
+ * @license GPL-2.0+
7
+ * @link https://pixelgrade.com
8
+ * @copyright 2014-2018 Pixelgrade
9
+ */
10
+
11
+ /**
12
+ * Plugin class.
13
+ * @package PixCustomify
14
+ * @author Pixelgrade <contact@pixelgrade.com>
15
+ */
16
+ class PixCustomifyPlugin {
17
+
18
+ /**
19
+ * Plugin version, used for cache-busting of style and script file references.
20
+ * @since 1.5.0
21
+ * @const string
22
+ */
23
+ protected $_version;
24
+ /**
25
+ * Unique identifier for your plugin.
26
+ * Use this value (not the variable name) as the text domain when internationalizing strings of text. It should
27
+ * match the Text Domain file header in the main plugin file.
28
+ * @since 1.0.0
29
+ * @var string
30
+ */
31
+ protected $plugin_slug = 'customify';
32
+
33
+ /**
34
+ * Instance of this class.
35
+ * @since 1.5.0
36
+ * @var object
37
+ */
38
+ protected static $_instance = null;
39
+
40
+ /**
41
+ * The main plugin file.
42
+ * @var string
43
+ * @access public
44
+ * @since 1.5.0
45
+ */
46
+ public $file;
47
+
48
+ /**
49
+ * Settings class object.
50
+ * @var Customify_Settings
51
+ * @access public
52
+ * @since 2.4.0
53
+ */
54
+ public $settings = null;
55
+
56
+ /**
57
+ * Customizer class object.
58
+ * @var Customify_Customizer
59
+ * @access public
60
+ * @since 2.4.0
61
+ */
62
+ public $customizer = null;
63
+
64
+ /**
65
+ * Style Manager class object.
66
+ * @var Customify_Style_Manager
67
+ * @access public
68
+ * @since 1.0.0
69
+ */
70
+ public $style_manager = null;
71
+
72
+ /**
73
+ * Gutenberg class object.
74
+ * @var Customify_Gutenberg
75
+ * @access public
76
+ * @since 2.2.0
77
+ */
78
+ public $gutenberg = null;
79
+
80
+ protected $options_minimal_details = array();
81
+ protected $options_details = array();
82
+
83
+ protected $opt_name;
84
+
85
+ protected $jetpack_default_modules = array();
86
+ protected $jetpack_blocked_modules = array();
87
+ protected $jetpack_sharing_default_options = array();
88
+
89
+ private $customizer_config = array();
90
+
91
+ /**
92
+ * Minimal Required PHP Version
93
+ * @var string
94
+ * @access private
95
+ * @since 1.5.0
96
+ */
97
+ private $minimalRequiredPhpVersion = '5.2';
98
+
99
+ protected function __construct( $file, $version = '1.0.0' ) {
100
+ //the main plugin file (the one that loads all this)
101
+ $this->file = $file;
102
+ //the current plugin version
103
+ $this->_version = $version;
104
+
105
+ if ( $this->php_version_check() ) {
106
+ // Only load and run the init function if we know PHP version can parse it.
107
+ $this->init();
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Initialize plugin
113
+ */
114
+ private function init() {
115
+ // We don't want to put extra load on the heartbeat AJAX request.
116
+ if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && 'heartbeat' === $_REQUEST['action'] ) {
117
+ return;
118
+ }
119
+
120
+ // Handle the install and uninstall logic
121
+ register_activation_hook( $this->get_file(), array( 'PixCustomifyPlugin', 'install' ) );
122
+
123
+ /* Initialize the plugin settings logic. */
124
+ require_once( $this->get_base_path() . 'includes/class-customify-settings.php' );
125
+ if ( is_null( $this->settings ) ) {
126
+ $this->settings = Customify_Settings::instance( $this->get_file(), $this->get_slug(), $this->get_version() );
127
+ }
128
+
129
+ /* Initialize the Customizer logic. */
130
+ require_once( $this->get_base_path() . 'includes/class-customify-customizer.php' );
131
+ if ( is_null( $this->customizer ) ) {
132
+ $this->customizer = Customify_Customizer::instance();
133
+ }
134
+
135
+ /* Initialize the Style Manager logic. */
136
+ require_once( $this->get_base_path() . 'includes/class-customify-style-manager.php' );
137
+ if ( is_null( $this->style_manager ) ) {
138
+ $this->style_manager = Customify_Style_Manager::instance();
139
+ }
140
+
141
+ /* Initialize the Gutenberg logic. */
142
+ require_once( $this->get_base_path() . 'includes/class-customify-gutenberg.php' );
143
+ if ( is_null( $this->gutenberg ) ) {
144
+ $this->gutenberg = Customify_Gutenberg::instance();
145
+ }
146
+
147
+ // Register all the needed hooks
148
+ $this->register_hooks();
149
+ }
150
+
151
+ /**
152
+ * Register our actions and filters
153
+ */
154
+ function register_hooks() {
155
+
156
+ /*
157
+ * Load plugin text domain
158
+ */
159
+ add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
160
+
161
+ /*
162
+ * Load the upgrade logic.
163
+ */
164
+ add_action( 'admin_init', array( $this, 'upgrade' ) );
165
+
166
+ /*
167
+ * Handle the force clearing of the caches. We clear in a proactive manner.
168
+ */
169
+ add_action( 'activated_plugin', array( $this, 'invalidate_customizer_config_cache' ), 1 );
170
+ add_action( 'activated_plugin', array( $this, 'invalidate_options_details_cache' ), 1 );
171
+ add_action( 'activated_plugin', array( $this, 'invalidate_customizer_opt_name_cache' ), 1 );
172
+
173
+ add_action( 'deactivated_plugin', array( $this, 'invalidate_customizer_config_cache' ), 1 );
174
+ add_action( 'deactivated_plugin', array( $this, 'invalidate_options_details_cache' ), 1 );
175
+ add_action( 'deactivated_plugin', array( $this, 'invalidate_customizer_opt_name_cache' ), 1 );
176
+
177
+ add_action( 'switch_theme', array( $this, 'invalidate_customizer_config_cache' ), 1 );
178
+ add_action( 'switch_theme', array( $this, 'invalidate_options_details_cache' ), 1 );
179
+ add_action( 'switch_theme', array( $this, 'invalidate_customizer_opt_name_cache' ), 1 );
180
+
181
+ add_action( 'upgrader_process_complete', array( $this, 'invalidate_customizer_config_cache' ), 1 );
182
+ add_action( 'upgrader_process_complete', array( $this, 'invalidate_options_details_cache' ), 1 );
183
+ add_action( 'upgrader_process_complete', array( $this, 'invalidate_customizer_opt_name_cache' ), 1 );
184
+
185
+ // Whenever we update data from the Customizer, we will invalidate the options details (that include the value).
186
+ add_filter( 'customize_changeset_save_data', array( $this, 'filter_invalidate_options_details_cache' ), 50, 1 );
187
+
188
+ // We also want to invalidate the cache whenever the Pixelgrade Care license is updated since it may unlock new features
189
+ // and so unlock new Customify options.
190
+ add_filter( 'pre_set_theme_mod_pixcare_license', array( $this, 'filter_invalidate_customizer_config_cache' ), 10, 1 );
191
+ add_filter( 'pre_set_theme_mod_pixcare_license', array( $this, 'filter_invalidate_options_details_cache' ), 10, 1 );
192
+ add_filter( 'pre_set_theme_mod_pixcare_license', array( $this, 'filter_invalidate_customizer_opt_name_cache' ), 10, 1 );
193
+ }
194
+
195
+ /**
196
+ * Handle the logic to upgrade between versions. It will run only one per version change.
197
+ */
198
+ public function upgrade() {
199
+ $customify_dbversion = get_option( 'customify_dbversion', '0.0.1' );
200
+ if ( $this->get_version() === $customify_dbversion ) {
201
+ return;
202
+ }
203
+
204
+ // For versions, previous of version 2.0.0 (the Color Palettes v2.0 release).
205
+ if ( version_compare( $customify_dbversion, '2.0.0', '<' ) ) {
206
+ // Delete the option holding the fact that the user offered feedback.
207
+ delete_option( 'style_manager_user_feedback_provided' );
208
+ }
209
+
210
+ // Put the current version in the database.
211
+ update_option( 'customify_dbversion', $this->get_version(), true );
212
+ }
213
+
214
+ public function get_options_key( $skip_cache = false ) {
215
+ if ( ! empty( $this->opt_name ) ) {
216
+ return $this->opt_name;
217
+ }
218
+
219
+ // First try and get the cached data
220
+ $data = get_option( $this->get_customizer_opt_name_cache_key() );
221
+
222
+ // Get the cache data expiration timestamp.
223
+ $expire_timestamp = get_option( $this->get_customizer_opt_name_cache_key() . '_timestamp' );
224
+
225
+ // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
226
+ if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
227
+
228
+ $data = $this->get_customizer_config( 'opt-name' );
229
+
230
+ // Cache the data in an option for 6 hours
231
+ update_option( $this->get_customizer_opt_name_cache_key() , $data, true );
232
+ update_option( $this->get_customizer_opt_name_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
233
+ }
234
+
235
+ $this->opt_name = $data;
236
+ return $data;
237
+ }
238
+
239
+ private function get_customizer_opt_name_cache_key() {
240
+ return 'customify_customizer_opt_name';
241
+ }
242
+
243
+ public function invalidate_customizer_opt_name_cache() {
244
+ update_option( $this->get_customizer_opt_name_cache_key() . '_timestamp' , time() - 24 * HOUR_IN_SECONDS, true );
245
+ }
246
+
247
+ public function filter_invalidate_customizer_opt_name_cache( $value ) {
248
+ $this->invalidate_customizer_opt_name_cache();
249
+
250
+ return $value;
251
+ }
252
+
253
+
254
+ public function get_options_details( $only_minimal_details = false, $skip_cache = false ) {
255
+
256
+ // If we already have the data, do as little as possible.
257
+ if ( true === $only_minimal_details && ! empty( $this->options_minimal_details ) ) {
258
+ return $this->options_minimal_details;
259
+ }
260
+
261
+ if ( ! empty( $this->options_details ) ) {
262
+ return $this->options_details;
263
+ }
264
+
265
+ // We will first look for cached data
266
+
267
+ // We don't force skip the cache for AJAX requests for performance reasons.
268
+ if ( defined('CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG' )
269
+ && true === CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG ) {
270
+ $skip_cache = true;
271
+ }
272
+
273
+ $data = $this->options_minimal_details = get_option( $this->get_options_minimal_details_cache_key() );
274
+ if ( false !== $data && false === $only_minimal_details ) {
275
+ $extra_details_data = get_option( $this->get_options_extra_details_cache_key() );
276
+ if ( is_array( $extra_details_data ) ) {
277
+ $data = $this->options_details = Customify_Array::array_merge_recursive_distinct( $data, $extra_details_data );
278
+ } else {
279
+ // Something is wrong with the extra details and we need to regenerate.
280
+ $skip_cache = true;
281
+ }
282
+ }
283
+
284
+ // For performance reasons, we will use the cached data (even if stale)
285
+ // when a user is not logged in or a user without administrative capabilities is logged in.
286
+ if ( false !== $data && false === $skip_cache && ! current_user_can( 'manage_options' ) ) {
287
+ return $data;
288
+ }
289
+
290
+ // Get the cached data expiration timestamp.
291
+ $expire_timestamp = get_option( $this->get_options_details_cache_timestamp_key() );
292
+
293
+ // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
294
+ if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
295
+ $options_minimal_details = array();
296
+ $options_extra_details = array();
297
+
298
+ $minimal_detail_keys = array(
299
+ 'type',
300
+ 'setting_type',
301
+ 'setting_id',
302
+ 'default',
303
+ 'css',
304
+ 'output',
305
+ 'value',
306
+ 'load_all_weights',
307
+ 'selector',
308
+ 'callback',
309
+ 'active_callback',
310
+ );
311
+
312
+ $customizer_config = $this->get_customizer_config();
313
+
314
+ if ( isset ( $customizer_config['panels'] ) ) {
315
+ foreach ( $customizer_config['panels'] as $pane_id => $panel_settings ) {
316
+ if ( isset( $panel_settings['sections'] ) ) {
317
+ foreach ( $panel_settings['sections'] as $section_id => $section_settings ) {
318
+ if ( isset( $section_settings['options'] ) ) {
319
+ foreach ( $section_settings['options'] as $option_id => $option_config ) {
320
+ if ( is_array( $option_config ) ) {
321
+ foreach ( $option_config as $key => $value ) {
322
+ if ( in_array( $key, $minimal_detail_keys ) ) {
323
+ $options_minimal_details[ $option_id ][ $key ] = $value;
324
+ } else {
325
+ $options_extra_details[ $option_id ][ $key ] = $value;
326
+ }
327
+ }
328
+
329
+ $options_minimal_details[ $option_id ]['value'] = $this->get_option( $option_id, null, $option_config );
330
+ }
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ }
337
+
338
+ if ( isset ( $customizer_config['sections'] ) ) {
339
+ foreach ( $customizer_config['sections'] as $section_id => $section_settings ) {
340
+ if ( isset( $section_settings['options'] ) ) {
341
+ foreach ( $section_settings['options'] as $option_id => $option_config ) {
342
+ if ( is_array( $option_config ) ) {
343
+ foreach ( $option_config as $key => $value ) {
344
+ if ( in_array( $key, $minimal_detail_keys ) ) {
345
+ $options_minimal_details[ $option_id ][ $key ] = $value;
346
+ } else {
347
+ $options_extra_details[ $option_id ][ $key ] = $value;
348
+ }
349
+ }
350
+
351
+ $options_minimal_details[ $option_id ]['value'] = $this->get_option( $option_id, null, $option_config );
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+
358
+ // Cache the data for 6 hours
359
+ update_option( $this->get_options_minimal_details_cache_key() , $options_minimal_details, true );
360
+ update_option( $this->get_options_extra_details_cache_key() , $options_extra_details, false ); // we will not autoload extra details for performance reasons.
361
+ update_option( $this->get_options_details_cache_timestamp_key(), time() + 6 * HOUR_IN_SECONDS, true );
362
+
363
+ $data = $this->options_minimal_details = $options_minimal_details;
364
+ $this->options_details = Customify_Array::array_merge_recursive_distinct( $options_minimal_details, $options_extra_details );
365
+ if ( false === $only_minimal_details ) {
366
+ $data = $this->options_details;
367
+ }
368
+ }
369
+
370
+ return $data;
371
+ }
372
+
373
+ private function get_options_minimal_details_cache_key() {
374
+ return 'customify_options_minimal_details';
375
+ }
376
+
377
+ private function get_options_extra_details_cache_key() {
378
+ return 'customify_options_extra_details';
379
+ }
380
+
381
+ private function get_options_details_cache_timestamp_key() {
382
+ return 'customify_options_details_timestamp';
383
+ }
384
+
385
+ public function invalidate_options_details_cache() {
386
+ update_option( $this->get_options_details_cache_timestamp_key(), time() - 24 * HOUR_IN_SECONDS, true );
387
+ }
388
+
389
+ public function filter_invalidate_options_details_cache( $value ) {
390
+ $this->invalidate_options_details_cache();
391
+
392
+ return $value;
393
+ }
394
+
395
+ public function has_option( $option ) {
396
+
397
+ $options_details = $this->get_options_details(true);
398
+ if ( isset( $options_details[ $option ] ) ) {
399
+ return true;
400
+ }
401
+
402
+ return false;
403
+ }
404
+
405
+ public function get_customizer_config( $key = false ) {
406
+ $customizer_config = $this->load_customizer_config();
407
+
408
+ if ( false !== $key ) {
409
+ if ( is_array( $customizer_config ) && isset( $customizer_config[ $key ] ) ) {
410
+ return $customizer_config[ $key ];
411
+ }
412
+
413
+ return null;
414
+ }
415
+
416
+ return $customizer_config;
417
+ }
418
+
419
+ /**
420
+ * Set the customizer configuration.
421
+ *
422
+ * @since 2.2.1
423
+ *
424
+ * @param bool $skip_cache Optional. Whether to use the cached config or generate a new one.
425
+ * @return array
426
+ */
427
+ protected function load_customizer_config( $skip_cache = false ) {
428
+ if ( ! empty( $this->customizer_config ) && true !== $skip_cache ) {
429
+ return $this->customizer_config;
430
+ }
431
+
432
+ // First try and get the cached data
433
+ $data = get_option( $this->get_customizer_config_cache_key() );
434
+
435
+ // We don't force skip the cache for AJAX requests for performance reasons.
436
+ if ( ! wp_doing_ajax()
437
+ && defined('CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG' )
438
+ && true === CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG ) {
439
+ $skip_cache = true;
440
+ }
441
+
442
+ // For performance reasons, we will use the cached data (even if stale)
443
+ // when a user is not logged in or a user without administrative capabilities is logged in.
444
+ if ( false !== $data && false === $skip_cache && ! current_user_can( 'manage_options' ) ) {
445
+ $this->customizer_config = $data;
446
+ return $data;
447
+ }
448
+
449
+ // Get the cache data expiration timestamp.
450
+ $expire_timestamp = get_option( $this->get_customizer_config_cache_key() . '_timestamp' );
451
+
452
+ // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
453
+ if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
454
+ // Allow themes or other plugins to filter the config.
455
+ $data = apply_filters( 'customify_filter_fields', array() );
456
+ // We apply a second filter for those that wish to work with the final config and not rely on a a huge priority number.
457
+ $data = apply_filters( 'customify_final_config', $data );
458
+
459
+ // Cache the data in an option for 6 hours
460
+ update_option( $this->get_customizer_config_cache_key() , $data, false );
461
+ update_option( $this->get_customizer_config_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
462
+ }
463
+
464
+ $this->customizer_config = $data;
465
+ return $data;
466
+ }
467
+
468
+ private function get_customizer_config_cache_key() {
469
+ return 'customify_customizer_config';
470
+ }
471
+
472
+ public function invalidate_customizer_config_cache() {
473
+ update_option( $this->get_customizer_config_cache_key() . '_timestamp' , time() - 24 * HOUR_IN_SECONDS, true );
474
+ }
475
+
476
+ /**
477
+ * Invalidate the customizer config cache, when hooked via a filter.
478
+ *
479
+ * @since 2.4.0
480
+ *
481
+ * @param mixed $value
482
+ * @return mixed
483
+ */
484
+ public function filter_invalidate_customizer_config_cache( $value ) {
485
+ $this->invalidate_customizer_config_cache();
486
+
487
+ return $value;
488
+ }
489
+
490
+ /**
491
+ * Get the Customify configuration (and value, hence "details") of a certain option.
492
+ *
493
+ * @param string $option_id
494
+ * @param bool $minimal_details Optional. Whether to return only the minimum amount of details (mainly what is needed on the frontend).
495
+ * The advantage is that these details are cached, thus skipping the customizer_config!
496
+ * @param bool $skip_cache Optional.
497
+ *
498
+ * @return array|false The option config or false on failure.
499
+ */
500
+ public function get_option_details( $option_id, $minimal_details = false, $skip_cache = false ) {
501
+ if ( empty( $option_id ) ) {
502
+ return false;
503
+ }
504
+
505
+ $options_details = $this->get_options_details( $minimal_details, $skip_cache );
506
+ if ( ! empty( $options_details ) && is_array( $options_details ) && isset( $options_details[ $option_id ] ) ) {
507
+ return $options_details[ $option_id ];
508
+ }
509
+
510
+ return false;
511
+ }
512
+
513
+ /**
514
+ * This is just a wrapper for get_options_details() for backwards compatibility.
515
+ *
516
+ * @param bool $only_minimal_details
517
+ * @param bool $skip_cache
518
+ *
519
+ * @return array|mixed|void
520
+ */
521
+ public function get_options_configs( $only_minimal_details = false, $skip_cache = false ) {
522
+ return $this->get_options_details( $only_minimal_details, $skip_cache );
523
+ }
524
+
525
+ protected function get_theme_mod_value( $setting_id ) {
526
+ global $wp_customize;
527
+
528
+ if ( empty( $setting_id ) ) {
529
+ return null;
530
+ }
531
+
532
+ if ( ! empty( $wp_customize ) && method_exists( $wp_customize, 'get_setting' ) ) {
533
+ $setting = $wp_customize->get_setting( $setting_id );
534
+ if ( ! empty( $setting ) ) {
535
+ return $setting->value();
536
+ }
537
+ }
538
+
539
+ $values = get_theme_mod( $this->get_options_key() );
540
+
541
+ if ( ! empty( $values ) && is_array( $values ) && isset( $values[ $setting_id ] ) ) {
542
+ return $values[ $setting_id ];
543
+ }
544
+
545
+ return null;
546
+ }
547
+
548
+ /**
549
+ * A public function to get an option's value.
550
+ * If there is a value and return it.
551
+ * Otherwise try to get the default parameter or the default from config.
552
+ *
553
+ * @param $option_id
554
+ * @param mixed $default Optional.
555
+ * @param array $option_details Optional.
556
+ *
557
+ * @return bool|null|string
558
+ */
559
+ public function get_option( $option_id, $default = null, $option_details = null ) {
560
+
561
+ if ( null === $option_details ) {
562
+ // Get the field config.
563
+ $option_details = $this->get_option_details( $option_id, true );
564
+ }
565
+
566
+ // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not retrieve anything from the database
567
+ // Always go with the default
568
+ if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' )
569
+ && true === CUSTOMIFY_DEV_FORCE_DEFAULTS
570
+ && ! $this->skip_dev_mode_force_defaults( $option_id, $option_details ) ) {
571
+
572
+ $value = null;
573
+ } else {
574
+
575
+ if ( empty( $option_id ) || empty( $option_details ) || ! is_array( $option_details ) ) {
576
+ $value = null;
577
+ } elseif ( isset( $option_details['value'] ) ) {
578
+ // If we already have the value cached in the option details, we will use that.
579
+ $value = $option_details['value'];
580
+ } else {
581
+ $setting_id = $this->get_options_key() . '[' . $option_id . ']';
582
+ // If we have been explicitly given a setting ID we will use that
583
+ if ( ! empty( $option_details['setting_id'] ) ) {
584
+ $setting_id = $option_details['setting_id'];
585
+ }
586
+
587
+ if ( isset( $option_details['setting_type'] ) && $option_details['setting_type'] === 'option' ) {
588
+ // We have a setting that is saved in the wp_options table, not in theme_mods.
589
+ // We will fetch it directly.
590
+ $value = get_option( $setting_id, null );
591
+ } else {
592
+ // Get the value stores in theme_mods.
593
+ $value = $this->get_theme_mod_value( $option_id );
594
+ }
595
+ }
596
+ }
597
+
598
+ // If we have a non-null value, return it.
599
+ if ( $value !== null ) {
600
+ return $value;
601
+ }
602
+
603
+ // If we have a non-null default, return it.
604
+ if ( $default !== null ) {
605
+ return $default;
606
+ }
607
+
608
+ // Finally, attempt to use the default value set in the config, if available.
609
+ if ( ! empty( $option_details ) && is_array( $option_details ) && isset( $option_details['default'] ) ) {
610
+ return $option_details['default'];
611
+ }
612
+
613
+ return null;
614
+ }
615
+
616
+ /**
617
+ * Determine if we should NOT enforce the CUSTOMIFY_DEV_FORCE_DEFAULTS behavior on a certain option.
618
+ *
619
+ * @param string $option_id
620
+ * @param array $option_config Optional.
621
+ *
622
+ * @return bool
623
+ */
624
+ public function skip_dev_mode_force_defaults( $option_id, $option_config = null ) {
625
+ // Preprocess the $option_id.
626
+ if ( false !== strpos( $option_id, '::' ) ) {
627
+ $option_id = substr( $option_id, strpos( $option_id, '::' ) + 2 );
628
+ }
629
+ if ( false !== strpos( $option_id, '[' ) ) {
630
+ $option_id = explode( '[', $option_id );
631
+ $option_id = rtrim( $option_id[1], ']' );
632
+ }
633
+
634
+ if ( null === $option_config ) {
635
+ $option_config = $this->get_option_details( $option_id, true );
636
+ }
637
+ if ( empty( $option_config ) || ! is_array( $option_config ) ) {
638
+ return false;
639
+ }
640
+
641
+ // We will skip certain field types that generally don't have a default value.
642
+ if ( ! empty( $option_config['type'] ) ) {
643
+ switch ( $option_config['type'] ) {
644
+ case 'cropped_image':
645
+ case 'cropped_media':
646
+ case 'image':
647
+ case 'media':
648
+ case 'custom_background':
649
+ case 'upload':
650
+ return true;
651
+ break;
652
+ default:
653
+ break;
654
+ }
655
+ }
656
+
657
+ return false;
658
+ }
659
+
660
+ public function get_version() {
661
+ return $this->_version;
662
+ }
663
+
664
+ public function get_slug() {
665
+ return $this->plugin_slug;
666
+ }
667
+
668
+ public function get_file() {
669
+ return $this->file;
670
+ }
671
+
672
+ public function get_base_path() {
673
+ return plugin_dir_path( $this->file );
674
+ }
675
+
676
+ /**
677
+ * Load the plugin text domain for translation.
678
+ * @since 1.0.0
679
+ */
680
+ function load_plugin_textdomain() {
681
+ $domain = $this->plugin_slug;
682
+ load_plugin_textdomain( $domain, false, basename( dirname( $this->file ) ) . '/languages/' );
683
+ }
684
+
685
+ /**
686
+ * Checks whether an array is associative or not
687
+ *
688
+ * @param array $array
689
+ *
690
+ * @return bool
691
+ */
692
+ public function is_assoc( $array ) {
693
+
694
+ if ( ! is_array( $array ) ) {
695
+ return false;
696
+ }
697
+
698
+ // Keys of the array
699
+ $keys = array_keys( $array );
700
+
701
+ // If the array keys of the keys match the keys, then the array must
702
+ // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
703
+ return array_keys( $keys ) !== $keys;
704
+ }
705
+
706
+ /**
707
+ * Does the same thing the JS encodeURIComponent() does
708
+ *
709
+ * @param string $str
710
+ *
711
+ * @return string
712
+ */
713
+ public static function encodeURIComponent( $str ) {
714
+ //if we get an array we just let it be
715
+ if ( is_string( $str ) ) {
716
+ $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')' );
717
+ $str = strtr( rawurlencode( $str ), $revert );
718
+ }
719
+
720
+ return $str;
721
+ }
722
+
723
+ /**
724
+ * Does the same thing the JS decodeURIComponent() does
725
+ *
726
+ * @param string $str
727
+ *
728
+ * @return string
729
+ */
730
+ public static function decodeURIComponent( $str ) {
731
+ // If we get an array we just let it be
732
+ if ( is_string( $str ) ) {
733
+ $revert = array( '!' => '%21', '*' => '%2A', "'" => '%27', '(' => '%28', ')' => '%29' );
734
+ $str = rawurldecode( strtr( $str, $revert ) );
735
+ }
736
+
737
+ return $str;
738
+ }
739
+
740
+ /**
741
+ * PHP version check
742
+ */
743
+ protected function php_version_check() {
744
+
745
+ if ( version_compare( phpversion(), $this->minimalRequiredPhpVersion ) < 0 ) {
746
+ add_action( 'admin_notices', array( $this, 'notice_php_version_wrong' ) );
747
+
748
+ return false;
749
+ }
750
+
751
+ return true;
752
+ }
753
+
754
+ /*
755
+ * Install everything needed
756
+ */
757
+ static public function install() {
758
+ $config = Customify_Settings::get_plugin_config();
759
+
760
+ $defaults = array(
761
+
762
+ # Hidden fields
763
+ 'settings_saved_once' => '0',
764
+ # General
765
+ 'values_store_mod' => 'theme_mod',
766
+
767
+ 'typography' => true,
768
+ 'typography_standard_fonts' => true,
769
+ 'typography_google_fonts' => true,
770
+ 'typography_group_google_fonts' => true,
771
+ 'disable_default_sections' => array(),
772
+ 'disable_customify_sections' => array(),
773
+ 'enable_reset_buttons' => false,
774
+ 'enable_editor_style' => true,
775
+ 'style_resources_location' => 'wp_head'
776
+ );
777
+
778
+ $current_data = get_option( $config['settings-key'] );
779
+
780
+ if ( $current_data === false ) {
781
+ add_option( $config['settings-key'], $defaults );
782
+ } elseif ( count( array_diff_key( $defaults, $current_data ) ) != 0) {
783
+ $plugin_data = array_merge( $defaults, $current_data );
784
+ update_option( $config['settings-key'], $plugin_data );
785
+ }
786
+ }
787
+
788
+ /**
789
+ * Main PixCustomifyPlugin Instance
790
+ *
791
+ * Ensures only one instance of PixCustomifyPlugin is loaded or can be loaded.
792
+ *
793
+ * @since 1.0.0
794
+ * @static
795
+ *
796
+ * @param string $file File.
797
+ * @param string $version Version.
798
+ *
799
+ * @see PixCustomifyPlugin()
800
+ * @return PixCustomifyPlugin Main PixCustomifyPlugin instance
801
+ */
802
+ public static function instance( $file = '', $version = '1.0.0' ) {
803
+ // If the single instance hasn't been set, set it now.
804
+ if ( is_null( self::$_instance ) ) {
805
+ self::$_instance = new self( $file, $version );
806
+ }
807
+
808
+ return self::$_instance;
809
+ }
810
+
811
+ /**
812
+ * Cloning is forbidden.
813
+ *
814
+ * @since 1.5.0
815
+ */
816
+ public function __clone() {
817
+
818
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
819
+ } // End __clone ()
820
+
821
+ /**
822
+ * Unserializing instances of this class is forbidden.
823
+ *
824
+ * @since 1.5.0
825
+ */
826
+ public function __wakeup() {
827
+
828
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
829
+ } // End __wakeup ()
830
+ }
includes/extras.php ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Extra functionality.
4
+ */
5
+
6
+ if ( ! function_exists('add_customify_base_options') ) {
7
+ /**
8
+ * This filter is used to change the Customizer Options
9
+ * You can also copy this function inside your functions.php
10
+ * file but DO NOT FORGET TO CHANGE ITS NAME
11
+ *
12
+ * @param $config array This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
13
+ * @return $config
14
+ */
15
+ function add_customify_base_options( $config ) {
16
+
17
+ $config['opt-name'] = 'customify_defaults';
18
+
19
+ $config['sections'] = array(
20
+ /**
21
+ * Presets - This section will handle other options
22
+ */
23
+ 'presets_section' => array(
24
+ 'title' => esc_html__( 'Style Presets', 'customify' ),
25
+ 'options' => array(
26
+ 'theme_style' => array(
27
+ 'type' => 'preset',
28
+ 'label' => esc_html__( 'Select a style:', 'customify' ),
29
+ 'desc' => esc_html__( 'Conveniently change the design of your site with built-in style presets. Easy as pie.', 'customify' ),
30
+ 'default' => 'royal',
31
+ 'choices_type' => 'awesome',
32
+ 'choices' => array(
33
+ 'royal' => array(
34
+ 'label' => esc_html__( 'Royal', 'customify' ),
35
+ 'preview' => array(
36
+ 'color-text' => '#ffffff',
37
+ 'background-card' => '#615375',
38
+ 'background-label' => '#46414c',
39
+ 'font-main' => 'Abril Fatface',
40
+ 'font-alt' => 'PT Serif',
41
+ ),
42
+ 'options' => array(
43
+ 'links_color' => '#8eb2c5',
44
+ 'headings_color' => '#725c92',
45
+ 'body_color' => '#6f8089',
46
+ 'page_background' => '#615375',
47
+ 'headings_font' => 'Abril Fatface',
48
+ 'body_font' => 'PT Serif',
49
+ )
50
+ ),
51
+ 'lovely' => array(
52
+ 'label' => esc_html__( 'Lovely', 'customify' ),
53
+ 'preview' => array(
54
+ 'color-text' => '#ffffff',
55
+ 'background-card' => '#d15c57',
56
+ 'background-label' => '#5c374b',
57
+ 'font-main' => 'Playfair Display',
58
+ 'font-alt' => 'Playfair Display',
59
+ ),
60
+ 'options' => array(
61
+ 'links_color' => '#cc3747',
62
+ 'headings_color' => '#d15c57',
63
+ 'body_color' => '#5c374b',
64
+ 'page_background' => '#d15c57',
65
+ 'headings_font' => 'Playfair Display',
66
+ 'body_font' => 'Playfair Display',
67
+ )
68
+ ),
69
+ 'queen' => array(
70
+ 'label' => esc_html__( 'Queen', 'customify' ),
71
+ 'preview' => array(
72
+ 'color-text' => '#fbedec',
73
+ 'background-card' => '#773347',
74
+ 'background-label' => '#41212a',
75
+ 'font-main' => 'Cinzel Decorative',
76
+ 'font-alt' => 'Gentium Basic',
77
+ ),
78
+ 'options' => array(
79
+ 'links_color' => '#cd8085',
80
+ 'headings_color' => '#54323c',
81
+ 'body_color' => '#cd8085',
82
+ 'page_background' => '#fff',
83
+ 'headings_font' => 'Cinzel Decorative',
84
+ 'body_font' => 'Gentium Basic',
85
+ )
86
+ ),
87
+ 'carrot' => array(
88
+ 'label' => esc_html__( 'Carrot', 'customify' ),
89
+ 'preview' => array(
90
+ 'color-text' => '#ffffff',
91
+ 'background-card' => '#df421d',
92
+ 'background-label' => '#85210a',
93
+ 'font-main' => 'Oswald',
94
+ 'font-alt' => 'PT Sans Narrow',
95
+ ),
96
+ 'options' => array(
97
+ 'links_color' => '#df421d',
98
+ 'headings_color' => '#df421d',
99
+ 'body_color' => '#7e7e7e',
100
+ 'page_background' => '#fff',
101
+ 'headings_font' => 'Oswald',
102
+ 'body_font' => 'PT Sans Narrow',
103
+ )
104
+ ),
105
+
106
+
107
+
108
+ 'adler' => array(
109
+ 'label' => esc_html__( 'Adler', 'customify' ),
110
+ 'preview' => array(
111
+ 'color-text' => '#fff',
112
+ 'background-card' => '#0e364f',
113
+ 'background-label' => '#000000',
114
+ 'font-main' => 'Permanent Marker',
115
+ 'font-alt' => 'Droid Sans Mono',
116
+ ),
117
+ 'options' => array(
118
+ 'links_color' => '#68f3c8',
119
+ 'headings_color' => '#0e364f',
120
+ 'body_color' => '#45525a',
121
+ 'page_background' => '#ffffff',
122
+ 'headings_font' => 'Permanent Marker',
123
+ 'body_font' => 'Droid Sans Mono'
124
+ )
125
+ ),
126
+ 'velvet' => array(
127
+ 'label' => esc_html__( 'Velvet', 'customify' ),
128
+ 'preview' => array(
129
+ 'color-text' => '#ffffff',
130
+ 'background-card' => '#282828',
131
+ 'background-label' => '#000000',
132
+ 'font-main' => 'Pinyon Script',
133
+ 'font-alt' => 'Josefin Sans',
134
+ ),
135
+ 'options' => array(
136
+ 'links_color' => '#000000',
137
+ 'headings_color' => '#000000',
138
+ 'body_color' => '#000000',
139
+ 'page_background' => '#000000',
140
+ 'headings_font' => 'Pinyon Script',
141
+ 'body_font' => 'Josefin Sans',
142
+ )
143
+ ),
144
+
145
+ )
146
+ ),
147
+ )
148
+ ),
149
+
150
+ /**
151
+ * COLORS - This section will handle different elements colors (eg. links, headings)
152
+ */
153
+ 'colors_section' => array(
154
+ 'title' => esc_html__( 'Colors', 'customify' ),
155
+ 'options' => array(
156
+ 'links_color' => array(
157
+ 'type' => 'color',
158
+ 'label' => esc_html__( 'Links Color', 'customify' ),
159
+ 'live' => true,
160
+ 'default' => '#6c6e70',
161
+ 'css' => array(
162
+ array(
163
+ 'property' => 'color',
164
+ 'selector' => 'a, .entry-meta a',
165
+ ),
166
+ )
167
+ ),
168
+ 'headings_color' => array(
169
+ 'type' => 'color',
170
+ 'label' => esc_html__( 'Headings Color', 'customify' ),
171
+ 'live' => true,
172
+ 'default' => '#0aa0d9',
173
+ 'css' => array(
174
+ array(
175
+ 'property' => 'color',
176
+ 'selector' => '.site-title a, h1, h2, h3, h4, h5, h6,
177
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a,
178
+ .widget-title,
179
+ a:hover, .entry-meta a:hover'
180
+ )
181
+ )
182
+ ),
183
+ 'body_color' => array(
184
+ 'type' => 'color',
185
+ 'label' => esc_html__( 'Body Color', 'customify' ),
186
+ 'live' => true,
187
+ 'default' => '#2d3033',
188
+ 'css' => array(
189
+ array(
190
+ 'selector' => 'body',
191
+ 'property' => 'color'
192
+ )
193
+ )
194
+ )
195
+ )
196
+ ),
197
+
198
+ /**
199
+ * FONTS - This section will handle different elements fonts (eg. headings, body)
200
+ */
201
+ 'typography_section' => array(
202
+ 'title' => esc_html__( 'Fonts', 'customify' ),
203
+ 'options' => array(
204
+ 'headings_font' => array(
205
+ 'type' => 'typography',
206
+ 'label' => esc_html__( 'Headings', 'customify' ),
207
+ 'default' => 'Playfair Display',
208
+ 'selector' => '.site-title a, h1, h2, h3, h4, h5, h6,
209
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a,
210
+ .widget-title',
211
+ 'font_weight' => true,
212
+ 'subsets' => true,
213
+ 'recommended' => array(
214
+ 'Playfair Display',
215
+ 'Oswald',
216
+ 'Lato',
217
+ 'Open Sans',
218
+ 'Exo',
219
+ 'PT Sans',
220
+ 'Ubuntu',
221
+ 'Vollkorn',
222
+ 'Lora',
223
+ 'Arvo',
224
+ 'Josefin Slab',
225
+ 'Crete Round',
226
+ 'Kreon',
227
+ 'Bubblegum Sans',
228
+ 'The Girl Next Door',
229
+ 'Pacifico',
230
+ 'Handlee',
231
+ 'Satify',
232
+ 'Pompiere'
233
+ )
234
+ ),
235
+ 'body_font' => array(
236
+ 'type' => 'typography',
237
+ 'label' => esc_html__( 'Body Text', 'customify' ),
238
+ 'default' => 'Lato',
239
+ 'selector' => 'html body',
240
+ 'load_all_weights' => true,
241
+ 'recommended' => array(
242
+ 'Lato',
243
+ 'Open Sans',
244
+ 'PT Sans',
245
+ 'Cabin',
246
+ 'Gentium Book Basic',
247
+ 'PT Serif',
248
+ 'Droid Serif'
249
+ )
250
+ )
251
+ )
252
+ ),
253
+
254
+ /**
255
+ * BACKGROUNDS - This section will handle different elements colors (eg. links, headings)
256
+ */
257
+ 'backgrounds_section' => array(
258
+ 'title' => esc_html__( 'Backgrounds', 'customify' ),
259
+ 'options' => array(
260
+ 'page_background' => array(
261
+ 'type' => 'color',
262
+ 'label' => esc_html__( 'Page Background', 'customify' ),
263
+ 'live' => true,
264
+ 'default' => '#ffffff',
265
+ 'css' => array(
266
+ array(
267
+ 'property' => 'background',
268
+ 'selector' => 'body, .site',
269
+ )
270
+ )
271
+ ),
272
+ )
273
+ ),
274
+ /**
275
+ * LAYOUTS - This section will handle different elements colors (eg. links, headings)
276
+ */
277
+ 'layout_options' => array(
278
+ 'title' => esc_html__( 'Layout', 'customify' ),
279
+ 'options' => array(
280
+ 'site_title_size' => array(
281
+ 'type' => 'range',
282
+ 'label' => 'Site Title Size',
283
+ 'live' => true,
284
+ 'input_attrs' => array(
285
+ 'min' => 24,
286
+ 'max' => 100,
287
+ 'step' => 1,
288
+ 'data-preview' => true
289
+ ),
290
+ 'default' => 24,
291
+ 'css' => array(
292
+ array(
293
+ 'property' => 'font-size',
294
+ 'selector' => '.site-title',
295
+ 'media' => 'screen and (min-width: 1000px)',
296
+ 'unit' => 'px',
297
+ )
298
+ )
299
+ ),
300
+ 'page_content_spacing' => array(
301
+ 'type' => 'range',
302
+ 'label' => 'Page Content Spacing',
303
+ 'live' => true,
304
+ 'input_attrs' => array(
305
+ 'min' => 0,
306
+ 'max' => 100,
307
+ 'step' => 1,
308
+ ),
309
+ 'default' => 18,
310
+ 'css' => array(
311
+ array(
312
+ 'property' => 'padding',
313
+ 'selector' => '.site-content',
314
+ 'media' => 'screen and (min-width: 1000px)',
315
+ 'unit' => 'px',
316
+ )
317
+ )
318
+ )
319
+ )
320
+ )
321
+ );
322
+
323
+ /**
324
+ * A self explanatory example of panels **
325
+ **/
326
+ // $config['panels'] = array(
327
+ // 'panel_id' => array(
328
+ // 'title' => esc_html__( 'Panel Title', 'customify' ),
329
+ // 'sections' => array(
330
+ // 'panel_section' => array(
331
+ // 'title' => esc_html__( 'Section Title', 'customify' ),
332
+ // 'options' => array(
333
+ // 'setting_id' => array(
334
+ // 'type' => 'color',
335
+ // 'label' => esc_html__( 'Label', 'customify' ),
336
+ // 'live' => true, // or false
337
+ // 'default' => '#6c6e70',
338
+ // 'css' => array(
339
+ // array(
340
+ // 'property' => 'color',
341
+ // 'selector' => 'a, .entry-meta a',
342
+ // ),
343
+ // )
344
+ // ),
345
+ // )
346
+ // )
347
+ // )
348
+ // )
349
+ // );
350
+
351
+ return $config;
352
+ }
353
+ }
354
+ add_filter( 'customify_filter_fields', 'add_customify_base_options', 5, 1 );
includes/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden
3
+ // Golden is deprecated
includes/lib/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden
3
+ // Golden is deprecated
js/admin.js CHANGED
@@ -1,5 +1,5 @@
1
  (function ($) {
2
- "use strict";
3
  $(function () {
4
 
5
  /**
@@ -18,68 +18,68 @@
18
  /** End Checkbox value switcher **/
19
 
20
  /* Ensure groups visibility */
21
- $('.switch input[type=checkbox], .select select').each(function(){
22
 
23
- if ( $(this).data('show_group') ) {
24
 
25
- var show = false;
26
- if ( $(this).attr('checked') ) {
 
 
27
  show = true
28
- } else if ( typeof $(this).data('display_option') !== "undefined" && $(this).data('display_option') === $(this).val() ) {
29
- show = true;
30
  }
31
 
32
- toggleGroup( $(this).data('show_group'), show);
33
  }
34
- });
35
 
36
- $('.switch, .select ').on('change', 'input[type=checkbox], select', function(){
37
- if ( $(this).data('show_group') ) {
38
- var show = false;
39
- if ( $(this).attr('checked') ) {
40
- show = true;
41
- } else if ( typeof $(this).data('display_option') !== "undefined" && $(this).data('display_option') === $(this).val() ) {
42
- show = true;
43
  }
44
- toggleGroup( $(this).data('show_group'), show);
45
  }
46
- });
47
- });
48
 
49
- var toggleGroup = function( name, show ){
50
- var $group = $( '#' + name );
51
 
52
- if ( show ) {
53
- $group.show();
54
  } else {
55
- $group.hide();
56
  }
57
- };
58
 
59
  /*
60
  * Useful functions
61
  */
62
- function check_checkbox_checked( input ){ // yes the name is an ironic
63
- if ( $(input).attr('checked') === 'checked' ) {
64
- $(input).siblings('input:hidden').val('on');
65
  } else {
66
- $(input).siblings('input:hidden').val('off');
67
  }
68
  } /* End check_checkbox_checked() */
69
 
70
- $.fn.check_for_extended_options = function() {
71
- var extended_options = $(this).siblings('fieldset.group');
72
- if ( $(this).data('show-next') ) {
73
- if ( extended_options.data('extended') === true) {
74
  extended_options
75
  .data('extended', false)
76
- .css('height', '0');
77
- } else if ( (typeof extended_options.data('extended') === 'undefined' && $(this).attr('checked') === 'checked' ) || extended_options.data('extended') === false ) {
78
  extended_options
79
  .data('extended', true)
80
- .css('height', 'auto');
81
  }
82
  }
83
- };
84
 
85
- }(jQuery));
1
  (function ($) {
2
+ 'use strict'
3
  $(function () {
4
 
5
  /**
18
  /** End Checkbox value switcher **/
19
 
20
  /* Ensure groups visibility */
21
+ $('.switch input[type=checkbox], .select select').each(function () {
22
 
23
+ if ($(this).data('show_group')) {
24
 
25
+ var show = false
26
+ if ($(this).attr('checked')) {
27
+ show = true
28
+ } else if (typeof $(this).data('display_option') !== 'undefined' && $(this).data('display_option') === $(this).val()) {
29
  show = true
 
 
30
  }
31
 
32
+ toggleGroup($(this).data('show_group'), show)
33
  }
34
+ })
35
 
36
+ $('.switch, .select ').on('change', 'input[type=checkbox], select', function () {
37
+ if ($(this).data('show_group')) {
38
+ var show = false
39
+ if ($(this).attr('checked')) {
40
+ show = true
41
+ } else if (typeof $(this).data('display_option') !== 'undefined' && $(this).data('display_option') === $(this).val()) {
42
+ show = true
43
  }
44
+ toggleGroup($(this).data('show_group'), show)
45
  }
46
+ })
47
+ })
48
 
49
+ var toggleGroup = function (name, show) {
50
+ var $group = $('#' + name)
51
 
52
+ if (show) {
53
+ $group.show()
54
  } else {
55
+ $group.hide()
56
  }
57
+ }
58
 
59
  /*
60
  * Useful functions
61
  */
62
+ function check_checkbox_checked (input) { // yes the name is an ironic
63
+ if ($(input).attr('checked') === 'checked') {
64
+ $(input).siblings('input:hidden').val('on')
65
  } else {
66
+ $(input).siblings('input:hidden').val('off')
67
  }
68
  } /* End check_checkbox_checked() */
69
 
70
+ $.fn.check_for_extended_options = function () {
71
+ var extended_options = $(this).siblings('fieldset.group')
72
+ if ($(this).data('show-next')) {
73
+ if (extended_options.data('extended') === true) {
74
  extended_options
75
  .data('extended', false)
76
+ .css('height', '0')
77
+ } else if ((typeof extended_options.data('extended') === 'undefined' && $(this).attr('checked') === 'checked') || extended_options.data('extended') === false) {
78
  extended_options
79
  .data('extended', true)
80
+ .css('height', 'auto')
81
  }
82
  }
83
+ }
84
 
85
+ }(jQuery))
js/customizer.js CHANGED
@@ -1,1263 +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
- // 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)
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/style-manager.js CHANGED
@@ -1,158 +1,157 @@
1
  (
2
- function( $, exports, wp ) {
3
- var api = wp.customize;
4
- var $window = $( window );
5
 
6
- wp.customize.bind( 'ready', function() {
7
 
8
  // Handle the Style Manager user feedback logic.
9
- var $styleManagerUserFeedbackModal = $('#style-manager-user-feedback-modal');
10
- if ( $styleManagerUserFeedbackModal.length ) {
11
- var $styleManagerUserFeedbackForm = $styleManagerUserFeedbackModal.find('form'),
12
- $styleManagerUserFeedbackCloseBtn = $styleManagerUserFeedbackModal.find('.close'),
13
- $styleManagerUserFeedbackFirstStep = $styleManagerUserFeedbackModal.find('.first-step'),
14
- $styleManagerUserFeedbackSecondStep = $styleManagerUserFeedbackModal.find('.second-step'),
15
- $styleManagerUserFeedbackThanksStep = $styleManagerUserFeedbackModal.find('.thanks-step'),
16
- $styleManagerUserFeedbackErrorStep = $styleManagerUserFeedbackModal.find('.error-step'),
17
- styleManagerUserFeedbackModalShown = false,
18
- styleManagerColorPaletteChanged = false;
19
-
20
- // Handle when to open the modal.
21
- api.bind('saved', function () {
22
- // We will only show the modal once per Customizer session.
23
- if (!styleManagerUserFeedbackModalShown && styleManagerColorPaletteChanged) {
24
- $('body').addClass('modal-open');
25
- styleManagerUserFeedbackModalShown = true;
26
- }
27
- });
28
-
29
- // Handle the color palette changed info update.
30
- const colorPaletteSetting = api( 'sm_color_palette' );
31
- if ( !_.isUndefined(colorPaletteSetting) ) {
32
- colorPaletteSetting.bind( function( new_value, old_value ) {
33
- if ( new_value != old_value ) {
34
- styleManagerColorPaletteChanged = true;
35
- }
36
- } )
37
- }
38
- const colorPaletteVariationSetting = api( 'sm_color_palette_variation' );
39
- if ( !_.isUndefined(colorPaletteVariationSetting) ) {
40
- colorPaletteVariationSetting.bind( function( new_value, old_value ) {
41
- if ( new_value != old_value ) {
42
- styleManagerColorPaletteChanged = true;
43
- }
44
- } )
45
- }
46
-
47
- // Handle the modal submit.
48
- $styleManagerUserFeedbackForm.on('submit', function (event) {
49
- event.preventDefault();
50
-
51
- let $form = $(event.target);
52
-
53
- let data = {
54
- action: 'customify_style_manager_user_feedback',
55
- nonce: customify_settings.style_manager_user_feedback_nonce,
56
- type: $form.find('input[name=type]').val(),
57
- rating: $form.find('input[name=rating]:checked').val(),
58
- message: $form.find('textarea[name=message]').val()
59
- };
60
-
61
- $.post(
62
- customify_settings.ajax_url,
63
- data,
64
- function (response) {
65
- if (true === response.success) {
66
- $styleManagerUserFeedbackFirstStep.hide();
67
- $styleManagerUserFeedbackSecondStep.hide();
68
- $styleManagerUserFeedbackThanksStep.show();
69
- $styleManagerUserFeedbackErrorStep.hide();
70
- } else {
71
- $styleManagerUserFeedbackFirstStep.hide();
72
- $styleManagerUserFeedbackSecondStep.hide();
73
- $styleManagerUserFeedbackThanksStep.hide();
74
- $styleManagerUserFeedbackErrorStep.show();
75
- }
76
- }
77
- );
78
- });
79
-
80
- $styleManagerUserFeedbackForm.find('input[name=rating]').on('change', function (event) {
81
- // Leave everything in working order
82
- setTimeout(function () {
83
- $styleManagerUserFeedbackSecondStep.show();
84
- }, 300);
85
-
86
- let rating = $styleManagerUserFeedbackForm.find('input[name=rating]:checked').val();
87
-
88
- $styleManagerUserFeedbackForm.find('.rating-placeholder').text(rating);
89
- });
90
-
91
- $styleManagerUserFeedbackCloseBtn.on('click', function (event) {
92
- event.preventDefault();
93
-
94
- $('body').removeClass('modal-open');
95
-
96
- // Leave everything in working order
97
- setTimeout(function () {
98
- $styleManagerUserFeedbackFirstStep.show();
99
- $styleManagerUserFeedbackSecondStep.hide();
100
- $styleManagerUserFeedbackThanksStep.hide();
101
- $styleManagerUserFeedbackErrorStep.hide();
102
- }, 300);
103
- });
104
- }
105
- } );
106
  }
107
- )( jQuery, window, wp );
108
-
109
 
110
  // Reverses a hex color to either black or white
111
- function customifyInverseHexColorToBlackOrWhite( hex ) {
112
- return customifyInverseHexColor( hex, true );
113
  }
114
 
115
  // Taken from here: https://stackoverflow.com/a/35970186/6260836
116
- function customifyInverseHexColor( hex, bw ) {
117
- if ( hex.indexOf( '#' ) === 0 ) {
118
- hex = hex.slice( 1 );
119
  }
120
  // convert 3-digit hex to 6-digits.
121
- if ( hex.length === 3 ) {
122
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
123
  }
124
- if ( hex.length !== 6 ) {
125
- throw new Error( 'Invalid HEX color.' );
126
  }
127
- var r = parseInt( hex.slice( 0, 2 ), 16 ),
128
- g = parseInt( hex.slice( 2, 4 ), 16 ),
129
- b = parseInt( hex.slice( 4, 6 ), 16 );
130
- if ( bw ) {
131
  // http://stackoverflow.com/a/3943023/112731
132
  return (
133
- r * 0.299 + g * 0.587 + b * 0.114
134
- ) > 186
135
  ? '#000000'
136
- : '#FFFFFF';
137
  }
138
  // invert color components
139
  r = (
140
  255 - r
141
- ).toString( 16 );
142
  g = (
143
  255 - g
144
- ).toString( 16 );
145
  b = (
146
  255 - b
147
- ).toString( 16 );
148
  // pad each with zeros and return
149
- return "#" + customifyPadZero( r ) + customifyPadZero( g ) + customifyPadZero( b );
150
  }
151
 
152
- function customifyPadZero( str, len ) {
153
- len = len || 2;
154
- var zeros = new Array( len ).join( '0' );
155
  return (
156
  zeros + str
157
- ).slice( - len );
158
  }
1
  (
2
+ function ($, exports, wp) {
3
+ var api = wp.customize
4
+ var $window = $(window)
5
 
6
+ wp.customize.bind('ready', function () {
7
 
8
  // Handle the Style Manager user feedback logic.
9
+ var $styleManagerUserFeedbackModal = $('#style-manager-user-feedback-modal')
10
+ if ($styleManagerUserFeedbackModal.length) {
11
+ var $styleManagerUserFeedbackForm = $styleManagerUserFeedbackModal.find('form'),
12
+ $styleManagerUserFeedbackCloseBtn = $styleManagerUserFeedbackModal.find('.close'),
13
+ $styleManagerUserFeedbackFirstStep = $styleManagerUserFeedbackModal.find('.first-step'),
14
+ $styleManagerUserFeedbackSecondStep = $styleManagerUserFeedbackModal.find('.second-step'),
15
+ $styleManagerUserFeedbackThanksStep = $styleManagerUserFeedbackModal.find('.thanks-step'),
16
+ $styleManagerUserFeedbackErrorStep = $styleManagerUserFeedbackModal.find('.error-step'),
17
+ styleManagerUserFeedbackModalShown = false,
18
+ styleManagerColorPaletteChanged = false
19
+
20
+ // Handle when to open the modal.
21
+ api.bind('saved', function () {
22
+ // We will only show the modal once per Customizer session.
23
+ if (!styleManagerUserFeedbackModalShown && styleManagerColorPaletteChanged) {
24
+ $('body').addClass('modal-open')
25
+ styleManagerUserFeedbackModalShown = true
26
+ }
27
+ })
28
+
29
+ // Handle the color palette changed info update.
30
+ const colorPaletteSetting = api('sm_color_palette')
31
+ if (!_.isUndefined(colorPaletteSetting)) {
32
+ colorPaletteSetting.bind(function (new_value, old_value) {
33
+ if (new_value != old_value) {
34
+ styleManagerColorPaletteChanged = true
35
+ }
36
+ })
37
+ }
38
+ const colorPaletteVariationSetting = api('sm_color_palette_variation')
39
+ if (!_.isUndefined(colorPaletteVariationSetting)) {
40
+ colorPaletteVariationSetting.bind(function (new_value, old_value) {
41
+ if (new_value != old_value) {
42
+ styleManagerColorPaletteChanged = true
43
+ }
44
+ })
45
+ }
46
+
47
+ // Handle the modal submit.
48
+ $styleManagerUserFeedbackForm.on('submit', function (event) {
49
+ event.preventDefault()
50
+
51
+ let $form = $(event.target)
52
+
53
+ let data = {
54
+ action: 'customify_style_manager_user_feedback',
55
+ nonce: customify_settings.style_manager_user_feedback_nonce,
56
+ type: $form.find('input[name=type]').val(),
57
+ rating: $form.find('input[name=rating]:checked').val(),
58
+ message: $form.find('textarea[name=message]').val()
59
+ }
60
+
61
+ $.post(
62
+ customify_settings.ajax_url,
63
+ data,
64
+ function (response) {
65
+ if (true === response.success) {
66
+ $styleManagerUserFeedbackFirstStep.hide()
67
+ $styleManagerUserFeedbackSecondStep.hide()
68
+ $styleManagerUserFeedbackThanksStep.show()
69
+ $styleManagerUserFeedbackErrorStep.hide()
70
+ } else {
71
+ $styleManagerUserFeedbackFirstStep.hide()
72
+ $styleManagerUserFeedbackSecondStep.hide()
73
+ $styleManagerUserFeedbackThanksStep.hide()
74
+ $styleManagerUserFeedbackErrorStep.show()
75
+ }
76
+ }
77
+ )
78
+ })
79
+
80
+ $styleManagerUserFeedbackForm.find('input[name=rating]').on('change', function (event) {
81
+ // Leave everything in working order
82
+ setTimeout(function () {
83
+ $styleManagerUserFeedbackSecondStep.show()
84
+ }, 300)
85
+
86
+ let rating = $styleManagerUserFeedbackForm.find('input[name=rating]:checked').val()
87
+
88
+ $styleManagerUserFeedbackForm.find('.rating-placeholder').text(rating)
89
+ })
90
+
91
+ $styleManagerUserFeedbackCloseBtn.on('click', function (event) {
92
+ event.preventDefault()
93
+
94
+ $('body').removeClass('modal-open')
95
+
96
+ // Leave everything in working order
97
+ setTimeout(function () {
98
+ $styleManagerUserFeedbackFirstStep.show()
99
+ $styleManagerUserFeedbackSecondStep.hide()
100
+ $styleManagerUserFeedbackThanksStep.hide()
101
+ $styleManagerUserFeedbackErrorStep.hide()
102
+ }, 300)
103
+ })
104
+ }
105
+ })
106
  }
107
+ )(jQuery, window, wp)
 
108
 
109
  // Reverses a hex color to either black or white
110
+ function customifyInverseHexColorToBlackOrWhite (hex) {
111
+ return customifyInverseHexColor(hex, true)
112
  }
113
 
114
  // Taken from here: https://stackoverflow.com/a/35970186/6260836
115
+ function customifyInverseHexColor (hex, bw) {
116
+ if (hex.indexOf('#') === 0) {
117
+ hex = hex.slice(1)
118
  }
119
  // convert 3-digit hex to 6-digits.
120
+ if (hex.length === 3) {
121
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
122
  }
123
+ if (hex.length !== 6) {
124
+ throw new Error('Invalid HEX color.')
125
  }
126
+ var r = parseInt(hex.slice(0, 2), 16),
127
+ g = parseInt(hex.slice(2, 4), 16),
128
+ b = parseInt(hex.slice(4, 6), 16)
129
+ if (bw) {
130
  // http://stackoverflow.com/a/3943023/112731
131
  return (
132
+ r * 0.299 + g * 0.587 + b * 0.114
133
+ ) > 186
134
  ? '#000000'
135
+ : '#FFFFFF'
136
  }
137
  // invert color components
138
  r = (
139
  255 - r
140
+ ).toString(16)
141
  g = (
142
  255 - g
143
+ ).toString(16)
144
  b = (
145
  255 - b
146
+ ).toString(16)
147
  // pad each with zeros and return
148
+ return '#' + customifyPadZero(r) + customifyPadZero(g) + customifyPadZero(b)
149
  }
150
 
151
+ function customifyPadZero (str, len) {
152
+ len = len || 2
153
+ var zeros = new Array(len).join('0')
154
  return (
155
  zeros + str
156
+ ).slice(-len)
157
  }
js/customizer_preview.js CHANGED
@@ -1,338 +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
- /*** 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)
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)
readme.txt CHANGED
@@ -1,13 +1,13 @@
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
6
- Stable tag: 2.3.2
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
- Customify is a Theme Customizer Booster that you can easily use to customizer Fonts, Colors, Live CSS Editor and other options for your site.
11
 
12
  == Description ==
13
 
@@ -45,6 +45,12 @@ With [Customify](https://github.com/pixelgrade/customify), developers can easily
45
 
46
  == Changelog ==
47
 
 
 
 
 
 
 
48
  = 2.3.2 =
49
  * Fixed the fact that Customizer style changes were not reflected in the live preview. A problem introduced in the previous update.
50
 
1
  === Customify - A Theme Customizer Booster ===
2
  Contributors: pixelgrade, euthelup, babbardel, vlad.olaru, raduconstantin, razvanonofrei
3
+ Tags: customizer, css, editor, gutenberg, live, preview, customizer
4
+ Requires at least: 4.9.9
5
+ Tested up to: 5.2.0
6
+ Stable tag: 2.3.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
+ Customify is a Theme Customizer Booster to easily customize Fonts, Colors, and other options for your site.
11
 
12
  == Description ==
13
 
45
 
46
  == Changelog ==
47
 
48
+ = 2.3.4 =
49
+ * Fixed warnings that were appearing when PHP has version 7.2.0+.
50
+
51
+ = 2.3.3 =
52
+ * Fixed Google Fonts not working in the new block editor (Gutenberg).
53
+
54
  = 2.3.2 =
55
  * Fixed the fact that Customizer style changes were not reflected in the live preview. A problem introduced in the previous update.
56