Form builder to get in touch with visitors, grow your email list and collect payments — Happyforms - Version 1.15.4

Version Description

  • New feature: "To email address" control under "Email me a copy of each submission" allows for more granular control of email settings.
  • New feature: "Reply email address" control under "Email me a copy of each submission" for easier exchanges with submitting users.
  • Improvement: "Added to" column in Forms screen now lists templates and reusable blocks for better integration with WordPress.
  • Improvement: Interface of choices in all choice-based fields has been redesigned for better usability and clarity.
  • Bugfix: Display of fields with limited choices wasn't updating correctly in builder's preview screen.
Download this release

Release Info

Developer happyforms
Plugin Icon 128x128 Form builder to get in touch with visitors, grow your email list and collect payments — Happyforms
Version 1.15.4
Comparing to
See all releases

Code changes from version 1.15.3 to 1.15.4

core/assets/css/color.css CHANGED
@@ -126,7 +126,7 @@
126
  .happyforms-styles .happyforms-part input[type=tel],
127
  .happyforms-styles .happyforms-part input[type=number],
128
  .happyforms-styles .happyforms-part input[type=range],
129
- .happyforms-select .happyforms-part select.happyforms-select {
130
  height: auto !important;
131
  }
132
 
126
  .happyforms-styles .happyforms-part input[type=tel],
127
  .happyforms-styles .happyforms-part input[type=number],
128
  .happyforms-styles .happyforms-part input[type=range],
129
+ .happyforms-styles .happyforms-part select.happyforms-select {
130
  height: auto !important;
131
  }
132
 
core/assets/css/customize.css CHANGED
@@ -268,11 +268,13 @@ span.members-only {
268
 
269
  #customize-control-abandoned_resume_send_alert_email,
270
  #customize-control-owner_attach_pdf,
271
- #customize-control-attach_pdf {
 
272
  display: none;
273
  }
274
 
275
  .happyforms-email-view.has-email.allow-abandoned-resume #customize-control-abandoned_resume_send_alert_email,
 
276
  #customize-control-receive_email_alerts.checked ~ #customize-control-owner_attach_pdf,
277
  .has-email #customize-control-send_confirmation_email.checked ~ #customize-control-attach_pdf {
278
  display: block;
@@ -372,7 +374,8 @@ p.happyforms-step-progress-counter {
372
  z-index: 1;
373
  }
374
 
