PluginProbe ʕ •ᴥ•ʔ
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More / 1.5.4.1
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More v1.5.4.1
1.10.1.1 1.10.1 1.10.0.5 trunk 1.1.4 1.1.4.2 1.1.5 1.1.5.1 1.1.6 1.1.6.1 1.1.7 1.1.7.1 1.1.7.2 1.1.8 1.1.8.1 1.1.8.2 1.1.8.3 1.1.8.4 1.10.0.1 1.10.0.2 1.10.0.3 1.10.0.4 1.2.0 1.2.0.1 1.2.1 1.2.2 1.2.2.1 1.2.2.2 1.2.3 1.2.3.1 1.2.3.2 1.2.4 1.2.4.1 1.2.5 1.2.5.1 1.2.6 1.2.7 1.2.8 1.2.8.1 1.2.9 1.3.0 1.3.1 1.3.1.1 1.3.1.2 1.3.2 1.3.3 1.3.5 1.3.6 1.3.6.1 1.3.6.2 1.3.7.2 1.3.7.3 1.3.7.4 1.3.8 1.3.9.1 1.4.0.1 1.4.1.1 1.4.2 1.4.2.1 1.4.2.2 1.4.3 1.4.4 1.4.4.1 1.4.5 1.4.5.1 1.4.5.2 1.4.5.3 1.4.6 1.4.7.1 1.4.7.2 1.4.8.1 1.4.9 1.5.0.1 1.5.0.3 1.5.0.4 1.5.1 1.5.1.1 1.5.1.3 1.5.2.1 1.5.2.2 1.5.2.3 1.5.3 1.5.3.1 1.5.4.1 1.5.4.2 1.5.5 1.5.5.1 1.5.6 1.5.6.2 1.5.7 1.5.8.2 1.5.9.1 1.5.9.4 1.5.9.5 1.6.0.1 1.6.0.2 1.6.1 1.6.2.2 1.6.2.3 1.6.3.1 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.7 1.6.7.1 1.6.7.2 1.6.7.3 1.6.8 1.6.8.1 1.6.9 1.7.0 1.7.1.1 1.7.1.2 1.7.2 1.7.2.1 1.7.3 1.7.4 1.7.4.1 1.7.4.2 1.7.5.1 1.7.5.2 1.7.5.3 1.7.5.5 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.7.9.1 1.8.0.1 1.8.0.2 1.8.1.1 1.8.1.2 1.8.1.3 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.3.1 1.8.4 1.8.4.1 1.8.5.2 1.8.5.3 1.8.5.4 1.8.6.2 1.8.6.3 1.8.6.4 1.8.7.2 1.8.8.2 1.8.8.3 1.8.9.1 1.8.9.2 1.8.9.4 1.8.9.5 1.8.9.6 1.9.0.1 1.9.0.2 1.9.0.3 1.9.0.4 1.9.1.1 1.9.1.2 1.9.1.3 1.9.1.4 1.9.1.5 1.9.1.6 1.9.2.1 1.9.2.2 1.9.2.3 1.9.3.1 1.9.3.2 1.9.4.1 1.9.4.2 1.9.5 1.9.5.1 1.9.5.2 1.9.6 1.9.6.1 1.9.6.2 1.9.7.1 1.9.7.2 1.9.7.3 1.9.8.1 1.9.8.2 1.9.8.4 1.9.8.7 1.9.9.2 1.9.9.3 1.9.9.4
wpforms-lite / assets / js / admin-builder.js
wpforms-lite / assets / js Last commit date
components 7 years ago admin-builder-conditional-logic-core.js 7 years ago admin-builder-providers.js 6 years ago admin-builder.js 6 years ago admin-editor.js 9 years ago admin-utils.js 7 years ago admin.js 6 years ago admin.min.js 6 years ago chart.min.js 7 years ago choices.min.js 8 years ago flatpickr.min.js 7 years ago jquery.conditionals.min.js 8 years ago jquery.inputmask.bundle.min.js 7 years ago jquery.insert-at-caret.min.js 10 years ago jquery.jquery-confirm.min.js 8 years ago jquery.matchHeight-min.js 8 years ago jquery.minicolors.min.js 8 years ago jquery.payment.min.js 10 years ago jquery.serialize-object.min.js 10 years ago jquery.timepicker.min.js 9 years ago jquery.tooltipster.min.js 7 years ago jquery.validate.js 6 years ago jquery.validate.min.js 7 years ago list.min.js 8 years ago mailcheck.min.js 7 years ago moment-with-locales.min.js 7 years ago moment.min.js 7 years ago wpforms-confirmation.js 7 years ago wpforms.js 6 years ago
admin-builder.js
3300 lines
1 /* global wpforms_builder, wp */
2
3 var WPFormsBuilder = window.WPFormsBuilder || ( function( document, window, $ ) {
4
5 var s,
6 $builder;
7
8 var app = {
9
10 settings: {
11 spinner: '<i class="fa fa-spinner fa-spin"></i>',
12 spinnerInline: '<i class="fa fa-spinner fa-spin wpforms-loading-inline"></i>',
13 tinymceDefaults: { tinymce: { toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,link' }, quicktags: true },
14 pagebreakTop: false,
15 pagebreakBottom: false,
16 upload_img_modal: false
17 },
18
19 /**
20 * Start the engine.
21 *
22 * @since 1.0.0
23 */
24 init: function() {
25
26 wpforms_panel_switch = true;
27 s = this.settings;
28
29 // Document ready.
30 $(document).ready(app.ready);
31
32 // Page load.
33 $(window).on('load', app.load);
34 },
35
36 /**
37 * Page load.
38 *
39 * @since 1.0.0
40 */
41 load: function() {
42
43 // Remove Loading overlay.
44 $('#wpforms-builder-overlay').fadeOut();
45
46 // Maybe display informational informational modal.
47 if ( wpforms_builder.template_modal_display == '1' && 'fields' === wpf.getQueryString('view') ) {
48 $.alert({
49 title: wpforms_builder.template_modal_title,
50 content: wpforms_builder.template_modal_msg,
51 icon: 'fa fa-info-circle',
52 type: 'blue',
53 buttons: {
54 confirm: {
55 text: wpforms_builder.close,
56 btnClass: 'btn-confirm',
57 keys: ['enter']
58 }
59 }
60 })
61 }
62 },
63
64 /**
65 * Document ready.
66 *
67 * @since 1.0.0
68 */
69 ready: function() {
70
71 // Cache builder element.
72 $builder = $('#wpforms-builder');
73
74 // Bind all actions.
75 app.bindUIActions();
76
77 // Trigger initial save for new forms.
78 var newForm = wpf.getQueryString('newform');
79 if (newForm) {
80 app.formSave(false);
81 }
82
83 // Setup/cache some vars not available before
84 s.formID = $('#wpforms-builder-form').data('id');
85 s.pagebreakTop = $('.wpforms-pagebreak-top').length;
86 s.pagebreakBottom = $('.wpforms-pagebreak-bottom').length;
87 s.templateList = new List('wpforms-setup-templates-additional', {
88 valueNames: [ 'wpforms-template-name' ]
89 });
90
91 // Disable implicit submission for every form inside the builder.
92 // All form values are managed by JS and should not be submitted by pressing Enter.
93 $builder.on( 'keypress', '#wpforms-builder-form input', function (e) {
94 if ( e.keyCode === 13 ) {
95 e.preventDefault();
96 }
97 });
98
99 // If there is a section configured, display it.
100 // Otherwise we show the first panel by default.
101 $('.wpforms-panel').each(function(index, el) {
102 var $this = $(this),
103 $configured = $this.find('.wpforms-panel-sidebar-section.configured').first();
104
105 if ( $configured.length ) {
106 var section = $configured.data('section');
107 $configured.addClass('active').find('.wpforms-toggle-arrow').toggleClass('fa-angle-down fa-angle-right');
108 $this.find('.wpforms-panel-content-section-'+section).show().addClass('active');
109 } else {
110 $this.find('.wpforms-panel-content-section:first-of-type').show().addClass('active');
111 $this.find('.wpforms-panel-sidebar-section:first-of-type').addClass('active').find('.wpforms-toggle-arrow').toggleClass('fa-angle-down fa-angle-right');
112 }
113 });
114
115 // Drag and drop sortable elements.
116 app.fieldSortable();
117 app.fieldChoiceSortable('select');
118 app.fieldChoiceSortable('radio');
119 app.fieldChoiceSortable('checkbox');
120 app.fieldChoiceSortable('payment-multiple');
121 app.fieldChoiceSortable('payment-checkbox');
122 app.fieldChoiceSortable('payment-select');
123
124 // Load match heights.
125 $('.wpforms-setup-templates.core .wpforms-template-inner').matchHeight({
126 byRow: false
127 });
128 $('.wpforms-setup-templates.additional .wpforms-template-inner').matchHeight({
129 byRow: false
130 });
131
132 // Set field group visibility.
133 $('.wpforms-add-fields-group').each(function(index, el) {
134 app.fieldGroupToggle($(this),'load');
135 });
136
137
138 app.registerTemplates();
139
140 // Trim long form titles.
141 app.trimFormTitle();
142
143 // Load Tooltips.
144 wpf.initTooltips();
145
146 // Load Tooltips.
147 app.loadColorPickers();
148
149 // Hide/Show reCAPTCHA in form.
150 app.recaptchaToggle();
151
152 // Confirmations initial setup
153 app.confirmationsSetup();
154
155 // Notification settings.
156 app.notificationToggle();
157
158 // Secret builder hotkeys.
159 app.builderHotkeys();
160
161 // Clone form title to setup page.
162 $('#wpforms-setup-name').val($('#wpforms-panel-field-settings-form_title').val());
163
164 // jquery-confirm defaults.
165 jconfirm.defaults = {
166 closeIcon: true,
167 backgroundDismiss: true,
168 escapeKey: true,
169 animationBounce: 1,
170 useBootstrap: false,
171 theme: 'modern',
172 boxWidth: '400px',
173 animateFromElement: false
174 };
175 },
176
177 /**
178 * Element bindings.
179 *
180 * @since 1.0.0
181 */
182 bindUIActions: function() {
183
184 // General Panels.
185 app.bindUIActionsPanels();
186
187 // Setup Panel.
188 app.bindUIActionsSetup();
189
190 // Fields Panel.
191 app.bindUIActionsFields();
192
193 // Settings Panel.
194 app.bindUIActionsSettings();
195
196 // Save and Exit.
197 app.bindUIActionsSaveExit();
198
199 // General/ global.
200 app.bindUIActionsGeneral();
201 },
202
203 //--------------------------------------------------------------------//
204 // General Panels
205 //--------------------------------------------------------------------//
206
207 /**
208 * Element bindings for general panel tasks.
209 *
210 * @since 1.0.0
211 */
212 bindUIActionsPanels: function() {
213
214 // Panel switching.
215 $builder.on('click', '#wpforms-panels-toggle button, .wpforms-panel-switch', function(e) {
216 e.preventDefault();
217 app.panelSwitch($(this).data('panel'));
218 });
219
220 // Panel sections switching.
221 $builder.on('click', '.wpforms-panel .wpforms-panel-sidebar-section', function(e) {
222 app.panelSectionSwitch(this, e);
223 });
224 },
225
226 /**
227 * Switch Panels.
228 *
229 * @since 1.0.0
230 */
231 panelSwitch: function(panel) {
232
233 var $panel = $('#wpforms-panel-'+panel),
234 $panelBtn = $('.wpforms-panel-'+panel+'-button');
235
236 if (!$panel.hasClass('active')) {
237
238 $builder.trigger('wpformsPanelSwitch', panel);
239
240 if (!wpforms_panel_switch) {
241 return false;
242 }
243
244 $('#wpforms-panels-toggle').find('button').removeClass('active');
245 $('.wpforms-panel').removeClass('active');
246 $panelBtn.addClass('active');
247 $panel.addClass('active');
248
249 history.replaceState({}, null, wpf.updateQueryString('view', panel));
250 }
251 },
252
253 /**
254 * Switch Panel section.
255 *
256 * @since 1.0.0
257 */
258 panelSectionSwitch: function(el, e) {
259 if (e) {
260 e.preventDefault();
261 }
262
263 var $this = $(el),
264 $panel = $this.parent().parent(),
265 section = $this.data('section'),
266 $sectionButtons = $panel.find('.wpforms-panel-sidebar-section'),
267 $sectionButton = $panel.find('.wpforms-panel-sidebar-section-'+section);
268
269 if ( $this.hasClass( 'upgrade-modal' ) || $this.hasClass( 'education-modal' ) ) {
270 return;
271 }
272
273 if ( ! $sectionButton.hasClass('active') ) {
274 $builder.trigger('wpformsPanelSectionSwitch', section);
275 $sectionButtons.removeClass('active');
276 $sectionButtons.find('.wpforms-toggle-arrow').removeClass('fa-angle-down').addClass('fa-angle-right');
277 $sectionButton.addClass('active');
278 $sectionButton.find('.wpforms-toggle-arrow').toggleClass('fa-angle-right fa-angle-down');
279 $panel.find('.wpforms-panel-content-section').hide();
280 $panel.find('.wpforms-panel-content-section-'+section).show();
281 }
282 },
283
284 //--------------------------------------------------------------------//
285 // Setup Panel
286 //--------------------------------------------------------------------//
287
288 /**
289 * Element bindings for Setup panel.
290 *
291 * @since 1.0.0
292 */
293 bindUIActionsSetup: function() {
294
295 // Focus on the form title field when displaying setup panel
296 $(window).load(function(e) {
297 app.setupTitleFocus(e, wpf.getQueryString('view'));
298 });
299 $builder.on('wpformsPanelSwitch', app.setupTitleFocus);
300
301 // Select and apply a template
302 $builder.on('click', '.wpforms-template-select', function(e) {
303 app.templateSelect(this, e);
304 });
305
306 // "Blank form" text should trigger template selection
307 $builder.on('click', '.wpforms-trigger-blank', function(e) {
308 e.preventDefault();
309 $('#wpforms-template-blank .wpforms-template-select').trigger('click');
310 });
311
312 // Keep Setup title and settings title instances the same
313 $builder.on('input ', '#wpforms-panel-field-settings-form_title', function() {
314 $('#wpforms-setup-name').val($('#wpforms-panel-field-settings-form_title').val());
315 });
316 $builder.on('input', '#wpforms-setup-name', function() {
317 $('#wpforms-panel-field-settings-form_title').val($('#wpforms-setup-name').val());
318 });
319
320 // Additional template searching
321 $builder.on('keyup', '#wpforms-setup-template-search' , function() {
322 s.templateList.search( $(this).val() );
323 });
324 },
325
326 /**
327 * Force focus on the form title field when the Setup panel is displaying.
328 *
329 * @since 1.0.0
330 */
331 setupTitleFocus: function(e, view) {
332
333 if (typeof view !== 'undefined' && view == 'setup') {
334 setTimeout(function (){
335 $('#wpforms-setup-name').focus();
336 }, 100);
337 }
338 },
339
340 /**
341 * Select template.
342 *
343 * @since 1.0.0
344 */
345 templateSelect: function(el, e) {
346 e.preventDefault();
347
348 var $this = $(el),
349 $parent = $this.parent().parent(),
350 $formName = $('#wpforms-setup-name'),
351 $templateBtns = $('.wpforms-template-select'),
352 formName = '',
353 labelOriginal = $this.html(),
354 template = $this.data('template'),
355 templateName = $this.data('template-name-raw'),
356 title = '',
357 action = '';
358
359 // Don't do anything for selects that trigger modal
360 if ($parent.hasClass('pro-modal')){
361 return;
362 }
363
364 // Disable all template buttons
365 $templateBtns.prop('disabled', true);
366
367 // Display loading indicator
368 $this.html(s.spinner+' '+ wpforms_builder.loading);
369
370 $builder.trigger('wpformsTemplateSelect', template);
371
372 // This is an existing form
373 if (s.formID) {
374
375 $.confirm({
376 title: wpforms_builder.heads_up,
377 content: wpforms_builder.template_confirm,
378 backgroundDismiss: false,
379 closeIcon: false,
380 icon: 'fa fa-exclamation-circle',
381 type: 'orange',
382 buttons: {
383 confirm: {
384 text: wpforms_builder.ok,
385 btnClass: 'btn-confirm',
386 action: function(){
387 // Ajax update form
388 var data = {
389 title : $formName.val(),
390 action : 'wpforms_update_form_template',
391 template: template,
392 form_id : s.formID,
393 nonce : wpforms_builder.nonce
394 }
395 $.post(wpforms_builder.ajax_url, data, function(res) {
396 if (res.success){
397 window.location.href = res.data.redirect;
398 } else {
399 console.log(res);
400 }
401 }).fail(function(xhr, textStatus, e) {
402 console.log(xhr.responseText);
403 });
404 }
405 },
406 cancel: {
407 text: wpforms_builder.cancel,
408 action: function(){
409 $templateBtns.prop('disabled', false);
410 $this.html(labelOriginal);
411 }
412 }
413 }
414 });
415
416 // This is a new form
417 } else {
418
419 // Check that form title is provided
420 if (!$formName.val()) {
421 formName = templateName;
422 } else {
423 formName = $formName.val();
424 }
425
426 // Ajax create new form
427 var data = {
428 title : formName,
429 action : 'wpforms_new_form',
430 template: template,
431 form_id : s.formID,
432 nonce : wpforms_builder.nonce
433 }
434 $.post(wpforms_builder.ajax_url, data, function(res) {
435 if (res.success){
436 window.location.href = res.data.redirect;
437 } else {
438 console.log(res);
439 }
440 }).fail(function(xhr, textStatus, e) {
441 console.log(xhr.responseText);
442 });
443 }
444 },
445
446
447 //--------------------------------------------------------------------//
448 // Fields Panel
449 //--------------------------------------------------------------------//
450
451 /**
452 * Element bindings for Fields panel.
453 *
454 * @since 1.0.0
455 */
456 bindUIActionsFields: function() {
457
458 // Field sidebar tab toggle
459 $builder.on('click', '.wpforms-tab a', function(e) {
460 e.preventDefault();
461 app.fieldTabToggle($(this).parent().attr('id'));
462 });
463
464 // Field sidebar group toggle
465 $builder.on('click', '.wpforms-add-fields-heading', function(e) {
466 e.preventDefault();
467 app.fieldGroupToggle($(this), 'click');
468 });
469
470 // Form field preview clicking
471 $builder.on('click', '.wpforms-field', function(e) {
472 app.fieldTabToggle($(this).data('field-id'));
473 });
474
475 // Field delete
476 $builder.on('click', '.wpforms-field-delete', function(e) {
477 e.preventDefault();
478 e.stopPropagation();
479 app.fieldDelete($(this).parent().data('field-id'));
480 });
481
482 // Field duplicate
483 $builder.on('click', '.wpforms-field-duplicate', function(e) {
484 e.preventDefault();
485 app.fieldDuplicate($(this).parent().data('field-id'));
486 });
487
488 // Field add
489 $builder.on('click', '.wpforms-add-fields-button', function(e) {
490 e.preventDefault();
491 app.fieldAdd($(this).data('field-type'));
492 });
493
494 // New field choices should be sortable
495 $builder.on('wpformsFieldAdd', function(event, id, type) {
496 if (type === 'select' || type === 'radio' || type === 'checkbox' || type === 'payment-multiple' || type === 'payment-checkbox' || type === 'payment-select' ) {
497 app.fieldChoiceSortable(type,'#wpforms-field-option-row-' + id + '-choices ul');
498 }
499 });
500
501 // Field choice add new
502 $builder.on('click', '.wpforms-field-option-row-choices .add', function(e) {
503 app.fieldChoiceAdd(e, $(this));
504 });
505
506 // Field choice delete
507 $builder.on('click', '.wpforms-field-option-row-choices .remove', function(e) {
508 app.fieldChoiceDelete(e, $(this));
509 });
510
511 // Field choices defaults - before change
512 $builder.on('mousedown', '.wpforms-field-option-row-choices input[type=radio]', function(e) {
513 var $this = $(this);
514 if ( $this.is(':checked') ) {
515 $this.attr('data-checked', '1');
516 } else {
517 $this.attr('data-checked', '0');
518 }
519 });
520
521 // Field choices defaults
522 $builder.on('click', '.wpforms-field-option-row-choices input[type=radio]', function(e) {
523 var $this = $(this),
524 list = $this.parent().parent();
525 $this.parent().parent().find('input[type=radio]').not(this).prop('checked',false);
526 if ( $this.attr('data-checked') === '1' ) {
527 $this.prop( 'checked', false );
528 $this.attr('data-checked', '0');
529 }
530 app.fieldChoiceUpdate(list.data('field-type'),list.data('field-id') );
531 });
532
533 // Field choices update preview area
534 $builder.on('change', '.wpforms-field-option-row-choices input[type=checkbox]', function(e) {
535 var list = $(this).parent().parent();
536 app.fieldChoiceUpdate(list.data('field-type'),list.data('field-id') );
537 });
538
539 // Field choices display value toggle
540 $builder.on('change', '.wpforms-field-option-row-show_values input', function(e) {
541 $(this).closest('.wpforms-field-option').find('.wpforms-field-option-row-choices ul').toggleClass('show-values');
542 });
543
544 // Field choices image toggle.
545 $builder.on('change', '.wpforms-field-option-row-choices_images input', function() {
546
547 var $this = $( this ),
548 fieldID = $this.parent().data( 'field-id' )
549 $fieldOptions = $( '#wpforms-field-option-'+fieldID ),
550 checked = $this.is( ':checked' ),
551 type = $( '#wpforms-field-option-'+fieldID ).find( '.wpforms-field-option-hidden-type' ).val();
552
553 $this.parent().find( '.wpforms-alert' ).toggleClass( 'wpforms-hidden' );
554 $fieldOptions.find( '.wpforms-field-option-row-choices ul' ).toggleClass( 'show-images' );
555 $fieldOptions.find( '.wpforms-field-option-row-choices_images_style' ).toggleClass( 'wpforms-hidden' );
556
557 if ( checked ) {
558 $( '#wpforms-field-option-'+fieldID+'-input_columns' ).val( 'inline' ).trigger( 'change' );
559 } else {
560 $( '#wpforms-field-option-'+fieldID+'-input_columns' ).val( '' ).trigger( 'change' );
561 }
562
563 app.fieldChoiceUpdate( type, fieldID );
564 });
565
566 // Field choices image upload add/remove image.
567 $builder.on( 'wpformsImageUploadAdd wpformsImageUploadRemove', function( event, $this, $container ) {
568
569 var $list = $container.closest( '.choices-list' ),
570 fieldID = $list.data( 'field-id' ),
571 type = $list.data( 'field-type' );
572
573 app.fieldChoiceUpdate( type, fieldID );
574 });
575
576 // Field choices image style toggle.
577 $builder.on( 'change', '.wpforms-field-option-row-choices_images_style select', function() {
578
579 var fieldID = $( this ).parent().data( 'field-id' ),
580 type = $( '#wpforms-field-option-'+fieldID ).find( '.wpforms-field-option-hidden-type' ).val();
581
582 app.fieldChoiceUpdate( type, fieldID );
583 });
584
585 // Updates field choices text in almost real time
586 $builder.on('focusout', '.wpforms-field-option-row-choices input.label', function(e) {
587 var list = $(this).parent().parent();
588 app.fieldChoiceUpdate(list.data('field-type'),list.data('field-id'));
589 });
590
591 // Field Choices Bulk Add
592 $builder.on('click', '.toggle-bulk-add-display', function(e) {
593 e.preventDefault();
594 app.fieldChoiceBulkAddToggle(this);
595 });
596 $builder.on('click', '.toggle-bulk-add-presets', function(e) {
597 e.preventDefault();
598 var $presetList = $(this).closest('.bulk-add-display').find('ul');
599 if ( $presetList.css('display') === 'block' ) {
600 $(this).text(wpforms_builder.bulk_add_presets_show);
601 } else {
602 $(this).text(wpforms_builder.bulk_add_presets_hide);
603 }
604 $presetList.slideToggle();
605 });
606 $builder.on('click', '.bulk-add-preset-insert', function(e) {
607 e.preventDefault();
608 var $this = $(this),
609 preset = $this.data('preset'),
610 $container = $this.closest('.bulk-add-display'),
611 $presetList = $container.find('ul'),
612 $presetToggle = $container.find('.toggle-bulk-add-presets'),
613 $textarea = $container.find('textarea');
614 $textarea.val('');
615 $textarea.insertAtCaret(wpforms_preset_choices[preset].choices.join("\n"));
616 $presetToggle.text(wpforms_builder.bulk_add_presets_show);
617 $presetList.slideUp();
618 });
619 $builder.on('click', '.bulk-add-insert', function(e) {
620 e.preventDefault();
621 app.fieldChoiceBulkAddInsert(this);
622 });
623
624 // Field Options group toggle
625 $builder.on('click', '.wpforms-field-option-group-toggle', function(e) {
626 e.preventDefault();
627 var $this = $(this);
628 $this.parent().toggleClass('wpforms-hide').find('.wpforms-field-option-group-inner').slideToggle();
629 $this.find('i').toggleClass('fa-angle-down fa-angle-right');
630 });
631
632 // Display toggle for Address field hide address line 2 option
633 $builder.on('change', '.wpforms-field-option-address input.hide', function(e) {
634 var $this = $(this),
635 id = $this.parent().parent().data('field-id'),
636 subfield = $this.parent().parent().data('subfield');
637 $('#wpforms-field-'+id).find('.wpforms-'+subfield).toggleClass('wpforms-hide');
638 });
639
640 // Real-time updates for "Show Label" field option
641 $builder.on('input', '.wpforms-field-option-row-label input', function(e) {
642 var $this = $(this),
643 value = $this.val(),
644 id = $this.parent().data('field-id');
645 $('#wpforms-field-'+id).find('.label-title .text').text(value);
646 });
647
648 // Real-time updates for "Description" field option
649 $builder.on( 'input', '.wpforms-field-option-row-description textarea', function() {
650 var $this = $( this ),
651 value = $this.val(),
652 id = $this.parent().data( 'field-id' ),
653 $desc = $( '#wpforms-field-'+id ).find( '.description' );
654
655 if ( $desc.hasClass( 'nl2br' ) ) {
656 $desc.html( value.replace( /\n/g, '<br>') );
657 } else {
658 $desc.html( value );
659 }
660 });
661
662 // Real-time updates for "Required" field option
663 $builder.on('change', '.wpforms-field-option-row-required input', function(e) {
664 var id = $(this).parent().data('field-id');
665 $('#wpforms-field-'+id).toggleClass('required');
666 });
667
668 // Real-time updates for "Confirmation" field option
669 $builder.on('change', '.wpforms-field-option-row-confirmation input', function(e) {
670 var id = $(this).parent().data('field-id');
671 $('#wpforms-field-'+id).find('.wpforms-confirm').toggleClass('wpforms-confirm-enabled wpforms-confirm-disabled');
672 $('#wpforms-field-option-'+id).toggleClass('wpforms-confirm-enabled wpforms-confirm-disabled');
673 });
674
675 // Real-time updates for "Size" field option
676 $builder.on('change', '.wpforms-field-option-row-size select', function(e) {
677 var $this = $(this),
678 value = $this.val(),
679 id = $this.parent().data('field-id');
680 $('#wpforms-field-'+id).removeClass('size-small size-medium size-large').addClass('size-'+value);
681 });
682
683 // Real-time updates for "Placeholder" field option
684 $builder.on('input', '.wpforms-field-option-row-placeholder input', function(e) {
685 var $this = $(this),
686 value = $this.val(),
687 id = $this.parent().data('field-id'),
688 $primary = $('#wpforms-field-'+id).find('.primary-input');
689
690 if ($primary.is('select')) {
691 if (!value.length) {
692 $primary.find('.placeholder').remove();
693 } else {
694 if ($primary.find('.placeholder').length) {
695 $primary.find('.placeholder').text(value);
696 } else {
697 $primary.prepend('<option class="placeholder" selected>'+value+'</option>')
698 }
699 }
700 } else {
701 $primary.attr('placeholder', value);
702 }
703 });
704
705 // Real-time updates for "Confirmation Placeholder" field option
706 $builder.on('input', '.wpforms-field-option-row-confirmation_placeholder input', function(e) {
707 var $this = $(this),
708 value = $this.val(),
709 id = $this.parent().data('field-id');
710 $('#wpforms-field-'+id).find('.secondary-input').attr('placeholder', value);
711 });
712
713 // Real-time updates for "Hide Label" field option
714 $builder.on('change', '.wpforms-field-option-row-label_hide input', function(e) {
715 var id = $(this).parent().data('field-id');
716 $('#wpforms-field-'+id).toggleClass('label_hide');
717 });
718
719 // Real-time updates for Sub Label visbility field option
720 $builder.on('change', '.wpforms-field-option-row-sublabel_hide input', function(e) {
721 var id = $(this).parent().data('field-id');
722 $('#wpforms-field-'+id).toggleClass('sublabel_hide');
723 });
724
725 // Real-time updates for Date/Time and Name "Format" option
726 $builder.on('change', '.wpforms-field-option-row-format select', function(e) {
727 var $this = $(this),
728 value = $this.val(),
729 id = $this.parent().data('field-id');
730 $('#wpforms-field-'+id).find('.format-selected').removeClass().addClass('format-selected format-selected-'+value);
731 $('#wpforms-field-option-'+id).find('.format-selected').removeClass().addClass('format-selected format-selected-'+value);
732 });
733
734 // Real-time updates specific for Address "Scheme" option
735 $builder.on('change', '.wpforms-field-option-row-scheme select', function(e) {
736 var $this = $(this),
737 value = $this.val(),
738 id = $this.parent().data('field-id'),
739 $field = $('#wpforms-field-'+id);
740
741 $field.find('.wpforms-address-scheme').addClass('wpforms-hide');
742 $field.find('.wpforms-address-scheme-'+value).removeClass('wpforms-hide');
743
744 if ( $field.find('.wpforms-address-scheme-'+value+' .wpforms-country' ).children().length == 0 ) {
745 $('#wpforms-field-option-'+id).find('.wpforms-field-option-row-country').addClass('wpforms-hidden');
746 } else {
747 $('#wpforms-field-option-'+id).find('.wpforms-field-option-row-country').removeClass('wpforms-hidden');
748 }
749 });
750
751 // Real-time updates for Address, Date/Time, and Name "Placeholder" field options
752 $builder.on('input', '.wpforms-field-option .format-selected input.placeholder, .wpforms-field-option-address input.placeholder', function(e) {
753 var $this = $(this),
754 value = $this.val(),
755 id = $this.parent().parent().data('field-id'),
756 subfield = $this.parent().parent().data('subfield');
757 $('#wpforms-field-'+id).find('.wpforms-'+ subfield+' input' ).attr('placeholder', value);
758 });
759
760 // Real-time updates for Date/Time date type
761 $builder.on('change', '.wpforms-field-option-row-date .type select', function(e) {
762 var $this = $(this),
763 value = $this.val(),
764 id = $(this).parent().parent().data('field-id');
765 $('#wpforms-field-'+id).find('.wpforms-date').toggleClass('wpforms-date-type-datepicker wpforms-date-type-dropdown');
766 $('#wpforms-field-option-'+id).toggleClass('wpforms-date-type-datepicker wpforms-date-type-dropdown');
767 });
768
769 // Real-time updates for Date/Time date select format
770 $builder.on('change', '.wpforms-field-option-row-date .format select', function(e) {
771 var $this = $(this),
772 value = $this.val(),
773 id = $(this).parent().parent().data('field-id');
774 if ( value === 'm/d/Y' ) {
775 $('#wpforms-field-'+id).find('.wpforms-date-dropdown .first option').text(wpforms_builder.date_select_month);
776 $('#wpforms-field-'+id).find('.wpforms-date-dropdown .second option').text(wpforms_builder.date_select_day);
777 } else if ( value == 'd/m/Y' ) {
778 $('#wpforms-field-'+id).find('.wpforms-date-dropdown .first option').text(wpforms_builder.date_select_day);
779 $('#wpforms-field-'+id).find('.wpforms-date-dropdown .second option').text(wpforms_builder.date_select_month);
780 }
781 });
782
783 // Consider the field active when a disabled nav button is clicked
784 $builder.on('click', '.wpforms-pagebreak-button', function(e) {
785 e.preventDefault();
786 $(this).closest('.wpforms-field').trigger('click');
787 });
788
789 // Real-time updates for "Next" and "Prev" pagebreak field option
790 $builder.on('input', '.wpforms-field-option-row-next input', function(e) {
791 var $this = $(this),
792 value = $this.val(),
793 id = $this.parent().data('field-id');
794 if (value) {
795 $('#wpforms-field-'+id).find('.wpforms-pagebreak-next').css('display','inline-block').text(value);
796 } else {
797 $('#wpforms-field-'+id).find('.wpforms-pagebreak-next').css('display','none').empty();
798 }
799 });
800 $builder.on('input', '.wpforms-field-option-row-prev input', function(e) {
801 var $this = $(this),
802 value = $this.val(),
803 id = $this.parent().data('field-id');
804 if (value) {
805 $('#wpforms-field-'+id).find('.wpforms-pagebreak-prev').css('display','inline-block').text(value);
806 } else {
807 $('#wpforms-field-'+id).find('.wpforms-pagebreak-prev').css('display','none').empty();
808 }
809 });
810
811 // Real-time updates for "Page Title" pagebreak field option
812 $builder.on('input', '.wpforms-field-option-row-title input', function(e) {
813 var $this = $(this),
814 value = $this.val(),
815 id = $this.parent().data('field-id');
816 if (value) {
817 $('#wpforms-field-'+id).find('.wpforms-pagebreak-title').text('('+value+')');
818 } else {
819 $('#wpforms-field-'+id).find('.wpforms-pagebreak-title').empty();
820 }
821 });
822
823 // Real-time updates for "Page Navigation Alignment" pagebreak field option
824 $builder.on('change', '.wpforms-field-option-row-nav_align select', function(e) {
825 var $this = $(this),
826 value = $this.val();
827 if (!value) {
828 value = 'center';
829 }
830 $('.wpforms-pagebreak-buttons').removeClass('wpforms-pagebreak-buttons-center wpforms-pagebreak-buttons-left wpforms-pagebreak-buttons-right wpforms-pagebreak-buttons-split').addClass('wpforms-pagebreak-buttons-'+value);
831 });
832
833 // Real-time updates for "Display Previous" pagebreak field option
834 $builder.on('change', '.wpforms-field-option-row-prev_toggle input', function(e) {
835 var $this = $(this),
836 $group = $this.closest('.wpforms-field-option-group-inner'),
837 $prev = $group.find('.wpforms-field-option-row-prev'),
838 $prevLabel = $prev.find('input');
839
840 $prev.toggleClass('wpforms-hidden');
841
842 if ( $(this).prop('checked') && !$prevLabel.val() ) {
843 $prevLabel.val(wpforms_builder.previous);
844 } else {
845 $prevLabel.val('');
846 }
847 $prevLabel.trigger('input');
848 });
849
850 // Real-time updates for Single Item field "Item Price" option
851 $builder.on('input', '.wpforms-field-option-row-price input', function(e) {
852 var $this = $(this),
853 value = $this.val(),
854 id = $this.parent().data('field-id'),
855 sanitized = wpf.amountSanitize(value),
856 formatted = wpf.amountFormat(sanitized),
857 singleItem;
858 if ( wpforms_builder.currency_symbol_pos == 'right' ) {
859 singleItem = formatted+' '+wpforms_builder.currency_symbol;
860 } else {
861 singleItem = wpforms_builder.currency_symbol+' '+formatted;
862 }
863 $('#wpforms-field-'+id).find('.primary-input').val(formatted);
864 $('#wpforms-field-'+id).find('.price').text(singleItem);
865 });
866
867 // Real-time updates for payment CC icons
868 $builder.on('change', '.wpforms-field-option-credit-card .payment-icons input', function(e) {
869 var $this = $(this),
870 card = $this.data('card')
871 id = $this.parent().data('field-id');
872 $('#wpforms-field-'+id).find('img.icon-'+card).toggleClass('card_hide');
873 });
874
875 // Generic updates for various additional placeholder fields
876 $builder.on('input', '.wpforms-field-option input.placeholder-update', function(e) {
877 var $this = $(this),
878 value = $this.val(),
879 id = $this.data('field-id'),
880 subfield = $this.data('subfield');
881 $('#wpforms-field-'+id).find('.wpforms-'+ subfield+' input' ).attr('placeholder', value);
882 });
883
884 // Toggle Choice Layout advanced field option.
885 $builder.on( 'change', '.wpforms-field-option-row-input_columns select', function() {
886 var $this = $( this ),
887 value = $this.val(),
888 cls = '',
889 id = $this.parent().data( 'field-id' );
890 if ( value === '2' ) {
891 cls = 'wpforms-list-2-columns';
892 } else if ( value === '3' ) {
893 cls = 'wpforms-list-3-columns';
894 } else if ( value === 'inline' ) {
895 cls = 'wpforms-list-inline';
896 }
897 $( '#wpforms-field-' + id ).removeClass( 'wpforms-list-2-columns wpforms-list-3-columns wpforms-list-inline' ).addClass( cls );
898 });
899
900 // Toggle the toggle field
901 $builder.on('click', '.wpforms-field-option-row .wpforms-toggle-icon', function(e) {
902 var $this = $(this),
903 $check = $this.find('input[type=checkbox]'),
904 $label = $this.find('.wpforms-toggle-icon-label');
905
906 $this.toggleClass('wpforms-off wpforms-on');
907 $this.find('i').toggleClass('fa-toggle-off fa-toggle-on');
908
909 if ($this.hasClass('wpforms-on')) {
910 $label.text(wpforms_builder.on);
911 $check.prop('checked', true);
912 } else {
913 $label.text(wpforms_builder.off);
914 $check.prop('checked', false);
915 }
916 $check.trigger('change');
917 });
918
919 // Watch for pagebreak field being added and deleted
920 $builder.on('wpformsFieldAdd', app.fieldPagebreakAdd);
921 $builder.on('wpformsFieldDelete', app.fieldPagebreakDelete);
922
923 // Real-time updates for "Dynamic Choices" field option, for Dropdown,
924 // Checkboxes, and Multiple choice fields
925 $builder.on('change', '.wpforms-field-option-row-dynamic_choices select', function(e) {
926 app.fieldDynamicChoiceToggle($(this));
927 });
928
929 // Real-time updates for "Dynamic [type] Source" field option, for Dropdown,
930 // Checkboxes, and Multiple choice fields
931 $builder.on('change', '.wpforms-field-option-row-dynamic_taxonomy select, .wpforms-field-option-row-dynamic_post_type select', function(e) {
932 app.fieldDynamicChoiceSource($(this));
933 });
934
935 // Toggle Layout selector
936 $builder.on('click', '.toggle-layout-selector-display', function(e) {
937 e.preventDefault();
938 app.fieldLayoutSelectorToggle(this);
939 });
940 $builder.on('click', '.layout-selector-display-layout', function(e) {
941 e.preventDefault();
942 app.fieldLayoutSelectorLayout(this);
943 });
944 $builder.on('click', '.layout-selector-display-columns span', function(e) {
945 e.preventDefault();
946 app.fieldLayoutSelectorInsert(this);
947 });
948
949 // Real-time updates for Rating field scale option.
950 $( document ).on( 'change', '.wpforms-field-option-row-scale select', function() {
951
952 var $this = $( this ),
953 value = $this.val(),
954 id = $this.parent().data( 'field-id' ),
955 $icons = $( '#wpforms-field-'+id +' .rating-icon' ),
956 x = 1;
957
958 $icons.each( function( index ) {;
959 if ( x <= value ) {
960 $( this ).show();
961 } else {
962 $( this ).hide();
963 }
964 x++;
965 });
966 });
967
968 // Real-time updates for Rating field icon option.
969 $( document ).on( 'change', '.wpforms-field-option-row-icon select', function() {
970
971 var $this = $( this ),
972 value = $this.val(),
973 id = $this.parent().data( 'field-id' ),
974 $icons = $( '#wpforms-field-'+id +' .rating-icon' ),
975 iconClass = 'fa-star';
976
977 if ( 'heart' === value ) {
978 iconClass = 'fa-heart';
979 } else if ( 'thumb' === value ) {
980 iconClass = 'fa-thumbs-up';
981 } else if ( 'smiley' === value ) {
982 iconClass = 'fa-smile-o';
983 }
984
985 $icons.removeClass( 'fa-star fa-heart fa-thumbs-up fa-smile-o' ).addClass( iconClass );
986 });
987
988 // Real-time updates for Rating field icon size option.
989 $( document ).on( 'change', '.wpforms-field-option-row-icon_size select', function() {
990
991 var $this = $( this ),
992 value = $this.val(),
993 id = $this.parent().data( 'field-id' ),
994 $icons = $( '#wpforms-field-'+id +' .rating-icon' );
995 fontSize = '28';
996
997 if ( 'small' === value ) {
998 fontSize = '18';
999 } else if ( 'large' === value ) {
1000 fontSize = '38';
1001 }
1002
1003 $icons.css( 'font-size', fontSize + 'px' );
1004 });
1005
1006 // Real-time updates for Rating field icon color option.
1007 $( document ).on( 'input', '.wpforms-field-option-row-icon_color input.wpforms-color-picker', function() {
1008
1009 var $this = $( this ),
1010 value = $this.val(),
1011 id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ),
1012 $icons = $( '#wpforms-field-'+id +' i.fa' );
1013
1014 $icons.css( 'color', value );
1015 });
1016
1017 // Real-time updates for Checkbox field Disclaimer option.
1018 $( document ).on( 'change', '.wpforms-field-option-row-disclaimer_format input', function() {
1019
1020 var $this = $( this ),
1021 id = $this.parent().data( 'field-id' ),
1022 $desc = $( '#wpforms-field-'+id +' .description' );
1023
1024 $desc.toggleClass( 'disclaimer' );
1025 });
1026 },
1027
1028 /**
1029 * Toggle field group visibility in the field sidebar.
1030 *
1031 * @since 1.0.0
1032 */
1033 fieldGroupToggle: function(el, action) {
1034
1035 if ( 'click' === action ) {
1036
1037 var $this = $(el),
1038 $buttons = $this.next('.wpforms-add-fields-buttons'),
1039 $group = $buttons.parent(),
1040 $icon = $this.find('i'),
1041 groupName = $this.data('group'),
1042 cookieName = 'wpforms_field_group_'+groupName;
1043
1044 if ($group.hasClass('wpforms-hide')) {
1045 wpCookies.remove(cookieName);
1046 } else {
1047 wpCookies.set(cookieName,'true',2592000); // 1 month
1048 }
1049 $icon.toggleClass('fa-angle-down fa-angle-right');
1050 $buttons.slideToggle();
1051 $group.toggleClass('wpforms-hide');
1052
1053 } else if ( 'load' === action ) {
1054
1055 var $this = $(el),
1056 $buttons = $this.find('.wpforms-add-fields-buttons'),
1057 $icon = $this.find('.wpforms-add-fields-heading i'),
1058 groupName = $this.find('.wpforms-add-fields-heading').data('group'),
1059 cookieName = 'wpforms_field_group_'+groupName;
1060
1061 if (wpCookies.get(cookieName) == 'true') {
1062 $icon.toggleClass('fa-angle-down fa-angle-right');
1063 $buttons.hide();
1064 $this.toggleClass('wpforms-hide');
1065 }
1066 }
1067 },
1068
1069 /**
1070 * Delete field
1071 *
1072 * @since 1.0.0
1073 */
1074 fieldDelete: function(id) {
1075
1076 var $field = $('#wpforms-field-'+id),
1077 type = $field.data('field-type');
1078
1079 if ($field.hasClass('no-delete')) {
1080 $.alert({
1081 title: wpforms_builder.field_locked,
1082 content: wpforms_builder.field_locked_msg,
1083 icon: 'fa fa-info-circle',
1084 type: 'blue',
1085 buttons: {
1086 confirm: {
1087 text: wpforms_builder.close,
1088 btnClass: 'btn-confirm',
1089 keys: ['enter']
1090 }
1091 }
1092 });
1093 } else {
1094 $.confirm({
1095 title: false,
1096 content: wpforms_builder.delete_confirm,
1097 backgroundDismiss: false,
1098 closeIcon: false,
1099 icon: 'fa fa-exclamation-circle',
1100 type: 'orange',
1101 buttons: {
1102 confirm: {
1103 text: wpforms_builder.ok,
1104 btnClass: 'btn-confirm',
1105 keys: ['enter'],
1106 action: function(){
1107 $('#wpforms-field-'+id).fadeOut(400, function(){
1108 $(this).remove();
1109 $('#wpforms-field-option-'+id).remove();
1110 $('.wpforms-field, .wpforms-title-desc').removeClass('active');
1111 app.fieldTabToggle('add-fields');
1112 if ( $('.wpforms-field').length < 1 ) {
1113 $( '#wpforms-builder-form .no-fields, #wpforms-builder-form .no-fields-preview' ).show();
1114 }
1115 $builder.trigger('wpformsFieldDelete', [id, type ]);
1116 });
1117 }
1118 },
1119 cancel: {
1120 text: wpforms_builder.cancel
1121 }
1122 }
1123 });
1124 }
1125 },
1126
1127 /**
1128 * Duplicate field
1129 *
1130 * @since 1.2.9
1131 */
1132 fieldDuplicate: function(id) {
1133
1134 var $field = $('#wpforms-field-'+id),
1135 type = $field.data('field-type');
1136
1137 if ($field.hasClass('no-duplicate')) {
1138 $.alert({
1139 title: wpforms_builder.field_locked,
1140 content: wpforms_builder.field_locked_msg,
1141 icon: 'fa fa-info-circle',
1142 type: 'blue',
1143 buttons : {
1144 confirm : {
1145 text: wpforms_builder.close,
1146 btnClass: 'btn-confirm',
1147 keys: ['enter']
1148 }
1149 }
1150 });
1151 } else {
1152 $.confirm({
1153 title: false,
1154 content: wpforms_builder.duplicate_confirm,
1155 backgroundDismiss: false,
1156 closeIcon: false,
1157 icon: 'fa fa-exclamation-circle',
1158 type: 'orange',
1159 buttons: {
1160 confirm: {
1161 text: wpforms_builder.ok,
1162 btnClass: 'btn-confirm',
1163 keys: ['enter'],
1164 action: function(){
1165 var $newField = $field.clone(),
1166 $fieldOptions = $('#wpforms-field-option-'+id),
1167 newFieldOptions = $fieldOptions.html(),
1168 newFieldID = $('#wpforms-field-id').val(),
1169 newFieldLabel = $('#wpforms-field-option-'+id+'-label').val()+' '+wpforms_builder.duplicate_copy,
1170 nextID = Number(newFieldID)+1,
1171 regex_fieldOptionsID = new RegExp( 'ID #'+id, "g"),
1172 regex_fieldID = new RegExp( 'fields\\['+id+'\\]', "g"),
1173 regex_dataFieldID = new RegExp( 'data-field-id="'+id+'"', "g"),
1174 regex_referenceID = new RegExp( 'data-reference="'+id+'"', "g"),
1175 regex_elementID = new RegExp( '\\b(id|for)="wpforms-(.*?)'+id+'(.*?)"', "ig");
1176
1177 // Toggle visibility states
1178 $field.after($newField);
1179 $field.removeClass('active');
1180 $newField.addClass('active').attr({
1181 'id' : 'wpforms-field-'+newFieldID,
1182 'data-field-id': newFieldID
1183 });
1184
1185 // Various regex to adjust the field options to work with
1186 // the new field ID
1187 function regex_elementID_replace(match, p1, p2, p3, offset, string) {
1188 return p1+'="wpforms-'+p2+newFieldID+p3+'"';
1189 }
1190 newFieldOptions = newFieldOptions.replace(regex_fieldOptionsID, 'ID #'+newFieldID);
1191 newFieldOptions = newFieldOptions.replace(regex_fieldID, 'fields['+newFieldID+']');
1192 newFieldOptions = newFieldOptions.replace(regex_dataFieldID, 'data-field-id="'+newFieldID+'"');
1193 newFieldOptions = newFieldOptions.replace(regex_referenceID, 'data-reference="'+newFieldID+'"');
1194 newFieldOptions = newFieldOptions.replace(regex_elementID, regex_elementID_replace);
1195
1196 // Add new field options panel
1197 $fieldOptions.hide().after('<div class="wpforms-field-option wpforms-field-option-'+type+'" id="wpforms-field-option-'+newFieldID+'" data-field-id="'+newFieldID+'">'+newFieldOptions+'</div>');
1198 var $newFieldOptions = $('#wpforms-field-option-'+newFieldID);
1199
1200 // Copy over values
1201 $fieldOptions.find(':input').each(function(index, el) {
1202
1203 var $this = $(this),
1204 name = $this.attr('name');
1205
1206 if ( ! name ) {
1207 return 'continue';
1208 }
1209
1210 var newName = name.replace(regex_fieldID, 'fields['+newFieldID+']'),
1211 type = $this.attr('type');
1212
1213 if ( type === 'checkbox' || type === 'radio' ) {
1214 if ($this.is(':checked')){
1215 $newFieldOptions.find('[name="'+newName+'"]').prop('checked', true).attr('checked','checked');
1216 } else {
1217 $newFieldOptions.find('[name="'+newName+'"]').prop('checked', false).attr('checked',false);
1218 }
1219 } else if ($this.is('select')) {
1220 if ($this.find('option:selected').length) {
1221 var optionVal = $this.find('option:selected').val();
1222 $newFieldOptions.find('[name="'+newName+'"]').find('[value="'+optionVal+'"]').prop('selected',true);
1223 }
1224 } else {
1225 if ($this.val() !== '') {
1226 $newFieldOptions.find('[name="'+newName+'"]').val( $this.val() );
1227 } else if ( $this.hasClass( 'wpforms-money-input' ) ) {
1228 $newFieldOptions.find('[name="'+newName+'"]').val( '0.00' );
1229 }
1230 }
1231 });
1232
1233 // ID adjustments
1234 $('#wpforms-field-option-'+newFieldID).find('.wpforms-field-option-hidden-id').val(newFieldID);
1235 $('#wpforms-field-id').val(nextID);
1236
1237 // Adjust label to indicate this is a copy
1238 $('#wpforms-field-option-'+newFieldID+'-label').val(newFieldLabel);
1239 $newField.find('.label-title .text').text(newFieldLabel);
1240
1241 // Fire field add custom event
1242 $builder.trigger('wpformsFieldAdd', [newFieldID, type]);
1243
1244 // Lastly, update the next ID stored in database
1245 $.post(wpforms_builder.ajax_url, {form_id : s.formID, nonce : wpforms_builder.nonce, action : 'wpforms_builder_increase_next_field_id'});
1246 }
1247 },
1248 cancel: {
1249 text: wpforms_builder.cancel
1250 }
1251 }
1252 });
1253 }
1254 },
1255
1256 /**
1257 * Add new field.
1258 *
1259 * @since 1.0.0
1260 */
1261 fieldAdd: function(type, options) {
1262
1263 var $btn = $( '#wpforms-add-fields-' + type );
1264
1265 if ( $btn.hasClass( 'upgrade-modal' ) || $btn.hasClass( 'education-modal' ) ) {
1266 return;
1267 }
1268
1269 var defaults = {
1270 position : 'bottom',
1271 placeholder: false,
1272 scroll : true,
1273 defaults : false
1274 };
1275 options = $.extend( {}, defaults, options);
1276
1277 var data = {
1278 action : 'wpforms_new_field_'+type,
1279 id : s.formID,
1280 type : type,
1281 defaults: options.defaults,
1282 nonce : wpforms_builder.nonce
1283 };
1284
1285 return $.post(wpforms_builder.ajax_url, data, function(res) {
1286 if (res.success) {
1287
1288 var totalFields = $('.wpforms-field').length,
1289 $preview = $('#wpforms-panel-fields .wpforms-panel-content-wrap'),
1290 $lastField = $('.wpforms-field').last(),
1291 $newField = $(res.data.preview),
1292 $newOptions = $(res.data.options);
1293
1294 $newField.css('display', 'none');
1295
1296 if (options.placeholder) {
1297 options.placeholder.remove();
1298 }
1299
1300 // Determine where field gets placed
1301 if ( 'bottom' === options.position ) {
1302
1303 if ( $lastField.length && $lastField.hasClass('wpforms-field-stick')) {
1304 // Check to see if the last field we have is configured to
1305 // be stuck to the bottom, if so add the field above it.
1306 $('.wpforms-field-wrap').children(':eq('+(totalFields-1)+')').before($newField);
1307 $('.wpforms-field-options').children(':eq('+(totalFields-1)+')').before($newOptions);
1308
1309 } else {
1310 // Add field to bottom
1311 $('.wpforms-field-wrap').append($newField);
1312 $('.wpforms-field-options').append($newOptions);
1313 }
1314
1315 if (options.scroll) {
1316 $preview.animate({ scrollTop: $preview.prop('scrollHeight') - $preview.height() }, 1000);
1317 }
1318
1319 } else if ( 'top' === options.position ) {
1320
1321 // Add field to top, scroll to
1322 $('.wpforms-field-wrap').prepend($newField);
1323 $('.wpforms-field-options').prepend($newOptions);
1324
1325 if (options.scroll) {
1326 $preview.animate({ scrollTop: 0 }, 1000);
1327 }
1328
1329 } else {
1330
1331 if ( options.position === totalFields && $lastField.length && $lastField.hasClass('wpforms-field-stick') ) {
1332 // Check to see if the user tried to add the field at
1333 // the end BUT the last field we have is configured to
1334 // be stuck to the bottom, if so add the field above it.
1335 $('.wpforms-field-wrap').children(':eq('+(totalFields-1)+')').before($newField);
1336 $('.wpforms-field-options').children(':eq('+(totalFields-1)+')').before($newOptions);
1337
1338 } else if ($('.wpforms-field-wrap').children(':eq('+options.position+')').length) {
1339 // Add field to a specific location
1340 $('.wpforms-field-wrap').children(':eq('+options.position+')').before($newField);
1341 $('.wpforms-field-options').children(':eq('+options.position+')').before($newOptions);
1342
1343 } else {
1344 // Something's wrong, just add the field. This should never occur.
1345 $('.wpforms-field-wrap').append($newField);
1346 $('.wpforms-field-options').append($newOptions);
1347 }
1348 }
1349
1350 $newField.fadeIn();
1351
1352 $('#wpforms-builder-form .no-fields, #wpforms-builder-form .no-fields-preview').hide();
1353 $('#wpforms-field-id').val(res.data.field.id+1);
1354
1355 wpf.initTooltips();
1356 app.loadColorPickers();
1357
1358 $builder.trigger('wpformsFieldAdd', [res.data.field.id, type ]);
1359
1360 } else {
1361 console.log(res);
1362 }
1363 }).fail(function(xhr, textStatus, e) {
1364 console.log(xhr.responseText);
1365 });
1366 },
1367
1368 /**
1369 * Sortable fields in the builder form preview area.
1370 *
1371 * @since 1.0.0
1372 */
1373 fieldSortable: function() {
1374
1375 var fieldOptions = $('.wpforms-field-options'),
1376 fieldReceived = false,
1377 fieldIndex,
1378 fieldIndexNew,
1379 field,
1380 fieldNew;
1381
1382 $('.wpforms-field-wrap').sortable({
1383 items : '> .wpforms-field:not(.wpforms-field-stick)',
1384 axis : 'y',
1385 delay : 100,
1386 opacity: 0.75,
1387 start:function(e,ui){
1388 fieldIndex = ui.item.index();
1389 field = fieldOptions[0].children[fieldIndex];
1390 },
1391 stop:function(e,ui){
1392 fieldIndexNew = ui.item.index();
1393 fieldNew = fieldOptions[0].children[fieldIndexNew];
1394 if (fieldIndex < fieldIndexNew){
1395 $(fieldNew).after(field);
1396 } else {
1397 $(fieldNew).before(field);
1398 }
1399 $builder.trigger('wpformsFieldMove', ui);
1400 fieldReceived = false;
1401 },
1402 over: function(e, ui){
1403 var $el = ui.item.first();
1404 $el.addClass('wpforms-field-dragging');
1405
1406 if ( $el.hasClass('wpforms-field-drag')){
1407 var width = $('.wpforms-field').first().outerWidth();
1408 $el.addClass('wpforms-field-drag-over').removeClass('wpforms-field-drag-out').css('width', width).css('height', 'auto');
1409 }
1410 },
1411 out: function(e, ui){
1412 var $el = ui.item.first();
1413 $el.removeClass('wpforms-field-dragging');
1414
1415 if ( !fieldReceived ) {
1416 var width = $el.attr('data-original-width');
1417 if ( $el.hasClass('wpforms-field-drag')){
1418 $el.addClass('wpforms-field-drag-out').removeClass('wpforms-field-drag-over').css('width', width).css('left', '').css('top', '');
1419 }
1420 }
1421 $el.css({
1422 'top': '',
1423 'left': '',
1424 'z-index': ''
1425 });
1426 },
1427 receive: function(e, ui) {
1428 fieldReceived = true;
1429
1430 var pos = $(this).data('ui-sortable').currentItem.index(),
1431 $el = ui.helper,
1432 type = $el.attr('data-field-type');
1433
1434 $el.addClass('wpforms-field-drag-over wpforms-field-drag-pending').removeClass('wpforms-field-drag-out').css('width', '100%');
1435 $el.append('<i class="fa fa-cog fa-spin"></i>');
1436
1437 app.fieldAdd(type, {position: pos, placeholder: $el});
1438 }
1439 });
1440
1441 $('.wpforms-add-fields-button').not('.upgrade-modal').draggable({
1442 connectToSortable: '.wpforms-field-wrap',
1443 delay: 200,
1444 helper: function(event) {
1445 var $this = $(this),
1446 width = $this.outerWidth(),
1447 text = $this.html(),
1448 type = $this.data('field-type'),
1449 $el = $('<div class="wpforms-field-drag-out wpforms-field-drag">');
1450 return $el.html(text).css('width',width).attr('data-original-width',width).attr('data-field-type',type);
1451 },
1452 revert: 'invalid',
1453 cancel: false,
1454 scroll: false,
1455 opacity: 0.75,
1456 containment: 'document'
1457 });
1458 },
1459
1460 /**
1461 * Add new field choice
1462 *
1463 * @since 1.0.0
1464 */
1465 fieldChoiceAdd: function( event, el ) {
1466
1467 event.preventDefault();
1468
1469 var $this = $( el ),
1470 $parent = $this.parent(),
1471 checked = $parent.find( 'input.default' ).is( ':checked' ),
1472 fieldID = $this.closest( '.wpforms-field-option-row-choices' ).data( 'field-id' ),
1473 id = $parent.parent().attr( 'data-next-id' ),
1474 type = $parent.parent().data( 'field-type' ),
1475 $choice = $parent.clone().insertAfter( $parent );
1476
1477 $choice.attr( 'data-key', id );
1478 $choice.find( 'input.label' ).val( '' ).attr( 'name', 'fields['+fieldID+'][choices]['+id+'][label]' );
1479 $choice.find( 'input.value' ).val( '' ).attr( 'name', 'fields['+fieldID+'][choices]['+id+'][value]' );
1480 $choice.find( 'input.source' ).val( '' ).attr( 'name', 'fields['+fieldID+'][choices]['+id+'][image]' );
1481 $choice.find( 'input.default').attr( 'name', 'fields['+fieldID+'][choices]['+id+'][default]' ).prop( 'checked', false );
1482 $choice.find( '.preview' ).empty();
1483 $choice.find( '.wpforms-image-upload-add' ).show();
1484 $choice.find( '.wpforms-money-input' ).trigger( 'focusout' );
1485
1486 if ( checked === true ) {
1487 $parent.find( 'input.default' ).prop( 'checked', true );
1488 }
1489 id++;
1490 $parent.parent().attr( 'data-next-id', id );
1491 $builder.trigger( 'wpformsFieldChoiceAdd' );
1492 app.fieldChoiceUpdate( type, fieldID );
1493 },
1494
1495 /**
1496 * Delete field choice
1497 *
1498 * @since 1.0.0
1499 */
1500 fieldChoiceDelete: function(e, el) {
1501
1502 e.preventDefault();
1503
1504 var $this = $(el),
1505 $list = $this.parent().parent(),
1506 total = $list.find('li').length;
1507
1508 if (total == '1') {
1509 $.alert({
1510 title: false,
1511 content: wpforms_builder.error_choice,
1512 icon: 'fa fa-info-circle',
1513 type: 'blue',
1514 buttons: {
1515 confirm: {
1516 text: wpforms_builder.ok,
1517 btnClass: 'btn-confirm',
1518 keys: ['enter']
1519 }
1520 }
1521 });
1522 } else {
1523 $this.parent().remove();
1524 app.fieldChoiceUpdate($list.data('field-type'), $list.data('field-id'));
1525 $builder.trigger('wpformsFieldChoiceDelete');
1526 }
1527 },
1528
1529 /**
1530 * Make field choices sortable.
1531 *
1532 * Currently used for select, radio, and checkboxes field types
1533 *
1534 * @since 1.0.0
1535 */
1536 fieldChoiceSortable: function(type, selector) {
1537
1538 selector = typeof selector !== 'undefined' ? selector : '.wpforms-field-option-'+type+' .wpforms-field-option-row-choices ul';
1539
1540 $(selector).sortable({
1541 items : 'li',
1542 axis : 'y',
1543 delay : 100,
1544 opacity: 0.6,
1545 handle : '.move',
1546 stop:function(e,ui){
1547 var id = ui.item.parent().data('field-id');
1548 app.fieldChoiceUpdate(type, id);
1549 $builder.trigger('wpformsFieldChoiceMove', ui);
1550 },
1551 update:function(e,ui){
1552 }
1553 });
1554 },
1555
1556 /**
1557 * Update field choices in preview area, for the Fields panel.
1558 *
1559 * Currently used for select, radio, and checkboxes field types.
1560 *
1561 * @since 1.0.0
1562 */
1563 fieldChoiceUpdate: function(type, id) {
1564
1565 // Radio, Checkbox, and Payment Multiple/Checkbox use _ template.
1566 if ( 'radio' === type || 'checkbox' === type || 'payment-multiple' === type || 'payment-checkbox' === type ) {
1567
1568 var tmpl = wp.template( 'wpforms-field-preview-checkbox-radio-payment-multiple' ),
1569 data = {
1570 settings: wpf.getField( id ),
1571 order: wpf.getChoicesOrder( id ),
1572 type: 'radio'
1573 };
1574
1575 if ( 'checkbox' === type || 'payment-checkbox' === type ) {
1576 data.type = 'checkbox';
1577 }
1578
1579 $( '#wpforms-field-' + id ).find( 'ul.primary-input' ).replaceWith( tmpl( data ) );
1580
1581 return;
1582 }
1583
1584 var new_choice;
1585
1586 // Multiple payment choices are radio buttons.
1587 if ( type === 'payment-multiple') {
1588 type = 'radio';
1589 }
1590 // Checkbox payment choices are checkboxes.
1591 if ( type === 'payment-checkbox') {
1592 type = 'checkbox';
1593 }
1594 // Dropdown payment choices are selects.
1595 if ( type === 'payment-select') {
1596 type = 'select';
1597 }
1598
1599 if (type === 'select') {
1600 $('#wpforms-field-'+id+' .primary-input option' ).not('.placeholder').remove();
1601 new_choice = '<option>{label}</option>';
1602 } else if (type === 'radio' || type === 'checkbox' || type === 'gdpr-checkbox' ) {
1603 type = 'gdpr-checkbox' === type ? 'checkbox' : type;
1604 $('#wpforms-field-'+id+' .primary-input li' ).remove();
1605 new_choice = '<li><input type="'+type+'" disabled>{label}</li>';
1606 }
1607 $('#wpforms-field-option-row-' + id + '-choices .choices-list li').each( function( index ) {
1608 var $this = $(this),
1609 label = $this.find('input.label').val(),
1610 selected = $this.find('input.default').is(':checked'),
1611 choice = $( new_choice.replace('{label}',label) );
1612 $('#wpforms-field-'+id+' .primary-input').append(choice);
1613 if ( selected === true ) {
1614 switch (type) {
1615 case 'select':
1616 choice.prop('selected', 'true');
1617 break;
1618 case 'radio':
1619 case 'checkbox':
1620 choice.find('input').prop('checked', 'true');
1621 break;
1622 }
1623 }
1624 });
1625 },
1626
1627 /**
1628 * Field choice bulk add toggling.
1629 *
1630 * @since 1.3.7
1631 */
1632 fieldChoiceBulkAddToggle: function(el) {
1633
1634 var $this = $(el),
1635 $label = $this.closest('label');
1636
1637 if ( $this.hasClass('bulk-add-showing') ) {
1638
1639 // Import details is showing, so hide/remove it
1640 var $selector = $label.next('.bulk-add-display');
1641 $selector.slideUp(400, function() {
1642 $selector.remove();
1643 });
1644 $this.find('span').text(wpforms_builder.bulk_add_show);
1645 } else {
1646
1647 var importOptions = '<div class="bulk-add-display">';
1648
1649 importOptions += '<p class="heading wpforms-clear">'+wpforms_builder.bulk_add_heading+' <a href="#" class="toggle-bulk-add-presets">'+wpforms_builder.bulk_add_presets_show+'</a></p>';
1650 importOptions += '<ul>';
1651 for(var key in wpforms_preset_choices) {
1652 importOptions += '<li><a href="#" data-preset="'+key+'" class="bulk-add-preset-insert">'+wpforms_preset_choices[key].name+'</a></li>';
1653 }
1654 importOptions += '</ul>';
1655 importOptions += '<textarea placeholder="'+wpforms_builder.bulk_add_placeholder+'"></textarea>';
1656 importOptions += '<button class="bulk-add-insert">'+wpforms_builder.bulk_add_button+'</button>';
1657 importOptions += '</div>';
1658
1659 $label.after(importOptions);
1660 $label.next('.bulk-add-display').slideDown(400, function() {
1661 $(this).find('textarea').focus();
1662 });
1663 $this.find('span').text(wpforms_builder.bulk_add_hide);
1664 }
1665
1666 $this.toggleClass('bulk-add-showing');
1667 },
1668
1669 /**
1670 * Field choice bulk insert the new choices.
1671 *
1672 * @since 1.3.7
1673 */
1674 fieldChoiceBulkAddInsert: function(el) {
1675
1676 var $this = $(el),
1677 $container = $this.closest('.wpforms-field-option-row'),
1678 $textarea = $container.find('textarea'),
1679 $list = $container.find('.choices-list'),
1680 $choice = $list.find('li:first-of-type').clone().wrap('<div>').parent(),
1681 choice = '',
1682 fieldID = $container.data('field-id'),
1683 type = $list.data('field-type'),
1684 nextID = Number( $list.attr('data-next-id') ),
1685 newValues = $textarea.val().split("\n"),
1686 newChoices = '';
1687
1688 $this.prop('disabled', true).html($this.html()+' '+s.spinner);
1689 $choice.find('input.value,input.label').attr('value','');
1690 choice = $choice.html();
1691
1692 for(var key in newValues) {
1693 var value = newValues[key],
1694 newChoice = choice;
1695 value = value.trim();
1696 newChoice = newChoice.replace( /\[choices\]\[(\d+)\]/g ,'[choices]['+nextID+']' );
1697 newChoice = newChoice.replace( /data-key="(\d+)"/g ,'data-key="'+nextID+'"' );
1698 newChoice = newChoice.replace( /value="" class="label"/g ,'value="'+value+'" class="label"' );
1699 newChoices += newChoice;
1700 nextID++;
1701 }
1702 $list.attr('data-next-id', nextID).append(newChoices)
1703
1704 app.fieldChoiceUpdate(type, fieldID);
1705 $builder.trigger('wpformsFieldChoiceAdd');
1706 app.fieldChoiceBulkAddToggle( $container.find('.toggle-bulk-add-display') );
1707 },
1708
1709 /**
1710 * Toggle fields tabs (Add Fields, Field Options.
1711 *
1712 * @since 1.0.0
1713 */
1714 fieldTabToggle: function(id) {
1715
1716 $('.wpforms-tab a').removeClass('active').find('i').removeClass('fa-angle-down').addClass('fa-angle-right');
1717 $('.wpforms-field, .wpforms-title-desc').removeClass('active');
1718
1719 if (id === 'add-fields') {
1720 $('#add-fields').find('a').addClass('active').find('i').addClass('fa-angle-down');
1721 $('.wpforms-field-options').hide();
1722 $('.wpforms-add-fields').show()
1723 } else {
1724 $('#field-options').find('a').addClass('active').find('i').addClass('fa-angle-down');
1725 if (id === 'field-options') {
1726 $('.wpforms-field').first().addClass('active');
1727 id = $('.wpforms-field').first().data('field-id');
1728 } else {
1729 $('#wpforms-field-'+id).addClass('active');
1730 }
1731 $('.wpforms-field-option').hide();
1732 $('#wpforms-field-option-'+id).show();
1733 $('.wpforms-add-fields').hide();
1734 $('.wpforms-field-options').show();
1735 }
1736 },
1737
1738 /**
1739 * Watches fields being added and listens for a pagebreak field.
1740 *
1741 * If a pagebreak field is added, and it's the first one, then we
1742 * automatically add the top and bottom pagebreak elements to the
1743 * builder.
1744 *
1745 * @since 1.2.1
1746 */
1747 fieldPagebreakAdd: function(event, id, type) {
1748
1749 if ( 'pagebreak' !== type )
1750 return;
1751
1752 if ( ! s.pagebreakTop ) {
1753
1754 s.pagebreakTop = true;
1755 var options = {
1756 position: 'top',
1757 scroll: false,
1758 defaults: {
1759 position: 'top',
1760 nav_align: 'left'
1761 }
1762 };
1763 app.fieldAdd('pagebreak', options).done(function(res){
1764 s.pagebreakTop = res.data.field.id;
1765 var $preview = $('#wpforms-field-'+res.data.field.id),
1766 $options = $('#wpforms-field-option-'+res.data.field.id);
1767
1768 $options.find('.wpforms-field-option-group').addClass('wpforms-pagebreak-top');
1769 $preview.addClass('wpforms-field-stick wpforms-pagebreak-top');
1770 });
1771
1772 } else if ( ! s.pagebreakBottom ) {
1773
1774 s.pagebreakBottom = true;
1775 var options = {
1776 position: 'bottom',
1777 scroll: false,
1778 defaults: {
1779 position: 'bottom'
1780 }
1781 };
1782 app.fieldAdd('pagebreak', options).done(function(res){
1783 s.pagebreakBottom = res.data.field.id;
1784 var $preview = $('#wpforms-field-'+res.data.field.id),
1785 $options = $('#wpforms-field-option-'+res.data.field.id);
1786
1787 $options.find('.wpforms-field-option-group').addClass('wpforms-pagebreak-bottom');
1788 $preview.addClass('wpforms-field-stick wpforms-pagebreak-bottom');
1789 });
1790 }
1791 },
1792
1793 /**
1794 * Watches fields being deleted and listens for a pagebreak field.
1795 *
1796 * If a pagebreak field is added, and it's the first one, then we
1797 * automatically add the top and bottom pagebreak elements to the
1798 * builder.
1799 *
1800 * @since 1.2.1
1801 */
1802 fieldPagebreakDelete: function(event, id, type) {
1803
1804 if ( 'pagebreak' !== type )
1805 return;
1806
1807 var pagebreaksRemaining = $('.wpforms-field-pagebreak').not('.wpforms-pagebreak-top, .wpforms-pagebreak-bottom').length;
1808
1809 // All pagebreaks, excluding top/bottom, are gone so we need to
1810 // remove the top and bottom pagebreak
1811 if ( !pagebreaksRemaining ) {
1812 var $top = $('.wpforms-preview-wrap').find('.wpforms-pagebreak-top'),
1813 topID = $top.data('field-id'),
1814 $bottom = $('.wpforms-preview-wrap').find('.wpforms-pagebreak-bottom'),
1815 bottomID = $bottom.data('field-id');
1816
1817 // Remove
1818 $top.remove();
1819 $('#wpforms-field-option-'+topID).remove();
1820 s.pagebreakTop = false;
1821 $bottom.remove();
1822 $('#wpforms-field-option-'+bottomID).remove();
1823 s.pagebreakBottom = false;
1824 }
1825 },
1826
1827 /**
1828 * Field Dynamic Choice toggle.
1829 *
1830 * @since 1.2.8
1831 */
1832 fieldDynamicChoiceToggle: function(el) {
1833
1834 var $this = $(el),
1835 $thisOption = $this.parent(),
1836 value = $this.val(),
1837 id = $thisOption.data('field-id'),
1838 type = $( '#wpforms-field-option-'+id ).find( '.wpforms-field-option-hidden-type' ).val(),
1839 $field = $('#wpforms-field-'+id),
1840 $choices = $('#wpforms-field-option-row-'+id+'-choices'),
1841 $images = $( '#wpforms-field-option-'+id+'-choices_images' );
1842
1843 // Loading
1844 wpf.fieldOptionLoading($thisOption);
1845
1846 // Remove previous dynamic post type or taxonomy source options.
1847 $('#wpforms-field-option-row-'+id+'-dynamic_post_type').remove();
1848 $('#wpforms-field-option-row-'+id+'-dynamic_taxonomy').remove();
1849
1850 if ( '' === value ) {
1851 // "Off" - no dynamic populating.
1852
1853 // Show choice images option.
1854 $images.removeClass( 'wpforms-hidden' );
1855 $( '#wpforms-field-' + id ).find( '.wpforms-alert' ).remove();
1856
1857 if ( 'checkbox' === type || 'radio' === type || 'payment-multiple' === type || 'payment-checkbox' === type ) {
1858
1859 app.fieldChoiceUpdate( type, id );
1860
1861 } else {
1862 // Get original field choices.
1863 var choices = [];
1864 $('#wpforms-field-option-row-'+id+'-choices .label').each(function(index) {
1865 choices.push($(this).val());
1866 });
1867
1868 // Restore field to display original field choices.
1869 if ($field.hasClass('wpforms-field-select')) {
1870
1871 $field.find('select option:first').text(choices[0]);
1872
1873 } else {
1874
1875 var type = 'radio',
1876 $list = $field.find('.primary-input');
1877
1878 if ($field.hasClass('wpforms-field-checkbox')) {
1879 type = 'checkbox';
1880 }
1881
1882 // Remove previous items.
1883 $list.empty();
1884
1885 // Add new items to radio or checkbox field.
1886 for(var key in choices) {
1887 $list.append('<li><input type="'+type+'" disabled> '+choices[key]+'</li>');
1888 }
1889 }
1890 }
1891
1892 // Toggle elements and hide loading indicator
1893 $choices.find('ul').removeClass('wpforms-hidden');
1894 $choices.find('.wpforms-alert').addClass('wpforms-hidden');
1895
1896 wpf.fieldOptionLoading($thisOption, true);
1897
1898 } else {
1899 // Post type or Taxonomy based dynamic populating.
1900
1901 // Hide choice images option, not applicable.
1902 $images.addClass( 'wpforms-hidden' );
1903
1904 var data = {
1905 type : value,
1906 field_id: id,
1907 action : 'wpforms_builder_dynamic_choices',
1908 nonce : wpforms_builder.nonce
1909 };
1910 $.post(wpforms_builder.ajax_url, data, function(res) {
1911 if (res.success){
1912 // New option markup
1913 $thisOption.after(res.data.markup)
1914 } else {
1915 console.log(res);
1916 }
1917 // Hide loading indicator.
1918 wpf.fieldOptionLoading($thisOption, true);
1919
1920 // Re-init tooltips for new field.
1921 wpf.initTooltips();
1922
1923 // Trigger Dynamic source updates.
1924 $('#wpforms-field-option-'+id+'-dynamic_'+value).find('option:first').prop('selected', true);
1925 $('#wpforms-field-option-'+id+'-dynamic_'+value).trigger('change');
1926
1927 }).fail(function(xhr, textStatus, e) {
1928 console.log(xhr.responseText);
1929 });
1930 }
1931 },
1932
1933 /**
1934 * Field Dynamic Choice Source toggle.
1935 *
1936 * @since 1.2.8
1937 */
1938 fieldDynamicChoiceSource: function(el) {
1939
1940 var $this = $(el),
1941 $thisOption = $this.parent(),
1942 value = $this.val(),
1943 id = $thisOption.data('field-id'),
1944 form_id = $('#wpforms-builder-form').data('id'),
1945 $choices = $('#wpforms-field-option-row-'+id+'-choices'),
1946 $field = $('#wpforms-field-'+id),
1947 type = $('#wpforms-field-option-'+id+'-dynamic_choices option:selected').val(),
1948 limit = 20;
1949
1950 // Loading
1951 wpf.fieldOptionLoading($thisOption);
1952
1953 var data = {
1954 type : type,
1955 source : value,
1956 field_id: id,
1957 form_id : form_id,
1958 action : 'wpforms_builder_dynamic_source',
1959 nonce : wpforms_builder.nonce
1960 };
1961 $.post(wpforms_builder.ajax_url, data, function(res) {
1962 if (res.success){
1963
1964 // Update info box and remove old choices
1965 $choices.find('.dynamic-name').text(res.data.source_name);
1966 $choices.find('.dynamic-type').text(res.data.type_name);
1967 $choices.find('ul').addClass('wpforms-hidden');
1968 $choices.find('.wpforms-alert').removeClass('wpforms-hidden');
1969
1970 if ($field.hasClass('wpforms-field-select')) {
1971
1972 // Add new items to select field
1973 $field.find('select option:first').text(res.data.items[0]);
1974 limit = 200;
1975
1976 } else {
1977
1978 var type = 'radio',
1979 $list = $field.find('.primary-input');
1980
1981 if ($field.hasClass('wpforms-field-checkbox')) {
1982 type = 'checkbox';
1983 }
1984
1985 // Remove previous items
1986 $list.empty();
1987
1988 // Add new items to radio or checkbox field
1989 for(var key in res.data.items) {
1990 $list.append('<li><input type="'+type+'" disabled> '+res.data.items[key]+'</li>');
1991 }
1992 }
1993
1994 // If the source has more items than the field type can
1995 // ideally handle alert the user
1996 if (Number(res.data.total) > limit) {
1997 var msg = wpforms_builder.dynamic_choice_limit;
1998 msg = msg.replace('{source}',res.data.source_name);
1999 msg = msg.replace('{type}',res.data.type_name);
2000 msg = msg.replace('{limit}',limit);
2001 msg = msg.replace('{total}',res.data.total);
2002 $.alert({
2003 title: wpforms_builder.heads_up,
2004 content: msg,
2005 icon: 'fa fa-info-circle',
2006 type: 'blue',
2007 buttons: {
2008 confirm: {
2009 text: wpforms_builder.ok,
2010 btnClass: 'btn-confirm',
2011 keys: ['enter']
2012 }
2013 }
2014 });
2015 }
2016 } else {
2017 console.log(res);
2018 }
2019
2020 // Toggle elements and hide loading indicator
2021 $choices.find('ul').addClass('wpforms-hidden');
2022 wpf.fieldOptionLoading($thisOption, true);
2023
2024 }).fail(function(xhr, textStatus, e) {
2025 console.log(xhr.responseText);
2026 });
2027 },
2028
2029 /**
2030 * Field layout selector toggling.
2031 *
2032 * @since 1.3.7
2033 */
2034 fieldLayoutSelectorToggle: function(el) {
2035
2036 var $this = $(el),
2037 $label = $this.closest('label'),
2038 layouts = {
2039 'layout-1' : [
2040 {
2041 'class': 'one-half',
2042 'data' : 'wpforms-one-half wpforms-first'
2043 },
2044 {
2045 'class': 'one-half',
2046 'data' : 'wpforms-one-half'
2047 }
2048 ],
2049 'layout-2' : [
2050 {
2051 'class': 'one-third',
2052 'data' : 'wpforms-one-third wpforms-first'
2053 },
2054 {
2055 'class': 'one-third',
2056 'data' : 'wpforms-one-third'
2057 },
2058 {
2059 'class': 'one-third',
2060 'data' : 'wpforms-one-third'
2061 }
2062 ],
2063 'layout-3' : [
2064 {
2065 'class': 'one-fourth',
2066 'data' : 'wpforms-one-fourth wpforms-first'
2067 },
2068 {
2069 'class': 'one-fourth',
2070 'data' : 'wpforms-one-fourth'
2071 },
2072 {
2073 'class': 'one-fourth',
2074 'data' : 'wpforms-one-fourth'
2075 },
2076 {
2077 'class': 'one-fourth',
2078 'data' : 'wpforms-one-fourth'
2079 }
2080 ],
2081 'layout-4' : [
2082 {
2083 'class': 'one-third',
2084 'data' : 'wpforms-one-third wpforms-first'
2085 },
2086 {
2087 'class': 'two-third',
2088 'data' : 'wpforms-two-thirds'
2089 }
2090 ],
2091 'layout-5' : [
2092 {
2093 'class': 'two-third',
2094 'data' : 'wpforms-two-thirds wpforms-first'
2095 },
2096 {
2097 'class': 'one-third',
2098 'data' : 'wpforms-one-third'
2099 }
2100 ],
2101 'layout-6' : [
2102 {
2103 'class': 'one-fourth',
2104 'data' : 'wpforms-one-fourth wpforms-first'
2105 },
2106 {
2107 'class': 'one-fourth',
2108 'data' : 'wpforms-one-fourth'
2109 },
2110 {
2111 'class': 'two-fourth',
2112 'data' : 'wpforms-two-fourths'
2113 }
2114 ],
2115 'layout-7' : [
2116 {
2117 'class': 'two-fourth',
2118 'data' : 'wpforms-two-fourths wpforms-first'
2119 },
2120 {
2121 'class': 'one-fourth',
2122 'data' : 'wpforms-one-fourth'
2123 },
2124 {
2125 'class': 'one-fourth',
2126 'data' : 'wpforms-one-fourth'
2127 }
2128 ],
2129 'layout-8' : [
2130 {
2131 'class': 'one-fourth',
2132 'data' : 'wpforms-one-fourth wpforms-first'
2133 },
2134 {
2135 'class': 'two-fourth',
2136 'data' : 'wpforms-two-fourths'
2137 },
2138 {
2139 'class': 'one-fourth',
2140 'data' : 'wpforms-one-fourth'
2141 }
2142 ]
2143 };
2144
2145 if ( $this.hasClass('layout-selector-showing') ) {
2146
2147 // Selector is showing, so hide/remove it
2148 var $selector = $label.next('.layout-selector-display');
2149 $selector.slideUp(400, function() {
2150 $selector.remove();
2151 });
2152 $this.find('span').text(wpforms_builder.layout_selector_show);
2153 } else {
2154
2155 // Create selector options
2156 var layoutOptions = '<div class="layout-selector-display">';
2157
2158 layoutOptions += '<p class="heading">'+wpforms_builder.layout_selector_layout+'</p>';
2159 for(var key in layouts) {
2160 var layout = layouts[key];
2161 layoutOptions += '<div class="layout-selector-display-layout">';
2162 for(var key in layout) {
2163 layoutOptions += '<span class="'+layout[key].class+'" data-classes="'+layout[key].data+'"></span>';
2164 }
2165 layoutOptions += '</div>';
2166 }
2167 layoutOptions += '</div>';
2168
2169 $label.after(layoutOptions);
2170 $label.next('.layout-selector-display').slideDown();
2171 $this.find('span').text(wpforms_builder.layout_selector_hide);
2172 }
2173
2174 $this.toggleClass('layout-selector-showing');
2175 },
2176
2177 /**
2178 * Field layout selector, selecting a layout.
2179 *
2180 * @since 1.3.7
2181 */
2182 fieldLayoutSelectorLayout: function(el) {
2183
2184 var $this = $(el),
2185 $label = $this.closest('label');
2186
2187 $this.parent().find('.layout-selector-display-layout').not($this).remove();
2188 $this.parent().find('.heading').text(wpforms_builder.layout_selector_column);
2189 $this.toggleClass('layout-selector-display-layout layout-selector-display-columns')
2190 },
2191
2192 /**
2193 * Field layout selector, insert into class field.
2194 *
2195 * @since 1.3.7
2196 */
2197 fieldLayoutSelectorInsert: function(el) {
2198 var $this = $(el),
2199 $selector = $this.closest('.layout-selector-display'),
2200 $parent = $selector.parent(),
2201 $label = $parent.find('label'),
2202 $input = $parent.find('input[type=text]'),
2203 classes = $this.data('classes');
2204
2205 if ( $input.val() ) {
2206 classes = ' ' + classes;
2207 }
2208
2209 $input.insertAtCaret(classes);
2210
2211 // remove list, all done!
2212 $selector.slideUp(400, function() {
2213 $selector.remove();
2214 });
2215
2216 $label.find('.toggle-layout-selector-display').removeClass('layout-selector-showing');
2217 $label.find('.toggle-layout-selector-display span').text(wpforms_builder.layout_selector_show);
2218 },
2219
2220 //--------------------------------------------------------------------//
2221 // Settings Panel
2222 //--------------------------------------------------------------------//
2223
2224 /**
2225 * Element bindings for Settings panel.
2226 *
2227 * @since 1.0.0
2228 */
2229 bindUIActionsSettings: function() {
2230
2231 // Clicking form title/desc opens Settings panel
2232 $builder.on('click', '.wpforms-title-desc, .wpforms-field-submit-button, .wpforms-center-form-name', function(e) {
2233 e.preventDefault();
2234 app.panelSwitch('settings');
2235 if ( $(this).hasClass( 'wpforms-center-form-name' ) || $(this).hasClass( 'wpforms-title-desc' ) ) {
2236 setTimeout( function() {
2237 $( '#wpforms-panel-field-settings-form_title' ).focus();
2238 }, 300 );
2239 }
2240 });
2241
2242 // Clicking form previous page break button
2243 $builder.on('click', '.wpforms-field-pagebreak-last button', function(e) {
2244 e.preventDefault();
2245 app.panelSwitch('settings');
2246 $('#wpforms-panel-field-settings-pagebreak_prev').focus();
2247 });
2248
2249 // Clicking form last page break button
2250 $builder.on('input', '#wpforms-panel-field-settings-pagebreak_prev', function(){
2251 $('.wpforms-field-pagebreak-last button').text( $(this).val() );
2252 });
2253
2254 // Real-time updates for editing the form title
2255 $builder.on('input', '#wpforms-panel-field-settings-form_title, #wpforms-setup-name', function(){
2256 var title = $(this).val();
2257 if (title.length > 38) {
2258 title = $.trim(title).substring(0, 38).split(" ").slice(0, -1).join(" ") + "..."
2259 }
2260 $('.wpforms-form-name').text( title );
2261 });
2262
2263 // Real-time updates for editing the form description
2264 $builder.on('input', '#wpforms-panel-field-settings-form_desc', function(){
2265 $('.wpforms-form-desc').text( $(this).val() );
2266 });
2267
2268 // Real-time updates for editing the form submit button
2269 $builder.on('input', '#wpforms-panel-field-settings-submit_text', function(){
2270 $('.wpforms-field-submit input[type=submit]').val( $(this).val() );
2271 });
2272
2273 // Toggle form reCAPTCHA setting
2274 $builder.on('change', '#wpforms-panel-field-settings-recaptcha', function() {
2275 app.recaptchaToggle();
2276 });
2277
2278 // Toggle form confirmation setting fields
2279 $builder.on('change', '.wpforms-panel-field-confirmations-type', function() {
2280 app.confirmationFieldsToggle( $(this) );
2281 });
2282
2283 // Toggle form notification setting fields
2284 $builder.on('change', '#wpforms-panel-field-settings-notification_enable', function() {
2285 app.notificationToggle();
2286 });
2287
2288 // Add new settings block
2289 $builder.on('click', '.wpforms-builder-settings-block-add', function(e) {
2290 e.preventDefault();
2291 if ( ! wpforms_builder.pro ) {
2292 return;
2293 }
2294 app.settingsBlockAdd( $(this) );
2295 });
2296
2297 // Edit settings block name
2298 $builder.on('click', '.wpforms-builder-settings-block-edit', function(e) {
2299 e.preventDefault();
2300
2301 var $el = $(this);
2302
2303 if ( $el.parents('.wpforms-builder-settings-block-header').find('.wpforms-builder-settings-block-name').hasClass('editing') ) {
2304 app.settingsBlockNameEditingHide( $el );
2305 } else {
2306 app.settingsBlockNameEditingShow( $el );
2307 }
2308 });
2309
2310 // Update settings block name and close editing interface
2311 $builder.on('blur', '.wpforms-builder-settings-block-name-edit input', function(e) {
2312 // Do not fire if for onBlur user clicked on edit button - it has own event processing.
2313 if ( ! $(e.relatedTarget).hasClass('wpforms-builder-settings-block-edit')) {
2314 app.settingsBlockNameEditingHide( $(this) );
2315 }
2316 });
2317
2318 // Close settings block editing interface with pressed Enter
2319 $builder.on('keypress', '.wpforms-builder-settings-block-name-edit input', function(e) {
2320 // On Enter - hide editing interface.
2321 if (e.keyCode === 13) {
2322 app.settingsBlockNameEditingHide( $(this) );
2323
2324 // We need this preventDefault() to stop jumping to form name editing input.
2325 e.preventDefault();
2326 }
2327 });
2328
2329 // Toggle settings block - slide up or down
2330 $builder.on('click', '.wpforms-builder-settings-block-toggle', function(e) {
2331 e.preventDefault();
2332
2333 app.settingsBlockPanelToggle( $(this) );
2334 });
2335
2336 // Remove settings block
2337 $builder.on('click', '.wpforms-builder-settings-block-delete', function(e) {
2338 e.preventDefault();
2339 app.settingsBlockDelete( $(this) );
2340 });
2341 },
2342
2343 /**
2344 * Toggle displaying the ReCAPTCHA.
2345 *
2346 * @since 1.0.0
2347 */
2348 recaptchaToggle: function() {
2349
2350 var $recaptchaPreview = $( '.wpforms-field-recaptcha' );
2351
2352 if ( $recaptchaPreview.length ) {
2353 if ( $( '#wpforms-panel-field-settings-recaptcha' ).is( ':checked' ) ) {
2354 $recaptchaPreview.show();
2355 } else {
2356 $recaptchaPreview.hide();
2357 }
2358 }
2359 },
2360
2361 /**
2362 * Setup the Confirmation blocks.
2363 *
2364 * @since 1.4.8
2365 */
2366 confirmationsSetup: function() {
2367 // Toggle the setting fields in each confirmation block.
2368 $('.wpforms-panel-field-confirmations-type').each( function () {
2369 app.confirmationFieldsToggle($(this));
2370 });
2371
2372 // Init TinyMCE in each confirmation block.
2373 $('.wpforms-panel-field-confirmations-message').each( function () {
2374 if (typeof tinymce !== 'undefined' && typeof wp.editor !== 'undefined') {
2375 wp.editor.initialize($(this).attr('id'), s.tinymceDefaults);
2376 }
2377 });
2378 },
2379
2380 /**
2381 * Toggle the different form Confirmation setting fields.
2382 *
2383 * @since 1.4.8
2384 */
2385 confirmationFieldsToggle: function($el) {
2386 if ( ! $el.length ) {
2387 return false;
2388 }
2389
2390 var type = $el.val();
2391 var $block = $el.closest('.wpforms-builder-settings-block-content');
2392
2393 $block.find('.wpforms-panel-field')
2394 .not($el.parent())
2395 .not('.wpforms-conditionals-enable-toggle')
2396 .hide();
2397 $block.find('.wpforms-panel-field-confirmations-'+type).closest('.wpforms-panel-field').show();
2398 if (type === 'message') {
2399 $block.find('.wpforms-panel-field-confirmations-message_scroll').parent().show();
2400 }
2401 },
2402
2403 /**
2404 * Toggle the displaying notification settings depending on if the
2405 * notifications are enabled.
2406 *
2407 * @since 1.1.9
2408 */
2409 notificationToggle: function() {
2410 var $notification = $('#wpforms-panel-field-settings-notification_enable');
2411 if ( $notification.find('option:selected').val() === '0'){
2412 $notification.parent().parent().find('.wpforms-builder-settings-block').hide();
2413 } else {
2414 $notification.parent().parent().find('.wpforms-builder-settings-block').show();
2415 }
2416 },
2417
2418 /**
2419 * Add new settings block.
2420 *
2421 * @since 1.4.8
2422 */
2423 settingsBlockAdd: function($el) {
2424
2425 var nextID = Number($el.attr('data-next-id')),
2426 blockType = $el.data('block-type'),
2427 namePrompt = wpforms_builder[blockType + '_prompt'],
2428 nameField = '<input autofocus="" type="text" id="settings-block-name" placeholder="'+wpforms_builder[blockType + '_ph']+'">',
2429 nameError = '<p class="error">'+wpforms_builder[blockType + '_error']+'</p>',
2430 modalContent = namePrompt + nameField + nameError;
2431
2432 var modal = $.confirm({
2433 container: $builder,
2434 title: false,
2435 content: modalContent,
2436 icon: 'fa fa-info-circle',
2437 type: 'blue',
2438 buttons: {
2439 confirm: {
2440 text: wpforms_builder.ok,
2441 btnClass: 'btn-confirm',
2442 keys: ['enter'],
2443 action: function() {
2444 var settingsBlockName = $.trim(this.$content.find('input#settings-block-name').val()),
2445 error = this.$content.find('.error');
2446
2447 if (settingsBlockName === '') {
2448 error.show();
2449 return false;
2450 } else {
2451 var $firstSettingsBlock = $el.closest('.wpforms-panel-content-section').find('.wpforms-builder-settings-block').first(),
2452 $newSettingsBlock = $firstSettingsBlock.clone(),
2453 newSettingsBlock;
2454
2455 $newSettingsBlock.attr('data-block-id', nextID);
2456 $newSettingsBlock.find('.wpforms-builder-settings-block-header span').text(settingsBlockName);
2457 $newSettingsBlock.find('input, textarea, select').each(function(index, el) {
2458 var $this = $(this);
2459 if ($this.attr('name')) {
2460 $this.val('')
2461 .attr('name', $this.attr('name').replace(/\[(\d+)\]/, '['+nextID+']'));
2462 if ($this.is('select')) {
2463 $this.find('option').prop('selected',false).attr('selected',false);
2464 $this.find('option:first').prop('selected',true).attr('selected','selected');
2465 } else if ( $this.attr('type') === 'checkbox') {
2466 $this.prop('checked', false).attr('checked', false).val('1');
2467 } else {
2468 $this.val('').attr('value','');
2469 }
2470 }
2471 });
2472
2473 $newSettingsBlock.find('.wpforms-builder-settings-block-header input').val(settingsBlockName).attr('value',settingsBlockName);
2474
2475 if ( blockType === 'notification' ) {
2476 $newSettingsBlock.find('.email-msg textarea').val('{all_fields}').attr('value', '{all_fields}');
2477 $newSettingsBlock.find('.email-recipient input').val('{admin_email}').attr('value', '{admin_email}');
2478 }
2479
2480 if ( blockType === 'confirmation' ) {
2481 $newSettingsBlock.removeClass('wpforms-confirmation-default');
2482 $newSettingsBlock.find('.wpforms-panel-field-textarea').remove();
2483 if (typeof WPForms !== 'undefined') {
2484 $newSettingsBlock.find('.wpforms-panel-field-confirmations-type-wrap')
2485 .after(WPForms.Admin.Builder.Templates
2486 .get('wpforms-builder-confirmations-message-field')({
2487 id: nextID
2488 })
2489 );
2490 }
2491 }
2492
2493 // Conditional logic, if present
2494 var $conditionalLogic = $newSettingsBlock.find('.wpforms-conditional-block');
2495 if ($conditionalLogic.length && typeof WPForms !== 'undefined') {
2496 $conditionalLogic
2497 .html(WPForms.Admin.Builder.Templates
2498 .get('wpforms-builder-conditional-logic-toggle-field')({
2499 id: nextID,
2500 type: blockType,
2501 actions: JSON.stringify($newSettingsBlock.find('.wpforms-panel-field-conditional_logic-checkbox').data('actions')),
2502 actionDesc: $newSettingsBlock.find('.wpforms-panel-field-conditional_logic-checkbox').data('action-desc')
2503 })
2504 );
2505 }
2506
2507 newSettingsBlock = $newSettingsBlock.wrap('<div>').parent().html();
2508 newSettingsBlock = newSettingsBlock.replace( /\[conditionals\]\[(\d+)\]\[(\d+)\]/g, '[conditionals][0][0]' );
2509
2510 $firstSettingsBlock.before( newSettingsBlock );
2511
2512 if ( blockType === 'confirmation' ) {
2513 app.confirmationFieldsToggle($('.wpforms-panel-field-confirmations-type').first());
2514 }
2515
2516 if (typeof tinymce !== 'undefined' && typeof wp.editor !== 'undefined' && blockType === 'confirmation') {
2517 wp.editor.initialize('wpforms-panel-field-confirmations-message-' + nextID, s.tinymceDefaults);
2518 }
2519
2520 $el.attr('data-next-id', nextID+1);
2521 }
2522 }
2523 },
2524 cancel: {
2525 text: wpforms_builder.cancel
2526 }
2527 }
2528 });
2529
2530 // We need to process this event here, because we need a confirm modal object defined, so we can intrude into it.
2531 // Pressing Enter will click the Ok button.
2532 $builder.on('keypress', '#settings-block-name', function(e) {
2533 if (e.keyCode === 13) {
2534 $(modal.buttons.confirm.el).trigger('click');
2535 }
2536 });
2537 },
2538
2539 /**
2540 * Show settings block editing interface.
2541 *
2542 * @since 1.4.8
2543 */
2544 settingsBlockNameEditingShow: function ($el) {
2545
2546 var header_holder = $el.parents('.wpforms-builder-settings-block-header'),
2547 name_holder = header_holder.find('.wpforms-builder-settings-block-name');
2548
2549 name_holder
2550 .addClass('editing')
2551 .hide();
2552
2553 // Make the editing interface active and in focus
2554 header_holder.find('.wpforms-builder-settings-block-name-edit').addClass('active');
2555 wpf.focusCaretToEnd(header_holder.find('input'));
2556 },
2557
2558 /**
2559 * Update settings block name and hide editing interface.
2560 *
2561 * @since 1.4.8
2562 */
2563 settingsBlockNameEditingHide: function ($el) {
2564
2565 var header_holder = $el.parents('.wpforms-builder-settings-block-header'),
2566 name_holder = header_holder.find('.wpforms-builder-settings-block-name'),
2567 edit_holder = header_holder.find('.wpforms-builder-settings-block-name-edit'),
2568 current_name = edit_holder.find('input').val().trim(),
2569 blockType = $el.closest('.wpforms-builder-settings-block').data('block-type');
2570
2571 // Provide a default value for empty settings block name.
2572 if (! current_name.length) {
2573 current_name = wpforms_builder[blockType + '_def_name'];
2574 }
2575
2576 // This is done for sanitizing.
2577 edit_holder.find('input').val(current_name);
2578 name_holder.text(current_name);
2579
2580 // Editing should be hidden, displaying - active.
2581 name_holder
2582 .removeClass('editing')
2583 .show();
2584 edit_holder.removeClass('active');
2585 },
2586
2587 /**
2588 * Show or hide settings block panel content.
2589 *
2590 * @since 1.4.8
2591 */
2592 settingsBlockPanelToggle: function($el) {
2593
2594 var $settingsBlock = $el.closest('.wpforms-builder-settings-block'),
2595 settingsBlockId = $settingsBlock.data('block-id'),
2596 settingsBlockType = $settingsBlock.data('block-type'),
2597 $content = $settingsBlock.find('.wpforms-builder-settings-block-content'),
2598 is_visible = $content.is(':visible');
2599
2600 $content.slideToggle({
2601 duration: 400,
2602 start: function () {
2603 // Send early to save fast.
2604 // It's animation start, so we should save the state for animation end (reversed).
2605 $.post(wpforms_builder.ajax_url, {
2606 action: 'wpforms_builder_settings_block_state_save',
2607 state: is_visible ? 'closed' : 'opened',
2608 form_id: s.formID,
2609 block_id: settingsBlockId,
2610 block_type: settingsBlockType,
2611 nonce : wpforms_builder.nonce
2612 });
2613 },
2614 always: function() {
2615 if ($content.is(':visible')) {
2616 $el.html('<i class="fa fa-chevron-up"></i>');
2617 } else {
2618 $el.html('<i class="fa fa-chevron-down"></i>');
2619 }
2620 }
2621 });
2622 },
2623
2624 /**
2625 * Delete settings block.
2626 *
2627 * @since 1.4.8
2628 */
2629 settingsBlockDelete: function($el) {
2630
2631 var $current_block = $el.closest('.wpforms-builder-settings-block'),
2632 blockType = $current_block.data('block-type');
2633
2634 $.confirm({
2635 title: false,
2636 content: wpforms_builder[blockType + '_delete'],
2637 icon: 'fa fa-exclamation-circle',
2638 type: 'orange',
2639 buttons: {
2640 confirm: {
2641 text: wpforms_builder.ok,
2642 btnClass: 'btn-confirm',
2643 keys: ['enter'],
2644 action: function () {
2645 var settingsBlock = $el.closest('.wpforms-panel-content-section').find('.wpforms-builder-settings-block');
2646
2647 if ( settingsBlock.length <= 1 ) {
2648 $.alert({
2649 title: false,
2650 content: wpforms_builder[blockType + '_error2'],
2651 icon: 'fa fa-exclamation-circle',
2652 type: 'orange',
2653 buttons: {
2654 confirm: {
2655 text: wpforms_builder.ok,
2656 btnClass: 'btn-confirm',
2657 keys: ['enter']
2658 }
2659 }
2660 });
2661 } else {
2662 var settingsBlockId = $current_block.data('block-id'),
2663 settingsBlockType = $current_block.data('block-type');
2664
2665 $.post( wpforms_builder.ajax_url, {
2666 action : 'wpforms_builder_settings_block_state_remove',
2667 nonce : wpforms_builder.nonce,
2668 block_id : settingsBlockId,
2669 block_type: settingsBlockType,
2670 form_id : s.formID,
2671 } );
2672
2673 $current_block.remove();
2674 }
2675 }
2676 },
2677 cancel: {
2678 text: wpforms_builder.cancel
2679 }
2680 }
2681 });
2682 },
2683
2684 //--------------------------------------------------------------------//
2685 // Save and Exit
2686 //--------------------------------------------------------------------//
2687
2688 /**
2689 * Element bindings for Embed and Save/Exit items.
2690 *
2691 * @since 1.0.0
2692 */
2693 bindUIActionsSaveExit: function() {
2694
2695 // Embed form
2696 $builder.on('click', '#wpforms-embed', function(e) {
2697 e.preventDefault();
2698 var content = wpforms_builder.embed_modal,
2699 video_id = wpforms_builder.is_gutenberg ? 'ccyJMwyI8x0' : 'IxGVz3AjEe0';
2700 content += '<input type=\'text\' value=\'[wpforms id="' + s.formID + '" title="false" description="false"]\' readonly id=\'wpforms-embed-shortcode\'>';
2701 content += wpforms_builder.embed_modal_2;
2702 content += '<br><br><iframe width="600" height="338" src="https://www.youtube-nocookie.com/embed/' + video_id + '?rel=0&amp;showinfo=0" frameborder="0" allowfullscreen></iframe>';
2703 $.alert({
2704 columnClass: 'modal-wide',
2705 title: false,
2706 content: content,
2707 boxWidth: '650px',
2708 buttons: {
2709 confirm: {
2710 text: wpforms_builder.close,
2711 btnClass: 'btn-confirm',
2712 keys: ['enter']
2713 }
2714 }
2715 });
2716 });
2717
2718 // Save form
2719 $builder.on('click', '#wpforms-save', function(e) {
2720 e.preventDefault();
2721 app.formSave(false);
2722 });
2723
2724 // Exit builder
2725 $builder.on('click', '#wpforms-exit', function(e) {
2726 e.preventDefault();
2727 app.formExit();
2728 });
2729 },
2730
2731 /**
2732 * Save form.
2733 *
2734 * @since 1.0.0
2735 */
2736 formSave: function(redirect) {
2737
2738 var $saveBtn = $('#wpforms-save'),
2739 $icon = $saveBtn.find('i'),
2740 $label = $saveBtn.find('span'),
2741 text = $label.text();
2742
2743 if (typeof tinyMCE !== 'undefined') {
2744 tinyMCE.triggerSave();
2745 }
2746
2747 $label.text(wpforms_builder.saving);
2748 $icon.toggleClass('fa-check fa-cog fa-spin');
2749
2750 var data = {
2751 action: 'wpforms_save_form',
2752 data : JSON.stringify($('#wpforms-builder-form').serializeArray()),
2753 id : s.formID,
2754 nonce : wpforms_builder.nonce
2755 };
2756 return $.post(wpforms_builder.ajax_url, data, function(res) {
2757 if (res.success) {
2758 $label.text(text);
2759 $icon.toggleClass('fa-check fa-cog fa-spin');
2760 wpf.savedState = wpf.getFormState( '#wpforms-builder-form');
2761 wpf.initialSave = false;
2762 $builder.trigger('wpformsSaved', res.data);
2763 if (true === redirect ) {
2764 window.location.href = wpforms_builder.exit_url;
2765 }
2766 } else {
2767 console.log(res);
2768 }
2769 }).fail(function(xhr, textStatus, e) {
2770 console.log(xhr.responseText);
2771 });
2772 },
2773
2774 /**
2775 * Exit form builder.
2776 *
2777 * @since 1.0.0
2778 */
2779 formExit: function() {
2780
2781 if ( app.formIsSaved() ) {
2782 window.location.href = wpforms_builder.exit_url;
2783 } else {
2784 $.confirm({
2785 title: false,
2786 content: wpforms_builder.exit_confirm,
2787 icon: 'fa fa-exclamation-circle',
2788 type: 'orange',
2789 backgroundDismiss: false,
2790 closeIcon: false,
2791 buttons: {
2792 confirm: {
2793 text: wpforms_builder.save_exit,
2794 btnClass: 'btn-confirm',
2795 keys: ['enter'],
2796 action: function(){
2797 app.formSave(true);
2798 }
2799 },
2800 cancel: {
2801 text: wpforms_builder.exit,
2802 action: function(){
2803 window.location.href = wpforms_builder.exit_url;
2804 }
2805 }
2806 }
2807 });
2808 }
2809 },
2810
2811 /**
2812 * Check current form state.
2813 *
2814 * @since 1.0.0
2815 */
2816 formIsSaved: function() {
2817
2818 if ( wpf.savedState == wpf.getFormState( '#wpforms-builder-form' ) ) {
2819 return true;
2820 } else {
2821 return false;
2822 }
2823 },
2824
2825 //--------------------------------------------------------------------//
2826 // General / global
2827 //--------------------------------------------------------------------//
2828
2829 /**
2830 * Element bindings for general and global items
2831 *
2832 * @since 1.2.0
2833 */
2834 bindUIActionsGeneral: function() {
2835
2836 // Toggle Smart Tags
2837 $builder.on('click', '.toggle-smart-tag-display', function(e) {
2838 e.preventDefault();
2839 app.smartTagToggle(this);
2840 });
2841
2842 $builder.on('click', '.smart-tags-list-display a', function(e) {
2843 e.preventDefault();
2844 app.smartTagInsert(this);
2845 });
2846
2847 // Field map table, update key source
2848 $builder.on('input', '.wpforms-field-map-table .key-source', function(){
2849 var value = $(this).val(),
2850 $dest = $(this).parent().parent().find('.key-destination'),
2851 name = $dest.data('name');
2852 if (value) {
2853 $dest.attr('name', name.replace('{source}', value.replace(/[^0-9a-zA-Z_-]/gi, '')));
2854 }
2855 });
2856
2857 // Field map table, delete row
2858 $builder.on('click', '.wpforms-field-map-table .remove', function(e) {
2859 e.preventDefault();
2860 app.fieldMapTableDeleteRow(e, $(this));
2861 });
2862
2863 // Field map table, Add row
2864 $builder.on('click', '.wpforms-field-map-table .add', function(e) {
2865 e.preventDefault();
2866 app.fieldMapTableAddRow(e, $(this));
2867 });
2868
2869 // Global select field mapping
2870 $(document).on('wpformsFieldUpdate', app.fieldMapSelect);
2871
2872 // Restrict user money input fields
2873 $builder.on('input', '.wpforms-money-input', function(event) {
2874 var $this = $(this),
2875 amount = $this.val(),
2876 start = $this[0].selectionStart,
2877 end = $this[0].selectionEnd;
2878 $this.val(amount.replace(/[^0-9.,]/g, ''));
2879 $this[0].setSelectionRange(start,end);
2880 });
2881
2882 // Format user money input fields
2883 $builder.on('focusout', '.wpforms-money-input', function(event) {
2884 var $this = $(this),
2885 amount = $this.val(),
2886 sanitized = wpf.amountSanitize(amount),
2887 formatted = wpf.amountFormat(sanitized);
2888 $this.val(formatted);
2889 });
2890
2891 // Don't allow users to enable payments if storing entries has
2892 // been disabled in the General settings.
2893 $builder.on('change', '#wpforms-panel-field-stripe-enable, #wpforms-panel-field-paypal_standard-enable', function(event) {
2894 var $this = $(this);
2895 if ( $this.prop('checked') ) {
2896 var disabled = $('#wpforms-panel-field-settings-disable_entries').prop('checked');
2897 if ( disabled ) {
2898 $.confirm({
2899 title: wpforms_builder.heads_up,
2900 content: wpforms_builder.payments_entries_off,
2901 backgroundDismiss: false,
2902 closeIcon: false,
2903 icon: 'fa fa-exclamation-circle',
2904 type: 'orange',
2905 buttons: {
2906 confirm: {
2907 text: wpforms_builder.ok,
2908 btnClass: 'btn-confirm'
2909 }
2910 }
2911 });
2912 $this.prop('checked',false);
2913 }
2914 }
2915 });
2916
2917 // Upload or add an image.
2918 $builder.on( 'click', '.wpforms-image-upload-add', function( event ) {
2919
2920 event.preventDefault();
2921
2922 var $this = $( this ),
2923 $container = $this.parent(),
2924 mediaModal;
2925
2926 mediaModal = wp.media.frames.wpforms_media_frame = wp.media({
2927 className: 'media-frame wpforms-media-frame',
2928 frame: 'select',
2929 multiple: false,
2930 title: wpforms_builder.upload_image_title,
2931 library: {
2932 type: 'image'
2933 },
2934 button: {
2935 text: wpforms_builder.upload_image_button
2936 }
2937 });
2938
2939 mediaModal.on( 'select', function(){
2940
2941 var media_attachment = mediaModal.state().get( 'selection' ).first().toJSON();
2942
2943 $container.find( '.source' ).val( media_attachment.url );
2944 $container.find( '.preview' ).empty();
2945 $container.find( '.preview' ).prepend( '<a href="#" title="'+wpforms_builder.upload_image_remove+'" class="wpforms-image-upload-remove"><img src="'+media_attachment.url+'"></a>' );
2946
2947 if ( 'hide' === $this.data( 'after-upload' ) ) {
2948 $this.hide();
2949 }
2950
2951 $builder.trigger( 'wpformsImageUploadAdd', [ $this, $container ] );
2952 });
2953
2954 // Now that everything has been set, let's open up the frame.
2955 mediaModal.open();
2956 });
2957
2958 // Remove and uploaded image.
2959 $builder.on( 'click', '.wpforms-image-upload-remove', function( event ) {
2960
2961 event.preventDefault();
2962
2963 var $container = $( this ).parent().parent();
2964
2965 $container.find( '.preview' ).empty();
2966 $container.find( '.wpforms-image-upload-add' ).show();
2967 $container.find( '.source' ).val( '' );
2968
2969 $builder.trigger( 'wpformsImageUploadRemove', [ $( this ), $container ] );
2970 });
2971
2972 // Validate email smart tags in Notifications fields.
2973 $builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-text input', function() {
2974 app.validateEmailSmartTags( $( this ) );
2975 });
2976 $builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-textarea textarea', function() {
2977 app.validateEmailSmartTags( $( this ) );
2978 });
2979 },
2980
2981 /**
2982 * Smart Tag toggling.
2983 *
2984 * @since 1.0.1
2985 */
2986 smartTagToggle: function(el) {
2987
2988 var $this = $( el ),
2989 $label = $this.closest( 'label' );
2990
2991 if ( $this.hasClass( 'smart-tag-showing' ) ) {
2992
2993 // Smart tags are showing, so hide/remove them
2994 var $list = $label.next( '.smart-tags-list-display' );
2995 $list.slideUp( 400, function () {
2996 $list.remove();
2997 } );
2998 $this.find( 'span' ).text( wpforms_builder.smart_tags_show );
2999 }
3000 else {
3001
3002 // Show all fields or narrow to specific field types
3003 var allowed = $this.data( 'fields' ),
3004 type = $this.data( 'type' ),
3005 fields = [];
3006
3007 if ( allowed && allowed.length ) {
3008 fields = wpf.getFields( allowed.split( ',' ), true );
3009 }
3010 else {
3011 fields = wpf.getFields( false, true );
3012 }
3013
3014 // Create smart tags list
3015 var smartTagList = '<ul class="smart-tags-list-display">';
3016
3017 if ( type === 'fields' || type === 'all' ) {
3018 if ( ! fields ) {
3019 smartTagList += '<li class="heading">' + wpforms_builder.fields_unavailable + '</li>';
3020 }
3021 else {
3022 smartTagList += '<li class="heading">' + wpforms_builder.fields_available + '</li>';
3023
3024 for ( var field_key in wpf.orders.fields ) {
3025
3026 var field_id = wpf.orders.fields[field_key];
3027 var label = '';
3028
3029 if ( ! fields[ field_id ] ) {
3030 continue;
3031 }
3032
3033 if ( fields[ field_id ].label ) {
3034 label = wpf.sanitizeString( fields[ field_id ].label );
3035 }
3036 else {
3037 label = wpforms_builder.field + ' #' + fields[ field_id ].id;
3038 }
3039 smartTagList += '<li><a href="#" data-type="field" data-meta=\'' + fields[ field_id ].id + '\'>' + label + '</a></li>';
3040 }
3041 }
3042 }
3043
3044 if ( type === 'other' || type === 'all' ) {
3045 smartTagList += '<li class="heading">' + wpforms_builder.other + '</li>';
3046 for ( var smarttag_key in wpforms_builder.smart_tags ) {
3047 smartTagList += '<li><a href="#" data-type="other" data-meta=\'' + smarttag_key + '\'>' + wpforms_builder.smart_tags[ smarttag_key ] + '</a></li>';
3048 }
3049 }
3050
3051 smartTagList += '</ul>';
3052
3053 $label.after( smartTagList );
3054 $label.next( '.smart-tags-list-display' ).slideDown();
3055 $this.find( 'span' ).text( wpforms_builder.smart_tags_hide );
3056 }
3057
3058 $this.toggleClass( 'smart-tag-showing' );
3059 },
3060
3061 /**
3062 * Smart Tag insert.
3063 *
3064 * @since 1.0.1
3065 */
3066 smartTagInsert: function(el) {
3067
3068 var $this = $(el),
3069 $list = $this.closest('.smart-tags-list-display'),
3070 $parent = $list.parent(),
3071 $label = $parent.find('label'),
3072 $input = $parent.find('input[type=text]'),
3073 meta = $this.data('meta'),
3074 type = $this.data('type');
3075
3076 if ( ! $input.length ) {
3077 $input = $parent.find('textarea');
3078 }
3079
3080 // insert smart tag
3081 if ( type === 'field' ) {
3082 $input.insertAtCaret('{field_id="'+meta+'"}');
3083 } else {
3084 $input.insertAtCaret('{'+meta+'}');
3085 }
3086
3087 // remove list, all done!
3088 $list.slideUp(400, function() {
3089 $list.remove();
3090 });
3091
3092 $label.find('.toggle-smart-tag-display span').text(wpforms_builder.smart_tags_show);
3093 $label.find('.toggle-smart-tag-display').removeClass('smart-tag-showing');
3094 },
3095
3096 /**
3097 * Field map table - Delete row
3098 *
3099 * @since 1.2.0
3100 */
3101 fieldMapTableDeleteRow: function(e, el) {
3102
3103 var $this = $(el),
3104 $row = $this.closest('tr'),
3105 $table = $this.closest('table'),
3106 total = $table.find('tr').length;
3107
3108 if (total > '1') {
3109 $row.remove();
3110 }
3111 },
3112
3113 /**
3114 * Field map table - Add row
3115 *
3116 * @since 1.2.0
3117 */
3118 fieldMapTableAddRow: function(e, el) {
3119
3120 var $this = $(el),
3121 $row = $this.closest('tr'),
3122 $table = $this.closest('tbody'),
3123 choice = $row.clone().insertAfter($row);
3124
3125 choice.find('input').val('');
3126 choice.find('select :selected').prop('selected', false);
3127 choice.find('.key-destination').attr('name','');
3128 },
3129
3130 /**
3131 * Update field mapped select items on form updates.
3132 *
3133 * @since 1.2.0
3134 */
3135 fieldMapSelect: function(e, fields) {
3136
3137 // Apply to all selects with identifier class
3138 $('.wpforms-field-map-select').each(function(index, el) {
3139
3140 var $this = $(this),
3141 selected = $this.find('option:selected').val(),
3142 allowedFields = $this.data('field-map-allowed'),
3143 placeholder = $this.data('field-map-placeholder');
3144
3145 // Check if custom placeholder was provided
3146 if (typeof placeholder === 'undefined' || !placeholder) {
3147 placeholder = wpforms_builder.select_field;
3148 }
3149
3150 // Reset select add placeholder option
3151 $this.empty().append($('<option>', { value: '', text : placeholder }));
3152
3153 // If allowed fields are not defined, bail
3154 if (typeof allowedFields !== 'undefined' && allowedFields) {
3155 allowedFields = allowedFields.split(' ');
3156 } else {
3157 return;
3158 }
3159
3160 // If we have no fields for the form, bail
3161 if ( !fields || $.isEmptyObject(fields) ) {
3162 return;
3163 }
3164
3165 // Loop through the current fields
3166 //for(var field_key in fields) {
3167 for( key in wpf.orders.fields ) {
3168 var field_id = wpf.orders.fields[key],
3169 label = '';
3170
3171 if ( ! fields[field_id] ) {
3172 continue;
3173 }
3174
3175 // Compile the label
3176 if (typeof fields[field_id].label !== 'undefined' && fields[field_id].label.length) {
3177 label = wpf.sanitizeString(fields[field_id].label);
3178 } else {
3179 label = wpforms_builder.field + ' #' + fields[field_id].val;
3180 }
3181
3182 // Add to select if it is a field type allowed
3183 if ($.inArray(fields[field_id].type, allowedFields) >= 0 || $.inArray('all-fields', allowedFields) >= 0) {
3184 $this.append($('<option>', { value: fields[field_id].id, text : label }));
3185 }
3186 }
3187
3188 // Restore previous value if found
3189 if (selected) {
3190 $this.find('option[value="'+selected+'"]').prop('selected',true);
3191 }
3192 });
3193 },
3194
3195 /**
3196 * Validate email smart tags in Notifications fields.
3197 *
3198 * @since 1.4.9
3199 */
3200 validateEmailSmartTags: function( $el ) {
3201 var val = $el.val();
3202 if ( ! val ) {
3203 return;
3204 }
3205 // Turns '{email@domain.com}' into 'email@domain.com'
3206 // Email RegEx inpired by http://emailregex.com
3207 val = val.replace( /{(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))}/g, function ( x ) {
3208 return x.slice( 1, -1 );
3209 } );
3210 $el.val( val );
3211 },
3212
3213 //--------------------------------------------------------------------//
3214 // Other functions
3215 //--------------------------------------------------------------------//
3216
3217 /**
3218 * Trim long form titles.
3219 *
3220 * @since 1.0.0
3221 */
3222 trimFormTitle: function() {
3223
3224 var $title = $('.wpforms-center-form-name');
3225 if ($title.text().length > 38) {
3226 var shortTitle = $.trim($title.text()).substring(0, 38).split(" ").slice(0, -1).join(" ") + "...";
3227 $title.text(shortTitle);
3228 }
3229 },
3230
3231 /**
3232 * Load or refresh color picker.
3233 *
3234 * @since 1.2.1
3235 */
3236 loadColorPickers: function() {
3237 $('.wpforms-color-picker').minicolors();
3238 },
3239
3240 /**
3241 * Secret preview hotkey.
3242 *
3243 * @since 1.2.4
3244 */
3245 builderHotkeys: function() {
3246
3247 var ctrlDown = false;
3248
3249 $(document).keydown(function(e) {
3250 if ( e.keyCode === 17 ) {
3251 ctrlDown = true;
3252 }
3253 else if ( ctrlDown && e.keyCode === 80 ) {
3254 // Open Form Preview tab on Ctrl+p.
3255 window.open( wpforms_builder.preview_url );
3256 ctrlDown = false;
3257 return false;
3258 }
3259 else if ( ctrlDown && e.keyCode === 69 ) {
3260 // Open Entries tab on Ctrl+e.
3261 window.open( wpforms_builder.entries_url );
3262 ctrlDown = false;
3263 return false;
3264 }
3265 else if ( ctrlDown && e.keyCode === 83 ) {
3266 // Trigger the Builder save on Ctrl+s.
3267 $( '#wpforms-save', $builder ).click();
3268 ctrlDown = false;
3269 return false;
3270 }
3271 }).keyup(function(e) {
3272 if (e.keyCode === 17) {
3273 ctrlDown = false;
3274 }
3275 });
3276 },
3277
3278 /**
3279 * Register JS templates for various elements.
3280 *
3281 * @since 1.4.8
3282 */
3283 registerTemplates: function () {
3284 if (typeof WPForms === 'undefined') {
3285 return;
3286 }
3287 WPForms.Admin.Builder.Templates.add([
3288 'wpforms-builder-confirmations-message-field',
3289 'wpforms-builder-conditional-logic-toggle-field'
3290 ]);
3291 }
3292 };
3293
3294 // Provide access to public functions/properties.
3295 return app;
3296
3297 }( document, window, jQuery ) );
3298
3299 WPFormsBuilder.init();
3300