Formidable Forms – Form Builder for WordPress - Version 5.5.2

Version Description

  • New: When dragging a field or field group, a smaller drag helper will be used that is more consistent with the field type options in the sidebar, and the field that is being dragged will gray out.
  • New: Improved error handling when installing templates from XML. A message will be shown when the simple XML extension is not installed, and errors will now be shown in a pop up when a template falls to install.
  • New: Added a new frm_before_simple_form_render action that triggers before a Gutenberg block is rendered.
  • New: Added a new frm_rich_text_emails filter for cases when a plain textarea may be preferred over rich text.
  • New: Fields with floating labels now take up less space in the front end.
  • Fix: When inserting a [default-plain] or [default-html] shortcode into a rich text editor, the shortcode would insert instead of changing dynamically as expected.
  • Fix: Rich text HTML emails were not automatically using wpautop so line breaks were not appearing in emails as expected.
  • Fix: Improved how drag and drop works when dragging multiple sections in a field group.
  • Fix: Prevent a warning that would trigger when previewing in-theme when using Twenty Twenty One.
  • Fix: Prevent a fatal error that would only ever happen once, immediately when upgrading to version 5.5 or higher from an older version.
  • Fix: Prevent a fatal error that was preventing add ons from installing via API.
  • Fix: Updated radio button styling on the Edit Entry page to fix visual issues at mobile screen sizes.
  • Embed examples no longer include title=true and description=true.
  • License types will no longer appear as application template category options.
  • Plain text email actions will no longer use a rich text editor.
Download this release

Release Info

Developer sswells
Plugin Icon 128x128 Formidable Forms – Form Builder for WordPress
Version 5.5.2
Comparing to
See all releases

Code changes from version 5.5.1 to 5.5.2

classes/controllers/FrmAddonsController.php CHANGED
@@ -317,11 +317,9 @@ class FrmAddonsController {
317
  return $transient;
318
  }
319
 
320
- $version_info = self::fill_update_addon_info( $installed_addons );
321
-
322
  $transient->last_checked = time();
323
-
324
- $wp_plugins = FrmAppHelper::get_plugins();
325
 
326
  foreach ( $version_info as $id => $plugin ) {
327
  $plugin = (object) $plugin;
@@ -356,6 +354,21 @@ class FrmAddonsController {
356
  return $transient;
357
  }
358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  /**
360
  * Check if a plugin is installed before showing an update for it
361
  *
@@ -366,7 +379,7 @@ class FrmAddonsController {
366
  * @return bool - True if installed
367
  */
368
  protected static function is_installed( $plugin ) {
369
- $all_plugins = FrmAppHelper::get_plugins();
370
  return isset( $all_plugins[ $plugin ] );
371
  }
372
 
@@ -861,7 +874,7 @@ class FrmAddonsController {
861
  $response = array();
862
 
863
  // It's already installed and active.
864
- $active = activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
865
  if ( is_wp_error( $active ) ) {
866
  // The plugin was installed, but not active. Download it now.
867
  self::ajax_install_addon();
@@ -874,6 +887,22 @@ class FrmAddonsController {
874
  wp_die();
875
  }
876
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
  /**
878
  * @since 5.0.10
879
  *
@@ -1028,7 +1057,7 @@ class FrmAddonsController {
1028
  return;
1029
  }
1030
 
1031
- $activate = activate_plugin( $installed );
1032
  if ( is_wp_error( $activate ) ) {
1033
  // Ignore the invalid header message that shows with nested plugins.
1034
  if ( $activate->get_error_code() !== 'no_plugin_header' ) {
@@ -1118,7 +1147,7 @@ class FrmAddonsController {
1118
  delete_option( 'frm_connect_token' );
1119
 
1120
  // It's already installed and active.
1121
- $active = activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
1122
  if ( is_wp_error( $active ) ) {
1123
  // Download plugin now.
1124
  $response = self::download_and_activate();
317
  return $transient;
318
  }
319
 
320
+ $version_info = self::fill_update_addon_info( $installed_addons );
 
321
  $transient->last_checked = time();
322
+ $wp_plugins = self::get_plugins();
 
323
 
324
  foreach ( $version_info as $id => $plugin ) {
325
  $plugin = (object) $plugin;
354
  return $transient;
355
  }
356
 
357
+ /**
358
+ * Copy of FrmAppHelper::get_plugins.
359
+ * Because this gets called on "pre_set_site_transient_update_plugins" an old version of FrmAppHelper may be loaded on plugin update.
360
+ * This means that trying to access FrmAppHelper::get_plugins when upgrading from a Lite version before v5.5 results in a one-off error.
361
+ *
362
+ * @since 5.5.2
363
+ * @return array
364
+ */
365
+ protected static function get_plugins() {
366
+ if ( ! function_exists( 'get_plugins' ) ) {
367
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
368
+ }
369
+ return get_plugins();
370
+ }
371
+
372
  /**
373
  * Check if a plugin is installed before showing an update for it
374
  *
379
  * @return bool - True if installed
380
  */
381
  protected static function is_installed( $plugin ) {
382
+ $all_plugins = self::get_plugins();
383
  return isset( $all_plugins[ $plugin ] );
384
  }
385
 
874
  $response = array();
875
 
876
  // It's already installed and active.
877
+ $active = self::activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
878
  if ( is_wp_error( $active ) ) {
879
  // The plugin was installed, but not active. Download it now.
880
  self::ajax_install_addon();
887
  wp_die();
888
  }
889
 
890
+ /**
891
+ * @since 5.5.2
892
+ *
893
+ * @param string $plugin
894
+ * @param string $redirect
895
+ * @param bool $network_wide
896
+ * @param bool $silent
897
+ * @return null|WP_Error Null on success, WP_Error on invalid file.
898
+ */
899
+ protected static function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
900
+ if ( ! function_exists( 'activate_plugin' ) ) {
901
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
902
+ }
903
+ return activate_plugin( $plugin, $redirect, $network_wide, $silent );
904
+ }
905
+
906
  /**
907
  * @since 5.0.10
908
  *
1057
  return;
1058
  }
1059
 
1060
+ $activate = self::activate_plugin( $installed );
1061
  if ( is_wp_error( $activate ) ) {
1062
  // Ignore the invalid header message that shows with nested plugins.
1063
  if ( $activate->get_error_code() !== 'no_plugin_header' ) {
1147
  delete_option( 'frm_connect_token' );
1148
 
1149
  // It's already installed and active.
1150
+ $active = self::activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
1151
  if ( is_wp_error( $active ) ) {
1152
  // Download plugin now.
1153
  $response = self::download_and_activate();
classes/controllers/FrmEntriesController.php CHANGED
@@ -666,6 +666,13 @@ class FrmEntriesController {
666
  if ( isset( $data['browser'] ) ) {
667
  $browser = FrmEntriesHelper::get_browser( $data['browser'] );
668
  }
 
 
 
 
 
 
 
669
  }
670
 
671
  include( FrmAppHelper::plugin_path() . '/classes/views/frm-entries/sidebar-shared.php' );
666
  if ( isset( $data['browser'] ) ) {
667
  $browser = FrmEntriesHelper::get_browser( $data['browser'] );
668
  }
669
+ /**
670
+ * Add or remove information in the entry sidebar.
671
+ *
672
+ * @since 5.5.2
673
+ * @param array $data
674
+ */
675
+ $data = apply_filters( 'frm_sidebar_data', $data, compact( 'entry' ) );
676
  }
677
 
678
  include( FrmAppHelper::plugin_path() . '/classes/views/frm-entries/sidebar-shared.php' );
classes/controllers/FrmFormsController.php CHANGED
@@ -318,7 +318,6 @@ class FrmFormsController {
318
  }
319
 
320
  $random_page = get_posts( $page_query );
321
-
322
  if ( ! $random_page ) {
323
  return;
324
  }
@@ -330,6 +329,23 @@ class FrmFormsController {
330
  'page_id' => $random_page->ID,
331
  )
332
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  }
334
 
335
  /**
318
  }
319
 
320
  $random_page = get_posts( $page_query );
 
321
  if ( ! $random_page ) {
322
  return;
323
  }
329
  'page_id' => $random_page->ID,
330
  )
331
  );
332
+
333
+ // Fixes Pro issue #3004. Prevent an undefined $post object.
334
+ // Otherwise WordPress themes will trigger a warning "Attempt to read property "comment_count" on null".
335
+ self::set_post_global( $random_page );
336
+ }
337
+
338
+ /**
339
+ * Set the WP $post global object. Used for in-theme preview when defining a page.
340
+ *
341
+ * @since 5.5.2
342
+ *
343
+ * @param WP_Post $post
344
+ * @return void
345
+ */
346
+ private static function set_post_global( $page ) {
347
+ global $post;
348
+ $post = $page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
349
  }
350
 
351
  /**
classes/controllers/FrmSimpleBlocksController.php CHANGED
@@ -131,8 +131,7 @@ class FrmSimpleBlocksController {
131
  /**
132
  * Renders a form given the specified attributes.
133
  *
134
- * @param $attributes
135
- *
136
  * @return string
137
  */