375
- .happyforms-widget-title h3 {
 
376
  margin: 0;
377
  padding: 15px;
378
  font-size: 1em !important;
@@ -479,7 +482,6 @@ a.happyforms-form-part-logic {
479
 
480
  a.happyforms-form-part-advanced-settings:before,
481
  a.happyforms-form-part-logic:before,
482
- a.advanced-option:before,
483
  a.advanced-column:before {
484
  position: relative;
485
  top: 2px;
@@ -490,13 +492,13 @@ a.advanced-column:before {
490
 
491
  a.happyforms-form-part-advanced-settings.opened:before,
492
  a.happyforms-form-part-logic.opened:before,
493
- a.advanced-option.opened:before,
494
  a.advanced-column.opened:before {
495
  content: "\f342";
496
  }
497
 
498
  a.happyforms-form-remove,
499
- a.happyforms-form-part-remove {
 
500
  color: #d63638;
501
  }
502
 
@@ -754,12 +756,18 @@ body.adding-happyforms-parts #customize-preview iframe {
754
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
755
  }
756
 
757
- .happyforms-part-widget-title-action {
 
758
  float: right;
759
  cursor: pointer;
760
  }
761
 
762
- .happyforms-widget-action {
 
 
 
 
 
763
  display: inline-block;
764
  padding: 10px;
765
  margin: 0;
@@ -777,7 +785,8 @@ body.adding-happyforms-parts #customize-preview iframe {
777
  line-height: 1;
778
  }
779
 
780
- .happyforms-widget-action .toggle-indicator:before {
 
781
  display: block;
782
  content: '\f140';
783
  font: normal 20px/1 dashicons;
@@ -792,7 +801,8 @@ body.adding-happyforms-parts #customize-preview iframe {
792
  content: '\f142';
793
  }
794
 
795
- .happyforms-widget-action:focus .toggle-indicator:before {
 
796
  box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
797
  }
798
 
@@ -850,8 +860,6 @@ body.adding-happyforms-parts #customize-preview iframe {
850
 
851
  .happyforms-part-widget .options,
852
  .happyforms-part-widget .options-import {
853
- border: 1px solid #ddd;
854
- padding: 10px;
855
  background-color: #fdfdfd;
856
  }
857
 
@@ -861,7 +869,6 @@ body.adding-happyforms-parts #customize-preview iframe {
861
  margin: 0;
862
  }
863
 
864
- .happyforms-part-widget .option-list li,
865
  .happyforms-part-widget .column-list li,
866
  .happyforms-part-widget .row-list li {
867
  margin: 0;
@@ -869,7 +876,6 @@ body.adding-happyforms-parts #customize-preview iframe {
869
  border-bottom: 1px solid #ddd;
870
  }
871
 
872
- .happyforms-part-widget .option-list li:last-child,
873
  .happyforms-part-widget .column-list li:last-child,
874
  .happyforms-part-widget .row-list li:last-child {
875
  border-bottom: none;
@@ -897,18 +903,6 @@ body.adding-happyforms-parts #customize-preview iframe {
897
  padding: 0px 5px 0 20px;
898
  }
899
 
900
- .happyforms-part-widget .happyforms-part-item-handle {
901
- position: absolute;
902
- left: 0;
903
- width: 12px;
904
- height: 100%;
905
- opacity: 0.2;
906
- cursor: move;
907
- background-image: url(../svg/draggable-handle.svg);
908
- background-size: 6px auto;
909
- background-repeat: round;
910
- }
911
-
912
  .happyforms-part-widget .happyforms-part-item-advanced {
913
  display: none;
914
  }
@@ -946,8 +940,9 @@ textarea.option-import-area {
946
  }
947
 
948
  .happyforms-part-widget .option-actions {
949
- margin-top: 5px;
950
  text-align: right;
 
 
951
  }
952
 
953
  .happyforms-part-widget .options .delete-option,
@@ -1812,3 +1807,82 @@ ul.happyforms-parts-list li[data-part-type="rank_order"] {
1812
  .happyforms-widget-content textarea[data-option-attribute=description] {
1813
  margin-bottom: 5px;
1814
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
  #customize-control-abandoned_resume_send_alert_email,
270
  #customize-control-owner_attach_pdf,
271
+ #customize-control-attach_pdf,
272
+ #customize-control-alert_email_reply_to {
273
  display: none;
274
  }
275
 
276
  .happyforms-email-view.has-email.allow-abandoned-resume #customize-control-abandoned_resume_send_alert_email,
277
+ .has-email #customize-control-alert_email_reply_to,
278
  #customize-control-receive_email_alerts.checked ~ #customize-control-owner_attach_pdf,
279
  .has-email #customize-control-send_confirmation_email.checked ~ #customize-control-attach_pdf {
280
  display: block;
374
  z-index: 1;
375
  }
376
 
377
+ .happyforms-widget-title h3,
378
+ .happyforms-part-widget .options .happyforms-choice-item-widget h3 {
379
  margin: 0;
380
  padding: 15px;
381
  font-size: 1em !important;
482
 
483
  a.happyforms-form-part-advanced-settings:before,
484
  a.happyforms-form-part-logic:before,
 
485
  a.advanced-column:before {
486
  position: relative;
487
  top: 2px;
492
 
493
  a.happyforms-form-part-advanced-settings.opened:before,
494
  a.happyforms-form-part-logic.opened:before,
 
495
  a.advanced-column.opened:before {
496
  content: "\f342";
497
  }
498
 
499
  a.happyforms-form-remove,
500
+ a.happyforms-form-part-remove,
501
+ a.happyforms-delete-item {
502
  color: #d63638;
503
  }
504
 
756
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
757
  }
758
 
759
+ .happyforms-part-widget-title-action,
760
+ .happyforms-part-item-advanced-option {
761
  float: right;
762
  cursor: pointer;
763
  }
764
 
765
+ .customize-control .option-list .happyforms-part-item-advanced input[type=checkbox] {
766
+ margin-right: 5px;
767
+ }
768
+
769
+ .happyforms-widget-action,
770
+ .happyforms-advanced-option-action {
771
  display: inline-block;
772
  padding: 10px;
773
  margin: 0;
785
  line-height: 1;
786
  }
787
 
788
+ .happyforms-widget-action .toggle-indicator:before,
789
+ .happyforms-part-item-advanced-option .toggle-indicator:before {
790
  display: block;
791
  content: '\f140';
792
  font: normal 20px/1 dashicons;
801
  content: '\f142';
802
  }
803
 
804
+ .happyforms-widget-action:focus .toggle-indicator:before,
805
+ .happyforms-part-item-advanced-option:focus .toggle-indicator:before {
806
  box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
807
  }
808
 
860
 
861
  .happyforms-part-widget .options,
862
  .happyforms-part-widget .options-import {
 
 
863
  background-color: #fdfdfd;
864
  }
865
 
869
  margin: 0;
870
  }
871
 
 
872
  .happyforms-part-widget .column-list li,
873
  .happyforms-part-widget .row-list li {
874
  margin: 0;
876
  border-bottom: 1px solid #ddd;
877
  }
878
 
 
879
  .happyforms-part-widget .column-list li:last-child,
880
  .happyforms-part-widget .row-list li:last-child {
881
  border-bottom: none;
903
  padding: 0px 5px 0 20px;
904
  }
905
 
 
 
 
 
 
 
 
 
 
 
 
 
906
  .happyforms-part-widget .happyforms-part-item-advanced {
907
  display: none;
908
  }
940
  }
941
 
942
  .happyforms-part-widget .option-actions {
 
943
  text-align: right;
944
+ margin-top: 5px;
945
+ margin-bottom: 14px;
946
  }
947
 
948
  .happyforms-part-widget .options .delete-option,
1807
  .happyforms-widget-content textarea[data-option-attribute=description] {
1808
  margin-bottom: 5px;
1809
  }
1810
+
1811
+
1812
+ /* New Choice Interface */
1813
+
1814
+ .happyforms-choice-item-widget {
1815
+ cursor: move;
1816
+ }
1817
+
1818
+ .happyforms-part-widget .options .happyforms-choice-item-widget h3 {
1819
+ display: block;
1820
+ }
1821
+
1822
+ .happyforms-choice-item-widget .happyforms-part-item-body {
1823
+ padding: 0 10px;
1824
+ margin-top: -13px;
1825
+ padding-top: 12px;
1826
+ z-index: 9;
1827
+ position: relative;
1828
+ }
1829
+
1830
+ .happyforms-choice-item-widget.happyforms-widget-choice-expanded .happyforms-part-item-body {
1831
+ border: 1px solid #ccd0d4;
1832
+ border-top: 0;
1833
+ }
1834
+
1835
+ .happyforms-widget-expanded .happyforms-choice-item-widget .happyforms-advanced-option-action .toggle-indicator:before {
1836
+ content: '\f140';
1837
+ }
1838
+
1839
+ .happyforms-widget-expanded .happyforms-choice-item-widget .happyforms-advanced-option-action .toggle-indicator.opened:before {
1840
+ content: '\f142';
1841
+ }
1842
+
1843
+ .happyforms-choice-item-widget {
1844
+ background: #fff;
1845
+ color: #23282d;
1846
+ line-height: 1.4em;
1847
+ -webkit-transition: opacity 0.5s;
1848
+ transition: opacity 0.5s;
1849
+ z-index: 1;
1850
+ }
1851
+
1852
+ .happyforms-choice-item-widget .happyforms-part-item-handle {
1853
+ border: 1px solid #ccd0d4;
1854
+ overflow: hidden;
1855
+ position: relative;
1856
+ z-index: 99;
1857
+ }
1858
+
1859
+ .happyforms-choice-item-widget .happyforms-part-item-handle:hover {
1860
+ border-color: #999;
1861
+ -webkit-box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
1862
+ box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
1863
+ }
1864
+
1865
+ .happyforms-part-widget .happyforms-choice-item-widget .option-actions {
1866
+ text-align: left;
1867
+ }
1868
+
1869
+ .happyforms-part-widget .links .button {
1870
+ float: right;
1871
+ margin: 0;
1872
+ }
1873
+
1874
+ .happyforms-part-widget .links .button {
1875
+ float: right;
1876
+ margin-right: 5px;
1877
+ }
1878
+
1879
+ .happyforms-part-widget .links {
1880
+ text-align: right;
1881
+ display: block;
1882
+ clear: both;
1883
+ padding: 0px 0 30px 0px;
1884
+ }
1885
+
1886
+ .happyforms-item-choice-widget-title .choice-in-widget-title {
1887
+ color: #646970;
1888
+ }
core/assets/css/layout.css CHANGED
@@ -444,7 +444,7 @@ h3.happyforms-form__title {
444
 
445
  .happyforms-part-option__description {
446
  display: block;
447
- width: 100%;
448
  max-width: 400px;
449
  margin-top: 0px;
450
  margin-left: 31px;
444
 
445
  .happyforms-part-option__description {
446
  display: block;
447
+ width: auto;
448
  max-width: 400px;
449
  margin-top: 0px;
450
  margin-left: 31px;
core/assets/js/parts/part-checkbox.js CHANGED
@@ -36,174 +36,9 @@
36
  model: OptionModel,
37
  } );
38
 
39
- happyForms.classes.views.parts.checkboxOptionHeading = Backbone.View.extend( {
40
- template: '#customize-happyforms-checkbox-item-heading-template',
41
-
42
- events: {
43
- 'click .delete-heading': 'onDeleteHeadingClick',
44
- 'keyup [name=label]': 'onHeadingLabelChange',
45
- 'change [name=label]': 'onHeadingLabelChange',
46
- },
47
-
48
- initialize: function( options ) {
49
- this.template = _.template( $( this.template ).text() );
50
- this.part = options.part;
51
-
52
- this.listenTo( this, 'ready', this.onReady );
53
- },
54
-
55
- render: function() {
56
- this.setElement( this.template( this.model.toJSON() ) );
57
-
58
- return this;
59
- },
60
-
61
- onReady: function() {
62
- $( '[name=label]', this.$el ).trigger( 'focus' );
63
- },
64
-
65
- onDeleteHeadingClick: function( e ) {
66
- e.preventDefault();
67
-
68
- this.model.collection.remove( this.model );
69
- },
70
-
71
- onHeadingLabelChange: function( e ) {
72
- this.model.set( 'label', $( e.target ).val() );
73
- this.part.trigger( 'change' );
74
-
75
- var data = {
76
- id: this.part.get( 'id' ),
77
- callback: 'onCheckboxHeadingLabelChangeCallback',
78
- options: {
79
- itemID: this.model.get( 'id' ),
80
- }
81
- };
82
-
83
- happyForms.previewSend( 'happyforms-part-dom-update', data );
84
- },
85
- } );
86
-
87
- happyForms.classes.views.parts.checkboxOption = Backbone.View.extend( {
88
  template: '#customize-happyforms-checkbox-item-template',
89
 
90
- events: {
91
- 'click .advanced-option': 'onAdvancedOptionClick',
92
- 'click .delete-option': 'onDeleteOptionClick',
93
- 'keyup [name=label]': 'onItemLabelChange',
94
- 'change [name=label]': 'onItemLabelChange',
95
- 'keyup [name=description]': 'onItemDescriptionChange',
96
- 'change [name=is_default]': 'onItemDefaultChange',
97
-
98
- 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
99
- 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
100
- },
101
-
102
- initialize: function( options ) {
103
- this.template = _.template( $( this.template ).text() );
104
- this.part = options.part;
105
-
106
- this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
107
- this.listenTo( this, 'ready', this.onReady );
108
- },
109
-
110
- onChangeMaxSubmissionsAmount: function( e ) {
111
- var model = this.part;
112
-
113
- if ( '' == this.model.get('limit_submissions_amount') ) {
114
- return;
115
- }
116
-
117
- this.part.fetchHtml( function( response ) {
118
- var data = {
119
- id: model.get( 'id' ),
120
- html: response,
121
- };
122
-
123
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
124
-
125
- } );
126
- },
127
-
128
- onItemLimitSubmissionsAmountChange: function( e ) {
129
- var value = $( '[name="limit_submissions_amount"]', this.$el ).val();
130
-
131
- if ( 0 > value ) {
132
- $( '[name="limit_submissions_amount"]', this.$el ).val( '' );
133
- return;
134
- }
135
-
136
- this.model.set( 'limit_submissions_amount', $( e.target ).val() );
137
- this.part.trigger( 'change' );
138
- },
139
-
140
- render: function() {
141
- this.setElement( this.template( this.model.toJSON() ) );
142
-
143
- return this;
144
- },
145
-
146
- onReady: function() {
147
- $( '[name=label]', this.$el ).trigger( 'focus' );
148
- },
149
-
150
- onAdvancedOptionClick: function( e ) {
151
- e.preventDefault();
152
-
153
- $( '.happyforms-part-item-advanced', this.$el ).slideToggle( 300, function() {
154
- $( e.target ).toggleClass( 'opened' );
155
- } );
156
- },
157
-
158
- onDeleteOptionClick: function( e ) {
159
- e.preventDefault();
160
-
161
- this.model.collection.remove( this.model );
162
- },
163
-
164
- onItemLabelChange: function( e ) {
165
- this.model.set( 'label', $( e.target ).val() );
166
- this.part.trigger( 'change' );
167
-
168
- var data = {
169
- id: this.part.get( 'id' ),
170
- callback: 'onCheckboxItemLabelChangeCallback',
171
- options: {
172
- itemID: this.model.get( 'id' ),
173
- }
174
- };
175
-
176
- happyForms.previewSend( 'happyforms-part-dom-update', data );
177
- },
178
-
179
- onItemDescriptionChange: function( e ) {
180
- this.model.set( 'description', $( e.target ).val() );
181
- this.part.trigger( 'change' );
182
-
183
- if ( '' == this.model.previousAttributes().description || '' == this.model.get( 'description' ) ) {
184
- var self = this;
185
- this.part.fetchHtml( function( response ) {
186
- var data = {
187
- id: self.part.get( 'id' ),
188
- html: response,
189
- };
190
-
191
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
192
- } );
193
- } else {
194
-
195
- var data = {
196
- id: this.part.get( 'id' ),
197
- callback: 'onCheckboxItemDescriptionChangeCallback',
198
- options: {
199
- itemID: this.model.get( 'id' ),
200
- }
201
- };
202
-
203
- happyForms.previewSend( 'happyforms-part-dom-update', data );
204
- }
205
- },
206
-
207
  onItemDefaultChange: function( e ) {
208
  var $target = $( e.target );
209
 
@@ -217,7 +52,7 @@
217
 
218
  var data = {
219
  id: this.part.get( 'id' ),
220
- callback: 'onCheckboxItemDefaultChangeCallback',
221
  options: {
222
  itemID: this.model.get( 'id' ),
223
  }
@@ -227,303 +62,41 @@
227
  },
228
  } );
229
 
230
- happyForms.classes.views.parts.checkbox = happyForms.classes.views.Part.extend( {
231
- template: '#customize-happyforms-checkbox-template',
 
232
 
233
- events: _.extend( {}, happyForms.classes.views.Part.prototype.events, {
234
- 'click .add-option': 'onAddOptionClick',
235
- 'click .add-heading': 'onAddHeadingClick',
236
- 'click .import-option': 'onImportOptionClick',
237
- 'click .import-options': 'onImportOptionsClick',
238
- 'click .add-options': 'onAddOptionsClick',
239
- 'change [data-bind=display_type]': 'onDisplayTypeChange',
240
 
 
241
  'change [data-bind=limit_choices_min]': 'refreshMinMaxChoices',
242
  'change [data-bind=limit_choices_max]': 'refreshMinMaxChoices',
243
  } ),
244
 
245
  initialize: function() {
246
- happyForms.classes.views.Part.prototype.initialize.apply( this, arguments );
247
-
248
- this.optionViews = new Backbone.Collection();
249
 
250
- this.listenTo( this.model.get( 'options' ), 'add', this.onOptionModelAdd );
251
- this.listenTo( this.model.get( 'options' ), 'change', this.onOptionModelChange );
252
- this.listenTo( this.model.get( 'options' ), 'remove', this.onOptionModelRemove );
253
- this.listenTo( this.model.get( 'options' ), 'reset', this.onOptionModelsSorted );
254
- this.listenTo( this.optionViews, 'add', this.onOptionViewAdd );
255
- this.listenTo( this.optionViews, 'remove', this.onOptionViewRemove );
256
- this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
257
- this.listenTo( this, 'sort-stop', this.onOptionSortStop );
258
- this.listenTo( this, 'ready', this.onReady );
259
-
260
- this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
261
- this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
262
- this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
263
  this.listenTo( this.model, 'change:limit_choices', this.onLimitChoices );
264
  this.listenTo( this.model.get( 'options' ), 'add remove', this.refreshMinMaxChoices );
265
  },
266
 
267
- onReady: function() {
268
- this.model.get( 'options' ).each( function( optionModel ) {
269
- this.addOptionView( optionModel );
270
- }, this );
271
-
272
- $( '.option-list', this.$el ).sortable( {
273
- handle: '.happyforms-part-item-handle',
274
- helper: 'clone',
275
 
276
- stop: function ( e, ui ) {
277
- this.trigger( 'sort-stop', e, ui );
278
- }.bind( this ),
279
- } );
280
  },
281
 
282
- onOptionModelAdd: function( optionModel, optionsCollection, options ) {
283
- this.model.trigger( 'change' );
284
- this.addOptionView( optionModel, options );
285
-
286
- var model = this.model;
287
 
288
- if ( options.refresh !== false ) {
289
- this.model.fetchHtml( function( response ) {
290
- var data = {
291
- id: model.get( 'id' ),
292
- html: response,
293
- };
294
-
295
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
296
- } );
297
- }
298
- },
299
-
300
- onOptionModelChange: function( optionModel ) {
301
- this.model.trigger( 'change' );
302
- },
303
-
304
- onOptionModelRemove: function( optionModel ) {
305
- this.model.trigger( 'change' );
306
-
307
- var optionViewModel = this.optionViews.find( function( viewModel ) {
308
- return viewModel.get( 'view' ).model.id === optionModel.id;
309
- }, this );
310
-
311
- this.optionViews.remove( optionViewModel );
312
-
313
- if ( this.model.get( 'options' ).length == 0 ) {
314
- $( '.options ul', this.$el ).html( '' );
315
- }
316
-
317
- var data = {
318
- id: this.model.get( 'id' ),
319
- callback: 'onCheckboxItemDeleteCallback',
320
- options: {
321
- itemID: optionModel.id,
322
- }
323
- };
324
-
325
- happyForms.previewSend( 'happyforms-part-dom-update', data );
326
- },
327
-
328
- onOptionModelsSorted: function() {
329
- this.optionViews.reset( _.map( this.model.get( 'options' ).pluck( 'id' ), function( id ) {
330
- return this.optionViews.get( id );
331
- }, this ) );
332
-
333
- this.model.trigger( 'change' );
334
-
335
- var model = this.model;
336
-
337
- this.model.fetchHtml( function( response ) {
338
- var data = {
339
- id: model.get( 'id' ),
340
- html: response,
341
- };
342
-
343
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
344
- } );
345
- },
346
-
347
- onOptionsChange: function() {
348
- this.model.trigger( 'change' );
349
- },
350
-
351
- addOptionView: function( optionModel, options ) {
352
- var optionView = null;
353
- var optionAttributes = optionModel.attributes;
354
- var isHeading = 'undefined' !== typeof optionAttributes.is_heading && 1 == optionAttributes.is_heading;
355
-
356
- if ( isHeading ) {
357
- optionView = new happyForms.classes.views.parts.checkboxOptionHeading( _.extend( {
358
- model: optionModel,
359
- part: this.model,
360
- }, options ) );
361
- } else {
362
- optionView = new happyForms.classes.views.parts.checkboxOption( _.extend( {
363
- model: optionModel,
364
- part: this.model,
365
- }, options ) );
366
- }
367
-
368
- var optionViewModel = new Backbone.Model( {
369
- id: optionModel.id,
370
- view: optionView,
371
- } );
372
-
373
- this.optionViews.add( optionViewModel, options );
374
- },
375
-
376
- onOptionViewAdd: function( viewModel, collection, options ) {
377
- var optionView = viewModel.get( 'view' );
378
- $( '.options ul', this.$el ).append( optionView.render().$el );
379
- optionView.trigger( 'ready' );
380
- },
381
-
382
- onOptionViewRemove: function( viewModel ) {
383
- var optionView = viewModel.get( 'view' );
384
- optionView.remove();
385
- },
386
-
387
- onOptionSortStop: function( e, ui ) {
388
- var $sortable = $( '.option-list', this.$el );
389
- var ids = $sortable.sortable( 'toArray', { attribute: 'data-option-id' } );
390
-
391
- this.model.get( 'options' ).reset( _.map( ids, function( id ) {
392
- return this.model.get( 'options' ).get( id );
393
- }, this ) );
394
- },
395
-
396
- onOptionViewsSorted: function( optionViews ) {
397
- var $stage = $( '.option-list', this.$el );
398
-
399
- optionViews.forEach( function( optionViewModel ) {
400
- var optionView = optionViewModel.get( 'view' );
401
- var $optionViewEl = optionView.$el;
402
- $optionViewEl.detach();
403
- $stage.append( $optionViewEl );
404
- optionView.trigger( 'refresh' );
405
- }, this );
406
- },
407
-
408
- getOptionModelID: function() {
409
- var prefix = this.model.get( 'id' ) + '_option_';
410
- var collection = this.model.get( 'options' );
411
- var timestamp = new Date().getTime();
412
- var id = prefix + timestamp;
413
-
414
- return id;
415
- },
416
-
417
- onAddOptionClick: function( e ) {
418
- e.preventDefault();
419
-
420
- var itemID = this.getOptionModelID();
421
- var itemModel = new OptionModel( { id: itemID } );
422
- this.model.get( 'options' ).add( itemModel );
423
- },
424
-
425
- onAddHeadingClick: function( e ) {
426
- e.preventDefault();
427
-
428
- var itemID = this.getOptionModelID();
429
- var itemModel = new OptionModel( { id: itemID, is_heading: 1 } );
430
- this.model.get( 'options' ).add( itemModel );
431
- },
432
-
433
- onDisplayTypeChange: function(e) {
434
- var $input = $( e.target );
435
- var attribute = $input.data( 'bind' );
436
- var value = $input.val();
437
-
438
- this.model.set( attribute, $input.val() );
439
-
440
- var data = {
441
- id: this.model.get( 'id' ),
442
- callback: 'onCheckboxDisplayTypeChangeCallback',
443
- };
444
-
445
- happyForms.previewSend( 'happyforms-part-dom-update', data );
446
- },
447
-
448
- onImportOptionsClick: function( e ) {
449
- e.preventDefault();
450
-
451
- $( '.options', this.$el ).hide();
452
- $( '.options-import', this.$el ).show();
453
- $( '.links.mode-manual', this.$el ).hide();
454
- $( '.links.mode-import', this.$el ).show();
455
- $( '.option-import-area', this.$el ).trigger( 'focus' );
456
- },
457
-
458
- onAddOptionsClick: function( e ) {
459
- e.preventDefault();
460
-
461
- $( '.options', this.$el ).show();
462
- $( '.options-import', this.$el ).hide();
463
- $( '.links.mode-import', this.$el ).hide();
464
- $( '.links.mode-manual', this.$el ).show();
465
- $( '.option-import-area', this.$el ).val( '' );
466
- },
467
-
468
- onImportOptionClick: function( e ) {
469
- e.preventDefault();
470
-
471
- var $textarea = $( '.option-import-area', this.$el );
472
- var list = $textarea.val();
473
- var self = this;
474
-
475
- var models = list
476
- .split( /[\r\n]+/g )
477
- .map( function( s ) {
478
- return s.trim();
479
- } )
480
- .filter( function( s ) {
481
- return s;
482
- } )
483
- .forEach( function( label, i, list ) {
484
- _.delay( function() {
485
- var itemID = self.getOptionModelID();
486
- var item = new OptionModel( {
487
- id: itemID,
488
- label: label
489
- } );
490
-
491
- self.model.get( 'options' ).add( item, { refresh: ( list.length - 1 === i ) } );
492
- }, i );
493
- } );
494
-
495
- $textarea.val( '' );
496
- $( '.add-options', this.$el ).trigger( 'click' );
497
- },
498
-
499
- onAddOtherOption: function( model, value ) {
500
- var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
501
-
502
- if ( 1 == value ) {
503
- $otherOptionOptions.show();
504
- } else {
505
- $otherOptionOptions.hide();
506
- }
507
-
508
- this.refreshPart();
509
- },
510
-
511
- onOtherOptionLabelChange: function( model, value ) {
512
- var data = {
513
- id: this.model.get( 'id' ),
514
- callback: 'onCheckboxOtherOptionLabelChangeCallback'
515
- };
516
-
517
- happyForms.previewSend( 'happyforms-part-dom-update', data );
518
- },
519
-
520
- onOtherOptionPlaceholderChange: function( model, value ) {
521
- var data = {
522
- id: this.model.get( 'id' ),
523
- callback: 'onCheckboxOtherOptionPlaceholderChangeCallback'
524
- };
525
-
526
- happyForms.previewSend( 'happyforms-part-dom-update', data );
527
  },
528
 
529
  onLimitChoices: function( model, value ) {
@@ -539,7 +112,9 @@
539
  refreshMinMaxChoices: function() {
540
  var minChoices = this.model.get( 'limit_choices_min' );
541
  var maxChoices = this.model.get( 'limit_choices_max' );
542
- var numChoices = this.model.get( 'options' ).length;
 
 
543
 
544
  var clamp = function( v, min, max ) {
545
  return Math.min( Math.max( v, min ), max );
@@ -560,78 +135,4 @@
560
  },
561
  } );
562
 
563
- happyForms.previewer = _.extend( happyForms.previewer, {
564
- onCheckboxDisplayTypeChangeCallback: function( id, html, options ) {
565
- var part = this.getPartModel( id );
566
- var $part = this.getPartElement( html );
567
-
568
- if ( 'block' === part.get( 'display_type' ) ) {
569
- $part.addClass( 'display-type--block' );
570
- } else {
571
- $part.removeClass( 'display-type--block' );
572
- }
573
- },
574
-
575
- onCheckboxItemLabelChangeCallback: function( id, html, options ) {
576
- var part = this.getPartModel( id );
577
- var $part = this.getPartElement( html );
578
- var option = part.get( 'options' ).get( options.itemID );
579
- var $option = $( '#' + options.itemID, $part );
580
-
581
- this.$( 'span.label', $option ).text( option.get( 'label' ) );
582
- },
583
-
584
- onCheckboxItemDefaultChangeCallback: function( id, html, options ) {
585
- var part = this.getPartModel( id );
586
- var $part = this.getPartElement( html );
587
- var option = part.get( 'options' ).get( options.itemID );
588
- var $option = $( '#' + options.itemID, $part );
589
-
590
- this.$( 'input', $option ).prop( 'checked', option.get( 'is_default' ) );
591
- },
592
-
593
- onCheckboxItemDescriptionChangeCallback: function( id, html, options ) {
594
- var part = this.getPartModel( id );
595
- var $part = this.getPartElement( html );
596
- var option = part.get( 'options' ).get( options.itemID );
597
- var $option = $( '#' + options.itemID, $part );
598
-
599
- this.$( '.happyforms-part-option__description', $option ).text( option.get( 'description' ) );
600
- },
601
-
602
- onCheckboxItemDeleteCallback: function( id, html, options ) {
603
- var part = this.getPartModel( id );
604
- var $part = this.getPartElement( html );
605
- var $option = $( '#' + options.itemID, $part );
606
-
607
- $option.remove();
608
- },
609
-
610
- onCheckboxOtherOptionLabelChangeCallback: function( id, html, options ) {
611
- var part = this.getPartModel( id );
612
- var $part = this.getPartElement( html );
613
- var $otherOptionLabel = $( '.happyforms-part-option--other .label', $part );
614
-
615
- $otherOptionLabel.text( part.get( 'other_option_label' ) );
616
- },
617
-
618
- onCheckboxOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
619
- var part = this.getPartModel( id );
620
- var $part = this.getPartElement( html );
621
- var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
622
-
623
- $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
624
- },
625
-
626
- onCheckboxHeadingLabelChangeCallback: function( id, html, options ) {
627
- var part = this.getPartModel( id );
628
- var $part = this.getPartElement( html );
629
- var option = part.get( 'options' ).get( options.itemID );
630
- var $option = $( '#' + options.itemID, $part );
631
-
632
- this.$( 'label.heading-label', $option ).text( option.get( 'label' ) );
633
- },
634
-
635
- } );
636
-
637
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
36
  model: OptionModel,
37
  } );
38
 
39
+ var OptionItemView = happyForms.classes.views.OptionItem.extend( {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  template: '#customize-happyforms-checkbox-item-template',
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  onItemDefaultChange: function( e ) {
43
  var $target = $( e.target );
44
 
52
 
53
  var data = {
54
  id: this.part.get( 'id' ),
55
+ callback: 'onOptionDefaultChangeCallback',
56
  options: {
57
  itemID: this.model.get( 'id' ),
58
  }
62
  },
63
  } );
64
 
65
+ var OptionHeadingView = happyForms.classes.views.OptionHeading.extend( {
66
+ template: '#customize-happyforms-checkbox-item-heading-template',
67
+ } );
68
 
69
+ happyForms.classes.views.parts.checkbox = happyForms.classes.views.ChoiceField.extend( {
70
+ template: '#customize-happyforms-checkbox-template',
 
 
 
 
 
71
 
72
+ events: _.extend( {}, happyForms.classes.views.ChoiceField.prototype.events, {
73
  'change [data-bind=limit_choices_min]': 'refreshMinMaxChoices',
74
  'change [data-bind=limit_choices_max]': 'refreshMinMaxChoices',
75
  } ),
76
 
77
  initialize: function() {
78
+ happyForms.classes.views.ChoiceField.prototype.initialize.apply( this, arguments );
 
 
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  this.listenTo( this.model, 'change:limit_choices', this.onLimitChoices );
81
  this.listenTo( this.model.get( 'options' ), 'add remove', this.refreshMinMaxChoices );
82
  },
83
 
84
+ getOptionItemView: function( optionModel, options ) {
85
+ var view = new OptionItemView( _.extend( {
86
+ model: optionModel,
87
+ part: this.model,
88
+ }, options ) );
 
 
 
89
 
90
+ return view;
 
 
 
91
  },
92
 
93
+ getOptionHeadingView: function( optionModel, options ) {
94
+ var view = new OptionHeadingView( _.extend( {
95
+ model: optionModel,
96
+ part: this.model,
97
+ }, options ) );
98
 
99
+ return view;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  },
101
 
102
  onLimitChoices: function( model, value ) {
112
  refreshMinMaxChoices: function() {
113
  var minChoices = this.model.get( 'limit_choices_min' );
114
  var maxChoices = this.model.get( 'limit_choices_max' );
115
+ var numChoices = this.model.get( 'options' ).filter( function( option ) {
116
+ return ! option.get( 'is_heading' );
117
+ } ).length;
118
 
119
  var clamp = function( v, min, max ) {
120
  return Math.min( Math.max( v, min ), max );
135
  },
136
  } );
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/assets/js/parts/part-field-choice.js ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $, _, Backbone, api, settings ) {
2
+
3
+ happyForms.classes.views.Option = Backbone.View.extend( {
4
+ events: {
5
+ 'click .happyforms-delete-item': 'onDeleteClick',
6
+ 'click .happyforms-duplicate-item': 'onDuplicateClick',
7
+ 'click .happyforms-advanced-option-action': 'onAdvancedOptionClick',
8
+ },
9
+
10
+ initialize: function( options ) {
11
+ this.template = _.template( $( this.template ).text() );
12
+ this.part = options.part;
13
+ this.listenTo( this, 'ready', this.onReady );
14
+ this.listenTo( this.model, 'open-widget', this.onAddOpenWidget );
15
+ },
16
+
17
+ onReady: function() {
18
+ // noop
19
+ },
20
+
21
+ render: function() {
22
+ this.setElement( this.template( this.model.toJSON() ) );
23
+
24
+ return this;
25
+ },
26
+
27
+ onDuplicateClick: function( e ) {
28
+ e.preventDefault();
29
+
30
+ this.$el.trigger( 'item-duplicate', this.model );
31
+ },
32
+
33
+ closeOpenWidgets: function( $currentElement ) {
34
+ var $openWidgets = $( '.happyforms-choice-item-widget').not( $currentElement );
35
+
36
+ $openWidgets.removeClass( 'happyforms-widget-choice-expanded' );
37
+ $openWidgets.find( '.happyforms-part-item-advanced' ).slideUp( 200, function() {
38
+ var $toggleIndicator = $openWidgets.find( '.toggle-indicator' );
39
+ $toggleIndicator.removeClass( 'opened' );
40
+ } );
41
+ },
42
+
43
+ onAddOpenWidget: function( e ) {
44
+ var $el = this.$el;
45
+
46
+ $el.toggleClass( 'happyforms-widget-choice-expanded' );
47
+ this.closeOpenWidgets( $el );
48
+
49
+ $( '.happyforms-part-item-advanced', this.$el ).slideToggle( 200, function() {
50
+ if ( $el.hasClass( 'happyforms-widget-choice-expanded' ) ) {
51
+ $( 'input[data-option-attribute=label]', $el ).trigger( 'focus' );
52
+ }
53
+ } );
54
+
55
+ $( '.toggle-indicator', $el ).toggleClass( 'opened' );
56
+ },
57
+
58
+ onAdvancedOptionClick: function( e ) {
59
+ e.preventDefault();
60
+
61
+ var $el = this.$el;
62
+ $el.toggleClass( 'happyforms-widget-choice-expanded' );
63
+ this.closeOpenWidgets( $el );
64
+
65
+ $( '.happyforms-part-item-advanced', this.$el ).slideToggle( 200, function() {
66
+ if ( $el.hasClass( 'happyforms-widget-choice-expanded' ) ) {
67
+ $( 'input[data-option-attribute=label]', $el ).trigger( 'focus' );
68
+ }
69
+ } );
70
+
71
+ $( '.toggle-indicator', $el ).toggleClass( 'opened' );
72
+ },
73
+
74
+ onDeleteClick: function( e ) {
75
+ e.preventDefault();
76
+
77
+ var self = this;
78
+
79
+ $( '.happyforms-part-item-advanced', this.$el ).slideUp( 'fast', function() {
80
+ self.model.collection.remove( self.model );
81
+ } );
82
+
83
+ },
84
+ } );
85
+
86
+ happyForms.classes.views.OptionHeading = happyForms.classes.views.Option.extend( {
87
+ events: _.extend( {}, happyForms.classes.views.Option.prototype.events, {
88
+ 'keyup [name=label]': 'onLabelChange',
89
+ 'change [name=label]': 'onLabelChange',
90
+ } ),
91
+
92
+ onReady: function() {
93
+ $( '.in-widget-title[name=label]', this.$el ).trigger( 'focus' );
94
+ },
95
+
96
+ onLabelChange: function( e ) {
97
+ var label = $( e.target ).val();
98
+ this.model.set( 'label', label );
99
+ this.part.trigger( 'change' );
100
+ $('.happyforms-item-choice-widget-title h3 .choice-in-widget-title span', this.$el ).text( label );
101
+
102
+ var data = {
103
+ id: this.part.get( 'id' ),
104
+ callback: 'onOptionHeadingLabelChangeCallback',
105
+ options: {
106
+ itemID: this.model.get( 'id' ),
107
+ }
108
+ };
109
+
110
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
111
+ },
112
+ } );
113
+
114
+ happyForms.classes.views.OptionItem = happyForms.classes.views.Option.extend( {
115
+ events: _.extend( {}, happyForms.classes.views.Option.prototype.events, {
116
+ 'keyup [name=label]': 'onItemLabelChange',
117
+ 'change [name=label]': 'onItemLabelChange',
118
+ 'keyup [name=description]': 'onItemDescriptionChange',
119
+ 'change [name=is_default]': 'onItemDefaultChange',
120
+ 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
121
+ 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
122
+ } ),
123
+
124
+ initialize: function( options ) {
125
+ happyForms.classes.views.Option.prototype.initialize.apply( this, arguments );
126
+
127
+ this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
128
+ },
129
+
130
+ onReady: function() {
131
+ $( '.in-widget-title[name=label]', this.$el ).trigger( 'focus' );
132
+ },
133
+
134
+ onItemLabelChange: function( e ) {
135
+ var label = $( e.target ).val();
136
+ this.model.set( 'label', label );
137
+ this.part.trigger( 'change' );
138
+ $('.happyforms-item-choice-widget-title h3 .choice-in-widget-title span', this.$el ).text( label );
139
+
140
+ var data = {
141
+ id: this.part.get( 'id' ),
142
+ callback: 'onOptionLabelChangeCallback',
143
+ options: {
144
+ itemID: this.model.get( 'id' ),
145
+ }
146
+ };
147
+
148
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
149
+ },
150
+
151
+ onItemDescriptionChange: function( e ) {
152
+ this.model.set( 'description', $( e.target ).val() );
153
+ this.part.trigger( 'change' );
154
+
155
+ if ( '' == this.model.previousAttributes().description || '' == this.model.get( 'description' ) ) {
156
+ var self = this;
157
+ this.part.fetchHtml( function( response ) {
158
+ var data = {
159
+ id: self.part.get( 'id' ),
160
+ html: response,
161
+ };
162
+
163
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
164
+ } );
165
+ } else {
166
+ var data = {
167
+ id: this.part.get( 'id' ),
168
+ callback: 'onOptionDescriptionChangeCallback',
169
+ options: {
170
+ itemID: this.model.get( 'id' ),
171
+ }
172
+ };
173
+
174
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
175
+ }
176
+ },
177
+
178
+ onItemDefaultChange: function( e ) {
179
+ var isChecked = $( e.target ).is( ':checked' );
180
+
181
+ this.model.collection.forEach( function( item ) {
182
+ item.set( 'is_default', 0 );
183
+ } );
184
+
185
+ $( '[name=is_default]', this.$el.siblings() ).prop( 'checked', false );
186
+
187
+ if ( isChecked ) {
188
+ this.model.set( 'is_default', 1 );
189
+ $( e.target ).prop( 'checked', true );
190
+ }
191
+
192
+ var data = {
193
+ id: this.part.get( 'id' ),
194
+ callback: 'onOptionDefaultChangeCallback',
195
+ options: {
196
+ itemID: this.model.get( 'id' ),
197
+ }
198
+ };
199
+
200
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
201
+ },
202
+
203
+ onChangeMaxSubmissionsAmount: function( e ) {
204
+ var model = this.part;
205
+
206
+ this.part.fetchHtml( function( response ) {
207
+ var data = {
208
+ id: model.get( 'id' ),
209
+ html: response,
210
+ };
211
+
212
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
213
+ } );
214
+ },
215
+
216
+ onItemLimitSubmissionsAmountChange: function( e ) {
217
+ var value = $( '[name="limit_submissions_amount"]', this.$el ).val();
218
+
219
+ if ( 0 > value ) {
220
+ $( '[name="limit_submissions_amount"]', this.$el ).val( '' );
221
+ return;
222
+ }
223
+
224
+ this.model.set( 'limit_submissions_amount', $( e.target ).val() );
225
+ this.part.trigger( 'change' );
226
+ },
227
+
228
+ } );
229
+
230
+ happyForms.classes.views.ChoiceField = happyForms.classes.views.Part.extend( {
231
+ events: _.extend( {}, happyForms.classes.views.Part.prototype.events, {
232
+ 'click .add-option': 'onAddOptionClick',
233
+ 'click .add-heading': 'onAddHeadingClick',
234
+ 'item-duplicate': 'onOptionDuplicate',
235
+ 'change [data-bind=display_type]': 'onDisplayTypeChange',
236
+ } ),
237
+
238
+ initialize: function() {
239
+ happyForms.classes.views.Part.prototype.initialize.apply( this, arguments );
240
+
241
+ this.optionViews = new Backbone.Collection();
242
+ this.listenTo( this.model.get( 'options' ), 'add', this.onOptionModelAdd );
243
+ this.listenTo( this.model.get( 'options' ), 'change', this.onOptionModelChange );
244
+ this.listenTo( this.model.get( 'options' ), 'remove', this.onOptionModelRemove );
245
+ this.listenTo( this.model.get( 'options' ), 'reset', this.onOptionModelsSorted );
246
+ this.listenTo( this.optionViews, 'add', this.onOptionViewAdd );
247
+ this.listenTo( this.optionViews, 'remove', this.onOptionViewRemove );
248
+ this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
249
+ this.listenTo( this, 'sort-stop', this.onOptionSortStop );
250
+ this.listenTo( this, 'ready', this.onReady );
251
+ this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
252
+ this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
253
+ this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
254
+ },
255
+
256
+ onReady: function() {
257
+ this.model.get( 'options' ).each( function( optionModel ) {
258
+ this.addOptionItemView( optionModel );
259
+ }, this );
260
+
261
+ $( '.option-list', this.$el ).sortable( {
262
+ handle: '.happyforms-part-item-handle',
263
+ helper: 'clone',
264
+
265
+ stop: function ( e, ui ) {
266
+ this.trigger( 'sort-stop', e, ui );
267
+ }.bind( this ),
268
+ } );
269
+ },
270
+
271
+ getOptionModelID: function() {
272
+ var prefix = this.model.get( 'id' ) + '_option_';
273
+ var collection = this.model.get( 'options' );
274
+ var timestamp = new Date().getTime();
275
+ var id = prefix + timestamp;
276
+
277
+ return id;
278
+ },
279
+
280
+ onAddOptionClick: function( e ) {
281
+ e.preventDefault();
282
+
283
+ var itemID = this.getOptionModelID();
284
+ var modelClass = this.model.get( 'options' ).model;
285
+ var itemModel = new modelClass( { id: itemID } );
286
+ this.model.get( 'options' ).add( itemModel );
287
+ this.model.get( 'options' ).findWhere( { id: itemID } ).trigger( 'open-widget' );
288
+ },
289
+
290
+ onAddHeadingClick: function( e ) {
291
+ e.preventDefault();
292
+
293
+ var itemID = this.getOptionModelID();
294
+ var modelClass = this.model.get( 'options' ).model;
295
+ var itemModel = new modelClass( { id: itemID, is_heading: 1 } );
296
+ this.model.get( 'options' ).add( itemModel );
297
+ this.model.get( 'options' ).findWhere( { id: itemID } ).trigger( 'open-widget' );
298
+ },
299
+
300
+ onOptionDuplicate: function( e, fieldChoice ) {
301
+ e.preventDefault();
302
+
303
+ var attrs = fieldChoice.toJSON();
304
+ var index = this.model.get( 'options' ).indexOf( fieldChoice );
305
+ index = index + 1;
306
+ attrs.id = this.getOptionModelID();
307
+ var modelClass = this.model.get( 'options' ).model;
308
+ var clonedModel = new modelClass( attrs );
309
+ this.model.get( 'options' ).add( clonedModel, { at: index } );
310
+ this.model.get( 'options' ).findWhere( { id: attrs.id } ).trigger( 'open-widget' );
311
+ },
312
+
313
+ onDisplayTypeChange: function(e) {
314
+ var $input = $( e.target );
315
+ var attribute = $input.data( 'bind' );
316
+ var value = $input.val();
317
+
318
+ this.model.set( attribute, value );
319
+
320
+ var data = {
321
+ id: this.model.get( 'id' ),
322
+ callback: 'onChoiceFieldDisplayTypeChangeCallback',
323
+ };
324
+
325
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
326
+ },
327
+
328
+ onOptionModelAdd: function( optionModel, optionsCollection, options ) {
329
+ this.model.trigger( 'change' );
330
+ this.addOptionItemView( optionModel, options );
331
+
332
+ var model = this.model;
333
+
334
+ if ( options.refresh !== false ) {
335
+ this.model.fetchHtml( function( response ) {
336
+ var data = {
337
+ id: model.get( 'id' ),
338
+ html: response,
339
+ };
340
+
341
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
342
+ } );
343
+ }
344
+ },
345
+
346
+ onOptionModelChange: function( optionModel ) {
347
+ this.model.trigger( 'change' );
348
+ },
349
+
350
+ onOptionModelRemove: function( optionModel ) {
351
+ this.model.trigger( 'change' );
352
+
353
+ var optionViewModel = this.optionViews.find( function( viewModel ) {
354
+ return viewModel.get( 'view' ).model.id === optionModel.id;
355
+ }, this );
356
+
357
+ this.optionViews.remove( optionViewModel );
358
+
359
+ if ( this.model.get( 'options' ).length == 0 ) {
360
+ $( '.options ul', this.$el ).html( '' );
361
+ }
362
+
363
+ var data = {
364
+ id: this.model.get( 'id' ),
365
+ callback: 'onOptionDeleteCallback',
366
+ options: {
367
+ itemID: optionModel.id,
368
+ }
369
+ };
370
+
371
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
372
+ },
373
+
374
+ onOptionModelsSorted: function() {
375
+ this.optionViews.reset( _.map( this.model.get( 'options' ).pluck( 'id' ), function( id ) {
376
+ return this.optionViews.get( id );
377
+ }, this ) );
378
+ this.model.trigger( 'change' );
379
+
380
+ var model = this.model;
381
+
382
+ this.model.fetchHtml( function( response ) {
383
+ var data = {
384
+ id: model.get( 'id' ),
385
+ html: response,
386
+ };
387
+
388
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
389
+ } );
390
+ },
391
+
392
+ addOptionItemView: function( optionModel, options ) {
393
+ var optionView = null;
394
+ var optionAttributes = optionModel.attributes;
395
+ var isHeading = 'undefined' !== typeof optionAttributes.is_heading && 1 == optionAttributes.is_heading;
396
+ var optionView = (
397
+ isHeading ?
398
+ this.getOptionHeadingView( optionModel ) :
399
+ this.getOptionItemView( optionModel )
400
+ );
401
+ var optionViewModel = new Backbone.Model( {
402
+ id: optionModel.id,
403
+ view: optionView,
404
+ } );
405
+
406
+ this.optionViews.add( optionViewModel, options );
407
+ },
408
+
409
+ getOptionItemView: function( optionModel, options ) {
410
+ var view = new happyForms.classes.views.OptionItem( _.extend( {
411
+ model: optionModel,
412
+ part: this.model,
413
+ }, options ) );
414
+
415
+ return view;
416
+ },
417
+
418
+ getOptionHeadingView: function( optionModel, options ) {
419
+ var view = new happyForms.classes.views.OptionHeading( _.extend( {
420
+ model: optionModel,
421
+ part: this.model,
422
+ }, options ) );
423
+
424
+ return view;
425
+ },
426
+
427
+ onOptionViewAdd: function( viewModel, collection, options ) {
428
+ var optionView = viewModel.get( 'view' );
429
+
430
+ if ( 'undefined' === typeof( options.index ) ) {
431
+ $( '.option-list', this.$el ).append( optionView.render().$el );
432
+ } else if ( 0 === options.index ) {
433
+ $( '.option-list', this.$el ).prepend( optionView.render().$el );
434
+ } else {
435
+ $( '.happyforms-choice-item-widget:nth-child(' + options.index + ')', this.$el ).after( optionView.render().$el );
436
+ }
437
+
438
+ optionView.trigger( 'ready' );
439
+ },
440
+
441
+ onOptionViewRemove: function( viewModel ) {
442
+ var optionView = viewModel.get( 'view' );
443
+ optionView.remove();
444
+ },
445
+
446
+ onOptionSortStop: function( e, ui ) {
447
+ var $sortable = $( '.option-list', this.$el );
448
+ var ids = $sortable.sortable( 'toArray', { attribute: 'data-option-id' } );
449
+
450
+ this.model.get( 'options' ).reset( _.map( ids, function( id ) {
451
+ return this.model.get( 'options' ).get( id );
452
+ }, this ) );
453
+ },
454
+
455
+ onOptionViewsSorted: function( optionViews ) {
456
+ var $stage = $( '.option-list', this.$el );
457
+
458
+ optionViews.forEach( function( optionViewModel ) {
459
+ var optionView = optionViewModel.get( 'view' );
460
+ var $optionViewEl = optionView.$el;
461
+ $optionViewEl.detach();
462
+ $stage.append( $optionViewEl );
463
+ optionView.trigger( 'refresh' );
464
+ }, this );
465
+ },
466
+
467
+ onAddOtherOption: function( model, value ) {
468
+ var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
469
+
470
+ if ( 1 == value ) {
471
+ $otherOptionOptions.show();
472
+ } else {
473
+ $otherOptionOptions.hide();
474
+ }
475
+
476
+ this.refreshPart();
477
+ },
478
+
479
+ onOtherOptionLabelChange: function() {
480
+ var data = {
481
+ id: this.model.get( 'id' ),
482
+ callback: 'onOtherOptionLabelChangeCallback'
483
+ };
484
+
485
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
486
+ },
487
+
488
+ onOtherOptionPlaceholderChange: function() {
489
+ var data = {
490
+ id: this.model.get( 'id' ),
491
+ callback: 'onOtherOptionPlaceholderChangeCallback'
492
+ };
493
+
494
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
495
+ },
496
+
497
+ } );
498
+
499
+ happyForms.previewer = _.extend( happyForms.previewer, {
500
+ onOptionDeleteCallback: function( id, html, options ) {
501
+ var part = this.getPartModel( id );
502
+ var $part = this.getPartElement( html );
503
+ var $option = $( '#' + options.itemID, $part );
504
+
505
+ $option.remove();
506
+ },
507
+
508
+ onOptionLabelChangeCallback: function( id, html, options ) {
509
+ var part = this.getPartModel( id );
510
+ var $part = this.getPartElement( html );
511
+ var option = part.get( 'options' ).get( options.itemID );
512
+ var $option = $( '#' + options.itemID, $part );
513
+
514
+ this.$( 'span.label', $option ).text( option.get( 'label' ) );
515
+ },
516
+
517
+ onOptionDescriptionChangeCallback: function( id, html, options ) {
518
+ var part = this.getPartModel( id );
519
+ var $part = this.getPartElement( html );
520
+ var option = part.get( 'options' ).get( options.itemID );
521
+ var $option = $( '#' + options.itemID, $part );
522
+
523
+ this.$( '.happyforms-part-option__description', $option ).text( option.get( 'description' ) );
524
+ },
525
+
526
+ onOptionDefaultChangeCallback: function( id, html, options ) {
527
+ var part = this.getPartModel( id );
528
+ var $part = this.getPartElement( html );
529
+ var option = part.get( 'options' ).get( options.itemID );
530
+ var $option = $( '#' + options.itemID, $part );
531
+
532
+ this.$( 'input', $option ).prop( 'checked', option.get( 'is_default' ) );
533
+ },
534
+
535
+ onOtherOptionLabelChangeCallback: function( id, html, options ) {
536
+ var part = this.getPartModel( id );
537
+ var $part = this.getPartElement( html );
538
+ var $otherOptionLabel = $( '.happyforms-part-option--other .label', $part );
539
+
540
+ $otherOptionLabel.text( part.get( 'other_option_label' ) );
541
+ },
542
+
543
+ onOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
544
+ var part = this.getPartModel( id );
545
+ var $part = this.getPartElement( html );
546
+ var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
547
+
548
+ $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
549
+ },
550
+
551
+ onOptionHeadingLabelChangeCallback: function( id, html, options ) {
552
+ var part = this.getPartModel( id );
553
+ var $part = this.getPartElement( html );
554
+ var option = part.get( 'options' ).get( options.itemID );
555
+ var $option = $( '#' + options.itemID, $part );
556
+
557
+ this.$( 'label.heading-label', $option ).text( option.get( 'label' ) );
558
+ },
559
+
560
+ onChoiceFieldDisplayTypeChangeCallback: function( id, html, options ) {
561
+ var part = this.getPartModel( id );
562
+ var $part = this.getPartElement( html );
563
+
564
+ if ( 'block' === part.get( 'display_type' ) ) {
565
+ $part.addClass( 'display-type--block' );
566
+ } else {
567
+ $part.removeClass( 'display-type--block' );
568
+ }
569
+ },
570
+ } );
571
+
572
+ } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/assets/js/parts/part-radio.js CHANGED
@@ -36,562 +36,38 @@
36
  model: OptionModel,
37
  } );
38
 
39
- happyForms.classes.views.parts.radioOptionHeading = Backbone.View.extend( {
40
- template: '#customize-happyforms-checkbox-item-heading-template',
41
-
42
- events: {
43
- 'click .delete-heading': 'onDeleteHeadingClick',
44
- 'keyup [name=label]': 'onHeadingLabelChange',
45
- 'change [name=label]': 'onHeadingLabelChange',
46
- },
47
-
48
- initialize: function( options ) {
49
- this.template = _.template( $( this.template ).text() );
50
- this.part = options.part;
51
-
52
- this.listenTo( this, 'ready', this.onReady );
53
- },
54
-
55
- render: function() {
56
- this.setElement( this.template( this.model.toJSON() ) );
57
-
58
- return this;
59
- },
60
-
61
- onReady: function() {
62
- $( '[name=label]', this.$el ).trigger( 'focus' );
63
- },
64
-
65
- onDeleteHeadingClick: function( e ) {
66
- e.preventDefault();
67
-
68
- this.model.collection.remove( this.model );
69
- },
70
-
71
- onHeadingLabelChange: function( e ) {
72
- this.model.set( 'label', $( e.target ).val() );
73
- this.part.trigger( 'change' );
74
-
75
- var data = {
76
- id: this.part.get( 'id' ),
77
- callback: 'onRadioHeadingLabelChangeCallback',
78
- options: {
79
- itemID: this.model.get( 'id' ),
80
- }
81
- };
82
-
83
- happyForms.previewSend( 'happyforms-part-dom-update', data );
84
- },
85
- } );
86
-
87
- happyForms.classes.views.parts.radioOption = Backbone.View.extend( {
88
  template: '#customize-happyforms-radio-item-template',
 
89
 
90
- events: {
91
- 'click .advanced-option': 'onAdvancedOptionClick',
92
- 'click .delete-option': 'onDeleteOptionClick',
93
- 'keyup [name=label]': 'onItemLabelChange',
94
- 'change [name=label]': 'onItemLabelChange',
95
- 'keyup [name=description]': 'onItemDescriptionChange',
96
- 'change [name=is_default]': 'onItemDefaultChange',
97
-
98
- 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
99
- 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
100
- },
101
-
102
- initialize: function( options ) {
103
- this.template = _.template( $( this.template ).text() );
104
- this.part = options.part;
105
-
106
- this.listenTo( this, 'ready', this.onReady );
107
- this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
108
- },
109
-
110
- render: function() {
111
- this.setElement( this.template( this.model.toJSON() ) );
112
- return this;
113
- },
114
-
115
- onReady: function() {
116
- $( '[name=label]', this.$el ).trigger( 'focus' );
117
- },
118
-
119
- onAdvancedOptionClick: function( e ) {
120
- e.preventDefault();
121
-
122
- $( '.happyforms-part-item-advanced', this.$el ).slideToggle( 300, function() {
123
- $( e.target ).toggleClass( 'opened' );
124
- } );
125
- },
126
-
127
- onDeleteOptionClick: function( e ) {
128
- e.preventDefault();
129
- this.model.collection.remove( this.model );
130
- },
131
-
132
- onItemLabelChange: function( e ) {
133
- this.model.set( 'label', $( e.target ).val() );
134
- this.part.trigger( 'change' );
135
-
136
- var data = {
137
- id: this.part.get( 'id' ),
138
- callback: 'onRadioItemLabelChangeCallback',
139
- options: {
140
- itemID: this.model.get( 'id' ),
141
- }
142
- };
143
-
144
- happyForms.previewSend( 'happyforms-part-dom-update', data );
145
- },
146
-
147
- onItemDescriptionChange: function( e ) {
148
- this.model.set( 'description', $( e.target ).val() );
149
- this.part.trigger( 'change' );
150
-
151
- if ( '' == this.model.previousAttributes().description || '' == this.model.get( 'description' ) ) {
152
- var self = this;
153
- this.part.fetchHtml( function( response ) {
154
- var data = {
155
- id: self.part.get( 'id' ),
156
- html: response,
157
- };
158
-
159
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
160
- } );
161
- } else {
162
- var data = {
163
- id: this.part.get( 'id' ),
164
- callback: 'onRadioItemDescriptionChangeCallback',
165
- options: {
166
- itemID: this.model.get( 'id' ),
167
- }
168
- };
169
-
170
- happyForms.previewSend( 'happyforms-part-dom-update', data );
171
-
172
- }
173
- },
174
-
175
- onItemDefaultChange: function( e ) {
176
- var isChecked = $( e.target ).is( ':checked' );
177
-
178
- this.model.collection.forEach( function( item ) {
179
- item.set( 'is_default', 0 );
180
- } );
181
-
182
- $( '[name=is_default]', this.$el.siblings() ).prop( 'checked', false );
183
-
184
- if ( isChecked ) {
185
- this.model.set( 'is_default', 1 );
186
- $( e.target ).prop( 'checked', true );
187
- }
188
-
189
- var data = {
190
- id: this.part.get( 'id' ),
191
- callback: 'onRadioItemDefaultChangeCallback',
192
- options: {
193
- itemID: this.model.get( 'id' ),
194
- }
195
- };
196
-
197
- happyForms.previewSend( 'happyforms-part-dom-update', data );
198
- },
199
-
200
- onChangeMaxSubmissionsAmount: function( e ) {
201
-
202
- var model = this.part;
203
-
204
- if ( '' == this.model.get('limit_submissions_amount') ) {
205
- return;
206
- }
207
-
208
- this.part.fetchHtml( function( response ) {
209
- var data = {
210
- id: model.get( 'id' ),
211
- html: response,
212
- };
213
-
214
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
215
-
216
- } );
217
- },
218
-
219
- onItemLimitSubmissionsAmountChange: function( e ) {
220
- var value = $( '[name="limit_submissions_amount"]', this.$el ).val();
221
-
222
- if ( 0 > value ) {
223
- $( '[name="limit_submissions_amount"]', this.$el ).val( '' );
224
- return;
225
- }
226
-
227
- this.model.set( 'limit_submissions_amount', $( e.target ).val() );
228
- this.part.trigger( 'change' );
229
- },
230
-
231
  } );
232
 
233
- happyForms.classes.views.parts.radio = happyForms.classes.views.Part.extend( {
234
  template: '#customize-happyforms-radio-template',
235
 
236
- events: _.extend( {}, happyForms.classes.views.Part.prototype.events, {
237
- 'click .add-option': 'onAddOptionClick',
238
- 'click .add-heading': 'onAddHeadingClick',
239
- 'click .import-option': 'onImportOptionClick',
240
- 'click .import-options': 'onImportOptionsClick',
241
- 'click .add-options': 'onAddOptionsClick',
242
  'change [data-bind=display_type]': 'onDisplayTypeChange',
243
  } ),
244
 
245
- initialize: function() {
246
- happyForms.classes.views.Part.prototype.initialize.apply( this, arguments );
247
-
248
- this.optionViews = new Backbone.Collection();
249
-
250
- this.listenTo( this.model.get( 'options' ), 'add', this.onOptionModelAdd );
251
- this.listenTo( this.model.get( 'options' ), 'change', this.onOptionModelChange );
252
- this.listenTo( this.model.get( 'options' ), 'remove', this.onOptionModelRemove );
253
- this.listenTo( this.model.get( 'options' ), 'reset', this.onOptionModelsSorted );
254
- this.listenTo( this.optionViews, 'add', this.onOptionViewAdd );
255
- this.listenTo( this.optionViews, 'remove', this.onOptionViewRemove );
256
- this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
257
- this.listenTo( this, 'sort-stop', this.onOptionSortStop );
258
- this.listenTo( this, 'ready', this.onReady );
259
-
260
- this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
261
- this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
262
- this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
263
- },
264
-
265
- onReady: function() {
266
- this.model.get( 'options' ).each( function( optionModel ) {
267
- this.addOptionView( optionModel );
268
- }, this );
269
-
270
- $( '.option-list', this.$el ).sortable( {
271
- handle: '.happyforms-part-item-handle',
272
- helper: 'clone',
273
-
274
- stop: function ( e, ui ) {
275
- this.trigger( 'sort-stop', e, ui );
276
- }.bind( this ),
277
- } );
278
- },
279
-
280
- onOptionModelAdd: function( optionModel, optionsCollection, options ) {
281
- this.model.trigger( 'change' );
282
- this.addOptionView( optionModel, options );
283
-
284
- var model = this.model;
285
-
286
- if ( options.refresh !== false ) {
287
- this.model.fetchHtml( function( response ) {
288
- var data = {
289
- id: model.get( 'id' ),
290
- html: response,
291
- };
292
-
293
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
294
- } );
295
- }
296
- },
297
-
298
- onOptionModelChange: function( optionModel ) {
299
- this.model.trigger( 'change' );
300
- },
301
-
302
- onOptionModelRemove: function( optionModel ) {
303
- this.model.trigger( 'change' );
304
-
305
- var optionViewModel = this.optionViews.find( function( viewModel ) {
306
- return viewModel.get( 'view' ).model.id === optionModel.id;
307
- }, this );
308
-
309
- this.optionViews.remove( optionViewModel );
310
-
311
- if ( this.model.get( 'options' ).length == 0 ) {
312
- $( '.options ul', this.$el ).html( '' );
313
- }
314
-
315
- var data = {
316
- id: this.model.get( 'id' ),
317
- callback: 'onRadioItemDeleteCallback',
318
- options: {
319
- itemID: optionModel.id,
320
- }
321
- };
322
-
323
- happyForms.previewSend( 'happyforms-part-dom-update', data );
324
- },
325
-
326
- onOptionModelsSorted: function() {
327
- this.optionViews.reset( _.map( this.model.get( 'options' ).pluck( 'id' ), function( id ) {
328
- return this.optionViews.get( id );
329
- }, this ) );
330
- this.model.trigger( 'change' );
331
-
332
- var model = this.model;
333
-
334
- this.model.fetchHtml( function( response ) {
335
- var data = {
336
- id: model.get( 'id' ),
337
- html: response,
338
- };
339
-
340
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
341
- } );
342
- },
343
-
344
- addOptionView: function( optionModel, options ) {
345
- var optionView = null;
346
- var optionAttributes = optionModel.attributes;
347
- var isHeading = 'undefined' !== typeof optionAttributes.is_heading && 1 == optionAttributes.is_heading;
348
-
349
- if ( isHeading ) {
350
- optionView = new happyForms.classes.views.parts.radioOptionHeading( _.extend( {
351
- model: optionModel,
352
- part: this.model,
353
- }, options ) );
354
- } else {
355
- optionView = new happyForms.classes.views.parts.radioOption( _.extend( {
356
- model: optionModel,
357
- part: this.model,
358
- }, options ) );
359
- }
360
-
361
- var optionViewModel = new Backbone.Model( {
362
- id: optionModel.id,
363
- view: optionView,
364
- } );
365
-
366
- this.optionViews.add( optionViewModel, options );
367
- },
368
-
369
- onOptionViewAdd: function( viewModel, collection, options ) {
370
- var optionView = viewModel.get( 'view' );
371
- $( '.option-list', this.$el ).append( optionView.render().$el );
372
- optionView.trigger( 'ready' );
373
- },
374
-
375
- onOptionViewRemove: function( viewModel ) {
376
- var optionView = viewModel.get( 'view' );
377
- optionView.remove();
378
- },
379
-
380
- onOptionSortStop: function( e, ui ) {
381
- var $sortable = $( '.option-list', this.$el );
382
- var ids = $sortable.sortable( 'toArray', { attribute: 'data-option-id' } );
383
-
384
- this.model.get( 'options' ).reset( _.map( ids, function( id ) {
385
- return this.model.get( 'options' ).get( id );
386
- }, this ) );
387
- },
388
-
389
- onOptionViewsSorted: function( optionViews ) {
390
- var $stage = $( '.option-list', this.$el );
391
-
392
- optionViews.forEach( function( optionViewModel ) {
393
- var optionView = optionViewModel.get( 'view' );
394
- var $optionViewEl = optionView.$el;
395
- $optionViewEl.detach();
396
- $stage.append( $optionViewEl );
397
- optionView.trigger( 'refresh' );
398
- }, this );
399
- },
400
-
401
- getOptionModelID: function() {
402
- var prefix = this.model.get( 'id' ) + '_option_';
403
- var collection = this.model.get( 'options' );
404
- var timestamp = new Date().getTime();
405
- var id = prefix + timestamp;
406
-
407
- return id;
408
- },
409
-
410
- onAddOptionClick: function( e ) {
411
- e.preventDefault();
412
-
413
- var itemID = this.getOptionModelID();
414
- var itemModel = new OptionModel( { id: itemID } );
415
- this.model.get( 'options' ).add( itemModel );
416
- },
417
-
418
- onAddHeadingClick: function( e ) {
419
- e.preventDefault();
420
-
421
- var itemID = this.getOptionModelID();
422
- var itemModel = new OptionModel( { id: itemID, is_heading: 1 } );
423
- this.model.get( 'options' ).add( itemModel );
424
- },
425
-
426
- onDisplayTypeChange: function(e) {
427
- var $input = $( e.target );
428
- var attribute = $input.data( 'bind' );
429
- var value = $input.val();
430
-
431
- this.model.set( attribute, value );
432
-
433
- var data = {
434
- id: this.model.get( 'id' ),
435
- callback: 'onRadioDisplayTypeChangeCallback',
436
- };
437
-
438
- happyForms.previewSend( 'happyforms-part-dom-update', data );
439
- },
440
-
441
- onImportOptionsClick: function( e ) {
442
- e.preventDefault();
443
-
444
- $( '.options', this.$el ).hide();
445
- $( '.options-import', this.$el ).show();
446
- $( '.links.mode-manual', this.$el ).hide();
447
- $( '.links.mode-import', this.$el ).show();
448
- $( '.option-import-area', this.$el ).trigger( 'focus' );
449
- },
450
-
451
- onAddOptionsClick: function( e ) {
452
- e.preventDefault();
453
-
454
- $( '.options', this.$el ).show();
455
- $( '.options-import', this.$el ).hide();
456
- $( '.links.mode-import', this.$el ).hide();
457
- $( '.links.mode-manual', this.$el ).show();
458
- $( '.option-import-area', this.$el ).val( '' );
459
- },
460
-
461
- onImportOptionClick: function( e ) {
462
- e.preventDefault();
463
-
464
- var $textarea = $( '.option-import-area', this.$el );
465
- var list = $textarea.val();
466
- var self = this;
467
-
468
- var models = list
469
- .split( /[\r\n]+/g )
470
- .map( function( s ) {
471
- return s.trim();
472
- } )
473
- .filter( function( s ) {
474
- return s;
475
- } )
476
- .forEach( function( label, i, list ) {
477
- _.delay( function() {
478
- var itemID = self.getOptionModelID();
479
- var item = new OptionModel( {
480
- id: itemID,
481
- label: label
482
- } );
483
-
484
- self.model.get( 'options' ).add( item, { refresh: ( list.length - 1 === i ) } );
485
- }, i );
486
- } );
487
-
488
- $textarea.val( '' );
489
- $( '.add-options', this.$el ).trigger( 'click' );
490
- },
491
-
492
- onAddOtherOption: function( model, value ) {
493
- var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
494
-
495
- if ( 1 == value ) {
496
- $otherOptionOptions.show();
497
- } else {
498
- $otherOptionOptions.hide();
499
- }
500
-
501
- this.refreshPart();
502
- },
503
-
504
- onOtherOptionLabelChange: function() {
505
- var data = {
506
- id: this.model.get( 'id' ),
507
- callback: 'onRadioOtherOptionLabelChangeCallback'
508
- };
509
-
510
- happyForms.previewSend( 'happyforms-part-dom-update', data );
511
- },
512
-
513
- onOtherOptionPlaceholderChange: function() {
514
- var data = {
515
- id: this.model.get( 'id' ),
516
- callback: 'onRadioOtherOptionPlaceholderChangeCallback'
517
- };
518
-
519
- happyForms.previewSend( 'happyforms-part-dom-update', data );
520
- },
521
-
522
- } );
523
-
524
- happyForms.previewer = _.extend( happyForms.previewer, {
525
- onRadioItemDeleteCallback: function( id, html, options ) {
526
- var part = this.getPartModel( id );
527
- var $part = this.getPartElement( html );
528
- var $option = $( '#' + options.itemID, $part );
529
-
530
- $option.remove();
531
- },
532
-
533
- onRadioDisplayTypeChangeCallback: function( id, html, options ) {
534
- var part = this.getPartModel( id );
535
- var $part = this.getPartElement( html );
536
-
537
- if ( 'block' === part.get( 'display_type' ) ) {
538
- $part.addClass( 'display-type--block' );
539
- } else {
540
- $part.removeClass( 'display-type--block' );
541
- }
542
- },
543
-
544
- onRadioItemLabelChangeCallback: function( id, html, options ) {
545
- var part = this.getPartModel( id );
546
- var $part = this.getPartElement( html );
547
- var option = part.get( 'options' ).get( options.itemID );
548
- var $option = $( '#' + options.itemID, $part );
549
-
550
- this.$( 'span.label', $option ).text( option.get( 'label' ) );
551
- },
552
-
553
- onRadioItemDescriptionChangeCallback: function( id, html, options ) {
554
- var part = this.getPartModel( id );
555
- var $part = this.getPartElement( html );
556
- var option = part.get( 'options' ).get( options.itemID );
557
- var $option = $( '#' + options.itemID, $part );
558
-
559
- this.$( '.happyforms-part-option__description', $option ).text( option.get( 'description' ) );
560
- },
561
-
562
- onRadioItemDefaultChangeCallback: function( id, html, options ) {
563
- var part = this.getPartModel( id );
564
- var $part = this.getPartElement( html );
565
- var option = part.get( 'options' ).get( options.itemID );
566
- var $option = $( '#' + options.itemID, $part );
567
-
568
- this.$( 'input', $option ).prop( 'checked', option.get( 'is_default' ) );
569
- },
570
-
571
- onRadioOtherOptionLabelChangeCallback: function( id, html, options ) {
572
- var part = this.getPartModel( id );
573
- var $part = this.getPartElement( html );
574
- var $otherOptionLabel = $( '.happyforms-part-option--other .label', $part );
575
-
576
- $otherOptionLabel.text( part.get( 'other_option_label' ) );
577
- },
578
-
579
- onRadioOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
580
- var part = this.getPartModel( id );
581
- var $part = this.getPartElement( html );
582
- var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
583
 
584
- $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
585
  },
586
 
587
- onRadioHeadingLabelChangeCallback: function( id, html, options ) {
588
- var part = this.getPartModel( id );
589
- var $part = this.getPartElement( html );
590
- var option = part.get( 'options' ).get( options.itemID );
591
- var $option = $( '#' + options.itemID, $part );
592
 
593
- this.$( 'label.heading-label', $option ).text( option.get( 'label' ) );
594
  },
595
  } );
