Unyson - Version 2.1.22

Version Description

  • Theme Settings form ajax submit #198
  • Added javascript helper fw.soleModal()
  • Minor fixes
Download this release

Release Info

Developer Unyson
Plugin Icon 128x128 Unyson
Version 2.1.22
Comparing to
See all releases

Code changes from version 2.1.21 to 2.1.22

framework/core/components/backend.php CHANGED
@@ -245,6 +245,10 @@ final class _FW_Component_Backend
245
  wp_localize_script('fw', '_fw_localized', array(
246
  'FW_URI' => fw_get_framework_directory_uri(),
247
  'SITE_URI' => site_url(),
 
 
 
 
248
  ));
249
  }
250
 
@@ -871,7 +875,7 @@ final class _FW_Component_Backend
871
  if (!empty($_POST['_fw_reset_options'])) { // The "Reset" button was pressed
872
  fw_set_db_settings_option(null, array());
873
 
874
- FW_Flash_Messages::add($flash_id, __('The options were successfully reset', 'fw'), 'info');
875
 
876
  do_action('fw_settings_form_reset', $old_values);
877
  } else { // The "Save" button was pressed
245
  wp_localize_script('fw', '_fw_localized', array(
246
  'FW_URI' => fw_get_framework_directory_uri(),
247
  'SITE_URI' => site_url(),
248
+ 'l10n' => array(
249
+ 'done' => __('Done', 'fw'),
250
+ 'ah_sorry' => __('Ah, Sorry', 'fw'),
251
+ ),
252
  ));
253
  }
254
 