138
  public static function simple_form_render( $attributes ) {
@@ -140,6 +139,12 @@ class FrmSimpleBlocksController {
140
  return '';
141
  }
142
 
 
 
 
 
 
 
143
  $params = array_filter( $attributes );
144
  $params['id'] = $params['formId'];
145
  unset( $params['formId'] );
131
  /**
132
  * Renders a form given the specified attributes.
133
  *
134
+ * @param array $attributes
 
135
  * @return string
136
  */
137
  public static function simple_form_render( $attributes ) {
139
  return '';
140
  }
141
 
142
+ /**
143
+ * @since 5.5.2
144
+ * @param array $attributes
145
+ */
146
+ do_action( 'frm_before_simple_form_render', $attributes );
147
+
148
  $params = array_filter( $attributes );
149
  $params['id'] = $params['formId'];
150
  unset( $params['formId'] );
classes/controllers/FrmXMLController.php CHANGED
@@ -35,11 +35,20 @@ class FrmXMLController {
35
  * Use the template link to install the XML template
36
  *
37
  * @since 3.06
 
38
  */
39
  public static function install_template() {
40
  FrmAppHelper::permission_check( 'frm_edit_forms' );
41
  check_ajax_referer( 'frm_ajax', 'nonce' );
42
 
 
 
 
 
 
 
 
 
43
  $url = FrmAppHelper::get_param( 'xml', '', 'post', 'esc_url_raw' );
44
 
45
  $form = self::get_posted_form();
@@ -51,7 +60,7 @@ class FrmXMLController {
51
 
52
  if ( ! $xml ) {
53
  $response = array(
54
- 'message' => __( 'There was an error reading the form template', 'formidable' ),
55
  );
56
  echo wp_json_encode( $response );
57
  wp_die();
35
  * Use the template link to install the XML template
36
  *
37
  * @since 3.06
38
+ * @return void
39
  */
40
  public static function install_template() {
41
  FrmAppHelper::permission_check( 'frm_edit_forms' );
42
  check_ajax_referer( 'frm_ajax', 'nonce' );
43
 
44
+ if ( ! function_exists( 'simplexml_load_string' ) ) {
45
+ $response = array(
46
+ 'message' => __( 'Your server is missing the Simple XML extension. This is required to install a template.', 'formidable' ),
47
+ );
48
+ echo wp_json_encode( $response );
49
+ wp_die();
50
+ }
51
+
52
  $url = FrmAppHelper::get_param( 'xml', '', 'post', 'esc_url_raw' );
53
 
54
  $form = self::get_posted_form();
60
 
61
  if ( ! $xml ) {
62
  $response = array(
63
+ 'message' => __( 'There was an error reading the form template.', 'formidable' ),
64
  );
65
  echo wp_json_encode( $response );
66
  wp_die();
classes/helpers/FrmAppHelper.php CHANGED
@@ -16,7 +16,7 @@ class FrmAppHelper {
16
  /**
17
  * @since 2.0
18
  */
19
- public static $plug_version = '5.5.1';
20
 
21
  /**
22
  * @since 1.07.02
16
  /**
17
  * @since 2.0
18
  */
19
+ public static $plug_version = '5.5.2';
20
 
21
  /**
22
  * @since 1.07.02
classes/helpers/FrmFormsHelper.php CHANGED
@@ -1340,6 +1340,8 @@ BEFORE_HTML;
1340
 
1341
  /**
1342
  * @since 4.03.01
 
 
1343
  */
1344
  public static function ignore_template_categories() {
1345
  return array( 'Business', 'Elite', 'Personal', 'Creator', 'Basic', 'free' );
1340
 
1341
  /**
1342
  * @since 4.03.01
1343
+ *
1344
+ * @return array<string>
1345
  */
1346
  public static function ignore_template_categories() {
1347
  return array( 'Business', 'Elite', 'Personal', 'Creator', 'Basic', 'free' );
classes/models/FrmApplicationTemplate.php CHANGED
@@ -80,13 +80,29 @@ class FrmApplicationTemplate {
80
  */
81
  private static function populate_category_information( $categories ) {
82
  foreach ( $categories as $category ) {
83
- if ( false !== strpos( $category, '+Views' ) || in_array( $category, self::$categories, true ) ) {
 
 
 
84
  continue;
85
  }
86
  self::$categories[] = $category;
87
  }
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  /**
91
  * @return array<string>
92
  */
80
  */
81
  private static function populate_category_information( $categories ) {
82
  foreach ( $categories as $category ) {
83
+ if ( self::category_matches_a_license_type( $category ) ) {
84
+ continue;
85
+ }
86
+ if ( in_array( $category, self::$categories, true ) ) {
87
  continue;
88
  }
89
  self::$categories[] = $category;
90
  }
91
  }
92
 
93
+ /**
94
+ * @since 5.5.2
95
+ *
96
+ * @param string $category
97
+ * @return bool
98
+ */
99
+ private static function category_matches_a_license_type( $category ) {
100
+ if ( false !== strpos( $category, '+Views' ) ) {
101
+ return true;
102
+ }
103
+ return in_array( $category, FrmFormsHelper::ignore_template_categories(), true );
104
+ }
105
+
106
  /**
107
  * @return array<string>
108
  */
classes/models/FrmEmail.php CHANGED
@@ -361,7 +361,13 @@ class FrmEmail {
361
  * @since 2.03.04
362
  */
363
  private function set_message() {
364
- $this->message = FrmFieldsHelper::basic_replace_shortcodes( $this->settings['email_message'], $this->form, $this->entry );
 
 
 
 
 
 
365
 
366
  $prev_mail_body = $this->message;
367
  $pass_entry = clone $this->entry; // make a copy to prevent changes by reference
@@ -390,6 +396,8 @@ class FrmEmail {
390
 
391
  if ( $this->is_plain_text ) {
392
  $this->message = wp_specialchars_decode( strip_tags( $this->message ), ENT_QUOTES );
 
 
393
  }
394
 
395
  $this->message = apply_filters( 'frm_email_message', $this->message, $this->package_atts() );
361
  * @since 2.03.04
362
  */
363
  private function set_message() {
364
+ $this->message = $this->settings['email_message'];
365
+
366
+ if ( ! $this->is_plain_text ) {
367
+ $this->message = html_entity_decode( $this->message ); // The decode is to support [default-html] shortcodes.
368
+ }
369
+
370
+ $this->message = FrmFieldsHelper::basic_replace_shortcodes( $this->message, $this->form, $this->entry );
371
 
372
  $prev_mail_body = $this->message;
373
  $pass_entry = clone $this->entry; // make a copy to prevent changes by reference
396
 
397
  if ( $this->is_plain_text ) {
398
  $this->message = wp_specialchars_decode( strip_tags( $this->message ), ENT_QUOTES );
399
+ } else {
400
+ $this->message = wpautop( $this->message, false ); // HTML emails should use autop.
401
  }
402
 
403
  $this->message = apply_filters( 'frm_email_message', $this->message, $this->package_atts() );
classes/models/FrmEntryValues.php CHANGED
@@ -273,7 +273,7 @@ class FrmEntryValues {
273
  * @param array $referrer
274
  * @param array @entry_description
275
  */
276
- $referrer = apply_filters( 'frm_user_info_referrer', $referrer, $entry_description );
277
 
278
  $this->user_info = array(
279
  'ip' => $ip,
273
  * @param array $referrer
274
  * @param array @entry_description
275
  */
276
+ $referrer = apply_filters( 'frm_user_info_referrer', $referrer, $entry_description, $this->entry );
277
 
278
  $this->user_info = array(
279
  'ip' => $ip,
classes/views/frm-form-actions/_email_settings.php CHANGED
@@ -77,16 +77,35 @@ if ( ! defined( 'ABSPATH' ) ) {
77
  <?php esc_html_e( 'Message', 'formidable' ); ?>
78
  </label>
79
  <?php
80
- $editor_args = array(
81
- 'textarea_name' => $this->get_field_name( 'email_message' ),
82
- 'textarea_rows' => 6,
83
- 'editor_class' => 'frm_not_email_message',
84
- );
85
- wp_editor(
86
- $form_action->post_content['email_message'],
87
- $this->get_field_id( 'email_message' ),
88
- $editor_args
89
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  ?>
91
  </p>
92
 
77
  <?php esc_html_e( 'Message', 'formidable' ); ?>
78
  </label>
79
  <?php
80
+ $rich_text_emails = empty( $form_action->post_content['plain_text'] );
81
+
82
+ /**
83
+ * @since 5.5.2
84
+ *
85
+ * @param bool $rich_text_emails True by default unless plain text is selected.
86
+ * @param array $args {
87
+ * @type stdClass $form
88
+ * @type WP_Post $form_action
89
+ * }
90
+ */
91
+ $rich_text_emails = apply_filters( 'frm_rich_text_emails', $rich_text_emails, compact( 'form', 'form_action' ) );
92
+
93
+ if ( $rich_text_emails ) {
94
+ $editor_args = array(
95
+ 'textarea_name' => $this->get_field_name( 'email_message' ),
96
+ 'textarea_rows' => 6,
97
+ 'editor_class' => 'frm_not_email_message',
98
+ );
99
+ wp_editor(
100
+ $form_action->post_content['email_message'],
101
+ $this->get_field_id( 'email_message' ),
102
+ $editor_args
103
+ );
104
+ } else {
105
+ ?>
106
+ <textarea name="<?php echo esc_attr( $this->get_field_name( 'email_message' ) ); ?>" class="frm_not_email_message frm_long_input" id="<?php echo esc_attr( $this->get_field_id( 'email_message' ) ); ?>" cols="50" rows="5"><?php echo FrmAppHelper::esc_textarea( $form_action->post_content['email_message'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></textarea>
107
+ <?php
108
+ }
109
  ?>
110
  </p>
111
 
css/custom_theme.css.php CHANGED
@@ -443,8 +443,8 @@ legend.frm_hidden{
443
  /* Floating labels */
444
  .with_frm_style .frm_inside_container {
445
  position: relative;
446
- padding-top: 27px;
447
- padding-top: calc(0.85 * var(--field-height));
448
  }
449
 
450
  .with_frm_style .frm_inside_container > input,
@@ -476,8 +476,8 @@ legend.frm_hidden{
476
  transition: all 0.3s ease-in;
477
 
478
  position: absolute;
479
- top: 28px;
480
- top: calc(1px + 0.85 * var(--field-height));
481
  left: 3px;
482
  width: 100%;
483
 
@@ -505,6 +505,7 @@ legend.frm_hidden{
505
  .with_frm_style .frm_inside_container.frm_label_float_top > label {
506
  top: 0;
507
  left: 0;
 
508
  font-size: 12px;
509
  font-size: calc(0.85 * var(--field-font-size));
510
  }
@@ -527,6 +528,7 @@ legend.frm_hidden{
527
  opacity: 1;
528
  transition: opacity 0.3s ease-in;
529
  }
 
530
 
531
  .with_frm_style .frm_description,
532
  .with_frm_style .frm_pro_max_limit_desc{
443
  /* Floating labels */
444
  .with_frm_style .frm_inside_container {
445
  position: relative;
446
+ padding-top: 16px;
447
+ padding-top: calc(0.5 * var(--field-height));
448
  }
449
 
450
  .with_frm_style .frm_inside_container > input,
476
  transition: all 0.3s ease-in;
477
 
478
  position: absolute;
479
+ top: 17px;
480
+ top: calc(1px + .5 * var(--field-height));
481
  left: 3px;
482
  width: 100%;
483
 
505
  .with_frm_style .frm_inside_container.frm_label_float_top > label {
506
  top: 0;
507
  left: 0;
508
+ padding: 0;
509
  font-size: 12px;
510
  font-size: calc(0.85 * var(--field-font-size));
511
  }
528
  opacity: 1;
529
  transition: opacity 0.3s ease-in;
530
  }
531
+ /* End floating label */
532
 
533
  .with_frm_style .frm_description,
534
  .with_frm_style .frm_pro_max_limit_desc{
css/frm_admin.css CHANGED
@@ -3350,6 +3350,19 @@ input[type="checkbox"] {
3350
  margin-right: 5px;
3351
  }
3352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3353
  .frm_inner_field_container {
3354
  margin-bottom: 10px;
3355
  }
@@ -3377,6 +3390,24 @@ li.ui-state-default.selected > .frm_inner_field_container > label {
3377
  max-width: calc(100% - 100px);
3378
  }
3379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3380
  .frm_form_settings #op-popup, /* 1Password */
3381
  .frm_form_settings com-1password-op-button,
3382
  .frm_conf_below .frm_conf_field_container .frm_primary_label,
@@ -3820,7 +3851,7 @@ li.sortable-placeholder {
3820
  border: none;
3821
  margin: 0;
3822
  visibility: visible !important;
3823
- box-shadow: 2px -10px 0 1px var(--primary-color);
3824
  }
3825
 
3826
  #frm_form_editor_container > ul > .frm_single_option.ui-sortable-placeholder,
@@ -3833,7 +3864,6 @@ li.sortable-placeholder {
3833
  position: relative;
3834
  }
3835
 
3836
- #frm_form_editor_container > ul > li > ul > .frm_single_option.ui-sortable-placeholder,
3837
  #frm_form_editor_container > ul > li > ul > li.sortable-placeholder,
3838
  #frm_form_editor_container ul.start_divider > li > ul > li.sortable-placeholder {
3839
  position: absolute;
@@ -3842,36 +3872,27 @@ li.sortable-placeholder {
3842
  }
3843
 
3844
  .edit_field_type_divider + li.sortable-placeholder {
3845
- box-shadow: 2px 5px 0 1px var(--primary-color);
3846
  }
3847
 
3848
  .start_divider li.sortable-placeholder {
3849
- box-shadow: 2px -5px 0 1px var(--primary-color);
3850
  }
3851
 
3852
- .frm_single_option.ui-sortable-placeholder {
3853
- box-shadow: 0 0 1px 1px var(--primary-color);
3854
  }
3855
 
3856
- .frm-is-collapsed + .sortable-placeholder {
3857
- box-shadow: 2px 15px 0 1px var(--primary-color);
3858
  }
3859
 
3860
  .frm_sorting > li.edit_field_type_end_divider:first-child,
3861
- .frm_sorting > .frmbutton + .sortable-placeholder, /* hide for first field */
3862
- .no-drop-placeholder,
3863
  .frm-show-click,
3864
  li.ui-state-default.edit_field_type_divider .frm-show-click {
3865
  display: none;
3866
  }
3867
 
3868
- .frm_form_field.ui-sortable-helper {
3869
- height: 30px !important;
3870
- background-color: #fff !important;
3871
- overflow: hidden !important;
3872
- box-sizing: border-box;
3873
- }
3874
-
3875
  .frm_form_field.ui-sortable-helper .frm-field-action-icons,
3876
  .frm_form_field.ui-sortable-helper .frm_form_fields,
3877
  .frm_form_field.ui-sortable-helper .frm_description,
@@ -5457,7 +5478,7 @@ a.frm_action_icon:hover {
5457
  }
5458
 
5459
  .frm-move {
5460
- cursor: move;
5461
  }
5462
 
5463
  span.howto {
@@ -5901,7 +5922,7 @@ li.selected .divider_section_only:before {
5901
  overflow: visible;
5902
  position: relative;
5903
  padding: 0 0 0 20px;
5904
- margin: 25px 0 0 -20px;
5905
  border-left: 1px solid var(--primary-hover);
5906
  transition: border 0.7s ease-out;
5907
  }
@@ -6132,7 +6153,7 @@ tr.frm_options_heading td {
6132
  border: 1px dashed var(--grey-border);
6133
  }
6134
 
6135
- .drop-me + .frm_no_fields {
6136
  border-style: solid;
6137
  }
6138
 
@@ -7162,35 +7183,26 @@ input[disabled],
7162
  color: var(--dark-grey);
7163
  }
7164
 
7165
- .frmbutton.ui-draggable-dragging {
7166
- width: auto !important;
7167
- }
7168
-
7169
  /* Icon while dragging */
7170
- .frmbutton.ui-draggable-dragging,
7171
- .frmbutton.ui-draggable-dragging a {
7172
- cursor: move;
7173
  }
7174
 
7175
- .frmbutton.ui-sortable-helper a,
7176
- .frmbutton.ui-draggable-dragging a:hover,
7177
  .frmbutton.ui-draggable-dragging a {
7178
  text-decoration: none;
7179
- height: 25px !important;
7180
  width: 180px;
7181
- text-align: center;
7182
- display: block !important;
7183
- background-color: var(--primary-hover) !important;
 
 
7184
  border-radius: 35px !important;
7185
- padding: 5px 20px !important;
7186
- color: #fff !important;
7187
- }
7188
-
7189
- .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) a {
7190
- /* When the new field is held outside of a dropzone */
7191
- cursor: not-allowed;
7192
- background: var(--sidebar-color) !important;
7193
  color: var(--dark-grey) !important;
 
 
 
7194
  }
7195
 
7196
  .frmbutton.ui-draggable-dragging span {
@@ -7198,29 +7210,27 @@ input[disabled],
7198
  }
7199
 
7200
  #frm-show-fields .frmbutton.ui-sortable-helper i,
7201
- .frmbutton.ui-sortable-helper span,
7202
  .frmbutton.ui-draggable-dragging i,
7203
  .frmbutton.ui-draggable-dragging .frmsvg {
7204
  color: #fff !important;
7205
  }
7206
 
7207
- .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) i,
7208
- .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) .frmsvg {
7209
  color: var(--dark-grey) !important;
7210
  }
7211
 
7212
- .frmbutton.ui-draggable-dragging .frm-dropdown-menu {
7213
- display: none;
7214
- }
7215
-
7216
  .frm_sorting li.ui-state-default.ui-sortable-helper,
7217
  .frmbutton.ui-sortable-helper {
7218
  transition: opacity .2s;
7219
  opacity: 1;
7220
  }
7221
 
7222
- .frmbutton.ui-sortable-helper:not(.ui-draggable-dragging) {
7223
- opacity: 0;
 
 
 
7224
  }
7225
 
7226
  /* End dragging */
@@ -8235,7 +8245,9 @@ Responsive Design
8235
  width: 100%;
8236
  }
8237
 
8238
- .with_frm_style .frm_radio input[type=radio],
 
 
8239
  .with_frm_style .frm_checkbox input[type=checkbox] {
8240
  width: 25px !important;
8241
  }
@@ -8671,6 +8683,11 @@ Responsive Design
8671
  pointer-events: none;
8672
  }
8673
 
 
 
 
 
 
8674
  #frm_banner {
8675
  width: 100%;
8676
  color: #fff;
@@ -8743,3 +8760,4 @@ Responsive Design
8743
  .frm-ready-made-solution img {
8744
  max-width: 100%;
8745
  }
 
3350
  margin-right: 5px;
3351
  }
3352
 
3353
+ .with_frm_style .frm_radio input[type=radio] {
3354
+ margin: 5px 0;
3355
+ width: 18px;
3356
+ position: relative; /* override Bootstrap */
3357
+ }
3358
+
3359
+ .with_frm_style .frm_radio input[type=radio]:before {
3360
+ position: relative !important;
3361
+ left: calc(50% - 6px);
3362
+ top: calc(50% - 6px);
3363
+ margin: 0 !important;
3364
+ }
3365
+
3366
  .frm_inner_field_container {
3367
  margin-bottom: 10px;
3368
  }
3390
  max-width: calc(100% - 100px);
3391
  }
3392
 
3393
+ .frm-dragging * {
3394
+ cursor: grabbing !important;
3395
+ }
3396
+
3397
+ .frm-drag-fade {
3398
+ background-color: var(--lightest-grey) !important;
3399
+ border-radius: 4px;
3400
+ }
3401
+
3402
+ .frm-drag-fade * {
3403
+ opacity: 0;
3404
+ }
3405
+
3406
+ .frm-dragging .divider_section_only,
3407
+ .frm-dragging .frm_field_box {
3408
+ pointer-events: none;
3409
+ }
3410
+
3411
  .frm_form_settings #op-popup, /* 1Password */
3412
  .frm_form_settings com-1password-op-button,
3413
  .frm_conf_below .frm_conf_field_container .frm_primary_label,
3851
  border: none;
3852
  margin: 0;
3853
  visibility: visible !important;
3854
+ box-shadow: 2px -10px 0 2px var(--primary-color);
3855
  }
3856
 
3857
  #frm_form_editor_container > ul > .frm_single_option.ui-sortable-placeholder,
3864
  position: relative;
3865
  }
3866
 
 
3867
  #frm_form_editor_container > ul > li > ul > li.sortable-placeholder,
3868
  #frm_form_editor_container ul.start_divider > li > ul > li.sortable-placeholder {
3869
  position: absolute;
3872
  }
3873
 
3874
  .edit_field_type_divider + li.sortable-placeholder {
3875
+ box-shadow: 2px 5px 0 2px var(--primary-color);
3876
  }
3877
 
3878
  .start_divider li.sortable-placeholder {
3879
+ box-shadow: 2px -5px 0 2px var(--primary-color);
3880
  }
3881
 
3882
+ .frm-is-collapsed + .sortable-placeholder {
3883
+ box-shadow: 2px 15px 0 2px var(--primary-color);
3884
  }
3885
 
3886
+ .frm_single_option.ui-sortable-placeholder {
3887
+ box-shadow: 0 0 1px 1px var(--primary-color);
3888
  }
3889
 
3890
  .frm_sorting > li.edit_field_type_end_divider:first-child,
 
 
3891
  .frm-show-click,
3892
  li.ui-state-default.edit_field_type_divider .frm-show-click {
3893
  display: none;
3894
  }
3895
 
 
 
 
 
 
 
 
3896
  .frm_form_field.ui-sortable-helper .frm-field-action-icons,
3897
  .frm_form_field.ui-sortable-helper .frm_form_fields,
3898
  .frm_form_field.ui-sortable-helper .frm_description,
5478
  }
5479
 
5480
  .frm-move {
5481
+ cursor: grab;
5482
  }
5483
 
5484
  span.howto {
5922
  overflow: visible;
5923
  position: relative;
5924
  padding: 0 0 0 20px;
5925
+ margin: 25px 0 0 -5px;
5926
  border-left: 1px solid var(--primary-hover);
5927
  transition: border 0.7s ease-out;
5928
  }
6153
  border: 1px dashed var(--grey-border);
6154
  }
6155
 
6156
+ .ui-droppable-active ~ .frm_no_fields {
6157
  border-style: solid;
6158
  }
6159
 
7183
  color: var(--dark-grey);
7184
  }
7185
 
 
 
 
 
7186
  /* Icon while dragging */
7187
+ .frmbutton.ui-draggable-dragging {
7188
+ pointer-events: none;
 
7189
  }
7190
 
 
 
7191
  .frmbutton.ui-draggable-dragging a {
7192
  text-decoration: none;
7193
+ height: 40px;
7194
  width: 180px;
7195
+ display: inline-flex !important;
7196
+ flex-direction: row;
7197
+ justify-content: center;
7198
+ align-items: center;
7199
+ background-color: var(--sidebar-color) !important;
7200
  border-radius: 35px !important;
7201
+ padding: 5px 10px !important;
 
 
 
 
 
 
 
7202
  color: var(--dark-grey) !important;
7203
+ box-sizing: border-box;
7204
+ border: 1px solid var(--grey-border);
7205
+ box-shadow: 0 0 8px rgba(0,0,0,.3);
7206
  }
7207
 
7208
  .frmbutton.ui-draggable-dragging span {
7210
  }
7211
 
7212
  #frm-show-fields .frmbutton.ui-sortable-helper i,
 
7213
  .frmbutton.ui-draggable-dragging i,
7214
  .frmbutton.ui-draggable-dragging .frmsvg {
7215
  color: #fff !important;
7216
  }
7217
 
7218
+ .frmbutton.ui-draggable-dragging i,
7219
+ .frmbutton.ui-draggable-dragging .frmsvg {
7220
  color: var(--dark-grey) !important;
7221
  }
7222
 
 
 
 
 
7223
  .frm_sorting li.ui-state-default.ui-sortable-helper,
7224
  .frmbutton.ui-sortable-helper {
7225
  transition: opacity .2s;
7226
  opacity: 1;
7227
  }
7228
 
7229
+ .frm-field-group-count {
7230
+ background-color: var(--sidebar-hover);
7231
+ padding: 5px;
7232
+ color: #fff !important;
7233
+ border-radius: 10px;
7234
  }
7235
 
7236
  /* End dragging */
8245
  width: 100%;
8246
  }
8247
 
8248
+ .with_frm_style .frm_radio input[type=radio] {
8249
+ width: 18px !important;
8250
+ }
8251
  .with_frm_style .frm_checkbox input[type=checkbox] {
8252
  width: 25px !important;
8253
  }
8683
  pointer-events: none;
8684
  }
8685
 
8686
+ .frm-sortable-helper {
8687
+ /* Make sure the item being dragged appears above other form builder fields */
8688
+ z-index: 99;
8689
+ }
8690
+
8691
  #frm_banner {
8692
  width: 100%;
8693
  color: #fff;
8760
  .frm-ready-made-solution img {
8761
  max-width: 100%;
8762
  }
8763
+
formidable.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Formidable Forms
4
  Description: Quickly and easily create drag-and-drop forms
5
- Version: 5.5.1
6
  Plugin URI: https://formidableforms.com/
7
  Author URI: https://formidableforms.com/
8
  Author: Strategy11 Form Builder Team
2
  /*
3
  Plugin Name: Formidable Forms
4
  Description: Quickly and easily create drag-and-drop forms
5
+ Version: 5.5.2
6
  Plugin URI: https://formidableforms.com/
7
  Author URI: https://formidableforms.com/
8
  Author: Strategy11 Form Builder Team
js/admin/dom.js CHANGED
@@ -389,7 +389,7 @@
389
  };
390
 
391
  const wysiwyg = {
392
- init( editor, { setupCallback, height } = {}) {
393
  if ( isTinyMceActive() ) {
394
  setTimeout( resetTinyMce, 0 );
395
  } else {
@@ -428,9 +428,24 @@
428
  }
429
  );
430
 
431
- if ( setupCallback ) {
432
- settings.setup = setupCallback;
433
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  if ( height ) {
435
  settings.height = height;
436
  }
389
  };
390
 
391
  const wysiwyg = {
392
+ init( editor, { setupCallback, height, addFocusEvents } = {}) {
393
  if ( isTinyMceActive() ) {
394
  setTimeout( resetTinyMce, 0 );
395
  } else {
428
  }
429
  );
430
 
431
+ settings.setup = editor => {
432
+ if ( addFocusEvents ) {
433
+ function focusInCallback() {
434
+ jQuery( editor.targetElm ).trigger( 'focusin' );
435
+ editor.off( 'focusin', '**' );
436
+ }
437
+
438
+ editor.on( 'focusin', focusInCallback );
439
+
440
+ editor.on( 'focusout', function() {
441
+ editor.on( 'focusin', focusInCallback );
442
+ });
443
+ }
444
+ if ( setupCallback ) {
445
+ setupCallback( editor );
446
+ }
447
+ };
448
+
449
  if ( height ) {
450
  settings.height = height;
451
  }
js/admin/embed.js CHANGED
@@ -377,13 +377,13 @@
377
  let examples = [
378
  {
379
  label: __( 'WordPress shortcode', 'formidable' ),
380
- example: '[formidable id=' + formId + ' title=true description=true]',
381
  link: 'https://formidableforms.com/knowledgebase/publish-a-form/#kb-insert-the-shortcode-manually',
382
  linkLabel: __( 'How to use shortcodes in WordPress', 'formidable' )
383
  },
384
  {
385
  label: __( 'Use PHP code', 'formidable' ),
386
- example: '<?php echo FrmFormsController::get_form_shortcode( array( \'id\' => ' + formId + ', \'title\' => true, \'description\' => true ) ); ?>'
387
  }
388
  ];
389
 
377
  let examples = [
378
  {
379
  label: __( 'WordPress shortcode', 'formidable' ),
380
+ example: '[formidable id=' + formId + ']',
381
  link: 'https://formidableforms.com/knowledgebase/publish-a-form/#kb-insert-the-shortcode-manually',
382
  linkLabel: __( 'How to use shortcodes in WordPress', 'formidable' )
383
  },
384
  {
385
  label: __( 'Use PHP code', 'formidable' ),
386
+ example: '<?php echo FrmFormsController::get_form_shortcode( array( \'id\' => ' + formId + ' ) ); ?>'
387
  }
388
  ];
389
 
js/formidable_admin.js CHANGED
@@ -324,17 +324,18 @@ function frmAdminBuildJS() {
324
  drag: svg({ href: '#frm_drag_icon', classList: [ 'frm_drag_icon', 'frm-drag' ] })
325
  };
326
 
327
- var $newFields = jQuery( document.getElementById( 'frm-show-fields' ) ),
328
  builderForm = document.getElementById( 'new_fields' ),
329
  thisForm = document.getElementById( 'form_id' ),
330
- cancelSort = false,
331
  copyHelper = false,
332
  fieldsUpdated = 0,
333
  thisFormId = 0,
334
  autoId = 0,
335
  optionMap = {},
336
- lastNewActionIdReturned = 0,
337
- __ = wp.i18n.__;
 
 
338
 
339
  if ( thisForm !== null ) {
340
  thisFormId = thisForm.value;
@@ -814,280 +815,418 @@ function frmAdminBuildJS() {
814
  }
815
  }
816
 
817
- /* Form Builder */
818
- function setupSortable( sort ) {
819
- var startSort, container, $previousFieldContainer, opts;
 
 
820
 
821
- startSort = false;
822
- container = jQuery( '#post-body-content' );
 
 
 
 
 
 
823
 
824
- opts = {
825
- connectWith: 'ul.frm_sorting',
826
- items: 'li.frm_field_box',
827
- placeholder: 'sortable-placeholder',
828
- axis: 'y',
829
- cancel: '.widget,.frm_field_opts_list,input,textarea,select,.edit_field_type_end_divider,.frm_sortable_field_opts,.frm_noallow',
830
- accepts: 'field_type_list',
831
- forcePlaceholderSize: false,
832
- tolerance: 'pointer',
833
- handle: '.frm-move',
834
- over: function() {
835
- this.classList.add( 'drop-me' );
836
- },
837
- out: function() {
838
- this.classList.remove( 'drop-me' );
839
- },
840
- receive: function( event, ui ) {
841
- // Receive event occurs when an item in one sortable list is dragged into another sortable list
842
 
843
- if ( cancelSort ) {
844
- ui.item.addClass( 'frm_cancel_sort' );
845
- return;
846
- }
847
 
848
- if ( typeof ui.item.attr( 'id' ) !== 'undefined' ) {
849
- if ( ui.item.attr( 'id' ).indexOf( 'frm_field_id' ) > -1 ) {
850
- // An existing field was dragged and dropped into, out of, or between sections
851
- updateFieldAfterMovingBetweenSections( ui.item );
852
- } else {
853
- // A new field was dragged into the form
854
- insertNewFieldByDragging( this, ui.item, opts );
855
- }
856
- } else if ( ui.item.hasClass( 'frm_field_box' ) ) {
857
- // dragging a group.
858
- getFieldsInRow( ui.item.children( 'ul' ) ).each(
859
- function() {
860
- updateFieldAfterMovingBetweenSections( jQuery( this ) );
861
- }
862
- );
863
- }
864
- },
865
- change: function( event, ui ) {
866
- // don't allow some field types inside section
867
- if ( allowDrop( ui ) ) {
868
- ui.placeholder.addClass( 'sortable-placeholder' ).removeClass( 'no-drop-placeholder' );
869
- cancelSort = false;
870
- } else {
871
- ui.placeholder.addClass( 'no-drop-placeholder' ).removeClass( 'sortable-placeholder' );
872
- cancelSort = true;
873
- }
874
- },
875
- start: function( event, ui ) {
876
- unselectFieldGroups();
877
- deleteEmptyDividerWrappers();
878
- maybeRemoveGroupHoverTarget();
879
- closeOpenFieldDropdowns();
880
- container.get( 0 ).classList.add( 'frm-dragging-field' );
881
- if ( ui.item[0].offsetHeight > 120 ) {
882
- jQuery( sort ).sortable( 'refreshPositions' );
883
- }
884
- if ( ui.item[0].classList.contains( 'frm-page-collapsed' ) ) {
885
- // If a page if collapsed, expand it before dragging since only the page break will move.
886
- toggleCollapsePage( jQuery( ui.item[0]) );
887
- }
888
- $previousFieldContainer = ui.item.closest( 'ul.frm_sorting' );
889
- },
890
- helper: function( e, li ) {
891
- copyHelper = li.clone().insertAfter( li );
892
- return li.clone().addClass( 'frm-sortable-helper' );
893
- },
894
- beforeStop: function( event, ui ) {
895
- // If this was dropped at the beginning of a collpased page, open it.
896
- var previous = ui.item[0].previousElementSibling;
897
- if ( previous !== null && previous.classList.contains( 'frm-page-collapsed' ) ) {
898
- toggleCollapsePage( jQuery( previous ) );
899
- }
900
- },
901
- stop: function( event, ui ) {
902
- var moving, $previousContainerFields, $closestFieldBox;
903
 
904
- container.get( 0 ).classList.remove( 'frm-dragging-field' );
905
- moving = jQuery( this );
906
- copyHelper && copyHelper.remove();
 
907
 
908
- if ( cancelSort ) {
909
- moving.sortable( 'cancel' );
910
- syncAfterDragAndDrop();
911
- return;
912
- }
 
 
 
 
 
 
 
 
913
 
914
- $previousContainerFields = $previousFieldContainer.length ? getFieldsInRow( $previousFieldContainer ) : [];
915
- if ( $previousFieldContainer.length ) {
916
- if ( ! $previousContainerFields.length ) {
917
- $closestFieldBox = $previousFieldContainer.closest( 'li.frm_field_box' );
918
- if ( ! $closestFieldBox.hasClass( 'edit_field_type_divider' ) ) {
919
- // remove an empty field group, but don't remove an empty section.
920
- $closestFieldBox.remove();
921
- }
922
- } else {
923
- syncLayoutClasses( $previousContainerFields.first() );
924
- }
925
- }
926
 
927
- if ( ( 'frm-show-fields' === ui.item.parent().attr( 'id' ) || ui.item.parent().hasClass( 'start_divider' ) ) && ui.item.hasClass( 'form-field' ) ) {
928
- // dragging an item into a new row.
929
- wrapFieldLiInPlace( ui.item );
930
- if ( $previousContainerFields.length ) {
931
- // only if the previous container had other sibling fields, remove the previous layout class.
932
- syncLayoutClasses( ui.item );
933
- }
934
- } else {
935
- syncLayoutClasses( ui.item );
936
- }
 
 
 
 
 
 
 
 
937
 
938
- syncAfterDragAndDrop();
939
- },
940
- sort: function( event, ui ) {
941
- var $row, $children, $lastChild, currentIndex, left;
942
 
943
- container.scrollTop( function( i, v ) {
944
- var moved, h, relativePos, y;
 
 
 
 
 
 
945
 
946
- if ( startSort === false ) {
947
- startSort = event.clientY;
948
- return v;
949
- }
 
 
 
 
950
 
951
- moved = event.clientY - startSort;
952
- h = this.offsetHeight;
953
- relativePos = event.clientY - this.offsetTop;
954
- y = relativePos - h / 2;
955
- if ( relativePos > ( h - 50 ) && moved > 5 ) {
956
- // scrolling down
957
- return v + y * 0.1;
958
- } else if ( relativePos < 50 && moved < -5 ) {
959
- //scrolling up
960
- return v - Math.abs( y * 0.1 );
961
- }
962
- });
963
 
964
- maybeFixPlaceholderParent( ui, event );
965
 
966
- $row = ui.placeholder.parent();
967
- $children = getFieldsInRow( $row );
968
- currentIndex = determineIndexBasedOffOfMousePositionInRow( $row, event.clientX );
 
969
 
970
- if ( ! $children.length ) {
971
- return;
972
- }
973
 
974
- if ( currentIndex === $children.length ) {
975
- $lastChild = jQuery( $children.get( currentIndex - 1 ) );
976
- left = $lastChild.offset().left - ui.placeholder.parent().offset().left + $lastChild.outerWidth();
977
- $row.append( ui.placeholder );
978
- } else {
979
- left = jQuery( $children.get( currentIndex ) ).offset().left - $row.offset().left;
980
- jQuery( $children.get( currentIndex ) ).before( ui.placeholder );
981
- }
982
 
983
- ui.placeholder.get( 0 ).style.left = left + 'px';
984
- }
985
- };
986
 
987
- jQuery( sort ).sortable( opts );
988
 
989
- setupFieldOptionSorting( jQuery( '#frm_builder_page' ) );
 
 
 
 
990
  }
991
 
992
- function syncAfterDragAndDrop() {
993
- maybeRemoveNewCancelledFields();
994
- maybeUncancelFields();
995
- maybeFixEndDividers();
996
- fixUnwrappedListItems();
997
- toggleSectionHolder();
998
- updateFieldOrder();
999
 
1000
- const event = new Event( 'frm_sync_after_drag_and_drop', { bubbles: false });
1001
- document.dispatchEvent( event );
 
 
1002
  }
1003
 
1004
- function maybeRemoveNewCancelledFields() {
1005
- Array.from( document.getElementById( 'frm-show-fields' ).children ).forEach(
1006
- function( fieldBox ) {
1007
- if ( fieldBox.classList.contains( 'frmbutton' ) && fieldBox.classList.contains( 'ui-draggable' ) ) {
1008
- fieldBox.remove();
1009
- }
 
 
 
1010
  }
1011
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1012
  }
1013
 
1014
- function maybeUncancelFields() {
1015
- document.querySelectorAll( '.frm_cancel_sort' ).forEach(
1016
- function( field ) {
1017
- field.classList.remove( 'frm_cancel_sort' );
1018
- }
1019
- );
 
 
 
1020
  }
1021
 
1022
- /**
1023
- * Make sure the end dividers are always a child of a start divider, at the bottom at the list.
1024
- */
1025
- function maybeFixEndDividers() {
1026
- var endDividers = document.querySelectorAll( '.edit_field_type_end_divider' );
1027
- if ( ! endDividers.length ) {
 
1028
  return;
1029
  }
1030
- endDividers.forEach(
1031
- function( endDivider ) {
1032
- if ( endDivider.parentNode.classList.contains( 'start_divider' ) ) {
1033
- // avoid having to call closest, but still append it as it might not be the last child.
1034
- endDivider.parentNode.appendChild( endDivider );
1035
- return;
1036
- }
1037
- endDivider.closest( '.start_divider' ).appendChild( endDivider );
1038
- }
1039
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1040
  }
1041
 
1042
  /**
1043
- * Sortable struggles to put the field into the proper section if there are multiple in a field group. This helps fix some of those issues.
 
 
 
1044
  */
1045
- function maybeFixPlaceholderParent( ui, event ) {
1046
- var elementFromPoint, wrapper, shouldAppend;
1047
- elementFromPoint = document.elementFromPoint( event.clientX, event.clientY );
1048
- if ( null === elementFromPoint ) {
1049
  return;
1050
  }
1051
- wrapper = elementFromPoint.closest( '.frm_sorting' );
1052
- if ( null === wrapper ) {
 
1053
  return;
1054
  }
1055
- if ( ui.placeholder.closest( '.start_divider' ).parent().parent().find( '.start_divider' ).length < 2 ) {
1056
- // placeholder is not in a problematic position that needs to be fixed so leave it.
 
 
 
 
 
 
 
1057
  return;
1058
  }
1059
- shouldAppend = false;
1060
- if ( wrapper.classList.contains( 'start_divider' ) ) {
1061
- shouldAppend = jQuery( wrapper ).parent().parent().find( '.start_divider' ).length >= 2;
1062
- } else if ( null !== wrapper.closest( '.start_divider' ) ) {
1063
- shouldAppend = jQuery( wrapper ).closest( '.start_divider' ).parent().parent().find( '.start_divider' ).length >= 2;
 
 
 
 
 
 
1064
  }
1065
- if ( null !== wrapper.closest( '.frm-sortable-helper' ) ) {
1066
- // avoid ever appending to the sortable helper.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1067
  return;
1068
  }
1069
- if ( shouldAppend ) {
1070
- // TODO instead of appendTo, we might need to look for the closest item, and appear above/below it.
1071
- ui.placeholder.appendTo( wrapper );
 
 
 
 
 
 
 
 
 
 
 
1072
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  }
1074
 
1075
  function fixUnwrappedListItems() {
1076
- document.querySelectorAll( 'ul.start_divider > li.form-field:not(.edit_field_type_end_divider)' ).forEach(
1077
- function( field ) {
1078
- wrapFieldLiInPlace( field );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1079
  }
1080
  );
1081
  }
1082
 
1083
  function deleteEmptyDividerWrappers() {
1084
- var dividers = document.querySelectorAll( 'ul.start_divider' );
1085
  if ( ! dividers.length ) {
1086
  return;
1087
  }
1088
  dividers.forEach(
1089
  function( divider ) {
1090
- var children = [].slice.call( divider.children );
1091
  children.forEach(
1092
  function( child ) {
1093
  if ( 0 === child.children.length ) {
@@ -1101,23 +1240,29 @@ function frmAdminBuildJS() {
1101
  );
1102
  }
1103
 
1104
- /**
1105
- * @returns {bool} true if the placeholder parent should be fixed.
1106
- */
1107
- function shouldTryFixingPlaceholderParent( $placeholder ) {
1108
- var closestSection = $placeholder.closest( '.start_divider' );
1109
- if ( null === closestSection ) {
1110
- return false;
1111
  }
1112
- return jQuery( closestSection ).siblings( 'li.start_divider' ).length >= 1;
1113
- }
1114
 
1115
- function getFieldsInRow( $row ) {
1116
- return $row.children( 'li.form-field' ).not( '.ui-sortable-helper' ).not( '.edit_field_type_end_divider' ).filter(
1117
- function() {
1118
- return 'none' !== this.style.display;
 
 
 
 
 
 
 
 
1119
  }
1120
  );
 
1121
  }
1122
 
1123
  function determineIndexBasedOffOfMousePositionInRow( $row, x ) {
@@ -1339,7 +1484,6 @@ function frmAdminBuildJS() {
1339
  resetDisplayedOpts( fieldId );
1340
  }
1341
  };
1342
-
1343
  jQuery( sort ).sortable( opts );
1344
  }
1345
 
@@ -1388,23 +1532,24 @@ function frmAdminBuildJS() {
1388
  * @param {object} currentItem
1389
  */
1390
  function updateFieldAfterMovingBetweenSections( currentItem ) {
1391
- var fieldId, section, formId, sectionId;
1392
-
1393
- fieldId = currentItem.attr( 'id' ).replace( 'frm_field_id_', '' );
1394
- section = getSectionForFieldPlacement( currentItem );
1395
- formId = getFormIdForFieldPlacement( section );
1396
- sectionId = getSectionIdForFieldPlacement( section );
1397
-
1398
- if ( currentItem.parent().hasClass( 'start_divider' ) ) {
1399
- wrapFieldLiInPlace( currentItem );
1400
  }
1401
 
1402
- currentItem[0].addEventListener( 'click', function() {
1403
- maybeAddSaveAndDragIcons( this.dataset.fid );
1404
- });
 
1405
 
1406
  jQuery.ajax({
1407
- type: 'POST', url: ajaxurl,
 
1408
  data: {
1409
  action: 'frm_update_field_after_move',
1410
  form_id: formId,
@@ -1427,25 +1572,26 @@ function frmAdminBuildJS() {
1427
  /**
1428
  * Add a new field by dragging and dropping it from the Fields sidebar
1429
  *
1430
- * @param {object} selectedItem
1431
- * @param {object} fieldButton
1432
- * @param {object} opts
1433
  */
1434
- function insertNewFieldByDragging( selectedItem, fieldButton ) {
1435
- const fieldType = fieldButton.attr( 'id' );
1436
-
1437
- const sortableData = jQuery( selectedItem ).data().uiSortable;
1438
- const currentItem = sortableData.currentItem;
1439
- const insertAtIndex = determineIndexBasedOffOfMousePositionInRow( currentItem.parent(), currentItem.offset().left );
1440
- jQuery( getFieldsInRow( currentItem.parent() ).get( insertAtIndex ) ).before( currentItem );
1441
- const section = getSectionForFieldPlacement( currentItem );
1442
- const formId = getFormIdForFieldPlacement( section );
1443
- const sectionId = getSectionIdForFieldPlacement( section );
1444
-
1445
  const loadingID = fieldType.replace( '|', '-' ) + '_' + getAutoId();
1446
- const $placeholder = jQuery( '<li class="frm-wait frmbutton_loadingnow" id="' + loadingID + '" ></li>' );
1447
- currentItem.replaceWith( $placeholder );
1448
-
 
 
 
 
 
 
 
 
 
 
 
 
1449
  syncLayoutClasses( $placeholder );
1450
 
1451
  let hasBreak = 0;
@@ -1472,7 +1618,11 @@ function frmAdminBuildJS() {
1472
  // if dragging into a new row, we need to wrap the li first.
1473
  replaceWith = wrapFieldLi( msg );
1474
  } else {
1475
- replaceWith = msg;
 
 
 
 
1476
  }
1477
  $placeholder.replaceWith( replaceWith );
1478
  updateFieldOrder();
@@ -1481,11 +1631,29 @@ function frmAdminBuildJS() {
1481
  syncLayoutClasses( $siblings.first() );
1482
  }
1483
  toggleSectionHolder();
 
 
 
 
 
 
 
 
1484
  },
1485
  error: handleInsertFieldError
1486
  });
1487
  }
1488
 
 
 
 
 
 
 
 
 
 
 
1489
  function handleInsertFieldError( jqXHR, _, errorThrown ) {
1490
  maybeShowInsertFieldError( errorThrown, jqXHR );
1491
  }
@@ -1511,94 +1679,140 @@ function frmAdminBuildJS() {
1511
  return ++autoId;
1512
  }
1513
 
1514
- // don't allow page break, embed form, or section inside section field
1515
- // don't allow page breaks inside of field groups.
1516
- // don't allow field groups with sections inside of sections.
1517
- // don't allow field groups in field groups.
1518
- // don't allow hidden fields inside of field groups but allow them in sections.
1519
- function allowDrop( ui ) {
1520
- var fieldsInRow, insideFieldGroup, insideSection, isNewField, isPageBreak, isFieldGroup, isSection;
1521
-
1522
- fieldsInRow = getFieldsInRow( ui.placeholder.parent() );
1523
-
1524
- if ( ! groupCanFitAnotherField( fieldsInRow, ui.item ) ) {
1525
  return false;
1526
  }
1527
 
1528
- insideFieldGroup = fieldsInRow.length > 0;
1529
- insideSection = ui.placeholder.closest( '.start_divider' ).length > 0;
 
 
1530
 
1531
- if ( ! insideSection && ! insideFieldGroup ) {
 
1532
  return true;
1533
  }
1534
 
1535
- if ( insideFieldGroup && ui.placeholder.siblings( '.edit_field_type_break, .edit_field_type_hidden' ).length ) {
1536
- // never allow any field beside a page break or a hidden field.
1537
- return false;
 
 
 
1538
  }
1539
 
1540
- if ( insideSection && ui.placeholder.siblings().length > 1 && ui.placeholder.prev().hasClass( 'edit_field_type_end_divider' ) ) {
1541
- return false;
 
1542
  }
1543
 
1544
- isNewField = ui.item.hasClass( 'frmbutton' );
1545
-
1546
- if ( isNewField ) {
1547
- isPageBreak = ui.item.hasClass( 'frm_tbreak' );
1548
 
1549
- if ( isPageBreak ) {
1550
- // do not allow page break in both sections and field groups.
1551
- return false;
1552
- }
 
 
 
 
 
1553
 
1554
- if ( insideFieldGroup && ( ui.item.hasClass( 'frm_thidden' ) || ui.item.hasClass( 'frm_tsummary' ) ) ) {
1555
- // do not allow a hidden field or summary field in a field group.
1556
- return false;
1557
- }
1558
 
1559
- if ( ! insideSection ) {
1560
- return true;
1561
- }
 
 
1562
 
1563
- return ! ui.item.hasClass( 'frm_tform' ) && ! ui.item.hasClass( 'frm_tdivider' ) && ! ui.item.hasClass( 'frm_tdivider-repeat' );
 
 
1564
  }
1565
 
1566
- isPageBreak = ui.item.hasClass( 'edit_field_type_break' );
 
 
 
 
 
 
1567
 
 
1568
  if ( isPageBreak ) {
1569
- // do not allow page break in both sections and field groups.
1570
  return false;
1571
  }
1572
 
1573
- isFieldGroup = ui.item.find( 'ul.frm_sorting' ).length > 0;
1574
- isSection = ui.item.hasClass( 'edit_field_type_divider' );
 
1575
 
1576
- if ( insideSection && isSection ) {
1577
- // but do not allow a section inside of a section.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1578
  return false;
1579
  }
1580
 
1581
- if ( isFieldGroup && insideFieldGroup ) {
1582
- // allow a field group inside of a field group if it is being placed within a section above/below another field group.
1583
- return insideSection && ui.placeholder.siblings( 'li.edit_field_type_end_divider' ).length > 0;
 
1584
  }
1585
 
1586
- if ( insideFieldGroup && ui.item.hasClass( 'edit_field_type_hidden' ) ) {
1587
- // do not allow a hidden field inside of a field group.
 
 
 
 
 
1588
  return false;
1589
  }
1590
 
1591
- if ( ! insideSection ) {
1592
- return true;
 
 
1593
  }
1594
 
1595
- if ( ui.item.find( '.edit_field_type_divider' ).length ) {
1596
- // if we are dragging a field group with a section, do not allow it in section.
 
 
 
1597
  return false;
1598
  }
1599
 
1600
- // moving an existing field
1601
- return ! ui.item.hasClass( 'edit_field_type_form' ) && ! isSection;
1602
  }
1603
 
1604
  function groupCanFitAnotherField( fieldsInRow, $field ) {
@@ -1615,20 +1829,18 @@ function frmAdminBuildJS() {
1615
  }
1616
 
1617
  function loadFields( fieldId ) {
1618
- var addHtmlToField, nextElement,
1619
- thisField = document.getElementById( fieldId ),
1620
- $thisField = jQuery( thisField ),
1621
- field = [];
1622
-
1623
- addHtmlToField = function( element ) {
1624
- var frmHiddenFdata = element.querySelector( '.frm_hidden_fdata' );
1625
  element.classList.add( 'frm_load_now' );
1626
  if ( frmHiddenFdata !== null ) {
1627
  field.push( frmHiddenFdata.innerHTML );
1628
  }
1629
  };
1630
 
1631
- nextElement = thisField;
1632
  addHtmlToField( nextElement );
1633
  while ( nextElement.nextElementSibling && field.length < 15 ) {
1634
  addHtmlToField( nextElement.nextElementSibling );
@@ -1636,49 +1848,52 @@ function frmAdminBuildJS() {
1636
  }
1637
 
1638
  jQuery.ajax({
1639
- type: 'POST', url: ajaxurl,
 
1640
  data: {
1641
  action: 'frm_load_field',
1642
  field: field,
1643
  form_id: thisFormId,
1644
  nonce: frmGlobal.nonce
1645
  },
1646
- success: function( html ) {
1647
- var key, $nextSet;
 
1648
 
1649
- html = html.replace( /^\s+|\s+$/g, '' );
1650
- if ( html.indexOf( '{' ) !== 0 ) {
1651
- jQuery( '.frm_load_now' ).removeClass( '.frm_load_now' ).html( 'Error' );
1652
- return;
1653
- }
1654
 
1655
- html = JSON.parse( html );
 
 
 
 
1656
 
1657
- for ( key in html ) {
1658
- jQuery( '#frm_field_id_' + key ).replaceWith( html[key]);
1659
- setupSortable( '#frm_field_id_' + key + '.edit_field_type_divider ul.frm_sorting' );
1660
- }
 
 
1661
 
1662
- $nextSet = $thisField.nextAll( '.frm_field_loading:not(.frm_load_now)' );
1663
- if ( $nextSet.length ) {
1664
- loadFields( $nextSet.attr( 'id' ) );
1665
- } else {
1666
- // go up a level
1667
- $nextSet = jQuery( document.getElementById( 'frm-show-fields' ) ).find( '.frm_field_loading:not(.frm_load_now)' );
1668
- if ( $nextSet.length ) {
1669
- loadFields( $nextSet.attr( 'id' ) );
1670
- }
1671
- }
1672
 
1673
- initiateMultiselect();
1674
- renumberPageBreaks();
1675
- maybeHideQuantityProductFieldOption();
1676
 
1677
- const loadedEvent = new Event( 'frm_ajax_loaded_field', { bubbles: false });
1678
- loadedEvent.frmFields = field.map( f => JSON.parse( f ) );
1679
- document.dispatchEvent( loadedEvent );
1680
- }
1681
- });
1682
  }
1683
 
1684
  function addFieldClick() {
@@ -1711,8 +1926,16 @@ function frmAdminBuildJS() {
1711
  },
1712
  success: function( msg ) {
1713
  document.getElementById( 'frm_form_editor_container' ).classList.add( 'frm-has-fields' );
1714
- $newFields.append( wrapFieldLi( msg ) );
 
1715
  afterAddField( msg, true );
 
 
 
 
 
 
 
1716
  },
1717
  error: handleInsertFieldError
1718
  });
@@ -1768,10 +1991,14 @@ function frmAdminBuildJS() {
1768
  success: function( msg ) {
1769
  var newRow;
1770
 
 
 
1771
  if ( null !== newRowId ) {
1772
  newRow = document.getElementById( newRowId );
1773
  if ( null !== newRow ) {
1774
- jQuery( newRow ).append( msg );
 
 
1775
  if ( null !== fieldOrder ) {
1776
  newRow.lastElementChild.setAttribute( 'frm-field-order', fieldOrder );
1777
  }
@@ -1788,10 +2015,15 @@ function frmAdminBuildJS() {
1788
  }
1789
 
1790
  if ( $field.siblings( 'li.form-field' ).length ) {
1791
- $field.after( msg );
 
1792
  syncLayoutClasses( $field );
 
1793
  } else {
1794
- $field.parent().parent().after( wrapFieldLi( msg ) );
 
 
 
1795
  }
1796
 
1797
  updateFieldOrder();
@@ -2041,16 +2273,51 @@ function frmAdminBuildJS() {
2041
  return option;
2042
  }
2043
 
2044
- function wrapFieldLi( li ) {
2045
- return jQuery( '<li>' )
2046
- .addClass( 'frm_field_box' )
2047
- .html(
2048
- jQuery( '<ul>' ).addClass( 'frm_grid_container frm_sorting' ).append( li )
2049
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2050
  }
2051
 
2052
  function wrapFieldLiInPlace( li ) {
2053
- jQuery( li ).wrap( '<li class="frm_field_box"><ul class="frm_grid_container frm_sorting"></ul></li>' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2054
  }
2055
 
2056
  function afterAddField( msg, addFocus ) {
@@ -3764,12 +4031,14 @@ function frmAdminBuildJS() {
3764
  }
3765
 
3766
  function breakRow( row ) {
3767
- getFieldsInRow( jQuery( row ) ).each(
 
3768
  function( index ) {
 
3769
  if ( 0 !== index ) {
3770
- jQuery( row ).closest( 'li' ).after( wrapFieldLi( this ) );
3771
  }
3772
- stripLayoutFromFields( jQuery( this ) );
3773
  }
3774
  );
3775
  }
@@ -4095,7 +4364,6 @@ function frmAdminBuildJS() {
4095
  function getSelectedFieldIds() {
4096
  var deleteFieldIds = [];
4097
  jQuery( '.frm-selected-field-group > li.form-field' )
4098
- .not( '.ui-sortable-helper' )
4099
  .each(
4100
  function() {
4101
  deleteFieldIds.push( this.dataset.fid );
@@ -4114,7 +4382,7 @@ function frmAdminBuildJS() {
4114
  function deleteAllSelectedFieldGroups( deleteFieldIds ) {
4115
  deleteFieldIds.forEach(
4116
  function( fieldId ) {
4117
- deleteField( fieldId );
4118
  }
4119
  );
4120
  }
@@ -5229,35 +5497,6 @@ function frmAdminBuildJS() {
5229
  }
5230
  }
5231
 
5232
- function setScaleValues() {
5233
- /*jshint validthis:true */
5234
- var isMin = this.id.indexOf( 'minnum' ) !== -1;
5235
- var fieldID = this.id.replace( 'scale_maxnum_', '' ).replace( 'scale_minnum_', '' );
5236
- var min = this.value;
5237
- var max = this.value;
5238
- if ( isMin ) {
5239
- max = document.getElementById( 'scale_maxnum_' + fieldID ).value;
5240
- } else {
5241
- min = document.getElementById( 'scale_minnum_' + fieldID ).value;
5242
- }
5243
-
5244
- updateScaleValues( parseInt( min, 10 ), parseInt( max, 10 ), fieldID );
5245
- }
5246
-
5247
- function updateScaleValues( min, max, fieldID ) {
5248
- var container = jQuery( '#field_' + fieldID + '_inner_container .frm_form_fields' );
5249
- container.html( '' );
5250
-
5251
- if ( min >= max ) {
5252
- max = min + 1;
5253
- }
5254
-
5255
- for ( var i = min; i <= max; i++ ) {
5256
- container.append( '<div class="frm_scale"><label><input type="hidden" name="field_options[options_' + fieldID + '][' + i + ']" value="' + i + '"> <input type="radio" name="item_meta[' + fieldID + ']" value="' + i + '"> ' + i + ' </label></div>' );
5257
- }
5258
- container.append( '<div class="clear"></div>' );
5259
- }
5260
-
5261
  function getFieldValues() {
5262
  /*jshint validthis:true */
5263
  var isTaxonomy,
@@ -7011,8 +7250,6 @@ function frmAdminBuildJS() {
7011
 
7012
  if ( rich ) {
7013
  wpActiveEditor = elementId;
7014
- send_to_editor( variable );
7015
- return;
7016
  }
7017
 
7018
  if ( ! contentBox.length ) {
@@ -7032,13 +7269,24 @@ function frmAdminBuildJS() {
7032
  plain_text: p,
7033
  nonce: frmGlobal.nonce
7034
  },
 
7035
  success: function( msg ) {
7036
- insertContent( contentBox, msg );
 
 
 
 
 
 
7037
  }
7038
  });
7039
  } else {
7040
  variable = maybeAddSanitizeUrlToShortcodeVariable( variable, element, contentBox );
7041
- insertContent( contentBox, variable );
 
 
 
 
7042
  }
7043
  return false;
7044
  }
@@ -7535,25 +7783,11 @@ function frmAdminBuildJS() {
7535
  const wysiwyg = settings.querySelector( '.wp-editor-area' );
7536
  if ( wysiwyg ) {
7537
  frmDom.wysiwyg.init(
7538
- wysiwyg,
7539
- { setupCallback: addFocusEvents, height: 160 }
7540
  );
7541
  }
7542
  }
7543
 
7544
- function addFocusEvents( editor ) {
7545
- function focusInCallback() {
7546
- jQuery( editor.targetElm ).trigger( 'focusin' );
7547
- editor.off( 'focusin', '**' );
7548
- }
7549
-
7550
- editor.on( 'focusin', focusInCallback );
7551
-
7552
- editor.on( 'focusout', function() {
7553
- editor.on( 'focusin', focusInCallback );
7554
- });
7555
- }
7556
-
7557
  /* Styling */
7558
  function setPosClass() {
7559
  /*jshint validthis:true */
@@ -8688,18 +8922,27 @@ function frmAdminBuildJS() {
8688
  jQuery( '.spinner' ).css( 'visibility', 'hidden' );
8689
 
8690
  // Show response.message
8691
- if ( response.message && typeof form.elements.show_response !== 'undefined' ) {
8692
- const showError = document.getElementById( form.elements.show_response.value );
8693
- if ( showError !== null ) {
8694
- showError.innerHTML = response.message;
8695
- showError.classList.remove( 'frm_hidden' );
8696
- }
8697
  }
8698
  }
8699
  button.classList.remove( 'frm_loading_button' );
8700
  });
8701
  }
8702
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8703
  function handleCaptchaTypeChange( e ) {
8704
  const thresholdContainer = document.getElementById( 'frm_captcha_threshold_container' );
8705
  if ( thresholdContainer ) {
@@ -9388,7 +9631,9 @@ function frmAdminBuildJS() {
9388
  },
9389
 
9390
  buildInit: function() {
9391
- var loadFieldId, $builderForm, builderArea;
 
 
9392
 
9393
  if ( jQuery( '.frm_field_loading' ).length ) {
9394
  loadFieldId = jQuery( '.frm_field_loading' ).first().attr( 'id' );
@@ -9397,13 +9642,8 @@ function frmAdminBuildJS() {
9397
 
9398
  setupSortable( 'ul.frm_sorting' );
9399
 
9400
- jQuery( '.field_type_list > li:not(.frm_noallow)' ).draggable({
9401
- connectToSortable: '#frm-show-fields',
9402
- helper: 'clone',
9403
- revert: 'invalid',
9404
- delay: 10,
9405
- cancel: '.frm-dropdown-menu'
9406
- });
9407
  jQuery( 'ul.field_type_list, .field_type_list li, ul.frm_code_list, .frm_code_list li, .frm_code_list li a, #frm_adv_info #category-tabs li, #frm_adv_info #category-tabs li a' ).disableSelection();
9408
 
9409
  jQuery( '.frm_submit_ajax' ).on( 'click', submitBuild );
@@ -9412,7 +9652,7 @@ function frmAdminBuildJS() {
9412
  jQuery( 'a.edit-form-status' ).on( 'click', slideDown );
9413
  jQuery( '.cancel-form-status' ).on( 'click', slideUp );
9414
  jQuery( '.save-form-status' ).on( 'click', function() {
9415
- var newStatus = jQuery( document.getElementById( 'form_change_status' ) ).val();
9416
  jQuery( 'input[name="new_status"]' ).val( newStatus );
9417
  jQuery( document.getElementById( 'form-status-display' ) ).html( newStatus );
9418
  jQuery( '.cancel-form-status' ).trigger( 'click' );
@@ -9432,7 +9672,6 @@ function frmAdminBuildJS() {
9432
  $builderForm.on( 'click', '.frm_add_watch_lookup_row', addWatchLookupRow );
9433
  $builderForm.on( 'change', '.frm_get_values_form', updateGetValueFieldSelection );
9434
  $builderForm.on( 'change', '.frm_logic_field_opts', getFieldValues );
9435
- $builderForm.on( 'change', '.scale_maxnum, .scale_minnum', setScaleValues );
9436
  $builderForm.on( 'change', '.radio_maxnum', setStarValues );
9437
  $builderForm.on( 'frm-multiselect-changed', 'select[name^="field_options[admin_only_"]', adjustVisibilityValuesForEveryoneValues );
9438
 
324
  drag: svg({ href: '#frm_drag_icon', classList: [ 'frm_drag_icon', 'frm-drag' ] })
325
  };
326
 
327
+ let $newFields = jQuery( document.getElementById( 'frm-show-fields' ) ),
328
  builderForm = document.getElementById( 'new_fields' ),
329
  thisForm = document.getElementById( 'form_id' ),
 
330
  copyHelper = false,
331
  fieldsUpdated = 0,
332
  thisFormId = 0,
333
  autoId = 0,
334
  optionMap = {},
335
+ lastNewActionIdReturned = 0;
336
+
337
+ const { __ } = wp.i18n;
338
+ let debouncedSyncAfterDragAndDrop;
339
 
340
  if ( thisForm !== null ) {
341
  thisFormId = thisForm.value;
815
  }
816
  }
817
 
818
+ function setupSortable( sortableSelector ) {
819
+ document.querySelectorAll( sortableSelector ).forEach(
820
+ list => {
821
+ makeDroppable( list );
822
+ Array.from( list.children ).forEach( child => makeDraggable( child, '.frm-move' ) );
823
 
824
+ const $sectionTitle = jQuery( list ).children( '[data-type="divider"]' ).children( '.divider_section_only' );
825
+ if ( $sectionTitle.length ) {
826
+ makeDroppable( $sectionTitle );
827
+ }
828
+ }
829
+ );
830
+ setupFieldOptionSorting( jQuery( '#frm_builder_page' ) );
831
+ }
832
 
833
+ function makeDroppable( list ) {
834
+ jQuery( list ).droppable({
835
+ accept: '.frmbutton, li.frm_field_box',
836
+ deactivate: handleFieldDrop,
837
+ over: onDragOverDroppable,
838
+ out: onDraggableLeavesDroppable,
839
+ tolerance: 'pointer'
840
+ });
841
+ }
 
 
 
 
 
 
 
 
 
842
 
843
+ function onDragOverDroppable( event, ui ) {
844
+ const droppable = getDroppableForOnDragOver( event.target );
845
+ const draggable = ui.draggable[0];
 
846
 
847
+ if ( ! allowDrop( draggable, droppable ) ) {
848
+ droppable.classList.remove( 'frm-over-droppable' );
849
+ jQuery( droppable ).parents( 'ul.frm_sorting' ).addClass( 'frm-over-droppable' );
850
+ return;
851
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
 
853
+ document.querySelectorAll( '.frm-over-droppable' ).forEach( droppable => droppable.classList.remove( 'frm-over-droppable' ) );
854
+ droppable.classList.add( 'frm-over-droppable' );
855
+ jQuery( droppable ).parents( 'ul.frm_sorting' ).addClass( 'frm-over-droppable' );
856
+ }
857
 
858
+ /**
859
+ * Maybe change the droppable.
860
+ * Section titles are made droppable, but are not a list, so we need to change the droppable to the section's list instead.
861
+ *
862
+ * @param {Element} droppable
863
+ * @returns {Element}
864
+ */
865
+ function getDroppableForOnDragOver( droppable ) {
866
+ if ( droppable.classList.contains( 'divider_section_only' ) ) {
867
+ droppable = jQuery( droppable ).nextAll( '.start_divider.frm_sorting' ).get( 0 );
868
+ }
869
+ return droppable;
870
+ }
871
 
872
+ function onDraggableLeavesDroppable( event ) {
873
+ const droppable = event.target;
874
+ droppable.classList.remove( 'frm-over-droppable' );
875
+ }
 
 
 
 
 
 
 
 
876
 
877
+ function makeDraggable( draggable, handle ) {
878
+ const settings = {
879
+ helper: getDraggableHelper,
880
+ revert: 'invalid',
881
+ delay: 10,
882
+ start: handleDragStart,
883
+ stop: handleDragStop,
884
+ drag: handleDrag,
885
+ cursorAt: {
886
+ top: 0,
887
+ left: 90 // The width of draggable button is 180. 90 should center the draggable on the cursor.
888
+ }
889
+ };
890
+ if ( 'string' === typeof handle ) {
891
+ settings.handle = handle;
892
+ }
893
+ jQuery( draggable ).draggable( settings );
894
+ }
895
 
896
+ function getDraggableHelper( event ) {
897
+ const draggable = event.delegateTarget;
 
 
898
 
899
+ if ( isFieldGroup( draggable ) ) {
900
+ const newTextFieldClone = document.getElementById( 'frm-insert-fields' ).querySelector( '.frm_ttext' ).cloneNode( true );
901
+ newTextFieldClone.querySelector( 'use' ).setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_field_group_layout_icon' );
902
+ newTextFieldClone.querySelector( 'span' ).textContent = __( 'Field Group' );
903
+ newTextFieldClone.classList.add( 'frm_field_box' );
904
+ newTextFieldClone.classList.add( 'ui-sortable-helper' );
905
+ return newTextFieldClone;
906
+ }
907
 
908
+ let copyTarget;
909
+ const isNewField = draggable.classList.contains( 'frmbutton' );
910
+ if ( isNewField ) {
911
+ copyTarget = draggable.cloneNode( true );
912
+ copyTarget.classList.add( 'ui-sortable-helper' );
913
+ draggable.classList.add( 'frm-new-field' );
914
+ return copyTarget;
915
+ }
916
 
917
+ if ( draggable.hasAttribute( 'data-ftype' ) ) {
918
+ const fieldType = draggable.getAttribute( 'data-ftype' );
919
+ copyTarget = document.getElementById( 'frm-insert-fields' ).querySelector( '.frm_t' + fieldType );
920
+ copyTarget = copyTarget.cloneNode( true );
921
+ copyTarget.classList.add( 'form-field' );
 
 
 
 
 
 
 
922
 
923
+ copyTarget.classList.add( 'ui-sortable-helper' );
924
 
925
+ if ( copyTarget ) {
926
+ return copyTarget.cloneNode( true );
927
+ }
928
+ }
929
 
930
+ return div({ className: 'frmbutton' });
931
+ }
 
932
 
933
+ function handleDragStart( event, ui ) {
934
+ const container = document.getElementById( 'post-body-content' );
935
+ container.classList.add( 'frm-dragging-field' );
 
 
 
 
 
936
 
937
+ document.body.classList.add( 'frm-dragging' );
938
+ ui.helper.addClass( 'frm-sortable-helper' );
 
939
 
940
+ event.target.classList.add( 'frm-drag-fade' );
941
 
942
+ unselectFieldGroups();
943
+ deleteEmptyDividerWrappers();
944
+ maybeRemoveGroupHoverTarget();
945
+ closeOpenFieldDropdowns();
946
+ deleteTooltips();
947
  }
948
 
949
+ function handleDragStop() {
950
+ const container = document.getElementById( 'post-body-content' );
951
+ container.classList.remove( 'frm-dragging-field' );
952
+ document.body.classList.remove( 'frm-dragging' );
 
 
 
953
 
954
+ const fade = document.querySelector( '.frm-drag-fade' );
955
+ if ( fade ) {
956
+ fade.classList.remove( 'frm-drag-fade' );
957
+ }
958
  }
959
 
960
+ function handleDrag( event ) {
961
+ const draggable = event.target;
962
+ const droppable = getDroppableTarget();
963
+
964
+ let placeholder = document.getElementById( 'frm_drag_placeholder' );
965
+
966
+ if ( ! allowDrop( draggable, droppable ) ) {
967
+ if ( placeholder ) {
968
+ placeholder.remove();
969
  }
970
+ return;
971
+ }
972
+
973
+ if ( ! placeholder ) {
974
+ placeholder = tag( 'li', {
975
+ id: 'frm_drag_placeholder',
976
+ className: 'sortable-placeholder'
977
+ });
978
+ }
979
+
980
+ if ( 'frm-show-fields' === droppable.id || droppable.classList.contains( 'start_divider' ) ) {
981
+ placeholder.style.left = 0;
982
+ handleDragOverYAxis({ droppable, y: event.clientY, placeholder });
983
+ return;
984
+ }
985
+
986
+ placeholder.style.top = '';
987
+ handleDragOverFieldGroup({ droppable, x: event.clientX, placeholder });
988
  }
989
 
990
+ function getDroppableTarget() {
991
+ let droppable = document.getElementById( 'frm-show-fields' );
992
+ while ( droppable.querySelector( '.frm-over-droppable' ) ) {
993
+ droppable = droppable.querySelector( '.frm-over-droppable' );
994
+ }
995
+ if ( 'frm-show-fields' === droppable.id && ! droppable.classList.contains( 'frm-over-droppable' ) ) {
996
+ droppable = false;
997
+ }
998
+ return droppable;
999
  }
1000
 
1001
+ function handleFieldDrop( _, ui ) {
1002
+ const draggable = ui.draggable[0];
1003
+ const placeholder = document.getElementById( 'frm_drag_placeholder' );
1004
+
1005
+ if ( ! placeholder ) {
1006
+ ui.helper.remove();
1007
+ debouncedSyncAfterDragAndDrop();
1008
  return;
1009
  }
1010
+
1011
+ maybeOpenCollapsedPage( placeholder );
1012
+
1013
+ const $previousFieldContainer = ui.helper.parent();
1014
+ const previousSection = ui.helper.get( 0 ).closest( 'ul.frm_sorting' );
1015
+ const newSection = placeholder.closest( 'ul.frm_sorting' );
1016
+
1017
+ if ( draggable.classList.contains( 'frm-new-field' ) ) {
1018
+ insertNewFieldByDragging( draggable.id );
1019
+ } else {
1020
+ moveFieldThatAlreadyExists( draggable, placeholder );
1021
+ }
1022
+
1023
+ const previousSectionId = previousSection && previousSection.classList.contains( 'start_divider' ) ? parseInt( previousSection.closest( '.edit_field_type_divider' ).getAttribute( 'data-fid' ) ) : 0;
1024
+ const newSectionId = newSection.classList.contains( 'start_divider' ) ? parseInt( newSection.closest( '.edit_field_type_divider' ).getAttribute( 'data-fid' ) ) : 0;
1025
+
1026
+ placeholder.remove();
1027
+ ui.helper.remove();
1028
+
1029
+ const $previousContainerFields = $previousFieldContainer.length ? getFieldsInRow( $previousFieldContainer ) : [];
1030
+ maybeUpdatePreviousFieldContainerAfterDrop( $previousFieldContainer, $previousContainerFields );
1031
+ maybeUpdateDraggableClassAfterDrop( draggable, $previousContainerFields );
1032
+
1033
+ if ( previousSectionId !== newSectionId ) {
1034
+ updateFieldAfterMovingBetweenSections( jQuery( draggable ) );
1035
+ }
1036
+
1037
+ debouncedSyncAfterDragAndDrop();
1038
  }
1039
 
1040
  /**
1041
+ * If a page if collapsed, expand it before dragging since only the page break will move.
1042
+ *
1043
+ * @param {Element} placeholder
1044
+ * @returns {void}
1045
  */
1046
+ function maybeOpenCollapsedPage( placeholder ) {
1047
+ if ( ! placeholder.previousElementSibling || ! placeholder.previousElementSibling.classList.contains( 'frm-is-collapsed' ) ) {
 
 
1048
  return;
1049
  }
1050
+
1051
+ const $pageBreakField = jQuery( placeholder ).prevUntil( '[data-type="break"]' );
1052
+ if ( ! $pageBreakField.length ) {
1053
  return;
1054
  }
1055
+
1056
+ const collapseButton = $pageBreakField.find( '.frm-collapse-page' ).get( 0 );
1057
+ if ( collapseButton ) {
1058
+ collapseButton.click();
1059
+ }
1060
+ }
1061
+
1062
+ function maybeUpdatePreviousFieldContainerAfterDrop( $previousFieldContainer, $previousContainerFields ) {
1063
+ if ( ! $previousFieldContainer.length ) {
1064
  return;
1065
  }
1066
+
1067
+ if ( $previousContainerFields.length ) {
1068
+ syncLayoutClasses( $previousContainerFields.first() );
1069
+ } else {
1070
+ maybeDeleteAnEmptyFieldGroup( $previousFieldContainer.get( 0 ) );
1071
+ }
1072
+