596
 
597
- } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
36
  model: OptionModel,
37
  } );
38
 
39
+ var OptionItemView = happyForms.classes.views.OptionItem.extend( {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  template: '#customize-happyforms-radio-item-template',
41
+ } );
42
 
43
+ var OptionHeadingView = happyForms.classes.views.OptionHeading.extend( {
44
+ template: '#customize-happyforms-radio-item-heading-template',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  } );
46
 
47
+ happyForms.classes.views.parts.radio = happyForms.classes.views.ChoiceField.extend( {
48
  template: '#customize-happyforms-radio-template',
49
 
50
+ events: _.extend( {}, happyForms.classes.views.ChoiceField.prototype.events, {
 
 
 
 
 
51
  'change [data-bind=display_type]': 'onDisplayTypeChange',
52
  } ),
53
 
54
+ getOptionItemView: function( optionModel, options ) {
55
+ var view = new OptionItemView( _.extend( {
56
+ model: optionModel,
57
+ part: this.model,
58
+ }, options ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ return view;
61
  },
62
 
63
+ getOptionHeadingView: function( optionModel, options ) {
64
+ var view = new OptionHeadingView( _.extend( {
65
+ model: optionModel,
66
+ part: this.model,
67
+ }, options ) );
68
 
69
+ return view;
70
  },
71
  } );
72
 
73
+ } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/assets/js/parts/part-select.js CHANGED
@@ -35,117 +35,45 @@
35
  model: OptionModel,
36
  } );