875
  if (!empty($_POST['_fw_reset_options'])) { // The "Reset" button was pressed
876
  fw_set_db_settings_option(null, array());
877
 
878
+ FW_Flash_Messages::add($flash_id, __('The options were successfully reset', 'fw'), 'success');
879
 
880
  do_action('fw_settings_form_reset', $old_values);
881
  } else { // The "Save" button was pressed
framework/core/components/extensions/manager/class--fw-extensions-manager.php CHANGED
@@ -501,12 +501,14 @@ final class _FW_Extensions_Manager
501
  $collected = $this->get_extensions_for_activation($ext_name);
502
 
503
  if (is_wp_error($collected)) {
504
- FW_Flash_Messages::add('fw_ext_auto_activate_hidden_standalone',
505
- sprintf(__('Cannot activate hidden standalone extension %s', 'fw'),
506
- fw_akg( 'name', $ext_data['manifest'], fw_id_to_title( $ext_name ) )
507
- ),
508
- 'error'
509
- );
 
 
510
  return;
511
  }
512
 
501
  $collected = $this->get_extensions_for_activation($ext_name);
502
 
503
  if (is_wp_error($collected)) {
504
+ if (defined('WP_DEBUG') && WP_DEBUG) {
505
+ FW_Flash_Messages::add('fw_ext_auto_activate_hidden_standalone',
506
+ sprintf(__('Cannot activate hidden standalone extension %s', 'fw'),
507
+ fw_akg('name', $ext_data['manifest'], fw_id_to_title($ext_name))
508
+ ),
509
+ 'error'
510
+ );
511
+ }
512
  return;
513
  }
514
 
framework/helpers/class-fw-form.php CHANGED
@@ -234,16 +234,58 @@ class FW_Form {
234
  $this->validate();
235
 
236
  if ( $this->is_ajax() ) {
 
 
237
  if ( $this->is_valid() ) {
238
- $this->save();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
- wp_send_json_success( array(
241
- 'flash_messages' => FW_Flash_Messages::_get_messages(true)
242
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  } else {
244
- wp_send_json_error( array(
245
- 'errors' => $this->get_errors()
246
- ) );
247
  }
248
  } else {
249
  if ( ! $this->is_valid() ) {
234
  $this->validate();
235
 
236
  if ( $this->is_ajax() ) {
237
+ $json_data = array();
238
+
239
  if ( $this->is_valid() ) {
240
+ $json_data['save_data'] = $this->save();
241
+ } else {
242
+ $json_data['errors'] = $this->get_errors();
243
+ }
244
+
245
+ /**
246
+ * Transform flash messages structure from
247
+ * array( 'type' => array( 'message_id' => array(...) ) )
248
+ * to
249
+ * array( 'type' => array( 'message_id' => 'Message' ) )
250
+ */
251
+ {
252
+ $flash_messages = array();
253
+
254
+ foreach (FW_Flash_Messages::_get_messages(true) as $type => $messages) {
255
+ $flash_messages[$type] = array();
256
+
257
+ foreach ($messages as $id => $message_data) {
258
+ $flash_messages[$type][$id] = $message_data['message'];
259
+ }
260
+ }
261
+
262
+ $json_data['flash_messages'] = $flash_messages;
263
+ }
264
 
265
+ /**
266
+ * Important!
267
+ * We can't send form html in response:
268
+ *
269
+ * ob_start();
270
+ * $this->render();
271
+ * $json_data['html'] = ob_get_clean();
272
+ *
273
+ * because the render() method is not called within this class
274
+ * but by the code that created and owns the $form,
275
+ * and it's usually called with some custom data $this->render(array(...))
276
+ * that it's impossible to know here which data is that.
277
+ * If we will call $this->render(); without data, this may throw errors because
278
+ * the render callback may expect some custom data.
279
+ * Also it may be called or not, depending on the owner code inner logic.
280
+ *
281
+ * The only way to get the latest form html on ajax submit
282
+ * is to make a new ajax GET to current page and extract form html from the response.
283
+ */
284
+
285
+ if ( $this->is_valid() ) {
286
+ wp_send_json_success($json_data);
287
  } else {
288
+ wp_send_json_error($json_data);
 
 
289
  }
290
  } else {
291
  if ( ! $this->is_valid() ) {
framework/includes/option-types/addable-popup/static/js/addable-popup.js CHANGED
@@ -1,6 +1,5 @@
1
  (function ($, _, fwEvents, window) {
2
-
3
- addablePopup = function () {
4
  var $this = $(this),
5
  $defaultItem = $this.find('.item.default'),
6
  nodes = {
1
  (function ($, _, fwEvents, window) {
2
+ var addablePopup = function () {
 
3
  var $this = $(this),
4
  $defaultItem = $this.find('.item.default'),
5
  nodes = {
framework/includes/option-types/multi-picker/class-fw-option-type-multi-picker.php CHANGED
@@ -102,7 +102,7 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
102
  $picker_key = key($option['picker']);
103
  $picker = $option['picker'][$picker_key];
104
  $picker_type = $picker['type'];
105
- $supported_picker_types = array('select', 'short-select', 'radio', 'image-picker', 'switch');
106
  if (!in_array($picker_type, $supported_picker_types)) {
107
  // TODO: think of text for error when incorrect picker type is used
108
  trigger_error(
102
  $picker_key = key($option['picker']);
103
  $picker = $option['picker'][$picker_key];
104
  $picker_type = $picker['type'];
105
+ $supported_picker_types = array('select', 'short-select', 'radio', 'image-picker', 'switch', 'color-palette');
106
  if (!in_array($picker_type, $supported_picker_types)) {
107
  // TODO: think of text for error when incorrect picker type is used
108
  trigger_error(
framework/includes/option-types/range-slider/class-fw-option-type-range-slider.php CHANGED
@@ -54,8 +54,9 @@ class FW_Option_Type_Range_Slider extends FW_Option_Type {
54
  $option['properties']['from'] = ( isset( $data['value']['from'] ) ) ? $data['value']['from'] : $option['value']['from'];
55
  $option['properties']['to'] = ( isset( $data['value']['to'] ) ) ? $data['value']['to'] : $option['value']['to'];
56
 
57
- if ($option['properties']['from'] > $option['properties']['to']) {
58
- $option['properties']['from'] = $option['properties']['to'];
 
59
  }
60
 
61
  $option['attr']['data-fw-irs-options'] = json_encode(
@@ -98,7 +99,7 @@ class FW_Option_Type_Range_Slider extends FW_Option_Type {
98
  if (is_null($input_value)) {
99
  return $option['value'];
100
  } else {
101
- $input_values = array_map('intval', explode(';', $input_value));
102
 
103
  return array(
104
  'from' => $input_values[0],
54
  $option['properties']['from'] = ( isset( $data['value']['from'] ) ) ? $data['value']['from'] : $option['value']['from'];
55
  $option['properties']['to'] = ( isset( $data['value']['to'] ) ) ? $data['value']['to'] : $option['value']['to'];
56
 
57
+ if ( isset( $option['properties']['values'] ) && is_array( $option['properties']['values'] ) ) {
58
+ $option['properties']['from'] = array_search( $option['properties']['from'], $option['properties']['values'] );
59
+ $option['properties']['to'] = array_search( $option['properties']['to'], $option['properties']['values'] );
60
  }
61
 
62
  $option['attr']['data-fw-irs-options'] = json_encode(
99
  if (is_null($input_value)) {
100
  return $option['value'];
101
  } else {
102
+ $input_values = ( isset( $option['properties']['values'] ) && is_array( $option['properties']['values'] ) ) ? explode( ';', $input_value ) : array_map( 'intval', explode( ';', $input_value ) );
103
 
104
  return array(
105
  'from' => $input_values[0],
framework/includes/option-types/range-slider/static/js/scripts.js CHANGED
@@ -1,13 +1,17 @@
1
  (function ($, fwEvents) {
2
  var defaults = {
3
  onChange: function (data) {
4
- data.input.next('.fw-irs-range-slider-hidden-input').val(data.from + ';' + data.to);
5
- data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.from').html(data.from);
6
- data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.to').html(data.to);
 
 
7
  },
8
  onStart: function (data) {
9
- data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.from').html(data.from);
10
- data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.to').html(data.to);
 
 
11
  },
12
  grid: true
13
  };
1
  (function ($, fwEvents) {
2
  var defaults = {
3
  onChange: function (data) {
4
+ var from = (data.from_value) ? data.from_value : data.from;
5
+ var to = (data.to_value) ? data.to_value : data.to;
6
+ data.input.next('.fw-irs-range-slider-hidden-input').val(from + ';' + to);
7
+ data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.from').html(from);
8
+ data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.to').html(to);
9
  },
10
  onStart: function (data) {
11
+ var from = (data.from_value) ? data.from_value : data.from;
12
+ var to = (data.to_value) ? data.to_value : data.to;
13
+ data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.from').html(from);
14
+ data.input.closest('.fw-option-type-range-slider').find('span.irs-slider.to').html(to);
15
  },
16
  grid: true
17
  };
framework/includes/option-types/slider/class-fw-option-type-slider.php CHANGED
@@ -53,6 +53,10 @@ class FW_Option_Type_Slider extends FW_Option_Type {
53
  $option['properties']['type'] = 'single';
54
  $option['properties']['from'] = isset( $data['value'] ) ? $data['value'] : $option['value'];
55
 
 
 
 
 
56
  $option['attr']['data-fw-irs-options'] = json_encode(
57
  $this->default_properties($option['properties'])
58
  );
@@ -90,7 +94,7 @@ class FW_Option_Type_Slider extends FW_Option_Type {
90
  if (is_null($input_value)) {
91
  return $option['value'];
92
  } else {
93
- $input_values = array_map('intval', explode(';', $input_value));
94
 
95
  return $input_values[0];
96
  }
53
  $option['properties']['type'] = 'single';
54
  $option['properties']['from'] = isset( $data['value'] ) ? $data['value'] : $option['value'];
55
 
56
+ if(isset($option['properties']['values']) && is_array($option['properties']['values'])){
57
+ $option['properties']['from'] = array_search($option['properties']['from'], $option['properties']['values']);
58
+ }
59
+
60
  $option['attr']['data-fw-irs-options'] = json_encode(
61
  $this->default_properties($option['properties'])
62
  );
94
  if (is_null($input_value)) {
95
  return $option['value'];
96
  } else {
97
+ $input_values = (isset($option['properties']['values']) && is_array($option['properties']['values'])) ? explode(';', $input_value) : array_map('intval', explode(';', $input_value));
98
 
99
  return $input_values[0];
100
  }
framework/includes/option-types/slider/static/js/scripts.js CHANGED
@@ -1,11 +1,14 @@
1
  (function ($, fwEvents) {
2
  var defaults = {
3
  onChange: function (data) {
4
- data.input.next('.fw-irs-range-slider-hidden-input').val(data.from + ';' + data.to);
5
- data.input.closest('.fw-option-type-slider').find('span span.irs-slider.single').html(data.from);
 
 
6
  },
7
  onStart: function (data) {
8
- data.input.closest('.fw-option-type-slider').find('span span.irs-slider.single').html(data.from);
 
9
  data.input.closest('.fw-option-type-slider').find('.irs-bar-edge').remove();
10
  },
11
  grid: true
1
  (function ($, fwEvents) {
2
  var defaults = {
3
  onChange: function (data) {
4
+ var from = (data.from_value) ? data.from_value : data.from;
5
+ var to = (data.to_value) ? data.to_value : data.to;
6
+ data.input.next('.fw-irs-range-slider-hidden-input').val(from + ';' + to);
7
+ data.input.closest('.fw-option-type-slider').find('span span.irs-slider.single').html(from);
8
  },
9
  onStart: function (data) {
10
+ var from = (data.from_value) ? data.from_value : data.from;
11
+ data.input.closest('.fw-option-type-slider').find('span span.irs-slider.single').html(from);
12
  data.input.closest('.fw-option-type-slider').find('.irs-bar-edge').remove();
13
  },
14
  grid: true
framework/includes/option-types/typography/class-fw-option-type-typography.php CHANGED
@@ -57,6 +57,9 @@ class FW_Option_Type_Typography extends FW_Option_Type
57
  array('fw-selectize'),
58
  fw()->manifest->get_version()
59
  );
 
 
 
60
  wp_enqueue_script(
61
  'fw-option-' . $this->get_type(),
62
  fw_get_framework_directory_uri('/includes/option-types/' . $this->get_type() . '/static/js/scripts.js'),
57
  array('fw-selectize'),
58
  fw()->manifest->get_version()
59
  );
60
+
61
+ fw()->backend->option_type('color-picker')->enqueue_static();
62
+
63
  wp_enqueue_script(
64
  'fw-option-' . $this->get_type(),
65
  fw_get_framework_directory_uri('/includes/option-types/' . $this->get_type() . '/static/js/scripts.js'),
framework/manifest.php CHANGED
@@ -4,4 +4,4 @@ $manifest = array();
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
- $manifest['version'] = '2.1.21';
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
+ $manifest['version'] = '2.1.22';
framework/static/css/fw.css CHANGED
@@ -2590,6 +2590,17 @@ a.fw-wp-link:hover {
2590
  100% {opacity: 1;}
2591
  }
2592
 
 
 
 
 
 
 
 
 
 
 
 
2593
  /* end: fadeIn */
2594
 
2595
  /* end: Animations */
@@ -2608,88 +2619,33 @@ a.fw-wp-link:hover {
2608
  /* end: WP Modal */
2609
 
2610
 
2611
- /* OptionsModal */
2612
 
2613
- .fw-options-modal.fw-options-modal-medium > .media-modal {
2614
- left: 15%;
2615
- right: 15%;
2616
- }
2617
-
2618
- .fw-options-modal.fw-options-modal-small > .media-modal {
2619
- top: 20%;
2620
- bottom: 20%;
2621
- left: 20%;
2622
- right: 20%;
2623
- }
2624
-
2625
- @media (min-width: 1824px) {
2626
- .fw-options-modal.fw-options-modal-large > .media-modal {
2627
- top: 10%;
2628
- bottom: 10%;
2629
- left: 10%;
2630
- right: 10%;
2631
- }
2632
-
2633
- .fw-options-modal.fw-options-modal-medium > .media-modal {
2634
- top: 15%;
2635
- bottom: 15%;
2636
- left: 20%;
2637
- right: 20%;
2638
- }
2639
-
2640
- .fw-options-modal.fw-options-modal-small > .media-modal {
2641
- top: 29%;
2642
- bottom: 29%;
2643
- left: 29%;
2644
- right: 29%;
2645
- }
2646
  }
2647
 
2648
- .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list {
2649
- padding-top: 20px;
2650
- }
2651
-
2652
- .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li a.nav-tab {
2653
- background-color: #f1f1f1;
2654
- }
2655
-
2656
- .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li a.nav-tab:hover {
2657
- background-color: #f8f8f8;
2658
- }
2659
-
2660
- .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li.ui-state-active a.nav-tab {
2661
- position: relative;
2662
- }
2663
-
2664
- .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li.ui-state-active a.nav-tab:after {
2665
- position: absolute;
2666
- left: 0;
2667
- bottom: -1px;
2668
- display: block;
2669
- content: '';
2670
- width: 100%;
2671
- height: 1px;
2672
- background: #fff;
2673
- border-right: 1px solid #ccc;
2674
- }
2675
-
2676
-
2677
- /* modal animations */
2678
-
2679
  /* open animation */
2680
 
2681
- .fw-options-modal > .media-modal-backdrop {
2682
  opacity: 0;
2683
- }
2684
-
2685
- .fw-options-modal.fw-options-modal-open > .media-modal-backdrop {
2686
- opacity: .7;
2687
 
2688
  -webkit-transition: opacity .3s ease-in-out; /* For Safari 3.1 to 6.0 */
2689
  transition: opacity .3s ease-in-out;
2690
  }
2691
 
2692
- .fw-options-modal.fw-options-modal-open > .media-modal > .media-modal-content {
 
 
 
 
 
 
 
 
 
 
2693
  animation: fwGrowIn .3s ease-in-out;
2694
  animation-iteration-count: 1;
2695
 
@@ -2706,31 +2662,29 @@ a.fw-wp-link:hover {
2706
  -ms-animation-iteration-count: 1;
2707
  }
2708
 
2709
- /* fix: close button is not animated on modal open */
2710
-
2711
- .fw-options-modal > .media-modal > .media-modal-close {
2712
  opacity: 0;
 
 
 
2713
  }
2714
 
2715
- .fw-options-modal.fw-options-modal-open > .media-modal > .media-modal-close {
2716
  opacity: 1;
2717
 
2718
- -webkit-transition: opacity .6s ease-in; /* For Safari 3.1 to 6.0 */
2719
- transition: opacity .6s ease-in;
2720
  }
2721
 
2722
  /* end: open animation */
2723
 
2724
  /* close animation */
2725
 
2726
- .fw-options-modal.fw-options-modal-closing > .media-modal-backdrop {
2727
  opacity: .0;
2728
-
2729
- -webkit-transition: opacity .3s ease-in-out; /* For Safari 3.1 to 6.0 */
2730
- transition: opacity .3s ease-in-out;
2731
  }
2732
 
2733
- .fw-options-modal.fw-options-modal-closing > .media-modal > .media-modal-content {
2734
  animation: fwGrowOut .3s ease-in-out;
2735
  animation-iteration-count: 1;
2736
 
@@ -2750,20 +2704,82 @@ a.fw-wp-link:hover {
2750
  animation-fill-mode: forwards;
2751
  }
2752
 
2753
- .fw-options-modal.fw-options-modal-closing > .media-modal > .media-modal-close {
2754
  opacity: 0;
2755
 
2756
- -webkit-transition: opacity .2s ease-out; /* For Safari 3.1 to 6.0 */
2757
- transition: opacity .2s ease-out;
2758
  }
2759
 
2760
  /* end: close animation */
2761
 
2762
- /* fix: close button */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2763
 
 
 
 
2764
 
2765
- /* end: modal animations */
 
 
2766
 
 
 
 
 
 
 
 
 
 
 
 
2767
 
2768
  /* Hide last border */
2769
 
@@ -2799,10 +2815,38 @@ body.rtl .fw-options-modal .media-frame-content > form .fw-options-tabs-contents
2799
 
2800
  /* end: Hide last border */
2801
 
2802
-
2803
  /* end: OptionsModal */
2804
 
2805
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2806
  /* qtip custom style */
2807
 
2808
  .qtip.qtip-fw {
2590
  100% {opacity: 1;}
2591
  }
2592
 
2593
+
2594
+ @-webkit-keyframes fwFadeIn07 {
2595
+ 0% {opacity: 0;}
2596
+ 100% {opacity: 0.7;}
2597
+ }
2598
+
2599
+ @keyframes fwFadeIn07 {
2600
+ 0% {opacity: 0;}
2601
+ 100% {opacity: 0.7;}
2602
+ }
2603
+
2604
  /* end: fadeIn */
2605
 
2606
  /* end: Animations */
2619
  /* end: WP Modal */
2620
 
2621
 
2622
+ /* modal */
2623
 
2624
+ .fw-modal .media-modal-close:focus {
2625
+ -webkit-box-shadow: none;
2626
+ box-shadow: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2627
  }
2628
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2629
  /* open animation */
2630
 
2631
+ .fw-modal > .media-modal-backdrop {
2632
  opacity: 0;
 
 
 
 
2633
 
2634
  -webkit-transition: opacity .3s ease-in-out; /* For Safari 3.1 to 6.0 */
2635
  transition: opacity .3s ease-in-out;
2636
  }
2637
 
2638
+ .fw-modal.fw-modal-open > .media-modal-backdrop {
2639
+ opacity: .7; /* must be the same as in .media-modal-backdrop {} wp style */
2640
+
2641
+ -webkit-animation-name: fwFadeIn07;
2642
+ animation-name: fwFadeIn07;
2643
+
2644
+ -webkit-animation-duration: .3s;
2645
+ animation-duration: .3s;
2646
+ }
2647
+
2648
+ .fw-modal.fw-modal-open > .media-modal > .media-modal-content {
2649
  animation: fwGrowIn .3s ease-in-out;
2650
  animation-iteration-count: 1;
2651
 
2662
  -ms-animation-iteration-count: 1;
2663
  }
2664
 
2665
+ .fw-modal > .media-modal > .media-modal-close {
 
 
2666
  opacity: 0;
2667
+
2668
+ -webkit-transition: opacity .3s ease-in; /* For Safari 3.1 to 6.0 */
2669
+ transition: opacity .3s ease-in;
2670
  }
2671
 
2672
+ .fw-modal.fw-modal-open > .media-modal > .media-modal-close {
2673
  opacity: 1;
2674
 
2675
+ -webkit-transition-duration: .6s;
2676
+ transition-duration: .6s;
2677
  }
2678
 
2679
  /* end: open animation */
2680
 
2681
  /* close animation */
2682
 
2683
+ .fw-modal.fw-modal-closing > .media-modal-backdrop {
2684
  opacity: .0;
 
 
 
2685
  }
2686
 
2687
+ .fw-modal.fw-modal-closing > .media-modal > .media-modal-content {
2688
  animation: fwGrowOut .3s ease-in-out;
2689
  animation-iteration-count: 1;
2690
 
2704
  animation-fill-mode: forwards;
2705
  }
2706
 
2707
+ .fw-modal.fw-modal-closing > .media-modal > .media-modal-close {
2708
  opacity: 0;
2709
 
2710
+ -webkit-transition-duration: .1s;
2711
+ transition-duration: .1s;
2712
  }
2713
 
2714
  /* end: close animation */
2715
 
2716
+ /* end: modal */
2717
+
2718
+
2719
+ /* OptionsModal */
2720
+
2721
+ .fw-options-modal.fw-options-modal-medium > .media-modal {
2722
+ left: 15%;
2723
+ right: 15%;
2724
+ }
2725
+
2726
+ .fw-options-modal.fw-options-modal-small > .media-modal {
2727
+ top: 20%;
2728
+ bottom: 20%;
2729
+ left: 20%;
2730
+ right: 20%;
2731
+ }
2732
+
2733
+ @media (min-width: 1824px) {
2734
+ .fw-options-modal.fw-options-modal-large > .media-modal {
2735
+ top: 10%;
2736
+ bottom: 10%;
2737
+ left: 10%;
2738
+ right: 10%;
2739
+ }
2740
+
2741
+ .fw-options-modal.fw-options-modal-medium > .media-modal {
2742
+ top: 15%;
2743
+ bottom: 15%;
2744
+ left: 20%;
2745
+ right: 20%;
2746
+ }
2747
+
2748
+ .fw-options-modal.fw-options-modal-small > .media-modal {
2749
+ top: 29%;
2750
+ bottom: 29%;
2751
+ left: 29%;
2752
+ right: 29%;
2753
+ }
2754
+ }
2755
+
2756
+ .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list {
2757
+ padding-top: 20px;
2758
+ }
2759
+
2760
+ .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li a.nav-tab {
2761
+ background-color: #f1f1f1;
2762
+ }
2763
 
2764
+ .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li a.nav-tab:hover {
2765
+ background-color: #f8f8f8;
2766
+ }
2767
 
2768
+ .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li.ui-state-active a.nav-tab {
2769
+ position: relative;
2770
+ }
2771
 
2772
+ .fw-options-modal .fw-options-tabs-first-level > .fw-options-tabs-list ul li.ui-state-active a.nav-tab:after {
2773
+ position: absolute;
2774
+ left: 0;
2775
+ bottom: -1px;
2776
+ display: block;
2777
+ content: '';
2778
+ width: 100%;
2779
+ height: 1px;
2780
+ background: #fff;
2781
+ border-right: 1px solid #ccc;
2782
+ }
2783
 
2784
  /* Hide last border */
2785
 
2815
 
2816
  /* end: Hide last border */
2817
 
 
2818
  /* end: OptionsModal */
2819
 
2820
 
2821
+ /* SoleModal */
2822
+
2823
+ .fw-sole-modal > .media-modal {
2824
+ z-index: 100001;
2825
+ margin: auto;
2826
+ }
2827
+
2828
+ .fw-sole-modal > .media-modal-backdrop {
2829
+ z-index: 100000;
2830
+ background-color: transparent;
2831
+ }
2832
+
2833
+ .fw-sole-modal > .media-modal > .media-modal-content {
2834
+ -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.25);
2835
+ box-shadow: 0 0px 10px rgba(0,0,0,.25);
2836
+ }
2837
+
2838
+ .fw-sole-modal .fw-sole-modal-content h2 {
2839
+ font-size: 22px;
2840
+ }
2841
+
2842
+ .fw-sole-modal .fw-sole-modal-content p {
2843
+ font-size: 13px;
2844
+ line-height: 19px;
2845
+ }
2846
+
2847
+ /* end: SoleModal */
2848
+
2849
+
2850
  /* qtip custom style */
2851
 
2852
  .qtip.qtip-fw {
framework/static/js/backend-options.js CHANGED
@@ -2,6 +2,29 @@
2
  * Included on pages where backend options are rendered
3
  */
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  jQuery(document).ready(function($){
6
  /**
7
  * Functions
2
  * Included on pages where backend options are rendered
3
  */
4
 
5
+ var fwBackendOptions = {
6
+ /**
7
+ * Open a tab or sub-tab
8
+ */
9
+ openTab: function(tabId) {
10
+ if (!tabId) {
11
+ return;
12
+ }
13
+
14
+ var $tabLink = jQuery(".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a[href=\'#"+ tabId +"\']");
15
+
16
+ while ($tabLink.length) {
17
+ $tabLink.trigger("click");
18
+ $tabLink = $tabLink
19
+ .closest(".fw-options-tabs-wrapper").parent().closest(".fw-options-tabs-wrapper")
20
+ .find("> .fw-options-tabs-list > ul > li > a[href=\'#"+ $tabLink.closest(".fw-options-tab").attr("id") +"\']");
21
+ }
22
+
23
+ // click again on focus tab to update the input value
24
+ jQuery(".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a[href=\'#"+ tabId +"\']").trigger("click");;
25
+ }
26
+ };
27
+
28
  jQuery(document).ready(function($){
29
  /**
30
  * Functions
framework/static/js/fw-form-helpers.js CHANGED
@@ -5,145 +5,80 @@
5
  */
6
 
7
  var fwForm = {
8
- /**
9
- * @type {Boolean}
10
- */
11
- isAdminPage: function() {
12
- return typeof ajaxurl != 'undefined'
13
- && typeof adminpage != 'undefined'
14
- && typeof pagenow != 'undefined'
15
- && jQuery(document.body).hasClass('wp-admin');
16
- },
17
  /**
18
  * Make forms ajax submittable
19
- * @param {Object} [opts]
20
  */
21
  initAjaxSubmit: function(opts) {
22
- var $ = jQuery,
23
- opts = $.extend({
24
- selector: 'form[data-fw-form-id]',
25
- ajaxUrl: '/wp-admin/admin-ajax.php',
26
- loading: function (show, $form) {
27
- $form.css('position', 'relative');
28
- $form.find('> .fw-form-loading').remove();
29
-
30
- if (show) {
31
- $form.append(
32
- '<div'+
33
- ' class="fw-form-loading"'+
34
- ' style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.5);"'+
35
- '></div>'
36
- );
37
- }
38
- },
39
- showErrors: function ($form, errors) {
 
 
 
 
40
  // Frontend
41
- $.each(errors, function(inputName, message) {
42
- var $input = $form.find('[name="'+ inputName +'"]').last();
43
- message = '<p class="form-error" style="color: #9b4c4c;">{message}</p>'.replace('{message}', message);
44
 
45
  if ($input.length) {
46
  // error message under input
47
  $input.parent().after(message);
48
  } else {
49
  // if input not found, show message in form
50
- $form.prepend(message);
51
  }
52
  });
53
- },
54
- hideErrors: function ($form) {
55
- $form.find('.form-error').remove();
56
- },
57
- onAjaxError: function(jqXHR, textStatus, errorThrown) {
58
- console.error(jqXHR, textStatus, errorThrown);
59
- },
60
- onSuccess: function ($form, ajaxData) {
61
- if (fwForm.isAdminPage()) {
62
- var html = [],
63
- typeHtml = [],
64
- messageClass = '';
65
-
66
- $.each(ajaxData.flash_messages, function(type, messages){
67
- typeHtml = [];
68
-
69
- switch (type) {
70
- case 'error':
71
- messageClass = 'error';
72
- break;
73
- case 'warning':
74
- messageClass = 'update-nag';
75
- break;
76
- default:
77
- messageClass = 'updated';
78
- }
79
-
80
- $.each(messages, function(messageId, messageData){
81
- typeHtml.push('<p>'+ messageData.message +'</p>');
82
- });
83
-
84
- if (typeHtml.length) {
85
- html.push(
86
- '<div class="fw-flash-messages '+ messageClass +'">'+ typeHtml.join('</div><div class="fw-flash-messages '+ messageClass +'">') +'</div>'
87
- );
88
- }
89
-
90
- var $pageTitle = jQuery('.wrap h2:first');
91
-
92
- while ($pageTitle.next().is('.fw-flash-message, .fw-flash-messages, .updated, .update-nag, .error')) {
93
- $pageTitle.next().remove();
94
- }
95
-
96
- var scrollTop = jQuery(document.body).scrollTop();
97
-
98
- $pageTitle.after(
99
- '<div class="fw-flash-messages">'+
100
- html.join('') +
101
- (scrollTop > 300
102
- ? '<p><a href="#" onclick="jQuery(document.body).animate({scrollTop: '+ scrollTop +'}, 300); jQuery(this).parent().remove();">Go back</a></p>'
103
- : ''
104
- )+
105
- '</div>');
106
-
107
- jQuery(document.body).animate({scrollTop: 0}, 300);
108
- });
109
- } else {
110
- var html = [],
111
- typeHtml = [];
112
-
113
- $.each(ajaxData.flash_messages, function(type, messages){
114
- typeHtml = [];
115
-
116
- $.each(messages, function(messageId, messageData){
117
- typeHtml.push(messageData.message);
118
- });
119
-
120
- if (typeHtml.length) {
121
- html.push(
122
- '<ul class="flash-messages-'+ type +'">'+
123
- ' <li>'+ typeHtml.join('</li><li>') +'</li>'+
124
- '</ul>'
125
- );
126
- }
127
- });
128
-
129
- if (html.length) {
130
- html = html.join('');
131
- } else {
132
- html = '<p>Success</p>';
133
- }
134
-
135
- $form.fadeOut(function(){
136
- $form.html(html).fadeIn();
137
- });
138
 
139
- // prevent multiple submit
140
- $form.on('submit', function(e){ e.preventDefault(); e.stopPropagation(); });
141
  }
 
 
 
 
 
 
 
142
  }
143
- }, opts || {}),
144
- isBusy = false;
 
 
145
 
146
- $(document.body).on('submit', opts.selector, function(e){
147
  e.preventDefault();
148
 
149
  if (isBusy) {
@@ -151,21 +86,33 @@ var fwForm = {
151
  return;
152
  }
153
 
154
- var $form = $(this),
155
- $submitButton = $form.find('input[type="submit"][name]:focus');
156
-
157
- if (!$submitButton.length) {
158
- // in case you use this solution http://stackoverflow.com/a/5721762
159
- $submitButton = $form.find('input[type="submit"][name][clicked]');
160
- }
161
 
162
  if (!$form.is('form[data-fw-form-id]')) {
163
  console.error('This is not a FW_Form');
164
  return;
165
  }
166
 
167
- opts.hideErrors($form);
168
- opts.loading(true, $form);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  isBusy = true;
170
 
171
  jQuery.ajax({
@@ -175,19 +122,101 @@ var fwForm = {
175
  dataType: 'json'
176
  }).done(function(r){
177
  isBusy = false;
178
- opts.loading(false, $form);
179
 
180
  if (r.success) {
181
- opts.onSuccess($form, r.data);
182
  } else {
183
- opts.showErrors($form, r.data.errors);
184
  }
185
  }).fail(function(jqXHR, textStatus, errorThrown){
186
  isBusy = false;
187
- opts.loading(false, $form);
188
- opts.onAjaxError(jqXHR, textStatus, errorThrown);
 
 
 
 
189
  });
190
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  }
192
  };
193
 
@@ -196,7 +225,7 @@ if (false) {
196
  jQuery(function ($) {
197
  fwForm.initAjaxSubmit({
198
  selector: 'form[data-fw-form-id][data-fw-ext-forms-type="contact-forms"]',
199
- ajaxUrl: ajaxurl || '/wp-admin/admin-ajax.php'
200
  });
201
  });
202
  }
5
  */
6
 
7
  var fwForm = {
 
 
 
 
 
 
 
 
 
8
  /**
9
  * Make forms ajax submittable
10
+ * @param {Object} [opts] You can overwrite any
11
  */
12
  initAjaxSubmit: function(opts) {
13
+ var opts = jQuery.extend({
14
+ selector: 'form[data-fw-form-id]',
15
+ ajaxUrl: (typeof ajaxurl == 'undefined') ? '/wp-admin/admin-ajax.php' : ajaxurl,
16
+ loading: function (elements, show) {
17
+ elements.$form.css('position', 'relative');
18
+ elements.$form.find('> .fw-form-loading').remove();
19
+
20
+ if (show) {
21
+ elements.$form.append(
22
+ '<div'+
23
+ ' class="fw-form-loading"'+
24
+ ' style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.1);"'+
25
+ '></div>'
26
+ );
27
+ }
28
+ },
29
+ onErrors: function (elements, data) {
30
+ if (isAdmin) {
31
+ fwForm.backend.showFlashMessages(
32
+ fwForm.backend.renderFlashMessages({error: data.errors})
33
+ );
34
+ } else {
35
  // Frontend
36
+ jQuery.each(data.errors, function (inputName, message) {
37
+ var $input = elements.$form.find('[name="' + inputName + '"]').last();
38
+ message = '<p class="form-error" style="color: #9b2922;">{message}</p>'.replace('{message}', message);
39
 
40
  if ($input.length) {
41
  // error message under input
42
  $input.parent().after(message);
43
  } else {
44
  // if input not found, show message in form
45
+ elements.$form.prepend(message);
46
  }
47
  });
48
+ }
49
+ },
50
+ hideErrors: function (elements) {
51
+ elements.$form.find('.form-error').remove();
52
+ },
53
+ onAjaxError: function(elements, data) {
54
+ console.error(data.jqXHR, data.textStatus, data.errorThrown);
55
+ alert('Ajax error (more details in console)');
56
+ },
57
+ onSuccess: function (elements, ajaxData) {
58
+ if (isAdmin) {
59
+ fwForm.backend.showFlashMessages(
60
+ fwForm.backend.renderFlashMessages(ajaxData.flash_messages)
61
+ );
62
+ } else {
63
+ var html = fwForm.frontend.renderFlashMessages(ajaxData.flash_messages);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ if (!html.length) {
66
+ html = '<p>Success</p>';
67
  }
68
+
69
+ elements.$form.fadeOut(function(){
70
+ elements.$form.html(html).fadeIn();
71
+ });
72
+
73
+ // prevent multiple submit
74
+ elements.$form.on('submit', function(e){ e.preventDefault(); e.stopPropagation(); });
75
  }
76
+ }
77
+ }, opts || {}),
78
+ isAdmin = (typeof adminpage != 'undefined' && jQuery(document.body).hasClass('wp-admin')),
79
+ isBusy = false;
80
 
81
+ jQuery(document.body).on('submit', opts.selector, function(e){
82
  e.preventDefault();
83
 
84
  if (isBusy) {
86
  return;
87
  }
88
 
89
+ var $form = jQuery(this);
 
 
 
 
 
 
90
 
91
  if (!$form.is('form[data-fw-form-id]')) {
92
  console.error('This is not a FW_Form');
93
  return;
94
  }
95
 
96
+ // get submit button
97
+ {
98
+ var $submitButton = $form.find('input[type="submit"][name]:focus')
99
+
100
+ if (!$submitButton.length) {
101
+ // in case you use this solution http://stackoverflow.com/a/5721762
102
+ $submitButton = $form.find('input[type="submit"][name][clicked]');
103
+ }
104
+
105
+ // make sure to remove the "clicked" attribute to prevent accidental settings reset
106
+ $form.find('input[type="submit"][name][clicked]').removeAttr('clicked');
107
+ }
108
+
109
+ var elements = {
110
+ $form: $form,
111
+ $submitButton: $submitButton
112
+ };
113
+
114
+ opts.hideErrors(elements);
115
+ opts.loading(elements, true);
116
  isBusy = true;
117
 
118
  jQuery.ajax({
122
  dataType: 'json'
123
  }).done(function(r){
124
  isBusy = false;
125
+ opts.loading(elements, false);
126
 
127
  if (r.success) {
128
+ opts.onSuccess(elements, r.data);
129
  } else {
130
+ opts.onErrors(elements, r.data);
131
  }
132
  }).fail(function(jqXHR, textStatus, errorThrown){
133
  isBusy = false;
134
+ opts.loading(elements, false);
135
+ opts.onAjaxError(elements, {
136
+ jqXHR: jqXHR,
137
+ textStatus: textStatus,
138
+ errorThrown: errorThrown
139
+ });
140
  });
141
  });
142
+ },
143
+ backend: {
144
+ showFlashMessages: function(messagesHtml) {
145
+ var $pageTitle = jQuery('.wrap h2:first');
146
+
147
+ while ($pageTitle.next().is('.fw-flash-messages, .fw-flash-message, .updated, .update-nag, .error')) {
148
+ $pageTitle.next().remove();
149
+ }
150
+
151
+ $pageTitle.after('<div class="fw-flash-messages">'+ messagesHtml +'</div>');
152
+
153
+ jQuery(document.body).animate({scrollTop: 0}, 300);
154
+ },
155
+ /**
156
+ * Html structure should be the same as generated by FW_Flash_Messages::_print_backend()
157
+ * @param {Object} flashMessages
158
+ * @returns {string}
159
+ */
160
+ renderFlashMessages: function(flashMessages) {
161
+ var html = [],
162
+ typeHtml = [],
163
+ messageClass = '';
164
+
165
+ jQuery.each(flashMessages, function(type, messages){
166
+ typeHtml = [];
167
+
168
+ switch (type) {
169
+ case 'error':
170
+ messageClass = 'error';
171
+ break;
172
+ case 'warning':
173
+ messageClass = 'update-nag';
174
+ break;
175
+ default:
176
+ messageClass = 'updated';
177
+ }
178
+
179
+ jQuery.each(messages, function(messageId, message){
180
+ typeHtml.push('<div class="'+ messageClass +' fw-flash-message"><p>'+ message +'</p></div>');
181
+ });
182
+
183
+ if (typeHtml.length) {
184
+ html.push(
185
+ '<div class="fw-flash-type-'+ type +'">'+ typeHtml.join('</div><div class="fw-flash-type-'+ type +'">') +'</div>'
186
+ );
187
+ }
188
+ });
189
+
190
+ return html.join('');
191
+ }
192
+ },
193
+ frontend: {
194
+ /**
195
+ * Html structure is the same as generated by FW_Flash_Messages::_print_frontend()
196
+ * @param {Object} flashMessages
197
+ * @returns {string}
198
+ */
199
+ renderFlashMessages: function(flashMessages) {
200
+ var html = [],
201
+ typeHtml = [],
202
+ messageClass = '';
203
+
204
+ jQuery.each(flashMessages, function(type, messages){
205
+ typeHtml = [];
206
+
207
+ jQuery.each(messages, function(messageId, message){
208
+ typeHtml.push('<li class="fw-flash-message">'+ message +'</li>');
209
+ });
210
+
211
+ if (typeHtml.length) {
212
+ html.push(
213
+ '<ul class="fw-flash-type-'+ type +'">'+ typeHtml.join('</ul><ul class="fw-flash-type-'+ type +'">') +'</ul>'
214
+ );
215
+ }
216
+ });
217
+
218
+ return html.join('');
219
+ }
220
  }
221
  };
222
 
225
  jQuery(function ($) {
226
  fwForm.initAjaxSubmit({
227
  selector: 'form[data-fw-form-id][data-fw-ext-forms-type="contact-forms"]',
228
+ ajaxUrl: ajaxurl
229
  });
230
  });
231
  }
framework/static/js/fw.js CHANGED
@@ -597,7 +597,7 @@ fw.getQueryString = function(name) {
597
  stackSize = modalsStack.getSize(),
598
  $close = $modalWrapper.find('.media-modal-close');
599
 
600
- $modalWrapper.addClass('fw-options-modal');
601
 
602
  /*
603
  * if the modal has specified what size it wants to have
@@ -640,7 +640,7 @@ fw.getQueryString = function(name) {
640
  clearTimeout(closingTimeout);
641
 
642
  // begin css animation
643
- $modalWrapper.addClass('fw-options-modal-closing');
644
 
645
  closingTimeout = setTimeout(function(){
646
  closingTimeout = 0;
@@ -654,7 +654,7 @@ fw.getQueryString = function(name) {
654
  $backdrop.trigger('click');
655
 
656
  // remove animation class
657
- $modalWrapper.removeClass('fw-options-modal-closing');
658
 
659
  preventOriginalClose();
660
  },
@@ -687,7 +687,7 @@ fw.getQueryString = function(name) {
687
  this.frame.on('open', function() {
688
  var $modalWrapper = modal.frame.modal.$el;
689
 
690
- $modalWrapper.addClass('fw-options-modal-open');
691
 
692
  modalsStack.push($modalWrapper.find('.media-modal'));
693
 
@@ -713,7 +713,7 @@ fw.getQueryString = function(name) {
713
  */
714
  modal.set('html', '');
715
 
716
- modal.frame.modal.$el.removeClass('fw-options-modal-open');
717
 
718
  modalsStack.pop();
719
  });
@@ -1185,4 +1185,354 @@ fw.elementEventHasListenerInContainer = function ($element, event, $container) {
1185
  }
1186
 
1187
  return false;
1188
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  stackSize = modalsStack.getSize(),
598
  $close = $modalWrapper.find('.media-modal-close');
599
 
600
+ $modalWrapper.addClass('fw-modal fw-options-modal');
601
 
602
  /*
603
  * if the modal has specified what size it wants to have
640
  clearTimeout(closingTimeout);
641
 
642
  // begin css animation
643
+ $modalWrapper.addClass('fw-modal-closing');
644
 
645
  closingTimeout = setTimeout(function(){
646
  closingTimeout = 0;
654
  $backdrop.trigger('click');
655
 
656
  // remove animation class
657
+ $modalWrapper.removeClass('fw-modal-closing');
658
 
659
  preventOriginalClose();
660
  },
687
  this.frame.on('open', function() {
688
  var $modalWrapper = modal.frame.modal.$el;
689
 
690
+ $modalWrapper.addClass('fw-modal-open');
691
 
692
  modalsStack.push($modalWrapper.find('.media-modal'));
693
 
713
  */
714
  modal.set('html', '');
715
 
716
+ modal.frame.modal.$el.removeClass('fw-modal-open');
717
 
718
  modalsStack.pop();
719
  });
1185
  }
1186
 
1187
  return false;
1188
+ };
1189
+
1190
+ /**
1191
+ * Simple modal
1192
+ * Meant to display success/error messages
1193
+ * Can be called multiple times,all calls will be pushed to queue and displayed one-by-one
1194
+ *
1195
+ * Usage:
1196
+ *
1197
+ * // open modal with close button and wait for user to close it
1198
+ * fw.soleModal.show('unique-id', 'Hello World!');
1199
+ *
1200
+ * // open modal with close button but auto hide it after 3 seconds
1201
+ * fw.soleModal.show('unique-id', 'Hello World!', {autoHide: 3000});
1202
+ *
1203
+ * fw.soleModal.hide('unique-id');
1204
+ */
1205
+ fw.soleModal = (function(){
1206
+ var inst = {
1207
+ queue: [
1208
+ /*
1209
+ {
1210
+ id: 'hello'
1211
+ html: 'Hello <b>World</b>!'
1212
+ autoHide: 0000 // auto hide timeout in ms
1213
+ allowClose: true // useful when you make an ajax and must force the user to wait until it will finish
1214
+ showCloseButton: true // false will hide the button, but the user will still be able to click on backdrop to close it
1215
+ width: 350
1216
+ height: 200
1217
+ hidePrevious: false // just replace the modal content or hide the previous modal and open it again with new content
1218
+ }
1219
+ */
1220
+ ],
1221
+ /** @type {Object|null} */
1222
+ current: null,
1223
+ animationTime: 300,
1224
+ $modal: null,
1225
+ backdropOpacity: 0.7, // must be the same as in .fw-modal style
1226
+ currentMethod: '',
1227
+ currentMethodTimeoutId: 0,
1228
+ pendingMethod: '',
1229
+ lazyInit: function(){
1230
+ if (this.$modal) {
1231
+ return false;
1232
+ }
1233
+
1234
+ this.$modal = jQuery(
1235
+ '<div class="fw-modal fw-sole-modal" style="display:none;">'+
1236
+ ' <div class="media-modal wp-core-ui" style="width: 350px; height: 200px;">'+
1237
+ ' <div class="media-modal-content" style="min-height: 200px;">' +
1238
+ ' <a class="media-modal-close" href="#" onclick="return false;"><span class="media-modal-icon"></span></a>'+
1239
+ ' <table width="100%" height="100%"><tbody><tr>'+
1240
+ ' <td valign="middle" class="fw-sole-modal-content fw-text-center"><!-- modal content --></td>'+
1241
+ ' </tr><tbody></table>'+
1242
+ ' </div>'+
1243
+ ' </div>'+
1244
+ ' <div class="media-modal-backdrop"></div>'+
1245
+ '</div>'
1246
+ );
1247
+
1248
+ ( this.$getCloseButton().add(this.$getBackdrop()) ).on('click', _.bind(function(){
1249
+ if (this.current && !this.current.allowClose) {
1250
+ // manual close not is allowed
1251
+ return;
1252
+ }
1253
+
1254
+ this.hide();
1255
+ }, this));
1256
+
1257
+ jQuery(document.body).append(this.$modal);
1258
+
1259
+ return true;
1260
+ },
1261
+ $getBackdrop: function() {
1262
+ this.lazyInit();
1263
+
1264
+ return this.$modal.find('.media-modal-backdrop:first');
1265
+ },
1266
+ $getCloseButton: function() {
1267
+ this.lazyInit();
1268
+
1269
+ return this.$modal.find('.media-modal-close:first');
1270
+ },
1271
+ $getContent: function() {
1272
+ return this.$modal.find('.fw-sole-modal-content:first');
1273
+ },
1274
+ setContent: function(html) {
1275
+ this.lazyInit();
1276
+
1277
+ this.$getContent().html(html || '&nbsp;');
1278
+ },
1279
+ runPendingMethod: function() {
1280
+ if (this.currentMethod) {
1281
+ return false;
1282
+ }
1283
+
1284
+ if (!this.pendingMethod) {
1285
+ if (this.queue.length) {
1286
+ // there are messages to display
1287
+ this.pendingMethod = 'show';
1288
+ } else {
1289
+ return false;
1290
+ }
1291
+ }
1292
+
1293
+ var pendingMethod = this.pendingMethod;
1294
+
1295
+ this.pendingMethod = '';
1296
+
1297
+ if (pendingMethod == 'hide') {
1298
+ this.hide();
1299
+ return true;
1300
+ } else if (pendingMethod == 'show') {
1301
+ this.show();
1302
+ return true;
1303
+ } else {
1304
+ console.warn('Unknown pending method:', pendingMethod);
1305
+ this.hide();
1306
+ return false;
1307
+ }
1308
+ },
1309
+ /**
1310
+ * Show modal
1311
+ * Call without arguments to display next from queue
1312
+ * @param {String} [id]
1313
+ * @param {String} [html]
1314
+ * @param {Object} [opts]
1315
+ * @returns {Boolean}
1316
+ */
1317
+ show: function (id, html, opts) {
1318
+ if (typeof id != 'undefined') {
1319
+ // make sure to remove this id from queue (if was added previously)
1320
+ this.queue = _.filter(this.queue, function (item) { return item.id != id; });
1321
+
1322
+ {
1323
+ opts = jQuery.extend({
1324
+ autoHide: 0,
1325
+ allowClose: true,
1326
+ showCloseButton: true,
1327
+ width: 350,
1328
+ height: 200,
1329
+ hidePrevious: false
1330
+ }, opts || {});
1331
+
1332
+ // hide close button if close is not allowed
1333
+ opts.showCloseButton = opts.showCloseButton && opts.allowClose;
1334
+
1335
+ opts.id = id;
1336
+ opts.html = html;
1337
+ }
1338
+
1339
+ this.queue.push(opts);
1340
+
1341
+ return this.show();
1342
+ }
1343
+
1344
+ if (this.currentMethod) {
1345
+ return false;
1346
+ }
1347
+
1348
+ if (this.current) {
1349
+ return false;
1350
+ }
1351
+
1352
+ this.currentMethod = '';
1353
+
1354
+ this.current = this.queue.shift();
1355
+
1356
+ if (!this.current) {
1357
+ this.hide();
1358
+ return false;
1359
+ }
1360
+
1361
+ this.currentMethod = 'show';
1362
+
1363
+ this.setContent(this.current.html);
1364
+
1365
+ this.$getCloseButton().css('display', this.current.showCloseButton ? '' : 'none');
1366
+
1367
+ this.$modal.removeClass('fw-modal-closing');
1368
+ this.$modal.addClass('fw-modal-open');
1369
+
1370
+ this.$modal.css('display', '');
1371
+
1372
+ // set size
1373
+ {
1374
+ var $size = this.$modal.find('> .media-modal');
1375
+
1376
+ if (
1377
+ $size.height() != this.current.height
1378
+ ||
1379
+ $size.width() != this.current.width
1380
+ ) {
1381
+ $size.animate({
1382
+ 'height': this.current.height +'px',
1383
+ 'width': this.current.width +'px'
1384
+ }, this.animationTime);
1385
+ }
1386
+
1387
+ $size = undefined;
1388
+ }
1389
+
1390
+ this.currentMethodTimeoutId = setTimeout(_.bind(function() {
1391
+ this.currentMethod = '';
1392
+
1393
+ if (this.runPendingMethod()) {
1394
+ return;
1395
+ }
1396
+
1397
+ if (this.current.autoHide > 0) {
1398
+ this.currentMethod = 'auto-hide';
1399
+ this.currentMethodTimeoutId = setTimeout(_.bind(function () {
1400
+ this.currentMethod = '';
1401
+ this.hide();
1402
+ }, this), this.current.autoHide);
1403
+ }
1404
+ }, this), this.animationTime * 2);
1405
+ },
1406
+ /**
1407
+ * @param {String} [id]
1408
+ * @returns {boolean}
1409
+ */
1410
+ hide: function(id) {
1411
+ if (typeof id != 'undefined') {
1412
+ if (this.current && this.current.id == id) {
1413
+ // this id is currently displayed, hide it
1414
+ } else {
1415
+ // remove id from queue
1416
+ this.queue = _.filter(this.queue, function (item) {
1417
+ return item.id != id;
1418
+ });
1419
+ return true;
1420
+ }
1421
+ }
1422
+
1423
+ if (this.currentMethod) {
1424
+ if (this.currentMethod == 'hide') {
1425
+ return false;
1426
+ } else if (this.currentMethod == 'auto-hide') {
1427
+ clearTimeout(this.currentMethodTimeoutId);
1428
+ } else {
1429
+ this.pendingMethod = 'hide';
1430
+ return true;
1431
+ }
1432
+ }
1433
+
1434
+ this.currentMethod = '';
1435
+
1436
+ if (!this.current) {
1437
+ // nothing to hide
1438
+ return this.runPendingMethod();;
1439
+ }
1440
+
1441
+ this.currentMethod = 'hide';
1442
+
1443
+ if (this.queue.length && !this.queue[0].hidePrevious) {
1444
+ // replace content
1445
+ this.$getContent().fadeOut('fast', _.bind(function(){
1446
+ this.currentMethod = '';
1447
+ this.current = null;
1448
+ this.show();
1449
+ this.$getContent().fadeIn('fast');
1450
+ }, this));
1451
+
1452
+ return true;
1453
+ }
1454
+
1455
+ this.$modal.addClass('fw-modal-closing');
1456
+
1457
+ this.currentMethodTimeoutId = setTimeout(_.bind(function(){
1458
+ this.currentMethod = '';
1459
+
1460
+ this.$modal.css('display', 'none');
1461
+
1462
+ this.$modal.removeClass('fw-modal-open');
1463
+ this.$modal.removeClass('fw-modal-closing');
1464
+
1465
+ this.setContent('');
1466
+
1467
+ this.current = null;
1468
+
1469
+ this.runPendingMethod();
1470
+ }, this), this.animationTime);
1471
+ }
1472
+ };
1473
+
1474
+ return {
1475
+ show: function(id, html, opts) {
1476
+ inst.show(id, html, opts);
1477
+ },
1478
+ hide: function(id){
1479
+ inst.hide(id);
1480
+ },
1481
+ /**
1482
+ * Generate flash messages html for soleModal content
1483
+ */
1484
+ renderFlashMessages: function (flashMessages) {
1485
+ var html = [],
1486
+ typeHtml = [],
1487
+ typeMessageClass = '',
1488
+ typeIconClass = '',
1489
+ typeTitle = '';
1490
+
1491
+ jQuery.each(flashMessages, function(type, messages){
1492
+ typeHtml = [];
1493
+
1494
+ switch (type) {
1495
+ case 'error':
1496
+ typeMessageClass = 'fw-text-danger';
1497
+ typeIconClass = 'dashicons dashicons-dismiss';
1498
+ typeTitle = _fw_localized.l10n.ah_sorry;
1499
+ break;
1500
+ case 'warning':
1501
+ typeMessageClass = 'fw-text-warning';
1502
+ typeIconClass = 'dashicons dashicons-no-alt';
1503
+ typeTitle = _fw_localized.l10n.ah_sorry;
1504
+ break;
1505
+ case 'success':
1506
+ typeMessageClass = 'fw-text-success';
1507
+ typeIconClass = 'dashicons dashicons-star-filled';
1508
+ typeTitle = _fw_localized.l10n.done;
1509
+ break;
1510
+ case 'info':
1511
+ typeMessageClass = 'fw-text-info';
1512
+ typeIconClass = 'dashicons dashicons-info';
1513
+ typeTitle = _fw_localized.l10n.done;
1514
+ break;
1515
+ default:
1516
+ typeMessageClass = typeIconClass = typeTitle = '';
1517
+ }
1518
+
1519
+ jQuery.each(messages, function(messageId, message){
1520
+ typeHtml.push(
1521
+ '<li>'+
1522
+ '<h2 class="'+ typeMessageClass +'"><span class="'+ typeIconClass +'"></span> '+ typeTitle +'</h2>'+
1523
+ '<p class="fw-text-muted"><em>'+ message +'</em></p>'+
1524
+ '</li>'
1525
+ );
1526
+ });
1527
+
1528
+ if (typeHtml.length) {
1529
+ html.push(
1530
+ '<ul>'+ typeHtml.join('</ul><ul>') +'</ul>'
1531
+ );
1532
+ }
1533
+ });
1534
+
1535
+ return html.join('');
1536
+ }
1537
+ };
1538
+ })();
framework/views/backend-settings-form.php CHANGED
@@ -6,11 +6,6 @@
6
  * @var string $reset_input_name
7
  */
8
  ?>
9
- <!--
10
- wp moves flash message error with js after first h2
11
- if there are no h2 on the page it shows them wrong
12
- -->
13
- <h2 class="fw-hidden"></h2>
14
 
15
  <?php echo fw()->backend->render_options($options, $values); ?>
16
 
@@ -30,29 +25,13 @@ jQuery(function($){
30
 
31
  $form.on("click", ".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a", function(){
32
  $form.find("input[name='<?php echo esc_js($focus_tab_input_name); ?>']").val(
33
- $(this).attr("href").replace(/^\\#/, "") // tab id
34
  );
35
  });
36
 
37
  /* "wait" after tabs initialized */
38
  setTimeout(function(){
39
- var focusTabId = $.trim("<?php echo esc_js($focus_tab_id) ?>");
40
-
41
- if (!focusTabId.length) {
42
- return;
43
- }
44
-
45
- var $tabLink = $(".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a[href=\'#"+ focusTabId +"\']");
46
-
47
- while ($tabLink.length) {
48
- $tabLink.trigger("click");
49
- $tabLink = $tabLink
50
- .closest(".fw-options-tabs-wrapper").parent().closest(".fw-options-tabs-wrapper")
51
- .find("> .fw-options-tabs-list > ul > li > a[href=\'#"+ $tabLink.closest(".fw-options-tab").attr("id") +"\']");
52
- }
53
-
54
- // click again on focus tab to update the input value
55
- $(".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a[href=\'#"+ focusTabId +"\']").trigger("click");;
56
  }, 200);
57
  });
58
  });
@@ -62,7 +41,7 @@ jQuery(function($){
62
  <!-- reset warning -->
63
  <script type="text/javascript">
64
  jQuery(function($){
65
- $('form[data-fw-form-id="fw_settings"] input[name="<?php echo esc_js($reset_input_name) ?>"]').on('click.fw-reset-warning', function(e){
66
  /**
67
  * on confirm() the submit input looses focus
68
  * fwForm.isAdminPage() must be able to select the input to send it in _POST
@@ -77,6 +56,7 @@ jQuery(function($){
77
  echo esc_js(__("Click OK to reset.\nAll settings will be lost and replaced with default settings!", 'fw'))
78
  ?>')) {
79
  e.preventDefault();
 
80
  }
81
  });
82
  });
@@ -84,14 +64,171 @@ jQuery(function($){
84
  <!-- end: reset warning -->
85
 
86
  <!-- ajax submit -->
87
- <!--
88
  <script type="text/javascript">
89
  jQuery(function ($) {
 
 
 
 
 
 
 
 
 
 
90
  fwForm.initAjaxSubmit({
91
- selector: 'form[data-fw-form-id="fw_settings"]',
92
- ajaxUrl: ajaxurl
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  });
94
  });
95
  </script>
96
- -->
97
  <!-- end: ajax submit -->
6
  * @var string $reset_input_name
7
  */
8
  ?>
 
 
 
 
 
9
 
10
  <?php echo fw()->backend->render_options($options, $values); ?>
11
 
25
 
26
  $form.on("click", ".fw-options-tabs-wrapper > .fw-options-tabs-list > ul > li > a", function(){
27
  $form.find("input[name='<?php echo esc_js($focus_tab_input_name); ?>']").val(
28
+ $(this).attr("href").replace(/^#/, "") // tab id
29
  );
30
  });
31
 
32
  /* "wait" after tabs initialized */
33
  setTimeout(function(){
34
+ fwBackendOptions.openTab($.trim("<?php echo esc_js($focus_tab_id) ?>"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }, 200);
36
  });
37
  });
41
  <!-- reset warning -->
42
  <script type="text/javascript">
43
  jQuery(function($){
44
+ $(document.body).on('click.fw-reset-warning', 'form[data-fw-form-id="fw_settings"] input[name="<?php echo esc_js($reset_input_name) ?>"]', function(e){
45
  /**
46
  * on confirm() the submit input looses focus
47
  * fwForm.isAdminPage() must be able to select the input to send it in _POST
56
  echo esc_js(__("Click OK to reset.\nAll settings will be lost and replaced with default settings!", 'fw'))
57
  ?>')) {
58
  e.preventDefault();
59
+ $(this).removeAttr('clicked');
60
  }
61
  });
62
  });
64
  <!-- end: reset warning -->
65
 
66
  <!-- ajax submit -->
 
67
  <script type="text/javascript">
68
  jQuery(function ($) {
69
+ <?php if (!fw()->theme->get_config('settings_form_ajax_submit', true)): ?>
70
+ return; // ajax submit is disabled in theme config
71
+ <?php endif; ?>
72
+
73
+ function isReset($submitButton) {
74
+ return $submitButton.length && $submitButton.attr('name') == '<?php echo esc_js($reset_input_name) ?>';
75
+ }
76
+
77
+ var formSelector = 'form[data-fw-form-id="fw_settings"]';
78
+
79
  fwForm.initAjaxSubmit({
80
+ selector: formSelector,
81
+ loading: function(elements, show) {
82
+ if (show) {
83
+ var title, description;
84
+
85
+ if (isReset(elements.$submitButton)) {
86
+ title = '<?php echo esc_js(__('Resetting', 'fw')) ?>';
87
+ description =
88
+ '<?php echo esc_js(__('We are currently resetting your settings.', 'fw')) ?>'+
89
+ '<br/>'+
90
+ '<?php echo esc_js(__('This may take a few moments.', 'fw')) ?>';
91
+ } else {
92
+ title = '<?php echo esc_js(__('Saving', 'fw')) ?>';
93
+ description =
94
+ '<?php echo esc_js(__('We are currently saving your settings.', 'fw')) ?>'+
95
+ '<br/>'+
96
+ '<?php echo esc_js(__('This may take a few moments.', 'fw')) ?>';
97
+ }
98
+
99
+ fw.soleModal.show(
100
+ 'fw-options-ajax-save-loading',
101
+ '<h2 class="fw-text-muted">'+
102
+ '<img src="'+ fw.img.loadingSpinner +'" style="vertical-align: bottom;" /> '+
103
+ title +
104
+ '</h2>'+
105
+ '<p class="fw-text-muted"><em>'+ description +'</em></p>',
106
+ {
107
+ autoHide: 60000,
108
+ allowClose: false
109
+ }
110
+ );
111
+ } else {
112
+ // fw.soleModal.hide('fw-options-ajax-save-loading'); // we need to show loading until the form reset ajax will finish
113
+ }
114
+ },
115
+ onErrors: function() {
116
+ fw.soleModal.hide('fw-options-ajax-save-loading');
117
+ },
118
+ onAjaxError: function() {
119
+ fw.soleModal.hide('fw-options-ajax-save-loading');
120
+ },
121
+ onSuccess: function(elements, ajaxData) {
122
+ /**
123
+ * Display messages
124
+ */
125
+ do {
126
+ /**
127
+ * Don't display the "Settings successfully saved" message
128
+ * users will click often on the Save button, it's obvious it was saved if no error is shown.
129
+ */
130
+ delete ajaxData.flash_messages.success.fw_settings_form_save;
131
+
132
+ if (
133
+ _.isEmpty(ajaxData.flash_messages.error)
134
+ &&
135
+ _.isEmpty(ajaxData.flash_messages.warning)
136
+ &&
137
+ _.isEmpty(ajaxData.flash_messages.info)
138
+ &&
139
+ _.isEmpty(ajaxData.flash_messages.success)
140
+ ) {
141
+ // no messages to display
142
+ break;
143
+ }
144
+
145
+ var noErrors = _.isEmpty(ajaxData.flash_messages.error) && _.isEmpty(ajaxData.flash_messages.warning);
146
+
147
+ fw.soleModal.show(
148
+ 'fw-options-ajax-save-success',
149
+ '<div style="margin: 0 35px;">'+ fw.soleModal.renderFlashMessages(ajaxData.flash_messages) +'</div>',
150
+ {
151
+ autoHide: noErrors
152
+ ? 1000 // hide fast the message if everything went fine
153
+ : 10000,
154
+ showCloseButton: false,
155
+ hidePrevious: noErrors ? false : true // close and open popup when there are errors
156
+ }
157
+ );
158
+ } while(false);
159
+
160
+ /**
161
+ * Refresh form html on Reset
162
+ */
163
+ if (isReset(elements.$submitButton)) {
164
+ jQuery.ajax({
165
+ type: "GET",
166
+ dataType: 'text'
167
+ }).done(function(html){
168
+ fw.soleModal.hide('fw-options-ajax-save-loading');
169
+
170
+ var $form = jQuery(formSelector, html);
171
+ html = undefined; // not needed anymore
172
+
173
+ if (!$form.length) {
174
+ alert('Can\'t find the form in the ajax response');
175
+ return;
176
+ }
177
+
178
+ // waitSoleModalFadeOut -> formFadeOut -> formReplace -> formFadeIn
179
+ setTimeout(function(){
180
+ elements.$form.css('transition', 'opacity ease .3s');
181
+ elements.$form.css('opacity', '0');
182
+ setTimeout(function() {
183
+ var focusTabId = elements.$form.find("input[name='<?php echo esc_js($focus_tab_input_name); ?>']").val();
184
+ var scrollTop = jQuery(window).scrollTop();
185
+
186
+ // replace form html
187
+ {
188
+ elements.$form.css({
189
+ 'display': 'block',
190
+ 'height': elements.$form.height() +'px'
191
+ });
192
+ elements.$form.get(0).innerHTML = $form.get(0).innerHTML;
193
+ $form = undefined; // not needed anymore
194
+ elements.$form.css({
195
+ 'display': '',
196
+ 'height': ''
197
+ });
198
+ elements.$form.find('.fw-options-tabs-wrapper').css('opacity', '');
199
+ }
200
+
201
+ fwEvents.trigger('fw:options:init', {$elements: elements.$form});
202
+
203
+ fwBackendOptions.openTab(focusTabId);
204
+
205
+ jQuery(window).scrollTop(scrollTop);
206
+
207
+ // fadeIn
208
+ {
209
+ elements.$form.css('opacity', '');
210
+ setTimeout(function(){
211
+ elements.$form.css('transition', '');
212
+ elements.$form.css('visibility', '');
213
+ }, 300);
214
+ }
215
+ }, 300);
216
+ }, 300);
217
+ }).fail(function(jqXHR, textStatus, errorThrown){
218
+ fw.soleModal.hide('fw-options-ajax-save-loading');
219
+ elements.$form.css({
220
+ 'opacity': '',
221
+ 'transition': '',
222
+ 'visibility': ''
223
+ });
224
+ console.error(jqXHR, textStatus, errorThrown);
225
+ alert('Ajax error (more details in console)');
226
+ });
227
+ } else {
228
+ fw.soleModal.hide('fw-options-ajax-save-loading');
229
+ }
230
+ }
231
  });
232
  });
233
  </script>
 
234
  <!-- end: ajax submit -->
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: unyson, themefusecom
3
  Tags: page builder, cms, grid, layout, responsive, back up, backup, db backup, dump, migrate, schedule, search engine optimization, seo, media, slideshow, shortcode, slide, slideshare, slideshow, google sitemaps, sitemaps, analytics, google analytics, calendar, event, events, google maps, learning, lessons, sidebars, breadcrumbs, review, portfolio, framework
4
  Requires at least: 4.0.0
5
  Tested up to: 4.1
6
- Stable tag: 2.1.21
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -86,8 +86,13 @@ Yes; Unyson will work with any theme.
86
 
87
  == Changelog ==
88
 
 
 
 
 
 
89
  = 2.1.21 =
90
- * Made `forms` and `mailer` extensions hidden.
91
 
92
  = 2.1.20 =
93
  * Added warning on Theme Settings reset [^](http://static.md/0fcf01628eddab75fdbedb3a24784db3.png)
@@ -95,7 +100,7 @@ Yes; Unyson will work with any theme.
95
  * Option type `slider` and `range-slider` fixes [#210](https://github.com/ThemeFuse/Unyson/issues/210)
96
  * Option type `typography`: Added filter on standard fonts [#212](https://github.com/ThemeFuse/Unyson/issues/212)
97
  * Option type `radio` and `checkboxes`: Added `inline` parameter [#216](https://github.com/ThemeFuse/Unyson/issues/216)
98
- * Minor internal fixes
99
 
100
  = 2.1.19 =
101
  * Option type `icon`: Updated Font Awesome to 4.3.0
@@ -117,7 +122,7 @@ Yes; Unyson will work with any theme.
117
 
118
  = 2.1.16 =
119
  * Added the "Reset" button on the Theme Settings page
120
- * Minor internal fixes
121
 
122
  = 2.1.15 =
123
  * Minor fix for extension download link
3
  Tags: page builder, cms, grid, layout, responsive, back up, backup, db backup, dump, migrate, schedule, search engine optimization, seo, media, slideshow, shortcode, slide, slideshare, slideshow, google sitemaps, sitemaps, analytics, google analytics, calendar, event, events, google maps, learning, lessons, sidebars, breadcrumbs, review, portfolio, framework
4
  Requires at least: 4.0.0
5
  Tested up to: 4.1
6
+ Stable tag: 2.1.22
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
86
 
87
  == Changelog ==
88
 
89
+ = 2.1.22 =
90
+ * Theme Settings form ajax submit [#198](https://github.com/ThemeFuse/Unyson/issues/198)
91
+ * Added javascript helper `fw.soleModal()`
92
+ * Minor fixes
93
+
94
  = 2.1.21 =
95
+ * Made the `forms` and `mailer` extensions hidden.
96
 
97
  = 2.1.20 =
98
  * Added warning on Theme Settings reset [^](http://static.md/0fcf01628eddab75fdbedb3a24784db3.png)
100
  * Option type `slider` and `range-slider` fixes [#210](https://github.com/ThemeFuse/Unyson/issues/210)
101
  * Option type `typography`: Added filter on standard fonts [#212](https://github.com/ThemeFuse/Unyson/issues/212)
102
  * Option type `radio` and `checkboxes`: Added `inline` parameter [#216](https://github.com/ThemeFuse/Unyson/issues/216)
103
+ * Minor fixes
104
 
105
  = 2.1.19 =
106
  * Option type `icon`: Updated Font Awesome to 4.3.0
122
 
123
  = 2.1.16 =
124
  * Added the "Reset" button on the Theme Settings page
125
+ * Minor fixes
126
 
127
  = 2.1.15 =
128
  * Minor fix for extension download link
unyson.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.themefuse.com/
5
  * Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
6
- * Version: 2.1.21
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.themefuse.com/
5
  * Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
6
+ * Version: 2.1.22
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+