37
 
38
- happyForms.classes.views.parts.selectOptionHeading = Backbone.View.extend( {
39
  template: '#customize-happyforms-select-item-heading-template',
40
 
41
- events: {
42
- 'click .delete-heading': 'onDeleteHeadingClick',
43
- 'keyup [name=label]': 'onHeadingLabelChange',
44
- 'change [name=label]': 'onHeadingLabelChange',
45
- },
46
-
47
- initialize: function( options ) {
48
- this.template = _.template( $( this.template ).text() );
49
- this.part = options.part;
50
-
51
- this.listenTo( this, 'ready', this.onReady );
52
- },
53
-
54
- render: function() {
55
- this.setElement( this.template( this.model.toJSON() ) );
56
-
57
- return this;
58
- },
59
-
60
- onReady: function() {
61
- $( '[name=label]', this.$el ).trigger( 'focus' );
62
- },
63
-
64
- onDeleteHeadingClick: function( e ) {
65
- e.preventDefault();
66
-
67
- this.model.collection.remove( this.model );
68
- },
69
-
70
- onHeadingLabelChange: function( e ) {
71
- this.model.set( 'label', $( e.target ).val() );
72
- var self = this;
73
 
74
- this.part.fetchHtml( function( response ) {
75
- var data = {
76
- id: self.part.id,
77
- html: response,
78
- };
 
 
79
 
80
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
81
- } );
82
  },
83
  } );
84
 
85
- happyForms.classes.views.parts.selectOption = Backbone.View.extend( {
86
  template: '#customize-happyforms-select-item-template',
87
 
88
- events: {
89
- 'click .advanced-option': 'onAdvancedOptionClick',
90
- 'click .delete-option': 'onDeleteOptionClick',
91
- 'keyup [name=label]': 'onItemLabelKeyup',
92
- 'change [name=label]': 'onItemLabelChange',
93
- 'change [name=is_default]': 'onItemDefaultChange',
94
- 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
95
- 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
96
- },
97
-
98
- initialize: function( options ) {
99
- this.template = _.template( $( this.template ).text() );
100
- this.part = options.part;
101
-
102
- this.listenTo( this, 'ready', this.onReady );
103
- this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
104
- },
105
-
106
- render: function() {
107
- this.setElement( this.template( this.model.toJSON() ) );
108
- return this;
109
- },
110
-
111
- onReady: function() {
112
- $( '[name=label]', this.$el ).trigger( 'focus' );
113
- },
114
-
115
- onAdvancedOptionClick: function( e ) {
116
- e.preventDefault();
117
-
118
- $( '.happyforms-part-item-advanced', this.$el ).slideToggle( 300, function() {
119
- $( e.target ).toggleClass( 'opened' );
120
- } );
121
- },
122
-
123
- onDeleteOptionClick: function( e ) {
124
- e.preventDefault();
125
- this.model.collection.remove( this.model );
126
- },
127
-
128
- onItemLabelKeyup: function( e ) {
129
- if ( 'Enter' === e.key ) {
130
- $( '.add-option', this.$el ).trigger( 'click' );
131
- return;
132
- }
133
-
134
- this.model.set( 'label', $( e.target ).val() );
135
- this.part.trigger( 'change' );
136
- },
137
-
138
  onItemLabelChange: function( e ) {
139
- var self = this;
 
 
 
140
 
141
- this.part.fetchHtml( function( response ) {
142
- var data = {
143
- id: self.part.id,
144
- html: response,
145
- };
 
 
146
 
147
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
148
- } );
149
  },
150
 
151
  onItemDefaultChange: function( e ) {
@@ -167,254 +95,39 @@
167
  callback: 'onSelectItemDefaultChangeCallback',
168
  options: {
169
  itemID: this.model.get( 'id' ),
 
170
  }
171
  };
172
 
173
  happyForms.previewSend( 'happyforms-part-dom-update', data );
174
  },
175
-
176
- onItemLimitSubmissionsAmountChange: function( e ) {
177
- var value = $( '[name="limit_submissions_amount"]', this.$el ).val();
178
-
179
- if ( 0 > value ) {
180
- $( '[name="limit_submissions_amount"]', this.$el ).val( '' );
181
- return;
182
- }
183
-
184
- this.model.set( 'limit_submissions_amount', $( e.target ).val() );
185
- this.part.trigger( 'change' );
186
- },
187
-
188
- onChangeMaxSubmissionsAmount: function( e ) {
189
-
190
- var model = this.part;
191
-
192
- if ( '' == this.model.get('limit_submissions_amount') ) {
193
- return;
194
- }
195
-
196
- this.part.fetchHtml( function( response ) {
197
- var data = {
198
- id: model.get( 'id' ),
199
- html: response,
200
- };
201
-
202
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
203
-
204
- } );
205
- },
206
  } );
207
 
208
- happyForms.classes.views.parts.select = happyForms.classes.views.Part.extend( {
209
  template: '#customize-happyforms-select-template',
210
 
211
- events: _.extend( {}, happyForms.classes.views.Part.prototype.events, {
212
- 'click .add-option': 'onAddOptionClick',
213
- 'click .add-heading': 'onAddHeadingClick',
214
- 'click .import-option': 'onImportOptionClick',
215
- 'click .import-options': 'onImportOptionsClick',
216
- 'click .add-options': 'onAddOptionsClick',
217
- 'click .show-all-options': 'onShowAllOptionsClick',
218
-
219
- 'change [data-bind=other_option_label]': 'onOtherSelectOptionLabelChange',
220
- } ),
221
-
222
  initialize: function() {
223
- happyForms.classes.views.Part.prototype.initialize.apply( this, arguments );
224
-
225
- this.optionViews = new Backbone.Collection();
226
 
227
  this.listenTo( this.model, 'change:placeholder', this.onPlaceholderChange );
228
- this.listenTo( this.model, 'change:required', this.onRequiredChange );
229
- this.listenTo( this.model.get( 'options' ), 'add', this.onOptionModelAdd );
230
- this.listenTo( this.model.get( 'options' ), 'change', this.onOptionModelChange );
231
- this.listenTo( this.model.get( 'options' ), 'remove', this.onOptionModelRemove );
232
- this.listenTo( this.model.get( 'options' ), 'reset', this.onOptionModelsSorted );
233
- this.listenTo( this.optionViews, 'add', this.onOptionViewAdd );
234
- this.listenTo( this.optionViews, 'remove', this.onOptionViewRemove );
235
- this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
236
- this.listenTo( this, 'sort-stop', this.onOptionSortStop );
237
- this.listenTo( this, 'ready', this.onReady );
238
-
239
- this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
240
- this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
241
- this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
242
  },
243
 
244
- onReady: function() {
245
- this.model.get( 'options' ).each( function( optionModel ) {
246
- this.addOptionView( optionModel );
247
- }, this );
 
248
 
249
- $( '.option-list', this.$el ).sortable( {
250
- handle: '.happyforms-part-item-handle',
251
- helper: 'clone',
252
-
253
- stop: function ( e, ui ) {
254
- this.trigger( 'sort-stop', e, ui );
255
- }.bind( this ),
256
- } );
257
  },
258
 
259
- onOptionModelAdd: function( optionModel, optionsCollection, options ) {
260
- this.model.trigger( 'change' );
261
- this.addOptionView( optionModel, options );
 
 
262
 
263
- var model = this.model;
264
-
265
- if ( options.refresh !== false ) {
266
- this.model.fetchHtml( function( response ) {
267
- var data = {
268
- id: model.get( 'id' ),
269
- html: response,
270
- };
271
-
272
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
273
- } );
274
- }
275
- },
276
-
277
- onOptionModelChange: function( optionModel ) {
278
- this.model.trigger( 'change' );
279
- },
280
-
281
- onOptionModelRemove: function( optionModel ) {
282
- this.model.trigger( 'change' );
283
-
284
- var optionViewModel = this.optionViews.find( function( viewModel ) {
285
- return viewModel.get( 'view' ).model.id === optionModel.id;
286
- }, this );
287
-
288
- if ( 1 == optionModel.get( 'is_default' ) ) {
289
- $( '[name=is_default]', optionViewModel.$el ).prop( 'checked', false ).trigger( 'change' );
290
- }
291
-
292
- this.optionViews.remove( optionViewModel );
293
-
294
- if ( this.model.get( 'options' ).length == 0 ) {
295
- $( '.options ul', this.$el ).html( '' );
296
- }
297
-
298
- var data = {
299
- id: this.model.get( 'id' ),
300
- callback: 'onSelectItemDeleteCallback',
301
- options: {
302
- itemID: optionModel.id,
303
- }
304
- };
305
-
306
- happyForms.previewSend( 'happyforms-part-dom-update', data );
307
- },
308
-
309
- onOptionModelsSorted: function() {
310
- this.optionViews.reset( _.map( this.model.get( 'options' ).pluck( 'id' ), function( id ) {
311
- return this.optionViews.get( id );
312
- }, this ) );
313
-
314
- this.model.trigger( 'change' );
315
-
316
- var model = this.model;
317
-
318
- this.model.fetchHtml( function( response ) {
319
- var data = {
320
- id: model.get( 'id' ),
321
- html: response,
322
- };
323
-
324
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
325
- } );
326
- },
327
-
328
- onOptionsChange: function() {
329
- this.model.trigger( 'change' );
330
- },
331
-
332
- addOptionView: function( optionModel, options ) {
333
- var optionView = null;
334
- var optionAttributes = optionModel.attributes;
335
- var isHeading = 'undefined' !== typeof optionAttributes.is_heading && 1 == optionAttributes.is_heading;
336
-
337
- if ( isHeading ) {
338
- optionView = new happyForms.classes.views.parts.selectOptionHeading( _.extend( {
339
- model: optionModel,
340
- part: this.model,
341
- }, options ) );
342
- } else {
343
- optionView = new happyForms.classes.views.parts.selectOption( _.extend( {
344
- model: optionModel,
345
- part: this.model,
346
- }, options ) );
347
- }
348
-
349
- var optionViewModel = new Backbone.Model( {
350
- id: optionModel.id,
351
- view: optionView,
352
- } );
353
-
354
- this.optionViews.add( optionViewModel, options );
355
- },
356
-
357
- onOptionViewAdd: function( viewModel, collection, options ) {
358
- var optionView = viewModel.get( 'view' );
359
- $( '.options ul', this.$el ).append( optionView.render().$el );
360
- optionView.trigger( 'ready' );
361
- },
362
-
363
- onOptionViewRemove: function( viewModel ) {
364
- var optionView = viewModel.get( 'view' );
365
- optionView.remove();
366
- },
367
-
368
- onOptionSortStop: function( e, ui ) {
369
- var $sortable = $( '.option-list', this.$el );
370
- var ids = $sortable.sortable( 'toArray', { attribute: 'data-option-id' } );
371
-
372
- this.model.get( 'options' ).reset( _.map( ids, function( id ) {
373
- return this.model.get( 'options' ).get( id );
374
- }, this ) );
375
- },
376
-
377
- onOptionViewsSorted: function( optionViews ) {
378
- var $stage = $( '.option-list', this.$el );
379
-
380
- optionViews.forEach( function( optionViewModel ) {
381
- var optionView = optionViewModel.get( 'view' );
382
- var $optionViewEl = optionView.$el;
383
- $optionViewEl.detach();
384
- $stage.append( $optionViewEl );
385
- optionView.trigger( 'refresh' );
386
- }, this );
387
- },
388
-
389
- getOptionModelID: function() {
390
- var prefix = this.model.get( 'id' ) + '_option_';
391
- var collection = this.model.get( 'options' );
392
- var timestamp = new Date().getTime();
393
- var id = prefix + timestamp;
394
-
395
- return id;
396
- },
397
-
398
- onAddOptionClick: function( e ) {
399
- e.preventDefault();
400
-
401
- var itemID = this.getOptionModelID();
402
- var itemModel = new OptionModel( { id: itemID } );
403
- this.model.get( 'options' ).add( itemModel );
404
- },
405
-
406
- onAddHeadingClick: function( e ) {
407
- e.preventDefault();
408
-
409
- var itemID = this.getOptionModelID();
410
- var itemModel = new OptionModel( { id: itemID, is_heading: 1 } );
411
- this.model.get( 'options' ).add( itemModel );
412
- },
413
-
414
- onShowAllOptionsClick: function(e) {
415
- var $link = $(e.target);
416
- this.$el.find('.happyforms-part-widget--sub').show();
417
- $link.hide();
418
  },
419
 
420
  onPlaceholderChange: function( model, value ) {
@@ -429,92 +142,27 @@
429
  happyForms.previewSend( 'happyforms-part-dom-update', data );
430
  },
431
 
432
- onRequiredChange: function( model, value ) {
433
- model.fetchHtml( function (response) {
434
- var data = {
435
- id: model.get('id'),
436
- html: response,
437
- };
438
-
439
- happyForms.previewSend('happyforms-form-part-refresh', data);
440
- } );
441
- },
442
-
443
- onImportOptionsClick: function( e ) {
444
- e.preventDefault();
445
-
446
- $( '.options', this.$el ).hide();
447
- $( '.options-import', this.$el ).show();
448
- $( '.links.mode-manual', this.$el ).hide();
449
- $( '.links.mode-import', this.$el ).show();
450
- $( '.option-import-area', this.$el ).trigger( 'focus' );
451
- },
452
-
453
- onAddOptionsClick: function( e ) {
454
- e.preventDefault();
455
-
456
- $( '.options', this.$el ).show();
457
- $( '.options-import', this.$el ).hide();
458
- $( '.links.mode-import', this.$el ).hide();
459
- $( '.links.mode-manual', this.$el ).show();
460
- $( '.option-import-area', this.$el ).val( '' );
461
- },
462
 
463
- onImportOptionClick: function( e ) {
464
- e.preventDefault();
465
-
466
- var $textarea = $( '.option-import-area', this.$el );
467
- var list = $textarea.val();
468
- var self = this;
469
-
470
- var models = list
471
- .split( /[\r\n]+/g )
472
- .map( function( s ) {
473
- return s.trim();
474
- } )
475
- .filter( function( s ) {
476
- return s;
477
- } )
478
- .forEach( function( label, i, list ) {
479
- _.delay( function() {
480
- var itemID = self.getOptionModelID();
481
- var item = new OptionModel( {
482
- id: itemID,
483
- label: label
484
- } );
485
-
486
- self.model.get( 'options' ).add( item, { refresh: ( list.length - 1 === i ) } );
487
- }, i );
488
- } );
489
-
490
- $textarea.val( '' );
491
- $( '.add-options', this.$el ).trigger( 'click' );
492
  },
493
 
494
- onAddOtherOption: function( model, value ) {
495
- var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
496
- var model = this.model;
497
-
498
- if ( 1 == value ) {
499
- $otherOptionOptions.show();
500
- } else {
501
- $otherOptionOptions.hide();
502
- }
503
-
504
  this.model.trigger( 'change' );
505
 
506
- this.model.fetchHtml( function( response ) {
507
- var data = {
508
- id: model.get( 'id' ),
509
- html: response,
510
- };
511
 
512
- happyForms.previewSend( 'happyforms-form-part-refresh', data );
513
- } );
514
- },
515
 
516
- onOtherSelectOptionLabelChange: function( model, value ) {
517
- this.model.trigger( 'change' );
 
518
 
519
  var model = this.model;
520
 
@@ -527,45 +175,23 @@
527
  happyForms.previewSend( 'happyforms-form-part-refresh', data );
528
  } );
529
  },
530
-
531
- onOtherOptionLabelChange: function() {
532
- var data = {
533
- id: this.model.get( 'id' ),
534
- callback: 'onSelectOtherOptionLabelChangeCallback'
535
- };
536
-
537
- happyForms.previewSend( 'happyforms-part-dom-update', data );
538
- },
539
-
540
- onOtherOptionPlaceholderChange: function() {
541
- var data = {
542
- id: this.model.get( 'id' ),
543
- callback: 'onSelectOtherOptionPlaceholderChangeCallback'
544
- };
545
-
546
- happyForms.previewSend( 'happyforms-part-dom-update', data );
547
- },
548
-
549
  } );
550
 
551
  happyForms.previewer = _.extend( happyForms.previewer, {
552
- onSelectItemAdd: function( id, html, options ) {
 
553
  var $part = this.getPartElement( html );
 
554
 
555
- this.$( '.happyforms-part__el', $part ).append( this.$( options.optionHTML ) );
556
  },
557
 
558
- onSelectItemLabelChangeCallback: function( id, html, options, $ ) {
559
  var part = this.getPartModel( id );
560
  var $part = this.getPartElement( html );
561
  var option = part.get( 'options' ).get( options.itemID );
562
- var $option = $( '[data-option-id=' + options.itemID + ']', $part );
563
-
564
- $option.text( option.get( 'label' ) );
565
 
566
- if ( option.get( 'is_default' ) ) {
567
- $( 'input', $part ).val( option.get( 'label' ) );
568
- }
569
  },
570
 
571
  onSelectItemDefaultChangeCallback: function( id, html, options, $ ) {
@@ -573,52 +199,25 @@
573
  var $part = this.getPartElement( html );
574
  var option = part.get( 'options' ).get( options.itemID );
575
 
576
- $( 'input', $part ).val( '' );
577
 
578
- if ( option && 1 === option.get('is_default') ) {
579
- $( 'input', $part ).val( option.get( 'label' ) );
580
  }
581
  },
582
 
583
- onSelectItemDeleteCallback: function( id, html, options ) {
584
- var $part = this.getPartElement( html );
585
- var $option = $( '[data-option-id=' + options.itemID + ']', $part );
586
-
587
- $option.remove();
588
- },
589
-
590
  onSelectPlaceholderChangeCallback: function( id, html, options, $ ) {
591
  var $part = this.getPartElement( html );
592
 
593
  $( 'select option.happyforms-placeholder-option', $part ).text( options.label );
594
  },
595
 
596
- onSelectOtherOptionLabelChangeCallback: function( id, html, options ) {
597
- var part = this.getPartModel( id );
598
- var $part = this.getPartElement( html );
599
- var $otherOptionLabel = $( '.happyforms-custom-select-dropdown [data-value="999"]', $part );
600
-
601
- $otherOptionLabel.text( part.get( 'other_option_label' ) );
602
- $otherOptionLabel.attr( 'data-label', part.get( 'other_option_label' ) );
603
- },
604
-
605
- onSelectOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
606
  var part = this.getPartModel( id );
607
  var $part = this.getPartElement( html );
608
- var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
609
 
610
- $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
611
  },
612
-
613
- onSelectHeadingLabelChangeCallback: function( id, html, options ) {
614
- var part = this.getPartModel( id );
615
- var $part = this.getPartElement( html );
616
- var option = part.get( 'options' ).get( options.itemID );
617
- var $option = $( '#' + options.itemID, $part );
618
-
619
- this.$( 'label.heading-label', $option ).text( option.get( 'label' ) );
620
- },
621
-
622
  } );
623
 
624
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
35
  model: OptionModel,
36
  } );
37
 
38
+ var OptionHeadingView = happyForms.classes.views.OptionHeading.extend( {
39
  template: '#customize-happyforms-select-item-heading-template',
40
 
41
+ onLabelChange: function( e ) {
42
+ var label = $( e.target ).val();
43
+ this.model.set( 'label', label );
44
+ this.part.trigger( 'change' );
45
+ $('.happyforms-item-choice-widget-title h3 .choice-in-widget-title span', this.$el ).text( label );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ var data = {
48
+ id: this.part.get( 'id' ),
49
+ callback: 'onSelectItemHeadingLabelChangeCallback',
50
+ options: {
51
+ itemID: this.model.get( 'id' ),
52
+ }
53
+ };
54
 
55
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
 
56
  },
57
  } );
58
 
59
+ var OptionItemView = happyForms.classes.views.OptionItem.extend( {
60
  template: '#customize-happyforms-select-item-template',
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  onItemLabelChange: function( e ) {
63
+ var label = $( e.target ).val();
64
+ this.model.set( 'label', label );
65
+ this.part.trigger( 'change' );
66
+ $('.happyforms-item-choice-widget-title h3 .choice-in-widget-title span', this.$el ).text( label );
67
 
68
+ var data = {
69
+ id: this.part.get( 'id' ),
70
+ callback: 'onSelectItemLabelChangeCallback',
71
+ options: {
72
+ itemID: this.model.get( 'id' ),
73
+ }
74
+ };
75
 
76
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
 
77
  },
78
 
79
  onItemDefaultChange: function( e ) {
95
  callback: 'onSelectItemDefaultChangeCallback',
96
  options: {
97
  itemID: this.model.get( 'id' ),
98
+ checked: isChecked
99
  }
100
  };
101
 
102
  happyForms.previewSend( 'happyforms-part-dom-update', data );
103
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  } );
105
 
106
+ happyForms.classes.views.parts.select = happyForms.classes.views.ChoiceField.extend( {
107
  template: '#customize-happyforms-select-template',
108
 
 
 
 
 
 
 
 
 
 
 
 
109
  initialize: function() {
110
+ happyForms.classes.views.ChoiceField.prototype.initialize.apply( this, arguments );
 
 
111
 
112
  this.listenTo( this.model, 'change:placeholder', this.onPlaceholderChange );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  },
114
 
115
+ getOptionItemView: function( optionModel, options ) {
116
+ var view = new OptionItemView( _.extend( {
117
+ model: optionModel,
118
+ part: this.model,
119
+ }, options ) );
120
 
121
+ return view;
 
 
 
 
 
 
 
122
  },
123
 
124
+ getOptionHeadingView: function( optionModel, options ) {
125
+ var view = new OptionHeadingView( _.extend( {
126
+ model: optionModel,
127
+ part: this.model,
128
+ }, options ) );
129
 
130
+ return view;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  },
132
 
133
  onPlaceholderChange: function( model, value ) {
142
  happyForms.previewSend( 'happyforms-part-dom-update', data );
143
  },
144
 
145
+ onOtherOptionLabelChange: function() {
146
+ var data = {
147
+ id: this.model.get( 'id' ),
148
+ callback: 'onSelectOtherLabelChangeCallback'
149
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  },
153
 
154
+ onOptionModelRemove: function( optionModel ) {
 
 
 
 
 
 
 
 
 
155
  this.model.trigger( 'change' );
156
 
157
+ var optionViewModel = this.optionViews.find( function( viewModel ) {
158
+ return viewModel.get( 'view' ).model.id === optionModel.id;
159
+ }, this );
 
 
160
 
161
+ this.optionViews.remove( optionViewModel );
 
 
162
 
163
+ if ( this.model.get( 'options' ).length == 0 ) {
164
+ $( '.options ul', this.$el ).html( '' );
165
+ }
166
 
167
  var model = this.model;
168
 
175
  happyForms.previewSend( 'happyforms-form-part-refresh', data );
176
  } );
177
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  } );
179
 
180
  happyForms.previewer = _.extend( happyForms.previewer, {
181
+ onSelectItemLabelChangeCallback: function( id, html, options ) {
182
+ var part = this.getPartModel( id );
183
  var $part = this.getPartElement( html );
184
+ var option = part.get( 'options' ).get( options.itemID );
185
 
186
+ this.$( '#' + options.itemID, $part ).text( option.get( 'label' ) );
187
  },
188
 
189
+ onSelectItemHeadingLabelChangeCallback: function( id, html, options ) {
190
  var part = this.getPartModel( id );
191
  var $part = this.getPartElement( html );
192
  var option = part.get( 'options' ).get( options.itemID );
 
 
 
193
 
194
+ this.$( '#' + options.itemID, $part ).attr( 'label', option.get( 'label' ) );
 
 
195
  },
196
 
197
  onSelectItemDefaultChangeCallback: function( id, html, options, $ ) {
199
  var $part = this.getPartElement( html );
200
  var option = part.get( 'options' ).get( options.itemID );
201
 
202
+ this.$( 'select option', $part ).removeAttr( 'selected' );
203
 
204
+ if ( options.checked ) {
205
+ this.$( '#' + options.itemID, $part ).prop( 'selected', 'selected' );
206
  }
207
  },
208
 
 
 
 
 
 
 
 
209
  onSelectPlaceholderChangeCallback: function( id, html, options, $ ) {
210
  var $part = this.getPartElement( html );
211
 
212
  $( 'select option.happyforms-placeholder-option', $part ).text( options.label );
213
  },
214
 
215
+ onSelectOtherLabelChangeCallback: function( id, html, options ) {
 
 
 
 
 
 
 
 
 
216
  var part = this.getPartModel( id );
217
  var $part = this.getPartElement( html );
 
218
 
219
+ this.$( '.happyforms-select option#other-option', $part ).text( part.get( 'other_option_label' ) );
220
  },
 
 
 
 
 
 
 
 
 
 
221
  } );
222
 
223
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/classes/class-form-admin.php CHANGED
@@ -678,6 +678,34 @@ class HappyForms_Form_Admin {
678
  }
679
  }
680
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
  add_filter( 'get_edit_post_link', array( $this, 'get_edit_post_link' ), 10, 3 );
682
  }
683
 
678
  }
679
  }
680
 
681
+ // check if added to templates
682
+ if ( current_theme_supports( 'block-templates' ) ) {
683
+ $templates = get_block_templates( array(), 'wp_template' );
684
+ $template_parts = get_block_templates( array(), 'wp_template_part' );
685
+
686
+ $theme_templates = array_merge( $templates, $template_parts );
687
+
688
+ foreach ( $theme_templates as $template ) {
689
+ $edit_url = add_query_arg(
690
+ array(
691
+ 'postType' => $template->type,
692
+ 'postId' => $template->id,
693
+ ),
694
+ admin_url( 'site-editor.php' )
695
+ );
696
+
697
+ $link_data = array(
698
+ 'content' => $template->content,
699
+ 'label' => $template->title,
700
+ 'edit_url' => $edit_url,
701
+ 'type' => $template->type,
702
+ );
703
+
704
+ $this->create_added_to_link( $link_data, $regex_block );
705
+ $this->create_added_to_link( $link_data, $regex_shortcode );
706
+ }
707
+ }
708
+
709
  add_filter( 'get_edit_post_link', array( $this, 'get_edit_post_link' ), 10, 3 );
710
  }
711
 
core/classes/class-form-controller.php CHANGED
@@ -564,10 +564,10 @@ class HappyForms_Form_Controller {
564
  // Double slash to preserve useful slashes in values
565
  $update_data = wp_slash( $update_data );
566
 
567
- $result = wp_update_post( $update_data, true );
568
 
569
- if ( is_wp_error( $result ) ) {
570
- return $result;
571
  }
572
 
573
  // Update parts
@@ -583,16 +583,16 @@ class HappyForms_Form_Controller {
583
  if ( ! is_wp_error( $validated_part ) ) {
584
  $part_id = $part_data['id'];
585
  $part_layout[] = $part_id;
586
- happyforms_update_meta( $result, $part_id, $validated_part );
587
  }
588
  }
589
 
590
- happyforms_update_meta( $result, 'layout', $part_layout );
591
  }
592
 
593
  // Cleanup stale parts
594
- $part_layout = happyforms_get_meta( $result, 'layout', true );
595
- $form_meta = happyforms_unprefix_meta( get_post_meta( $result ) );
596
  $form_meta = array_diff_key( $form_meta, $this->get_meta_fields() );
597
  $stale_parts = array_diff_key( $form_meta, array_flip( $part_layout ) );
598
  $part_library = happyforms_get_part_library();
@@ -610,17 +610,21 @@ class HappyForms_Form_Controller {
610
  $stale_parts = array_keys( $stale_parts );
611
 
612
  foreach ( $stale_parts as $part_meta ) {
613
- delete_post_meta( $result, $part_meta );
 
 
 
 
614
  }
615
 
616
  // Cleanup stale parts array meta
617
- delete_post_meta( $result, '_happyforms_parts' );
618
 
619
- $result = $this->to_array( get_post( $result ) );
620
 
621
- do_action( 'happyforms_form_updated', $result );
622
 
623
- return $result;
624
  }
625
 
626
  /**
564
  // Double slash to preserve useful slashes in values
565
  $update_data = wp_slash( $update_data );
566
 
567
+ $form_id = wp_update_post( $update_data, true );
568
 
569
+ if ( is_wp_error( $form_id ) ) {
570
+ return $form_id;
571
  }
572
 
573
  // Update parts
583
  if ( ! is_wp_error( $validated_part ) ) {
584
  $part_id = $part_data['id'];
585
  $part_layout[] = $part_id;
586
+ happyforms_update_meta( $form_id, $part_id, $validated_part );
587
  }
588
  }
589
 
590
+ happyforms_update_meta( $form_id, 'layout', $part_layout );
591
  }
592
 
593
  // Cleanup stale parts
594
+ $part_layout = happyforms_get_meta( $form_id, 'layout', true );
595
+ $form_meta = happyforms_unprefix_meta( get_post_meta( $form_id ) );
596
  $form_meta = array_diff_key( $form_meta, $this->get_meta_fields() );
597
  $stale_parts = array_diff_key( $form_meta, array_flip( $part_layout ) );
598
  $part_library = happyforms_get_part_library();
610
  $stale_parts = array_keys( $stale_parts );
611
 
612
  foreach ( $stale_parts as $part_meta ) {
613
+ delete_post_meta( $form_id, $part_meta );
614
+ }
615
+
616
+ if ( ! empty( $stale_parts ) ) {
617
+ do_action( 'happyforms_stale_fields_deleted', $stale_parts, $form_id );
618
  }
619
 
620
  // Cleanup stale parts array meta
621
+ delete_post_meta( $form_id, '_happyforms_parts' );
622
 
623
+ $form = $this->to_array( get_post( $form_id ) );
624
 
625
+ do_action( 'happyforms_form_updated', $form );
626
 
627
+ return $form;
628
  }
629
 
630
  /**
core/classes/class-form-email.php CHANGED
@@ -17,6 +17,7 @@ class HappyForms_Form_Email {
17
  public function hook() {
18
  add_filter( 'happyforms_meta_fields', array( $this, 'meta_fields' ) );
19
  add_action( 'happyforms_do_email_control', array( happyforms_get_setup(), 'do_control' ), 10, 3 );
 
20
  }
21
 
22
  public function get_fields() {
@@ -27,6 +28,10 @@ class HappyForms_Form_Email {
27
  'default' => 1,
28
  'sanitize' => 'happyforms_sanitize_checkbox'
29
  ),
 
 
 
 
30
  'email_recipient' => array(
31
  'default' => ( $current_user->user_email ) ? $current_user->user_email : '',
32
  'sanitize' => 'happyforms_sanitize_emails',
@@ -47,6 +52,10 @@ class HappyForms_Form_Email {
47
  'default' => __( 'You received a new message', 'happyforms' ),
48
  'sanitize' => 'sanitize_text_field',
49
  ),
 
 
 
 
50
  'send_confirmation_email' => array(
51
  'default' => 1,
52
  'sanitize' => 'happyforms_sanitize_checkbox'
@@ -91,16 +100,27 @@ class HappyForms_Form_Email {
91
  'type' => 'group_start',
92
  'trigger' => 'receive_email_alerts'
93
  ),
 
94
  350 => array(
95
  'type' => 'text',
96
- 'label' => __( 'Email address', 'happyforms' ),
97
  'field' => 'email_recipient',
98
  ),
99
- 360 => array(
100
  'type' => 'text',
101
- 'label' => __( 'Email Bcc address', 'happyforms' ),
102
  'field' => 'email_bccs',
103
  ),
 
 
 
 
 
 
 
 
 
 
104
  440 => array(
105
  'type' => 'text',
106
  'label' => __( 'Email display name', 'happyforms' ),
@@ -123,12 +143,12 @@ class HappyForms_Form_Email {
123
  'type' => 'group_start',
124
  'trigger' => 'send_confirmation_email'
125
  ),
126
- 630 => array(
127
  'type' => 'text',
128
  'label' => __( 'From email address', 'happyforms' ),
129
  'field' => 'confirmation_email_sender_address',
130
  ),
131
- 640 => array(
132
  'type' => 'text',
133
  'label' => __( 'Reply email address', 'happyforms' ),
134
  'field' => 'confirmation_email_reply_to',
@@ -165,6 +185,14 @@ class HappyForms_Form_Email {
165
  return $controls;
166
  }
167
 
 
 
 
 
 
 
 
 
168
  /**
169
  * Filter: add fields to form meta.
170
  *
17
  public function hook() {
18
  add_filter( 'happyforms_meta_fields', array( $this, 'meta_fields' ) );
19
  add_action( 'happyforms_do_email_control', array( happyforms_get_setup(), 'do_control' ), 10, 3 );
20
+ add_action( 'happyforms_do_email_control', array( $this, 'do_control' ), 10, 3 );
21
  }
22
 
23
  public function get_fields() {
28
  'default' => 1,
29
  'sanitize' => 'happyforms_sanitize_checkbox'
30
  ),
31
+ 'alert_email_from_address' => array(
32
+ 'default' => ( $current_user->user_email ) ? $current_user->user_email : '',
33
+ 'sanitize' => 'happyforms_sanitize_emails',
34
+ ),
35
  'email_recipient' => array(
36
  'default' => ( $current_user->user_email ) ? $current_user->user_email : '',
37
  'sanitize' => 'happyforms_sanitize_emails',
52
  'default' => __( 'You received a new message', 'happyforms' ),
53
  'sanitize' => 'sanitize_text_field',
54
  ),
55
+ 'alert_email_reply_to' => array(
56
+ 'default' => '',
57
+ 'sanitize' => 'sanitize_text_field',
58
+ ),
59
  'send_confirmation_email' => array(
60
  'default' => 1,
61
  'sanitize' => 'happyforms_sanitize_checkbox'
100
  'type' => 'group_start',
101
  'trigger' => 'receive_email_alerts'
102
  ),
103
+
104
  350 => array(
105
  'type' => 'text',
106
+ 'label' => __( 'To email address', 'happyforms' ),
107
  'field' => 'email_recipient',
108
  ),
109
+ 351 => array(
110
  'type' => 'text',
111
+ 'label' => __( 'To Bcc email address', 'happyforms' ),
112
  'field' => 'email_bccs',
113
  ),
114
+ 352 => array(
115
+ 'type' => 'text',
116
+ 'label' => __( 'From email address', 'happyforms' ),
117
+ 'field' => 'alert_email_from_address',
118
+ ),
119
+ 353 => array(
120
+ 'type' => 'email-parts-list',
121
+ 'label' => __( 'Reply email address', 'happyforms' ),
122
+ 'field' => 'alert_email_reply_to',
123
+ ),
124
  440 => array(
125
  'type' => 'text',
126
  'label' => __( 'Email display name', 'happyforms' ),
143
  'type' => 'group_start',
144
  'trigger' => 'send_confirmation_email'
145
  ),
146
+ 631 => array(
147
  'type' => 'text',
148
  'label' => __( 'From email address', 'happyforms' ),
149
  'field' => 'confirmation_email_sender_address',
150
  ),
151
+ 632 => array(
152
  'type' => 'text',
153
  'label' => __( 'Reply email address', 'happyforms' ),
154
  'field' => 'confirmation_email_reply_to',
185
  return $controls;
186
  }
187
 
188
+ public function do_control( $control, $field, $index ) {
189
+ $type = $control['type'];
190
+
191
+ if ('email-parts-list' == $type ) {
192
+ require( happyforms_get_core_folder() . '/templates/customize-controls/email/email-parts-list.php' );
193
+ }
194
+ }
195
+
196
  /**
197
  * Filter: add fields to form meta.
198
  *
core/classes/class-form-messages.php CHANGED
@@ -48,11 +48,11 @@ class HappyForms_Form_Messages {
48
  'sanitize' => 'sanitize_text_field',
49
  ),
50
  'number_min_invalid' => array(
51
- 'default' => __( "Oops. This number isn't big enough.", 'happyforms' ),
52
  'sanitize' => 'sanitize_text_field',
53
  ),
54
  'number_max_invalid' => array(
55
- 'default' => __( 'Oops. This number is too big.', 'happyforms' ),
56
  'sanitize' => 'sanitize_text_field',
57
  ),
58
  'optional_part_label' => array(
@@ -64,11 +64,11 @@ class HappyForms_Form_Messages {
64
  'sanitize' => 'sanitize_text_field'
65
  ),
66
  'select_less_choices' => array(
67
- 'default' => __( 'Oops. Too many choices are selected.', 'happyforms' ),
68
  'sanitize' => 'sanitize_text_field',
69
  ),
70
  'select_more_choices' => array(
71
- 'default' => __( 'Oops. Not enough choices are selected.', 'happyforms' ),
72
  'sanitize' => 'sanitize_text_field',
73
  ),
74
  'submissions_left_label' => array(
48
  'sanitize' => 'sanitize_text_field',
49
  ),
50
  'number_min_invalid' => array(
51
+ 'default' => __( "This number isn't big enough.", 'happyforms' ),
52
  'sanitize' => 'sanitize_text_field',
53
  ),
54
  'number_max_invalid' => array(
55
+ 'default' => __( 'This number is too big.', 'happyforms' ),
56
  'sanitize' => 'sanitize_text_field',
57
  ),
58
  'optional_part_label' => array(
64
  'sanitize' => 'sanitize_text_field'
65
  ),
66
  'select_less_choices' => array(
67
+ 'default' => __( 'Too many choices are selected.', 'happyforms' ),
68
  'sanitize' => 'sanitize_text_field',
69
  ),
70
  'select_more_choices' => array(
71
+ 'default' => __( 'Not enough choices are selected.', 'happyforms' ),
72
  'sanitize' => 'sanitize_text_field',
73
  ),
74
  'submissions_left_label' => array(
core/classes/class-form-option-limiter.php CHANGED
@@ -200,9 +200,8 @@ class HappyForms_Form_Option_Limiter {
200
 
201
  public function try_migrate_limit_count_options( $form ) {
202
  $form_id = $form['ID'];
203
- $meta_counters = happyforms_get_meta( $form_id, $this->counter_key, true );
204
 
205
- if ( 0 == $form_id || ! empty( $meta_counters ) ) {
206
  return;
207
  }
208
 
@@ -245,12 +244,25 @@ class HappyForms_Form_Option_Limiter {
245
 
246
  $meta_counters = [];
247
  $message_controller = happyforms_get_message_controller();
248
- $messages = $message_controller->get_by_form( $form_id );
249
-
250
- foreach( $messages as $message ){
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  foreach( $parts_with_limits as $part_name => $part ) {
253
- $request_value = $message['request'];
254
 
255
  if ( ! isset( $request_value[ $part_name ] ) ) {
256
  continue;
200
 
201
  public function try_migrate_limit_count_options( $form ) {
202
  $form_id = $form['ID'];
 
203
 
204
+ if ( 0 == $form_id || happyforms_meta_exists( $form_id, $this->counter_key ) ) {
205
  return;
206
  }
207
 
244
 
245
  $meta_counters = [];
246
  $message_controller = happyforms_get_message_controller();
 
 
 
247
 
248
+ global $wpdb;
249
+
250
+ $messages = $wpdb->get_results( $wpdb->prepare( "
251
+ SELECT f.meta_value
252
+ FROM $wpdb->postmeta f
253
+ JOIN $wpdb->postmeta p ON f.post_id = p.post_id
254
+ WHERE p.meta_key = '_happyforms_form_id'
255
+ AND p.meta_value = %d
256
+ AND f.meta_key = '_happyforms_request';
257
+ ", $form_id ), ARRAY_A );
258
+
259
+ $messages = array_map( function( $message ) {
260
+ return maybe_unserialize( $message['meta_value'] );
261
+ }, $messages );
262
+
263
+ foreach( $messages as $message ) {
264
  foreach( $parts_with_limits as $part_name => $part ) {
265
+ $request_value = $message;
266
 
267
  if ( ! isset( $request_value[ $part_name ] ) ) {
268
  continue;
core/classes/class-form-part-library.php CHANGED
@@ -144,7 +144,13 @@ class HappyForms_Form_Part_Library {
144
  * @return void
145
  */
146
  public function customize_enqueue_scripts() {
147
- $deps = array( 'happyforms-customize' );
 
 
 
 
 
 
148
 
149
  foreach ( $this->parts as $part ) {
150
  $part->customize_enqueue_scripts( $deps );
144
  * @return void
145
  */
146
  public function customize_enqueue_scripts() {
147
+ wp_register_script(
148
+ 'part-field-choice',
149
+ happyforms_get_plugin_url() . 'core/assets/js/parts/part-field-choice.js',
150
+ 'happyforms-customize', happyforms_get_version(), true
151
+ );
152
+
153
+ $deps = array( 'happyforms-customize', 'part-field-choice' );
154
 
155
  foreach ( $this->parts as $part ) {
156
  $part->customize_enqueue_scripts( $deps );
core/classes/class-validation-messages.php CHANGED
@@ -103,23 +103,23 @@ class HappyForms_Validation_Messages {
103
 
104
  $fields = array(
105
  'field_invalid' => array(
106
- 'default' => __( "Oops. Looks like there's a mistake here.", 'happyforms' ),
107
  'sanitize' => 'sanitize_text_field',
108
  ),
109
  'field_empty' => array(
110
- 'default' => __( 'Oops. Please answer this question.', 'happyforms' ),
111
  'sanitize' => 'sanitize_text_field',
112
  ),
113
  'no_selection' => array(
114
- 'default' => __( 'Oops. Please make a selection.', 'happyforms' ),
115
  'sanitize' => 'sanitize_text_field',
116
  ),
117
  'message_too_long' => array(
118
- 'default' => __( 'Oops. This answer is too long.', 'happyforms' ),
119
  'sanitize' => 'sanitize_text_field',
120
  ),
121
  'message_too_short' => array(
122
- 'default' => __( "Oops. This answer isn't long enough.", 'happyforms' ),
123
  'sanitize' => 'sanitize_text_field',
124
  ),
125
  // TODO remove this field once deprecated global messages is fully removed
103
 
104
  $fields = array(
105
  'field_invalid' => array(
106
+ 'default' => __( "Looks like there's a mistake here.", 'happyforms' ),
107
  'sanitize' => 'sanitize_text_field',
108
  ),
109
  'field_empty' => array(
110
+ 'default' => __( 'Please answer this question.', 'happyforms' ),
111
  'sanitize' => 'sanitize_text_field',
112
  ),
113
  'no_selection' => array(
114
+ 'default' => __( 'Please make a selection.', 'happyforms' ),
115
  'sanitize' => 'sanitize_text_field',
116
  ),
117
  'message_too_long' => array(
118
+ 'default' => __( 'This answer is too long.', 'happyforms' ),
119
  'sanitize' => 'sanitize_text_field',
120
  ),
121
  'message_too_short' => array(
122
+ 'default' => __( "This answer isn't long enough.", 'happyforms' ),
123
  'sanitize' => 'sanitize_text_field',
124
  ),
125
  // TODO remove this field once deprecated global messages is fully removed
core/templates/customize-controls/email/email-parts-list.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="customize-control" id="customize-control-<?php echo $control['field']; ?>">
2
+ <?php do_action( "happyforms_setup_control_{$control['field']}_before", $control ); ?>
3
+
4
+ <label for="<?php echo $control['field']; ?>" class="customize-control-title"><?php echo $control['label']; ?></label>
5
+ <select id="<?php echo $control['field']; ?>" data-attribute="<?php echo $control['field']; ?>">
6
+ <%
7
+ var options = _( parts ).where( { type: 'email' } );
8
+
9
+ options.forEach( function( option ) { %>
10
+ <option value="<%= option.id %>"<%= ( option.id === <?php echo $control['field']; ?> ) ? ' selected' : '' %>>"<%= option.label %>" <?php _e( 'field', 'happyforms' ); ?></option>
11
+ <% } ); %>
12
+
13
+ <option value="all"<%= ( 'all' === <?php echo $control['field']; ?> ) ? ' selected' : '' %>><?php _e( 'All Email fields', 'happyforms' ); ?></option>
14
+ </select>
15
+
16
+ <?php do_action( "happyforms_setup_control_{$control['field']}_after", $control ); ?>
17
+ </div>
core/templates/parts/customize-checkbox.php CHANGED
@@ -26,7 +26,7 @@
26
  <div class="options">
27
  <ul class="option-list"></ul>
28
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
29
- <p class="no-options description"><?php _e( 'No choices added yet.', 'happyforms' ); ?></p>
30
  </div>
31
  <div class="options-import">
32
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
@@ -35,15 +35,6 @@
35
  <p class="links mode-manual">
36
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
37
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
38
- <span class="centered">
39
- <a href="#" class="import-options"><?php _e( 'Or, bulk add choices', 'happyforms' ); ?></a>
40
- </span>
41
- </p>
42
- <p class="links mode-import">
43
- <a href="#" class="button import-option"><?php _e( 'Add choices', 'happyforms' ); ?></a>
44
- <span class="centered">
45
- <a href="#" class="add-options"><?php _e( 'Cancel', 'happyforms' ); ?></a>
46
- </span>
47
  </p>
48
 
49
  <?php do_action( 'happyforms_part_customize_checkbox_after_options' ); ?>
@@ -124,14 +115,25 @@
124
  <?php happyforms_customize_part_footer(); ?>
125
  </script>
126
  <script type="text/template" id="customize-happyforms-checkbox-item-template">
127
- <li data-option-id="<%= id %>">
 
 
 
 
 
 
 
 
 
 
128
  <div class="happyforms-part-item-body">
129
- <div class="happyforms-part-item-handle"></div>
130
- <label>
131
- <?php _e( 'Label', 'happyforms' ); ?>:
132
- <input type="text" class="widefat" name="label" value="<%= label %>">
133
- </label>
134
  <div class="happyforms-part-item-advanced">
 
 
 
 
 
 
135
  <p>
136
  <label>
137
  <?php _e( 'Hint', 'happyforms' ); ?>:
@@ -149,24 +151,38 @@
149
  <input type="checkbox" name="is_default" value="1" <% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
150
  </label>
151
  </p>
152
- </div>
153
- <div class="option-actions">
154
- <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
155
- <a href="#" class="advanced-option"><?php _e( 'More', 'happyforms' ); ?></a>
156
  </div>
157
  </div>
158
  </li>
159
  </script>
160
  <script type="text/template" id="customize-happyforms-checkbox-item-heading-template">
161
- <li data-option-id="<%= id %>" data-is-heading="yes">
 
 
 
 
 
 
 
 
 
 
162
  <div class="happyforms-part-item-body">
163
- <div class="happyforms-part-item-handle"></div>
164
- <label>
165
- <?php _e( 'Heading', 'happyforms' ); ?>:
166
- <input type="text" class="widefat" name="label" value="<%= label %>">
167
- </label>
168
- <div class="option-actions">
169
- <a href="#" class="delete-heading"><?php _e( 'Delete', 'happyforms' ); ?></a>
 
 
 
 
170
  </div>
171
  </div>
172
  </li>
26
  <div class="options">
27
  <ul class="option-list"></ul>
28
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
29
+ <p class="no-options description"><?php _e( 'It doesn\'t look like your field has any choices yet. Want to add one? Click the "Add Choice" button to start.', 'happyforms' ); ?></p>
30
  </div>
31
  <div class="options-import">
32
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
35
  <p class="links mode-manual">
36
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
37
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
 
 
 
 
 
 
 
 
 
38
  </p>
39
 
40
  <?php do_action( 'happyforms_part_customize_checkbox_after_options' ); ?>
115
  <?php happyforms_customize_part_footer(); ?>
116
  </script>
117
  <script type="text/template" id="customize-happyforms-checkbox-item-template">
118
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget">
119
+ <div class="happyforms-part-item-handle">
120
+ <div class="happyforms-part-item-advanced-option">
121
+ <button type="button" class="happyforms-advanced-option-action">
122
+ <span class="toggle-indicator"></span>
123
+ </button>
124
+ </div>
125
+ <div class="happyforms-item-choice-widget-title">
126
+ <h3><?php _e( 'Choice', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
127
+ </div>
128
+ </div>
129
  <div class="happyforms-part-item-body">
 
 
 
 
 
130
  <div class="happyforms-part-item-advanced">
131
+ <p>
132
+ <label>
133
+ <?php _e( 'Label', 'happyforms' ); ?>:
134
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
135
+ </label>
136
+ </p>
137
  <p>
138
  <label>
139
  <?php _e( 'Hint', 'happyforms' ); ?>:
151
  <input type="checkbox" name="is_default" value="1" <% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
152
  </label>
153
  </p>
154
+ <div class="option-actions">
155
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
156
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
157
+ </div>
158
  </div>
159
  </div>
160
  </li>
161
  </script>
162
  <script type="text/template" id="customize-happyforms-checkbox-item-heading-template">
163
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget" data-is-heading="yes">
164
+ <div class="happyforms-part-item-handle">
165
+ <div class="happyforms-part-item-advanced-option">
166
+ <button type="button" class="happyforms-advanced-option-action">
167
+ <span class="toggle-indicator"></span>
168
+ </button>
169
+ </div>
170
+ <div class="happyforms-item-choice-widget-title">
171
+ <h3><?php _e( 'Heading', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
172
+ </div>
173
+ </div>
174
  <div class="happyforms-part-item-body">
175
+ <div class="happyforms-part-item-advanced">
176
+ <p>
177
+ <label>
178
+ <?php _e( 'Label', 'happyforms' ); ?>:
179
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
180
+ </label>
181
+ </p>
182
+ <div class="option-actions">
183
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
184
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
185
+ </div>
186
  </div>
187
  </div>
188
  </li>
core/templates/parts/customize-radio.php CHANGED
@@ -26,24 +26,12 @@
26
  <div class="options">
27
  <ul class="option-list"></ul>
28
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
29
- <p class="no-options description"><?php _e( 'No choices added yet.', 'happyforms' ); ?></p>
30
- </div>
31
- <div class="options-import">
32
- <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
33
- <textarea class="option-import-area" cols="30" rows="10" placeholder="<?php _e( 'Type or paste your choices here, adding each on a new line.' ); ?>"></textarea>
34
  </div>
35
  <p class="links mode-manual">
36
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
37
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
38
- <span class="centered">
39
- <a href="#" class="import-options"><?php _e( 'Or, bulk add choices', 'happyforms' ); ?></a>
40
- </span>
41
- </p>
42
- <p class="links mode-import">
43
- <a href="#" class="button import-option"><?php _e( 'Add choices', 'happyforms' ); ?></a>
44
- <span class="centered">
45
- <a href="#" class="add-options"><?php _e( 'Cancel', 'happyforms' ); ?></a>
46
- </span>
47
  </p>
48
 
49
  <?php do_action( 'happyforms_part_customize_radio_after_options' ); ?>
@@ -109,14 +97,25 @@
109
  <?php happyforms_customize_part_footer(); ?>
110
  </script>
111
  <script type="text/template" id="customize-happyforms-radio-item-template">
112
- <li data-option-id="<%= id %>">
 
 
 
 
 
 
 
 
 
 
113
  <div class="happyforms-part-item-body">
114
- <div class="happyforms-part-item-handle"></div>
115
- <label>
116
- <?php _e( 'Label', 'happyforms' ); ?>:
117
- <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
118
- </label>
119
  <div class="happyforms-part-item-advanced">
 
 
 
 
 
 
120
  <p>
121
  <label>
122
  <?php _e( 'Hint', 'happyforms' ); ?>:
@@ -134,24 +133,38 @@
134
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
135
  </label>
136
  </p>
137
- </div>
138
- <div class="option-actions">
139
- <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
140
- <a href="#" class="advanced-option"><?php _e( 'More', 'happyforms' ); ?></a>
141
  </div>
142
  </div>
143
  </li>
144
  </script>
145
  <script type="text/template" id="customize-happyforms-radio-item-heading-template">
146
- <li data-option-id="<%= id %>" data-is-heading="yes">
 
 
 
 
 
 
 
 
 
 
147
  <div class="happyforms-part-item-body">
148
- <div class="happyforms-part-item-handle"></div>
149
- <label>
150
- <?php _e( 'Heading', 'happyforms' ); ?>:
151
- <input type="text" class="widefat" name="label" value="<%= label %>">
152
- </label>
153
- <div class="option-actions">
154
- <a href="#" class="delete-heading"><?php _e( 'Delete', 'happyforms' ); ?></a>
 
 
 
 
155
  </div>
156
  </div>
157
  </li>
26
  <div class="options">
27
  <ul class="option-list"></ul>
28
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
29
+ <p class="no-options description"><?php _e( 'It doesn\'t look like your field has any choices yet. Want to add one?
30
+ Click the "Add Choice" button to start.', 'happyforms' ); ?></p>
 
 
 
31
  </div>
32
  <p class="links mode-manual">
33
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
34
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
 
 
 
 
 
 
 
 
 
35
  </p>
36
 
37
  <?php do_action( 'happyforms_part_customize_radio_after_options' ); ?>
97
  <?php happyforms_customize_part_footer(); ?>
98
  </script>
99
  <script type="text/template" id="customize-happyforms-radio-item-template">
100
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget">
101
+ <div class="happyforms-part-item-handle">
102
+ <div class="happyforms-part-item-advanced-option">
103
+ <button type="button" class="happyforms-advanced-option-action">
104
+ <span class="toggle-indicator"></span>
105
+ </button>
106
+ </div>
107
+ <div class="happyforms-item-choice-widget-title">
108
+ <h3><?php _e( 'Choice', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
109
+ </div>
110
+ </div>
111
  <div class="happyforms-part-item-body">
 
 
 
 
 
112
  <div class="happyforms-part-item-advanced">
113
+ <p>
114
+ <label>
115
+ <?php _e( 'Label', 'happyforms' ); ?>:
116
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
117
+ </label>
118
+ </p>
119
  <p>
120
  <label>
121
  <?php _e( 'Hint', 'happyforms' ); ?>:
133
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
134
  </label>
135
  </p>
136
+ <div class="option-actions">
137
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
138
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
139
+ </div>
140
  </div>
141
  </div>
142
  </li>
143
  </script>
144
  <script type="text/template" id="customize-happyforms-radio-item-heading-template">
145
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget" data-is-heading="yes">
146
+ <div class="happyforms-part-item-handle">
147
+ <div class="happyforms-part-item-advanced-option">
148
+ <button type="button" class="happyforms-advanced-option-action">
149
+ <span class="toggle-indicator"></span>
150
+ </button>
151
+ </div>
152
+ <div class="happyforms-item-choice-widget-title">
153
+ <h3><?php _e( 'Heading', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
154
+ </div>
155
+ </div>
156
  <div class="happyforms-part-item-body">
157
+ <div class="happyforms-part-item-advanced">
158
+ <p>
159
+ <label>
160
+ <?php _e( 'Label', 'happyforms' ); ?>:
161
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
162
+ </label>
163
+ </p>
164
+ <div class="option-actions">
165
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
166
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
167
+ </div>
168
  </div>
169
  </div>
170
  </li>
core/templates/parts/customize-select.php CHANGED
@@ -30,24 +30,11 @@
30
  <div class="options">
31
  <ul class="option-list"></ul>
32
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
33
- <p class="no-options description"><?php _e( 'No choices added yet.', 'happyforms' ); ?></p>
34
- </div>
35
- <div class="options-import">
36
- <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
37
- <textarea class="option-import-area" cols="30" rows="10" placeholder="<?php _e( 'Type or paste your choices here, adding each on a new line.' ); ?>"></textarea>
38
  </div>
39
  <p class="links mode-manual">
40
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
41
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
42
- <span class="centered">
43
- <a href="#" class="import-options"><?php _e( 'Or, bulk add choices', 'happyforms' ); ?></a>
44
- </span>
45
- </p>
46
- <p class="links mode-import">
47
- <a href="#" class="button import-option"><?php _e( 'Add choices', 'happyforms' ); ?></a>
48
- <span class="centered">
49
- <a href="#" class="add-options"><?php _e( 'Cancel', 'happyforms' ); ?></a>
50
- </span>
51
  </p>
52
 
53
  <% if ( instance.other_option ) { %>
@@ -101,14 +88,25 @@
101
  <?php happyforms_customize_part_footer(); ?>
102
  </script>
103
  <script type="text/template" id="customize-happyforms-select-item-template">
104
- <li data-option-id="<%= id %>">
 
 
 
 
 
 
 
 
 
 
105
  <div class="happyforms-part-item-body">
106
- <div class="happyforms-part-item-handle"></div>
107
- <label>
108
- <?php _e( 'Label', 'happyforms' ); ?>:
109
- <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
110
- </label>
111
  <div class="happyforms-part-item-advanced">
 
 
 
 
 
 
112
  <p>
113
  <label>
114
  <?php _e( 'Max number of submissions', 'happyforms' ); ?>:
@@ -120,24 +118,38 @@
120
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
121
  </label>
122
  </p>
123
- </div>
124
- <div class="option-actions">
125
- <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
126
- <a href="#" class="advanced-option"><?php _e( 'More', 'happyforms' ); ?></a>
127
  </div>
128
  </div>
129
  </li>
130
  </script>
131
  <script type="text/template" id="customize-happyforms-select-item-heading-template">
132
- <li data-option-id="<%= id %>" data-is-heading="yes">
 
 
 
 
 
 
 
 
 
 
133
  <div class="happyforms-part-item-body">
134
- <div class="happyforms-part-item-handle"></div>
135
- <label>
136
- <?php _e( 'Heading', 'happyforms' ); ?>:
137
- <input type="text" class="widefat" name="label" value="<%= label %>">
138
- </label>
139
- <div class="option-actions">
140
- <a href="#" class="delete-heading"><?php _e( 'Delete', 'happyforms' ); ?></a>
 
 
 
 
141
  </div>
142
  </div>
143
  </li>
30
  <div class="options">
31
  <ul class="option-list"></ul>
32
  <h3><?php _e( 'Choices', 'happyforms' ); ?></h3>
33
+ <p class="no-options description"><?php _e( 'It doesn\'t look like your field has any choices yet. Want to add one? Click the "Add Choice" button to start.', 'happyforms' ); ?></p>
 
 
 
 
34
  </div>
35
  <p class="links mode-manual">
36
  <a href="#" class="button add-heading"><?php _e( 'Add heading', 'happyforms' ); ?></a>
37
  <a href="#" class="button add-option centered"><?php _e( 'Add choice', 'happyforms' ); ?></a>
 
 
 
 
 
 
 
 
 
38
  </p>
39
 
40
  <% if ( instance.other_option ) { %>
88
  <?php happyforms_customize_part_footer(); ?>
89
  </script>
90
  <script type="text/template" id="customize-happyforms-select-item-template">
91
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget">
92
+ <div class="happyforms-part-item-handle">
93
+ <div class="happyforms-part-item-advanced-option">
94
+ <button type="button" class="happyforms-advanced-option-action">
95
+ <span class="toggle-indicator"></span>
96
+ </button>
97
+ </div>
98
+ <div class="happyforms-item-choice-widget-title">
99
+ <h3><?php _e( 'Choice', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
100
+ </div>
101
+ </div>
102
  <div class="happyforms-part-item-body">
 
 
 
 
 
103
  <div class="happyforms-part-item-advanced">
104
+ <p>
105
+ <label>
106
+ <?php _e( 'Label', 'happyforms' ); ?>:
107
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
108
+ </label>
109
+ </p>
110
  <p>
111
  <label>
112
  <?php _e( 'Max number of submissions', 'happyforms' ); ?>:
118
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
119
  </label>
120
  </p>
121
+ <div class="option-actions">
122
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
123
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
124
+ </div>
125
  </div>
126
  </div>
127
  </li>
128
  </script>
129
  <script type="text/template" id="customize-happyforms-select-item-heading-template">
130
+ <li data-option-id="<%= id %>" class="happyforms-choice-item-widget" data-is-heading="yes">
131
+ <div class="happyforms-part-item-handle">
132
+ <div class="happyforms-part-item-advanced-option">
133
+ <button type="button" class="happyforms-advanced-option-action">
134
+ <span class="toggle-indicator"></span>
135
+ </button>
136
+ </div>
137
+ <div class="happyforms-item-choice-widget-title">
138
+ <h3><?php _e( 'Heading', 'happyforms' ); ?><span class="choice-in-widget-title">: <span><%= label %></span></span></h3>
139
+ </div>
140
+ </div>
141
  <div class="happyforms-part-item-body">
142
+ <div class="happyforms-part-item-advanced">
143
+ <p>
144
+ <label>
145
+ <?php _e( 'Label', 'happyforms' ); ?>:
146
+ <input type="text" class="widefat" name="label" value="<%= label %>" data-option-attribute="label">
147
+ </label>
148
+ </p>
149
+ <div class="option-actions">
150
+ <a href="#" class="happyforms-delete-item"><?php _e( 'Delete', 'happyforms' ); ?></a> |
151
+ <a href="#" class="happyforms-duplicate-item"><?php _e( 'Duplicate', 'happyforms' ); ?></a>
152
+ </div>
153
  </div>
154
  </div>
155
  </li>
core/templates/parts/frontend-select.php CHANGED
@@ -29,13 +29,6 @@
29
  <div class="happyforms-part__select-wrap">
30
  <?php
31
  $other_select = ( !empty( $part['other_option'] ) ) ? $part['other_option_label'] : '';
32
-
33
- if ( !empty( $other_select ) ) {
34
- $options[] = array(
35
- 'value' => 999,
36
- 'label' => $other_select,
37
- );
38
- }
39
  ?>
40
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>" data-serialize class='happyforms-select' required>
41
  <option disabled hidden <?php echo ( $value === '' ) ? ' selected' : ''; ?> value='' class="happyforms-placeholder-option"><?php echo $placeholder_text; ?></option>
@@ -46,7 +39,7 @@
46
  if ( $is_grouped ) : ?>
47
  </optgroup>
48
  <?php endif; ?>
49
- <optgroup label="<?php echo esc_attr( $option['label'] ); ?>">
50
  <?php
51
  $is_grouped = true;
52
  continue;
@@ -57,11 +50,14 @@
57
  $selected = ( $value != '' && $value == $option_value ) ? ' selected' : '';
58
  $disabled = ( '' != $option['limit_submissions_amount'] && $option['submissions_left'] == 0 ) ? ' disabled' : '';
59
  ?>
60
- <option value="<?php echo $option_value; ?>" <?php echo $selected; ?> <?php echo $disabled; ?>><?php echo esc_attr( $option['label'] ); ?><?php echo $submissions_left_label; ?></option>
61
  <?php endforeach; ?>
62
  <?php if ( $is_grouped ) : ?>
63
  </optgroup>
64
  <?php endif; ?>
 
 
 
65
  </select>
66
  </div>
67
  </div>
29
  <div class="happyforms-part__select-wrap">
30
  <?php
31
  $other_select = ( !empty( $part['other_option'] ) ) ? $part['other_option_label'] : '';
 
 
 
 
 
 
 
32
  ?>
33
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>" data-serialize class='happyforms-select' required>
34
  <option disabled hidden <?php echo ( $value === '' ) ? ' selected' : ''; ?> value='' class="happyforms-placeholder-option"><?php echo $placeholder_text; ?></option>
39
  if ( $is_grouped ) : ?>
40
  </optgroup>
41
  <?php endif; ?>
42
+ <optgroup label="<?php echo esc_attr( $option['label'] ); ?>" id="<?php echo esc_attr( $option['id'] ); ?>">
43
  <?php
44
  $is_grouped = true;
45
  continue;
50
  $selected = ( $value != '' && $value == $option_value ) ? ' selected' : '';
51
  $disabled = ( '' != $option['limit_submissions_amount'] && $option['submissions_left'] == 0 ) ? ' disabled' : '';
52
  ?>
53
+ <option value="<?php echo $option_value; ?>" <?php echo $selected; ?> <?php echo $disabled; ?> id="<?php echo esc_attr( $option['id'] ); ?>"><?php echo esc_attr( $option['label'] ); ?><?php echo $submissions_left_label; ?></option>
54
  <?php endforeach; ?>
55
  <?php if ( $is_grouped ) : ?>
56
  </optgroup>
57
  <?php endif; ?>
58
+ <?php if ( !empty( $other_select ) ) : ?>
59
+ <option id="other-option" value="<?php echo '999';?>"><?php echo $other_select; ?></option>
60
+ <?php endif; ?>
61
  </select>
62
  </div>
63
  </div>
happyforms.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://happyforms.io
6
  * Description: We're changin' WordPress forms.
7
  * Author: Happyforms
8
- * Version: 1.15.3
9
  * Author URI: https://happyforms.io
10
  * Upgrade URI: https://happyforms.io/upgrade
11
  */
@@ -22,7 +22,7 @@ if ( defined( 'HAPPYFORMS_UPGRADE_VERSION' ) ) {
22
  /**
23
  * The current version of the plugin.
24
  */
25
- define( 'HAPPYFORMS_VERSION', '1.15.3' );
26
 
27
  if ( ! function_exists( 'happyforms_get_version' ) ):
28
 
5
  * Plugin URI: https://happyforms.io
6
  * Description: We're changin' WordPress forms.
7
  * Author: Happyforms
8
+ * Version: 1.15.4
9
  * Author URI: https://happyforms.io
10
  * Upgrade URI: https://happyforms.io/upgrade
11
  */
22
  /**
23
  * The current version of the plugin.
24
  */
25
+ define( 'HAPPYFORMS_VERSION', '1.15.4' );
26
 
27
  if ( ! function_exists( 'happyforms_get_version' ) ):
28
 
inc/assets/js/customize.js CHANGED
@@ -1707,7 +1707,6 @@
1707
  'change [data-attribute="characters_label_max"]': 'onCharLimitMaxCharsChange',
1708
  'keyup [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1709
  'change [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1710
- 'keyup [data-attribute="submissions_left_label"]': 'onSubmissionsLeftLabelChange',
1711
  'click [data-reset]': 'resetDefaultMessage',
1712
  'keyup input[data-attribute]': 'onMessageValueChange',
1713
  } ),
@@ -1717,6 +1716,10 @@
1717
 
1718
  initialize: function() {
1719
  classes.views.Base.prototype.initialize.apply( this, arguments );
 
 
 
 
1720
  },
1721
 
1722
  render: function() {
@@ -1918,17 +1921,19 @@
1918
 
1919
 
1920
  var partsWithLimitedOptions = happyForms.form.get( 'parts' ).filter( function( part ) {
1921
- var hasOptions = [ 'radio', 'checkbox', 'select' ].includes( part.get( 'type' ) );
1922
- if ( ! hasOptions ) {
1923
- return false;
1924
- }
1925
- var hasLimitedOptions = part.get( 'options' ).findWhere( {
1926
- show_submissions_amount: "1"
1927
- } );
1928
- if ( hasLimitedOptions ) {
 
 
 
 
1929
  return hasLimitedOptions;
1930
- }
1931
- return false;
1932
  } );
1933
 
1934
  if ( partsWithLimitedOptions.length ) {
@@ -2012,6 +2017,30 @@
2012
  };
2013
 
2014
  happyForms.previewSend( 'happyforms-form-dom-update', data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2015
  },
2016
  } );
2017
 
1707
  'change [data-attribute="characters_label_max"]': 'onCharLimitMaxCharsChange',
1708
  'keyup [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1709
  'change [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
 
1710
  'click [data-reset]': 'resetDefaultMessage',
1711
  'keyup input[data-attribute]': 'onMessageValueChange',
1712
  } ),
1716
 
1717
  initialize: function() {
1718
  classes.views.Base.prototype.initialize.apply( this, arguments );
1719
+
1720
+ this.listenTo( this.model, 'change:submissions_left_label', _.debounce( function( model, value ) {
1721
+ this.onSubmissionsLeftLabelChange( model, value );
1722
+ }, 800 ) );
1723
  },
1724
 
1725
  render: function() {
1921
 
1922
 
1923
  var partsWithLimitedOptions = happyForms.form.get( 'parts' ).filter( function( part ) {
1924
+ var hasOptions = [ 'radio', 'checkbox', 'select' ].includes( part.get( 'type' ) );
1925
+
1926
+ if ( ! hasOptions ) {
1927
+ return false;
1928
+ }
1929
+
1930
+ var hasLimitedOptions = part.get( 'options' ).find( function( option ) {
1931
+ var limitSubmissionAmount = option.get( 'limit_submissions_amount' );
1932
+
1933
+ return typeof limitSubmissionAmount !== 'undefined' && limitSubmissionAmount != '';
1934
+ } );
1935
+
1936
  return hasLimitedOptions;
 
 
1937
  } );
1938
 
1939
  if ( partsWithLimitedOptions.length ) {
2017
  };
2018
 
2019
  happyForms.previewSend( 'happyforms-form-dom-update', data );
2020
+
2021
+ var dropdowns = happyForms.form.get( 'parts' ).filter( function( part ) {
2022
+ if ( 'select' != part.get( 'type' ) ) {
2023
+ return false;
2024
+ }
2025
+
2026
+ var hasLimitedOptions = part.get( 'options' ).find( function( option ) {
2027
+ var limitSubmissionAmount = option.get( 'limit_submissions_amount' );
2028
+ return typeof limitSubmissionAmount !== 'undefined' && limitSubmissionAmount != '';
2029
+ } );
2030
+
2031
+ return hasLimitedOptions;
2032
+ } );
2033
+
2034
+ dropdowns.forEach( function( dropdown ) {
2035
+ dropdown.fetchHtml( function( response ) {
2036
+ var data = {
2037
+ id: dropdown.get( 'id' ),
2038
+ html: response,
2039
+ };
2040
+
2041
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
2042
+ } );
2043
+ } );
2044
  },
2045
  } );
2046
 
inc/classes/class-happyforms.php CHANGED
@@ -172,7 +172,7 @@ class HappyForms extends HappyForms_Core {
172
  'label' => __( 'Include referral web address', 'happyforms' ),
173
  );
174
 
175
- $controls[645] = array(
176
  'type' => 'email-parts-list_dummy',
177
  'dummy_id' => 'confirmation_email_respondent_address',
178
  'label' => __( 'To email address', 'happyforms' ),
172
  'label' => __( 'Include referral web address', 'happyforms' ),
173
  );
174
 
175
+ $controls[630] = array(
176
  'type' => 'email-parts-list_dummy',
177
  'dummy_id' => 'confirmation_email_respondent_address',
178
  'label' => __( 'To email address', 'happyforms' ),
inc/classes/class-message-controller.php CHANGED
@@ -385,6 +385,10 @@ class HappyForms_Message_Controller {
385
  $name = $form['alert_email_from_name'];
386
  $to = explode( ',', $form['email_recipient'] );
387
 
 
 
 
 
388
  $email_message->set_from_name( $name );
389
  $email_message->set_to( $to[0] );
390
 
@@ -400,11 +404,33 @@ class HappyForms_Message_Controller {
400
 
401
  $email_message->set_subject( $subject );
402
 
403
- $email_part = happyforms_get_form_controller()->get_first_part_by_type( $form, 'email' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
- if ( false !== $email_part ) {
406
- $email_part_id = $email_part['id'];
407
- $reply_to = happyforms_get_message_part_value( $message[$email_part_id], $email_part );
408
  $email_message->set_reply_to( $reply_to );
409
  }
410
 
385
  $name = $form['alert_email_from_name'];
386
  $to = explode( ',', $form['email_recipient'] );
387
 
388
+ $from_address = happyforms_get_form_property( $form, 'alert_email_from_address' );
389
+ $from_address = explode( ',', $from_address );
390
+
391
+ $email_message->set_from( $from_address[0] );
392
  $email_message->set_from_name( $name );
393
  $email_message->set_to( $to[0] );
394
 
404
 
405
  $email_message->set_subject( $subject );
406
 
407
+ $email_reply_to_id = happyforms_get_form_property( $form, 'alert_email_reply_to' );
408
+ $email_reply_parts = array();
409
+
410
+ if ( 'all' === $email_reply_to_id ) {
411
+ $email_reply_parts = happyforms_get_form_controller()->get_parts_by_type( $form, 'email' );
412
+ } else {
413
+ $email_part = happyforms_get_form_controller()->get_part_by_id( $form, $email_reply_to_id );
414
+
415
+ if ( ! $email_part ) {
416
+ $email_part = happyforms_get_form_controller()->get_first_part_by_type( $form, 'email' );
417
+ }
418
+
419
+ if ( $email_part ) {
420
+ $email_reply_parts[] = $email_part;
421
+ }
422
+ }
423
+
424
+ if ( ! empty( $email_reply_parts ) ) {
425
+ $reply_to = array_map( function( $email_part ) use( $message ) {
426
+ $part_id = $email_part['id'];
427
+ $part_value = happyforms_get_message_part_value( $message[$part_id], $email_part );
428
+
429
+ return $part_value;
430
+ }, $email_reply_parts );
431
+
432
+ $reply_to = array_values( array_filter( array_map( 'trim', $reply_to ) ) );
433
 
 
 
 
434
  $email_message->set_reply_to( $reply_to );
435
  }
436
 
inc/templates/customize-controls/email-parts-list-dummy.php CHANGED
@@ -1,5 +1,5 @@
1
  <div class="customize-control customize-control-email-parts-list_dummy" id="customize-control-<?php echo $control['dummy_id']; ?>">
2
- <label for="<?php echo $control['dummy_id']; ?>" class="customize-control-title"><?php echo $control['label']; ?>:</label>&nbsp<span class="members-only"><?php _e( 'Members Only', 'happyforms') ?></span>
3
  <select id="<?php echo $control['dummy_id']; ?>">
4
  <%
5
  var options = _( parts ).where( { type: 'email' } );
1
  <div class="customize-control customize-control-email-parts-list_dummy" id="customize-control-<?php echo $control['dummy_id']; ?>">
2
+ <label for="<?php echo $control['dummy_id']; ?>" class="customize-control-title"><?php echo $control['label']; ?></label>&nbsp<span class="members-only"><?php _e( 'Members Only', 'happyforms') ?></span>
3
  <select id="<?php echo $control['dummy_id']; ?>">
4
  <%
5
  var options = _( parts ).where( { type: 'email' } );
languages/happyforms.pot CHANGED
@@ -2,16 +2,16 @@
2
  # This file is distributed under the same license as the Happyforms (free) plugin.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Happyforms (free) 1.15.3\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/happyforms/\n"
7
  "Last-Translator: The Theme Foundry\n"
8
  "Language-Team: The Theme Foundry\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "POT-Creation-Date: 2022-05-24T06:56:42+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
- "X-Generator: WP-CLI 2.5.0\n"
15
  "X-Domain: happyforms\n"
16
 
17
  #. Plugin Name of the plugin
@@ -80,6 +80,12 @@ msgstr ""
80
  #: core/classes/class-form-admin.php:454
81
  #: core/templates/customize-form-item.php:27
82
  #: core/templates/customize-form-part-footer.php:4
 
 
 
 
 
 
83
  msgid "Duplicate"
84
  msgstr ""
85
 
@@ -129,7 +135,6 @@ msgstr ""
129
 
130
  #: core/classes/class-form-controller.php:82
131
  #: core/assets/jsx/build/admin/block.js:144
132
- #: core/assets/jsx/src/admin/block.js:32
133
  msgid "No forms found."
134
  msgstr ""
135
 
@@ -148,63 +153,64 @@ msgstr ""
148
  msgid "(no title)"
149
  msgstr ""
150
 
151
- #: core/classes/class-form-controller.php:640
152
  msgid " Copy"
153
  msgstr ""
154
 
155
- #: core/classes/class-form-email.php:47
156
  msgid "You received a new message"
157
  msgstr ""
158
 
159
- #: core/classes/class-form-email.php:67
160
  msgid "We received your message"
161
  msgstr ""
162
 
163
- #: core/classes/class-form-email.php:71
164
  msgid "Your message has been successfully sent. We appreciate you contacting us and we’ll be in touch soon."
165
  msgstr ""
166
 
167
- #: core/classes/class-form-email.php:87
168
  msgid "Email me a copy of each submission"
169
  msgstr ""
170
 
171
- #: core/classes/class-form-email.php:96
172
- #: inc/assets/jsx/build/admin/dashboard-modals.js:213
173
- #: inc/assets/jsx/src/admin/dashboard-modals.js:49
174
- msgid "Email address"
175
  msgstr ""
176
 
177
- #: core/classes/class-form-email.php:101
178
- msgid "Email Bcc address"
179
  msgstr ""
180
 
181
- #: core/classes/class-form-email.php:106
182
- #: core/classes/class-form-email.php:138
183
- msgid "Email display name"
184
  msgstr ""
185
 
186
- #: core/classes/class-form-email.php:111
187
- #: core/classes/class-form-email.php:143
188
- msgid "Email subject"
189
  msgstr ""
190
 
191
- #: core/classes/class-form-email.php:119
192
- msgid "Email respondent a copy of their submission"
 
193
  msgstr ""
194
 
195
- #: core/classes/class-form-email.php:128
196
- msgid "From email address"
 
197
  msgstr ""
198
 
199
- #: core/classes/class-form-email.php:133
200
- msgid "Reply email address"
201
  msgstr ""
202
 
203
- #: core/classes/class-form-email.php:148
204
  msgid "Email content"
205
  msgstr ""
206
 
207
- #: core/classes/class-form-email.php:153
208
  msgid "Include submitted values"
209
  msgstr ""
210
 
@@ -235,11 +241,11 @@ msgid "Nothing found"
235
  msgstr ""
236
 
237
  #: core/classes/class-form-messages.php:51
238
- msgid "Oops. This number isn't big enough."
239
  msgstr ""
240
 
241
  #: core/classes/class-form-messages.php:55
242
- msgid "Oops. This number is too big."
243
  msgstr ""
244
 
245
  #: core/classes/class-form-messages.php:59
@@ -247,11 +253,11 @@ msgid "(optional)"
247
  msgstr ""
248
 
249
  #: core/classes/class-form-messages.php:67
250
- msgid "Oops. Too many choices are selected."
251
  msgstr ""
252
 
253
  #: core/classes/class-form-messages.php:71
254
- msgid "Oops. Not enough choices are selected."
255
  msgstr ""
256
 
257
  #: core/classes/class-form-messages.php:75
@@ -338,7 +344,7 @@ msgstr ""
338
  msgid "Remaining submissions"
339
  msgstr ""
340
 
341
- #: core/classes/class-form-part-library.php:227
342
  msgid "Missing validation callback for field %s"
343
  msgstr ""
344
 
@@ -685,14 +691,17 @@ msgstr ""
685
 
686
  #: core/classes/class-form-styles.php:716
687
  #: core/templates/parts/customize-checkbox.php:4
688
- #: core/templates/parts/customize-checkbox.php:131
 
689
  #: core/templates/parts/customize-email.php:4
690
  #: core/templates/parts/customize-multi-line-text.php:4
691
  #: core/templates/parts/customize-number.php:4
692
  #: core/templates/parts/customize-radio.php:4
693
- #: core/templates/parts/customize-radio.php:116
 
694
  #: core/templates/parts/customize-select.php:4
695
- #: core/templates/parts/customize-select.php:108
 
696
  #: core/templates/parts/customize-single-line-text.php:4
697
  msgid "Label"
698
  msgstr ""
@@ -712,12 +721,12 @@ msgstr ""
712
 
713
  #: core/classes/class-form-styles.php:731
714
  #: core/templates/parts/customize-checkbox.php:20
715
- #: core/templates/parts/customize-checkbox.php:137
716
  #: core/templates/parts/customize-email.php:28
717
  #: core/templates/parts/customize-multi-line-text.php:28
718
  #: core/templates/parts/customize-number.php:28
719
  #: core/templates/parts/customize-radio.php:20
720
- #: core/templates/parts/customize-radio.php:122
721
  #: core/templates/parts/customize-select.php:24
722
  #: core/templates/parts/customize-single-line-text.php:28
723
  msgid "Hint"
@@ -824,8 +833,8 @@ msgstr ""
824
 
825
  #: core/classes/class-happyforms-core.php:210
826
  #: core/classes/class-happyforms-core.php:211
827
- #: inc/classes/class-message-controller.php:535
828
- #: inc/classes/class-message-controller.php:536
829
  msgid "Activity"
830
  msgstr ""
831
 
@@ -838,7 +847,6 @@ msgstr ""
838
  #: core/classes/class-happyforms-core.php:228
839
  #: core/classes/class-happyforms-core.php:229
840
  #: core/assets/jsx/build/admin/block.js:173
841
- #: core/assets/jsx/src/admin/block.js:61
842
  msgid "Settings"
843
  msgstr ""
844
 
@@ -875,23 +883,23 @@ msgid "Not enough words/characters typed"
875
  msgstr ""
876
 
877
  #: core/classes/class-validation-messages.php:106
878
- msgid "Oops. Looks like there's a mistake here."
879
  msgstr ""
880
 
881
  #: core/classes/class-validation-messages.php:110
882
- msgid "Oops. Please answer this question."
883
  msgstr ""
884
 
885
  #: core/classes/class-validation-messages.php:114
886
- msgid "Oops. Please make a selection."
887
  msgstr ""
888
 
889
  #: core/classes/class-validation-messages.php:118
890
- msgid "Oops. This answer is too long."
891
  msgstr ""
892
 
893
  #: core/classes/class-validation-messages.php:122
894
- msgid "Oops. This answer isn't long enough."
895
  msgstr ""
896
 
897
  #: core/classes/class-wp-customize-form-manager.php:162
@@ -2631,10 +2639,18 @@ msgstr ""
2631
 
2632
  #: core/templates/admin-form-modal.php:12
2633
  #: core/assets/jsx/build/admin/block.js:158
2634
- #: core/assets/jsx/src/admin/block.js:41
2635
  msgid "Insert"
2636
  msgstr ""
2637
 
 
 
 
 
 
 
 
 
 
2638
  #: core/templates/customize-controls/messages/text.php:7
2639
  msgid "Reset"
2640
  msgstr ""
@@ -2691,12 +2707,12 @@ msgstr ""
2691
 
2692
  #: core/templates/customize-form-item.php:33
2693
  #: core/templates/customize-form-part-footer.php:3
2694
- #: core/templates/parts/customize-checkbox.php:154
2695
- #: core/templates/parts/customize-checkbox.php:169
2696
- #: core/templates/parts/customize-radio.php:139
2697
- #: core/templates/parts/customize-radio.php:154
2698
- #: core/templates/parts/customize-select.php:125
2699
- #: core/templates/parts/customize-select.php:140
2700
  msgid "Delete"
2701
  msgstr ""
2702
 
@@ -2784,138 +2800,117 @@ msgstr ""
2784
  #: core/templates/parts/customize-checkbox.php:28
2785
  #: core/templates/parts/customize-checkbox.php:32
2786
  #: core/templates/parts/customize-radio.php:28
2787
- #: core/templates/parts/customize-radio.php:32
2788
  #: core/templates/parts/customize-select.php:32
2789
- #: core/templates/parts/customize-select.php:36
2790
  msgid "Choices"
2791
  msgstr ""
2792
 
2793
  #: core/templates/parts/customize-checkbox.php:29
2794
- #: core/templates/parts/customize-radio.php:29
2795
  #: core/templates/parts/customize-select.php:33
2796
- msgid "No choices added yet."
2797
  msgstr ""
2798
 
2799
  #: core/templates/parts/customize-checkbox.php:36
2800
- #: core/templates/parts/customize-radio.php:36
2801
- #: core/templates/parts/customize-select.php:40
2802
  msgid "Add heading"
2803
  msgstr ""
2804
 
2805
  #: core/templates/parts/customize-checkbox.php:37
2806
- #: core/templates/parts/customize-radio.php:37
2807
- #: core/templates/parts/customize-select.php:41
2808
  msgid "Add choice"
2809
  msgstr ""
2810
 
2811
- #: core/templates/parts/customize-checkbox.php:39
2812
- #: core/templates/parts/customize-radio.php:39
2813
  #: core/templates/parts/customize-select.php:43
2814
- msgid "Or, bulk add choices"
2815
- msgstr ""
2816
-
2817
- #: core/templates/parts/customize-checkbox.php:43
2818
- #: core/templates/parts/customize-radio.php:43
2819
- #: core/templates/parts/customize-select.php:47
2820
- msgid "Add choices"
2821
- msgstr ""
2822
-
2823
- #: core/templates/parts/customize-checkbox.php:45
2824
- #: core/templates/parts/customize-radio.php:45
2825
- #: core/templates/parts/customize-select.php:49
2826
- msgid "Cancel"
2827
- msgstr ""
2828
-
2829
- #: core/templates/parts/customize-checkbox.php:56
2830
- #: core/templates/parts/customize-radio.php:56
2831
- #: core/templates/parts/customize-select.php:56
2832
  msgid "Add 'other' choice"
2833
  msgstr ""
2834
 
2835
- #: core/templates/parts/customize-checkbox.php:61
2836
- #: core/templates/parts/customize-radio.php:61
2837
- #: core/templates/parts/customize-select.php:62
2838
  msgid "'Other' label"
2839
  msgstr ""
2840
 
2841
- #: core/templates/parts/customize-checkbox.php:65
2842
- #: core/templates/parts/customize-radio.php:65
2843
- #: core/templates/parts/customize-select.php:66
2844
  msgid "'Other' placeholder"
2845
  msgstr ""
2846
 
2847
- #: core/templates/parts/customize-checkbox.php:72
2848
- #: core/templates/parts/customize-radio.php:72
2849
- #: core/templates/parts/customize-select.php:78
2850
  msgid "Shuffle order of choices"
2851
  msgstr ""
2852
 
2853
- #: core/templates/parts/customize-checkbox.php:77
2854
  msgid "Limit choices"
2855
  msgstr ""
2856
 
2857
- #: core/templates/parts/customize-checkbox.php:82
2858
  msgid "Min choices"
2859
  msgstr ""
2860
 
2861
- #: core/templates/parts/customize-checkbox.php:86
2862
  msgid "Max choices"
2863
  msgstr ""
2864
 
2865
- #: core/templates/parts/customize-checkbox.php:91
2866
- #: core/templates/parts/customize-radio.php:76
2867
  msgid "Align choices"
2868
  msgstr ""
2869
 
2870
- #: core/templates/parts/customize-checkbox.php:95
2871
- #: core/templates/parts/customize-radio.php:80
2872
  msgid "Vertically"
2873
  msgstr ""
2874
 
2875
- #: core/templates/parts/customize-checkbox.php:99
2876
- #: core/templates/parts/customize-radio.php:84
2877
  msgid "Horizontally"
2878
  msgstr ""
2879
 
2880
- #: core/templates/parts/customize-checkbox.php:105
2881
  #: core/templates/parts/customize-email.php:48
2882
  #: core/templates/parts/customize-multi-line-text.php:61
2883
  #: core/templates/parts/customize-number.php:76
2884
- #: core/templates/parts/customize-radio.php:90
2885
- #: core/templates/parts/customize-select.php:83
2886
  #: core/templates/parts/customize-single-line-text.php:47
2887
  msgid "Require an answer"
2888
  msgstr ""
2889
 
2890
- #: core/templates/parts/customize-checkbox.php:114
2891
  #: core/templates/parts/customize-email.php:57
2892
  #: core/templates/parts/customize-multi-line-text.php:70
2893
  #: core/templates/parts/customize-number.php:83
2894
- #: core/templates/parts/customize-radio.php:97
2895
- #: core/templates/parts/customize-select.php:89
2896
  #: core/templates/parts/customize-single-line-text.php:56
2897
  msgid "Additional CSS class(es)"
2898
  msgstr ""
2899
 
2900
- #: core/templates/parts/customize-checkbox.php:143
2901
- msgid "Max submissions"
 
 
2902
  msgstr ""
2903
 
2904
- #: core/templates/parts/customize-checkbox.php:149
2905
- #: core/templates/parts/customize-radio.php:134
2906
- #: core/templates/parts/customize-select.php:120
2907
- msgid "Make this choice default"
2908
  msgstr ""
2909
 
2910
- #: core/templates/parts/customize-checkbox.php:155
2911
- #: core/templates/parts/customize-radio.php:140
2912
- #: core/templates/parts/customize-select.php:126
2913
- msgid "More"
2914
  msgstr ""
2915
 
2916
- #: core/templates/parts/customize-checkbox.php:165
2917
- #: core/templates/parts/customize-radio.php:150
2918
- #: core/templates/parts/customize-select.php:136
2919
  #: inc/classes/parts/class-part-layout-title-dummy.php:8
2920
  msgid "Heading"
2921
  msgstr ""
@@ -2975,8 +2970,14 @@ msgstr ""
2975
  msgid "Decimal"
2976
  msgstr ""
2977
 
2978
- #: core/templates/parts/customize-radio.php:128
2979
- #: core/templates/parts/customize-select.php:114
 
 
 
 
 
 
2980
  #: inc/classes/class-happyforms.php:138
2981
  msgid "Max number of submissions"
2982
  msgstr ""
@@ -3033,10 +3034,6 @@ msgstr ""
3033
  msgid "Include referral web address"
3034
  msgstr ""
3035
 
3036
- #: inc/classes/class-happyforms.php:178
3037
- msgid "To email address"
3038
- msgstr ""
3039
-
3040
  #: inc/classes/class-happyforms.php:184
3041
  msgid "Send abandonment email"
3042
  msgstr ""
@@ -3069,12 +3066,12 @@ msgstr ""
3069
  msgid "Spam"
3070
  msgstr ""
3071
 
3072
- #: inc/classes/class-message-controller.php:537
3073
- #: inc/classes/class-message-controller.php:538
3074
  msgid "All Activity"
3075
  msgstr ""
3076
 
3077
- #: inc/classes/class-message-controller.php:539
3078
  msgid "No activity found."
3079
  msgstr ""
3080
 
@@ -3226,10 +3223,6 @@ msgstr ""
3226
  msgid "For formatted site URLs."
3227
  msgstr ""
3228
 
3229
- #: inc/templates/customize-controls/email-parts-list-dummy.php:8
3230
- msgid "field"
3231
- msgstr ""
3232
-
3233
  #: integrations/classes/class-integrations-page-controller.php:26
3234
  msgid "ActiveCampaign"
3235
  msgstr ""
@@ -3468,22 +3461,18 @@ msgid "Publishable key"
3468
  msgstr ""
3469
 
3470
  #: core/assets/jsx/build/admin/block.js:127
3471
- #: core/assets/jsx/src/admin/block.js:15
3472
  msgid "Choose"
3473
  msgstr ""
3474
 
3475
  #: core/assets/jsx/build/admin/block.js:144
3476
- #: core/assets/jsx/src/admin/block.js:27
3477
  msgid "The form previously added has been trashed or deleted."
3478
  msgstr ""
3479
 
3480
  #: core/assets/jsx/build/admin/block.js:144
3481
- #: core/assets/jsx/src/admin/block.js:31
3482
  msgid "Pick a form to display on your site."
3483
  msgstr ""
3484
 
3485
  #: core/assets/jsx/build/admin/block.js:175
3486
- #: core/assets/jsx/src/admin/block.js:63
3487
  msgid "Pick a form"
3488
  msgstr ""
3489
 
@@ -3497,6 +3486,11 @@ msgstr ""
3497
  msgid "We'll occasionally send you emails about plugin updates. And don't sweat it, you can unsubscribe anytime."
3498
  msgstr ""
3499
 
 
 
 
 
 
3500
  #: inc/assets/jsx/build/admin/dashboard-modals.js:223
3501
  #: inc/assets/jsx/src/admin/dashboard-modals.js:54
3502
  msgid "Continue"
2
  # This file is distributed under the same license as the Happyforms (free) plugin.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Happyforms (free) 1.15.4\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/happyforms/\n"
7
  "Last-Translator: The Theme Foundry\n"
8
  "Language-Team: The Theme Foundry\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
+ "POT-Creation-Date: 2022-06-13T07:21:30+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
+ "X-Generator: WP-CLI 2.4.0\n"
15
  "X-Domain: happyforms\n"
16
 
17
  #. Plugin Name of the plugin
80
  #: core/classes/class-form-admin.php:454
81
  #: core/templates/customize-form-item.php:27
82
  #: core/templates/customize-form-part-footer.php:4
83
+ #: core/templates/parts/customize-checkbox.php:156
84
+ #: core/templates/parts/customize-checkbox.php:184
85
+ #: core/templates/parts/customize-radio.php:138
86
+ #: core/templates/parts/customize-radio.php:166
87
+ #: core/templates/parts/customize-select.php:123
88
+ #: core/templates/parts/customize-select.php:151
89
  msgid "Duplicate"
90
  msgstr ""
91
 
135
 
136
  #: core/classes/class-form-controller.php:82
137
  #: core/assets/jsx/build/admin/block.js:144
 
138
  msgid "No forms found."
139
  msgstr ""
140
 
153
  msgid "(no title)"
154
  msgstr ""
155
 
156
+ #: core/classes/class-form-controller.php:644
157
  msgid " Copy"
158
  msgstr ""
159
 
160
+ #: core/classes/class-form-email.php:52
161
  msgid "You received a new message"
162
  msgstr ""
163
 
164
+ #: core/classes/class-form-email.php:76
165
  msgid "We received your message"
166
  msgstr ""
167
 
168
+ #: core/classes/class-form-email.php:80
169
  msgid "Your message has been successfully sent. We appreciate you contacting us and we’ll be in touch soon."
170
  msgstr ""
171
 
172
+ #: core/classes/class-form-email.php:96
173
  msgid "Email me a copy of each submission"
174
  msgstr ""
175
 
176
+ #: core/classes/class-form-email.php:106
177
+ #: inc/classes/class-happyforms.php:178
178
+ msgid "To email address"
 
179
  msgstr ""
180
 
181
+ #: core/classes/class-form-email.php:111
182
+ msgid "To Bcc email address"
183
  msgstr ""
184
 
185
+ #: core/classes/class-form-email.php:116
186
+ #: core/classes/class-form-email.php:148
187
+ msgid "From email address"
188
  msgstr ""
189
 
190
+ #: core/classes/class-form-email.php:121
191
+ #: core/classes/class-form-email.php:153
192
+ msgid "Reply email address"
193
  msgstr ""
194
 
195
+ #: core/classes/class-form-email.php:126
196
+ #: core/classes/class-form-email.php:158
197
+ msgid "Email display name"
198
  msgstr ""
199
 
200
+ #: core/classes/class-form-email.php:131
201
+ #: core/classes/class-form-email.php:163
202
+ msgid "Email subject"
203
  msgstr ""
204
 
205
+ #: core/classes/class-form-email.php:139
206
+ msgid "Email respondent a copy of their submission"
207
  msgstr ""
208
 
209
+ #: core/classes/class-form-email.php:168
210
  msgid "Email content"
211
  msgstr ""
212
 
213
+ #: core/classes/class-form-email.php:173
214
  msgid "Include submitted values"
215
  msgstr ""
216
 
241
  msgstr ""
242
 
243
  #: core/classes/class-form-messages.php:51
244
+ msgid "This number isn't big enough."
245
  msgstr ""
246
 
247
  #: core/classes/class-form-messages.php:55
248
+ msgid "This number is too big."
249
  msgstr ""
250
 
251
  #: core/classes/class-form-messages.php:59
253
  msgstr ""
254
 
255
  #: core/classes/class-form-messages.php:67
256
+ msgid "Too many choices are selected."
257
  msgstr ""
258
 
259
  #: core/classes/class-form-messages.php:71
260
+ msgid "Not enough choices are selected."
261
  msgstr ""
262
 
263
  #: core/classes/class-form-messages.php:75
344
  msgid "Remaining submissions"
345
  msgstr ""
346
 
347
+ #: core/classes/class-form-part-library.php:233
348
  msgid "Missing validation callback for field %s"
349
  msgstr ""
350
 
691
 
692
  #: core/classes/class-form-styles.php:716
693
  #: core/templates/parts/customize-checkbox.php:4
694
+ #: core/templates/parts/customize-checkbox.php:133
695
+ #: core/templates/parts/customize-checkbox.php:178
696
  #: core/templates/parts/customize-email.php:4
697
  #: core/templates/parts/customize-multi-line-text.php:4
698
  #: core/templates/parts/customize-number.php:4
699
  #: core/templates/parts/customize-radio.php:4
700
+ #: core/templates/parts/customize-radio.php:115
701
+ #: core/templates/parts/customize-radio.php:160
702
  #: core/templates/parts/customize-select.php:4
703
+ #: core/templates/parts/customize-select.php:106
704
+ #: core/templates/parts/customize-select.php:145
705
  #: core/templates/parts/customize-single-line-text.php:4
706
  msgid "Label"
707
  msgstr ""
721
 
722
  #: core/classes/class-form-styles.php:731
723
  #: core/templates/parts/customize-checkbox.php:20
724
+ #: core/templates/parts/customize-checkbox.php:139
725
  #: core/templates/parts/customize-email.php:28
726
  #: core/templates/parts/customize-multi-line-text.php:28
727
  #: core/templates/parts/customize-number.php:28
728
  #: core/templates/parts/customize-radio.php:20
729
+ #: core/templates/parts/customize-radio.php:121
730
  #: core/templates/parts/customize-select.php:24
731
  #: core/templates/parts/customize-single-line-text.php:28
732
  msgid "Hint"
833
 
834
  #: core/classes/class-happyforms-core.php:210
835
  #: core/classes/class-happyforms-core.php:211
836
+ #: inc/classes/class-message-controller.php:561
837
+ #: inc/classes/class-message-controller.php:562
838
  msgid "Activity"
839
  msgstr ""
840
 
847
  #: core/classes/class-happyforms-core.php:228
848
  #: core/classes/class-happyforms-core.php:229
849
  #: core/assets/jsx/build/admin/block.js:173
 
850
  msgid "Settings"
851
  msgstr ""
852
 
883
  msgstr ""
884
 
885
  #: core/classes/class-validation-messages.php:106
886
+ msgid "Looks like there's a mistake here."
887
  msgstr ""
888
 
889
  #: core/classes/class-validation-messages.php:110
890
+ msgid "Please answer this question."
891
  msgstr ""
892
 
893
  #: core/classes/class-validation-messages.php:114
894
+ msgid "Please make a selection."
895
  msgstr ""
896
 
897
  #: core/classes/class-validation-messages.php:118
898
+ msgid "This answer is too long."
899
  msgstr ""
900
 
901
  #: core/classes/class-validation-messages.php:122
902
+ msgid "This answer isn't long enough."
903
  msgstr ""
904
 
905
  #: core/classes/class-wp-customize-form-manager.php:162
2639
 
2640
  #: core/templates/admin-form-modal.php:12
2641
  #: core/assets/jsx/build/admin/block.js:158
 
2642
  msgid "Insert"
2643
  msgstr ""
2644
 
2645
+ #: core/templates/customize-controls/email/email-parts-list.php:10
2646
+ #: inc/templates/customize-controls/email-parts-list-dummy.php:8
2647
+ msgid "field"
2648
+ msgstr ""
2649
+
2650
+ #: core/templates/customize-controls/email/email-parts-list.php:13
2651
+ msgid "All Email fields"
2652
+ msgstr ""
2653
+
2654
  #: core/templates/customize-controls/messages/text.php:7
2655
  msgid "Reset"
2656
  msgstr ""
2707
 
2708
  #: core/templates/customize-form-item.php:33
2709
  #: core/templates/customize-form-part-footer.php:3
2710
+ #: core/templates/parts/customize-checkbox.php:155
2711
+ #: core/templates/parts/customize-checkbox.php:183
2712
+ #: core/templates/parts/customize-radio.php:137
2713
+ #: core/templates/parts/customize-radio.php:165
2714
+ #: core/templates/parts/customize-select.php:122
2715
+ #: core/templates/parts/customize-select.php:150
2716
  msgid "Delete"
2717
  msgstr ""
2718
 
2800
  #: core/templates/parts/customize-checkbox.php:28
2801
  #: core/templates/parts/customize-checkbox.php:32
2802
  #: core/templates/parts/customize-radio.php:28
 
2803
  #: core/templates/parts/customize-select.php:32
 
2804
  msgid "Choices"
2805
  msgstr ""
2806
 
2807
  #: core/templates/parts/customize-checkbox.php:29
 
2808
  #: core/templates/parts/customize-select.php:33
2809
+ msgid "It doesn't look like your field has any choices yet. Want to add one? Click the \"Add Choice\" button to start."
2810
  msgstr ""
2811
 
2812
  #: core/templates/parts/customize-checkbox.php:36
2813
+ #: core/templates/parts/customize-radio.php:33
2814
+ #: core/templates/parts/customize-select.php:36
2815
  msgid "Add heading"
2816
  msgstr ""
2817
 
2818
  #: core/templates/parts/customize-checkbox.php:37
2819
+ #: core/templates/parts/customize-radio.php:34
2820
+ #: core/templates/parts/customize-select.php:37
2821
  msgid "Add choice"
2822
  msgstr ""
2823
 
2824
+ #: core/templates/parts/customize-checkbox.php:47
2825
+ #: core/templates/parts/customize-radio.php:44
2826
  #: core/templates/parts/customize-select.php:43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2827
  msgid "Add 'other' choice"
2828
  msgstr ""
2829
 
2830
+ #: core/templates/parts/customize-checkbox.php:52
2831
+ #: core/templates/parts/customize-radio.php:49
2832
+ #: core/templates/parts/customize-select.php:49
2833
  msgid "'Other' label"
2834
  msgstr ""
2835
 
2836
+ #: core/templates/parts/customize-checkbox.php:56
2837
+ #: core/templates/parts/customize-radio.php:53
2838
+ #: core/templates/parts/customize-select.php:53
2839
  msgid "'Other' placeholder"
2840
  msgstr ""
2841
 
2842
+ #: core/templates/parts/customize-checkbox.php:63
2843
+ #: core/templates/parts/customize-radio.php:60
2844
+ #: core/templates/parts/customize-select.php:65
2845
  msgid "Shuffle order of choices"
2846
  msgstr ""
2847
 
2848
+ #: core/templates/parts/customize-checkbox.php:68
2849
  msgid "Limit choices"
2850
  msgstr ""
2851
 
2852
+ #: core/templates/parts/customize-checkbox.php:73
2853
  msgid "Min choices"
2854
  msgstr ""
2855
 
2856
+ #: core/templates/parts/customize-checkbox.php:77
2857
  msgid "Max choices"
2858
  msgstr ""
2859
 
2860
+ #: core/templates/parts/customize-checkbox.php:82
2861
+ #: core/templates/parts/customize-radio.php:64
2862
  msgid "Align choices"
2863
  msgstr ""
2864
 
2865
+ #: core/templates/parts/customize-checkbox.php:86
2866
+ #: core/templates/parts/customize-radio.php:68
2867
  msgid "Vertically"
2868
  msgstr ""
2869
 
2870
+ #: core/templates/parts/customize-checkbox.php:90
2871
+ #: core/templates/parts/customize-radio.php:72
2872
  msgid "Horizontally"
2873
  msgstr ""
2874
 
2875
+ #: core/templates/parts/customize-checkbox.php:96
2876
  #: core/templates/parts/customize-email.php:48
2877
  #: core/templates/parts/customize-multi-line-text.php:61
2878
  #: core/templates/parts/customize-number.php:76
2879
+ #: core/templates/parts/customize-radio.php:78
2880
+ #: core/templates/parts/customize-select.php:70
2881
  #: core/templates/parts/customize-single-line-text.php:47
2882
  msgid "Require an answer"
2883
  msgstr ""
2884
 
2885
+ #: core/templates/parts/customize-checkbox.php:105
2886
  #: core/templates/parts/customize-email.php:57
2887
  #: core/templates/parts/customize-multi-line-text.php:70
2888
  #: core/templates/parts/customize-number.php:83
2889
+ #: core/templates/parts/customize-radio.php:85
2890
+ #: core/templates/parts/customize-select.php:76
2891
  #: core/templates/parts/customize-single-line-text.php:56
2892
  msgid "Additional CSS class(es)"
2893
  msgstr ""
2894
 
2895
+ #: core/templates/parts/customize-checkbox.php:126
2896
+ #: core/templates/parts/customize-radio.php:108
2897
+ #: core/templates/parts/customize-select.php:99
2898
+ msgid "Choice"
2899
  msgstr ""
2900
 
2901
+ #: core/templates/parts/customize-checkbox.php:145
2902
+ msgid "Max submissions"
 
 
2903
  msgstr ""
2904
 
2905
+ #: core/templates/parts/customize-checkbox.php:151
2906
+ #: core/templates/parts/customize-radio.php:133
2907
+ #: core/templates/parts/customize-select.php:118
2908
+ msgid "Make this choice default"
2909
  msgstr ""
2910
 
2911
+ #: core/templates/parts/customize-checkbox.php:171
2912
+ #: core/templates/parts/customize-radio.php:153
2913
+ #: core/templates/parts/customize-select.php:138
2914
  #: inc/classes/parts/class-part-layout-title-dummy.php:8
2915
  msgid "Heading"
2916
  msgstr ""
2970
  msgid "Decimal"
2971
  msgstr ""
2972
 
2973
+ #: core/templates/parts/customize-radio.php:29
2974
+ msgid ""
2975
+ "It doesn't look like your field has any choices yet. Want to add one?\n"
2976
+ "Click the \"Add Choice\" button to start."
2977
+ msgstr ""
2978
+
2979
+ #: core/templates/parts/customize-radio.php:127
2980
+ #: core/templates/parts/customize-select.php:112
2981
  #: inc/classes/class-happyforms.php:138
2982
  msgid "Max number of submissions"
2983
  msgstr ""
3034
  msgid "Include referral web address"
3035
  msgstr ""
3036
 
 
 
 
 
3037
  #: inc/classes/class-happyforms.php:184
3038
  msgid "Send abandonment email"
3039
  msgstr ""
3066
  msgid "Spam"
3067
  msgstr ""
3068
 
3069
+ #: inc/classes/class-message-controller.php:563
3070
+ #: inc/classes/class-message-controller.php:564
3071
  msgid "All Activity"
3072
  msgstr ""
3073
 
3074
+ #: inc/classes/class-message-controller.php:565
3075
  msgid "No activity found."
3076
  msgstr ""
3077
 
3223
  msgid "For formatted site URLs."
3224
  msgstr ""
3225
 
 
 
 
 
3226
  #: integrations/classes/class-integrations-page-controller.php:26
3227
  msgid "ActiveCampaign"
3228
  msgstr ""
3461
  msgstr ""
3462
 
3463
  #: core/assets/jsx/build/admin/block.js:127
 
3464
  msgid "Choose"
3465
  msgstr ""
3466
 
3467
  #: core/assets/jsx/build/admin/block.js:144
 
3468
  msgid "The form previously added has been trashed or deleted."
3469
  msgstr ""
3470
 
3471
  #: core/assets/jsx/build/admin/block.js:144
 
3472
  msgid "Pick a form to display on your site."
3473
  msgstr ""
3474
 
3475
  #: core/assets/jsx/build/admin/block.js:175
 
3476
  msgid "Pick a form"
3477
  msgstr ""
3478
 
3486
  msgid "We'll occasionally send you emails about plugin updates. And don't sweat it, you can unsubscribe anytime."
3487
  msgstr ""
3488
 
3489
+ #: inc/assets/jsx/build/admin/dashboard-modals.js:213
3490
+ #: inc/assets/jsx/src/admin/dashboard-modals.js:49
3491
+ msgid "Email address"
3492
+ msgstr ""
3493
+
3494
  #: inc/assets/jsx/build/admin/dashboard-modals.js:223
3495
  #: inc/assets/jsx/src/admin/dashboard-modals.js:54
3496
  msgid "Continue"
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: contact form, contact form plugin, forms, form builder, custom form, intak
5
  Requires at least: 5.0
6
  Tested up to: 6.0
7
  Requires PHP: 7.0
8
- Stable tag: 1.15.3
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -34,11 +34,11 @@ https://www.youtube.com/watch?v=X1snk2vJbXI
34
  * Easily add forms anywhere to your pages, posts and widget areas.
35
  * Add multiple forms to the same page.
36
  * Completely mobile responsive forms.
37
- * Unlimited forms, fields, emails and messages.
38
  * Create multi-column layouts with a single click.
39
  * Easily duplicate forms.
40
  * Confirmation message on submission.
41
- * Confirmation email sent to user.
42
  * One-click HoneyPot spam prevention.
43
  * Over 50 styles controls built-in.
44
  * GDPR and 508 compliant forms and secure forms.
@@ -56,22 +56,16 @@ https://www.youtube.com/watch?v=X1snk2vJbXI
56
  = Upgraded Happyforms Features: =
57
 
58
  * Access to all 20+ form fields.
59
- * Manage unlimited responses easily in the dashboard.
60
- * Let folks upload files to your forms.
61
  * Redirect to any webpage after the form is submitted.
62
- * Multi-page forms with progress bar.
63
- * Show and hide forms using date/time scheduler.
64
- * Limit the number of responses per form.
65
  * Google ReCaptcha spam prevention integration.
66
- * Collect abandoned responses.
67
- * Enable people to save and resume responses.
68
- * Inherit theme styles option.
69
- * Preview values before submission.
70
  * Log IP address automatically.
71
- * Poll part with real-time results.
72
- * Randomize form fields and values to avoid biases.
73
- * Advanced filtering of responses.
74
- * Fade submit button until valid.
75
  * Unlimited personal and client use plans.
76
  * No-nonsense support team, happy to help over email.
77
 
@@ -81,19 +75,19 @@ https://www.youtube.com/watch?v=X1snk2vJbXI
81
 
82
  = Can you read my replies? =
83
 
84
- Heck nah! No one likes peepers. All replies are saved in your WordPress database or sent to your inbox.
85
 
86
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
87
 
88
  = Can I translate forms into my language? =
89
 
90
- Absolutely! Every last word read by your respondent can be translated — just type and replace. No extra plugins. No code changes. No fuss.
91
 
92
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
93
 
94
  = Is Happyforms GDPR and CCPA ready? =
95
 
96
- Yep! We have a special field for collecting respondents’ consent for things just like this.
97
 
98
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
99
 
@@ -101,7 +95,7 @@ Note: you'll need to upgrade to our paid contact form builder plugin to get some
101
 
102
  Our competitors claim to be HIPAA compliant, but that’s misleading. Those dodgy buggers! Why have a compliant form if your site’s server, connected services and chosen inbox aren’t?
103
 
104
- To keep things simple, we say to disable Happyforms from saving replies and instead point replies to an email address hosted by an end-to-end encrypted email service like ProtonMail.
105
 
106
  It’s easier than it sounds. Promise. Hit us up if you have questions.
107
 
@@ -151,11 +145,17 @@ Aw, honestly, the thought that you're writing about our contact form builder is
151
 
152
  == Changelog ==
153
 
 
 
 
 
 
 
 
154
  = 1.15.3 =
155
  * New feature: Submissions are checked against headless browsers for better antispam protection.
156
  * Improvement: Field choices preserves its shuffled state when a validation occurs on submission for better usability.
157
  * Improvement: Better wording and style fixes in the form builder.
158
- * Bugfix: Opt-In Choice field label spacing was off.
159
 
160
  = 1.15.2 =
161
  * New feature: "Screen Options" tab now includes "View mode" for better consistency with core WordPress.
@@ -944,6 +944,9 @@ Aw, honestly, the thought that you're writing about our contact form builder is
944
 
945
  == Upgrade Notice ==
946
 
 
 
 
947
  = 1.15.3 =
948
  * Improved antispam protection, better shuffling of field choices, miscellaneous improvements and bugfixes.
949
 
@@ -1017,7 +1020,7 @@ Aw, honestly, the thought that you're writing about our contact form builder is
1017
  * Improved honeypot and asset loading, miscellaneous bugfixes.
1018
 
1019
  = 1.12.9 =
1020
- * Jetpack synchronization was triggering a fatal error.
1021
 
1022
  = 1.12.8 =
1023
  * Safer builder controls internal API.
5
  Requires at least: 5.0
6
  Tested up to: 6.0
7
  Requires PHP: 7.0
8
+ Stable tag: 1.15.4
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
34
  * Easily add forms anywhere to your pages, posts and widget areas.
35
  * Add multiple forms to the same page.
36
  * Completely mobile responsive forms.
37
+ * Unlimited forms, fields, emails and submissions.
38
  * Create multi-column layouts with a single click.
39
  * Easily duplicate forms.
40
  * Confirmation message on submission.
41
+ * Confirmation email sent to submitter.
42
  * One-click HoneyPot spam prevention.
43
  * Over 50 styles controls built-in.
44
  * GDPR and 508 compliant forms and secure forms.
56
  = Upgraded Happyforms Features: =
57
 
58
  * Access to all 20+ form fields.
59
+ * Manage unlimited submissions easily in the dashboard.
60
+ * Let submitters upload files to your forms.
61
  * Redirect to any webpage after the form is submitted.
62
+ * Multi-page forms with step indicator.
63
+ * Limit the number of submissions per form.
 
64
  * Google ReCaptcha spam prevention integration.
65
+ * Enable people to save and resume their submission.
66
+ * Preview submission before submitting.
 
 
67
  * Log IP address automatically.
68
+ * Shuffle form fields and choices to avoid biases.
 
 
 
69
  * Unlimited personal and client use plans.
70
  * No-nonsense support team, happy to help over email.
71
 
75
 
76
  = Can you read my replies? =
77
 
78
+ Heck nah! No one likes peepers. All submissions are saved in your WordPress database or sent to your inbox.
79
 
80
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
81
 
82
  = Can I translate forms into my language? =
83
 
84
+ Absolutely! Every last word read by your submitters can be translated — just type and replace. No extra plugins. No code changes. No fuss.
85
 
86
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
87
 
88
  = Is Happyforms GDPR and CCPA ready? =
89
 
90
+ Yep! We have a special field for collecting submitters' consent for things just like this.
91
 
92
  Note: you'll need to upgrade to our paid contact form builder plugin to get some of the best goodies mentioned here.
93
 
95
 
96
  Our competitors claim to be HIPAA compliant, but that’s misleading. Those dodgy buggers! Why have a compliant form if your site’s server, connected services and chosen inbox aren’t?
97
 
98
+ To keep things simple, we say to disable Happyforms from saving submissions and instead point replies to an email address hosted by an end-to-end encrypted email service like ProtonMail.
99
 
100
  It’s easier than it sounds. Promise. Hit us up if you have questions.
101
 
145
 
146
  == Changelog ==
147
 
148
+ = 1.15.4 =
149
+ * New feature: "To email address" control under "Email me a copy of each submission" allows for more granular control of email settings.
150
+ * New feature: "Reply email address" control under "Email me a copy of each submission" for easier exchanges with submitting users.
151
+ * Improvement: "Added to" column in Forms screen now lists templates and reusable blocks for better integration with WordPress.
152
+ * Improvement: Interface of choices in all choice-based fields has been redesigned for better usability and clarity.
153
+ * Bugfix: Display of fields with limited choices wasn't updating correctly in builder's preview screen.
154
+
155
  = 1.15.3 =
156
  * New feature: Submissions are checked against headless browsers for better antispam protection.
157
  * Improvement: Field choices preserves its shuffled state when a validation occurs on submission for better usability.
158
  * Improvement: Better wording and style fixes in the form builder.
 
159
 
160
  = 1.15.2 =
161
  * New feature: "Screen Options" tab now includes "View mode" for better consistency with core WordPress.
944
 
945
  == Upgrade Notice ==
946
 
947
+ = 1.15.4 =
948
+ * New email controls, redesigned choice interface, miscellaneous improvements and bugfixes.
949
+
950
  = 1.15.3 =
951
  * Improved antispam protection, better shuffling of field choices, miscellaneous improvements and bugfixes.
952
 
1020
  * Improved honeypot and asset loading, miscellaneous bugfixes.
1021
 
1022
  = 1.12.9 =
1023
+ * Jetpack synchronization was triggering a fatal error.
1024
 
1025
  = 1.12.8 =
1026
  * Safer builder controls internal API.